Download losses.py from YMmim/object-detection-scratch: direct link, hf CLI and curl.
- Browser
- Download file 5.9 kB
-
https://huggingface.co/YMmim/object-detection-scratch/resolve/main/losses.py
- Command line
-
hf download hf://YMmim/object-detection-scratch/losses.py
-
curl -L -o losses.py https://huggingface.co/YMmim/object-detection-scratch/resolve/main/losses.py
5.9 kB
| """ | |
| Faster R-CNN ๋ฐ๋ฐ๋ฅ ๊ตฌํ โ [4/5] ์์ค ๊ณ์ฐ | |
| ============================================= | |
| Faster R-CNN์ ๋ ๊ณณ์์ ์์ค์ด ๋ฐ์ํ๋ค. | |
| RPN ์์ค : ์ต์ปค๊ฐ ๊ฐ์ฒด์ธ๊ฐ(๋ถ๋ฅ) + ์ต์ปคโ์ ๋ต ๋ฐ์ค ๋ณด์ (ํ๊ท) | |
| RoI ์์ค : ํ๋ณด์์ญ์ ํด๋์ค(๋ถ๋ฅ) + ํด๋์ค๋ณ ๋ฐ์ค ๋ณด์ (ํ๊ท) | |
| ํต์ฌ์ "ํ๊น ํ ๋น(target assignment)": | |
| - ๊ฐ ์ต์ปค/ํ๋ณด์์ญ์ด ์ด๋ค ์ ๋ต ๋ฐ์ค๋ฅผ ๋ด๋นํ๋์ง IoU๋ก ์ ํ๋ค. | |
| - IoU ๋์ผ๋ฉด positive(๊ฐ์ฒด), ๋ฎ์ผ๋ฉด negative(๋ฐฐ๊ฒฝ), ์ ๋งคํ๋ฉด ๋ฌด์. | |
| - ๊ทธ ๋ค์ positive์๋ง ํ๊ท ์์ค, pos+neg์ ๋ถ๋ฅ ์์ค์ ๊ฑด๋ค. | |
| """ | |
| import torch | |
| import torch.nn.functional as F | |
| from box_utils import box_iou, encode_boxes, decode_boxes, clip_boxes | |
| def _sample(pos_idx, neg_idx, num, pos_frac): | |
| """pos/neg๋ฅผ ์ ํด์ง ๊ฐ์ยท๋น์จ๋ก ๋ฌด์์ ์ํ๋ง(ํด๋์ค ๋ถ๊ท ํ ๋ฐฉ์ง).""" | |
| num_pos = min(int(num * pos_frac), pos_idx.numel()) | |
| num_neg = min(num - num_pos, neg_idx.numel()) | |
| pos = pos_idx[torch.randperm(pos_idx.numel())[:num_pos]] | |
| neg = neg_idx[torch.randperm(neg_idx.numel())[:num_neg]] | |
| return pos, neg | |
| # --------------------------------------------------------------- | |
| # RPN ์์ค | |
| # --------------------------------------------------------------- | |
| def rpn_loss(rpn_logits, rpn_deltas, anchors, gt_boxes, img_hw, | |
| pos_iou=0.7, neg_iou=0.3, num_samples=256, pos_frac=0.5): | |
| device = rpn_logits.device | |
| # ์ด๋ฏธ์ง ๋ฐ์ผ๋ก ๋๊ฐ ์ต์ปค๋ ํ์ต์์ ์ ์ธ | |
| inside = ((anchors[:, 0] >= 0) & (anchors[:, 1] >= 0) & | |
| (anchors[:, 2] <= img_hw[1]) & (anchors[:, 3] <= img_hw[0])) | |
| idx_inside = torch.where(inside)[0] | |
| anc = anchors[idx_inside] | |
| labels = torch.full((anc.shape[0],), -1, dtype=torch.float32, device=device) # -1=๋ฌด์ | |
| if gt_boxes.numel() > 0: | |
| ious = box_iou(anc, gt_boxes) # [A, G] | |
| max_iou, argmax = ious.max(dim=1) # ๊ฐ ์ต์ปค์ ์ต๊ณ IoU ์ ๋ต | |
| labels[max_iou < neg_iou] = 0 # ๋ฐฐ๊ฒฝ | |
| labels[max_iou >= pos_iou] = 1 # ๊ฐ์ฒด | |
| # ๊ฐ ์ ๋ต ๋ฐ์ค์ ๋ํด IoU ์ต๋์ธ ์ต์ปค๋ ๊ฐ์ ๋ก positive | |
| gt_best = ious.argmax(dim=0) | |
| labels[gt_best] = 1 | |
| matched_gt = gt_boxes[argmax] | |
| else: | |
| labels[:] = 0 | |
| matched_gt = torch.zeros_like(anc) | |
| pos = torch.where(labels == 1)[0] | |
| neg = torch.where(labels == 0)[0] | |
| pos, neg = _sample(pos, neg, num_samples, pos_frac) | |
| samp = torch.cat([pos, neg]) | |
| # --- ๋ถ๋ฅ ์์ค (๊ฐ์ฒด/๋ฐฐ๊ฒฝ) --- | |
| logits_inside = rpn_logits[idx_inside] | |
| cls_loss = F.binary_cross_entropy_with_logits( | |
| logits_inside[samp], labels[samp]) | |
| # --- ํ๊ท ์์ค (positive ์ต์ปค๋ง) --- | |
| if pos.numel() > 0: | |
| deltas_inside = rpn_deltas[idx_inside] | |
| reg_targets = encode_boxes(matched_gt[pos], anc[pos]) | |
| reg_loss = F.smooth_l1_loss(deltas_inside[pos], reg_targets, | |
| beta=1.0 / 9.0) | |
| else: | |
| reg_loss = torch.tensor(0.0, device=device) | |
| return cls_loss + reg_loss | |
| # --------------------------------------------------------------- | |
| # RoI ํ๊น ํ ๋น (ํ๋ณด์์ญ โ ํ์ต ์ํ) | |
| # --------------------------------------------------------------- | |
| def assign_roi_targets(proposals, gt_boxes, gt_labels, | |
| pos_iou=0.5, neg_iou_hi=0.5, neg_iou_lo=0.0, | |
| num_samples=128, pos_frac=0.25): | |
| """ํ๋ณด์์ญ์ ํด๋์ค ๋ผ๋ฒจ๊ณผ ํ๊ท ํ๊น์ ๋ถ์ด๊ณ ์ํ๋ง.""" | |
| device = proposals.device | |
| # ์ ๋ต ๋ฐ์ค๋ ํ๋ณด์ ์ถ๊ฐ(ํ์ต ์ด๊ธฐ ์์ ํ) | |
| if gt_boxes.numel() > 0: | |
| proposals = torch.cat([proposals, gt_boxes], dim=0) | |
| if gt_boxes.numel() == 0: | |
| # ์ ๋ต์ด ์์ผ๋ฉด ์ ๋ถ ๋ฐฐ๊ฒฝ | |
| n = min(num_samples, proposals.shape[0]) | |
| sel = proposals[:n] | |
| labels = torch.zeros((n,), dtype=torch.int64, device=device) | |
| reg_t = torch.zeros((n, 4), device=device) | |
| return sel, labels, reg_t, torch.zeros((n,), dtype=torch.bool, device=device) | |
| ious = box_iou(proposals, gt_boxes) | |
| max_iou, argmax = ious.max(dim=1) | |
| gt_for_prop = gt_labels[argmax] | |
| matched_gt = gt_boxes[argmax] | |
| labels = torch.zeros_like(gt_for_prop) # 0=๋ฐฐ๊ฒฝ ๊ธฐ๋ณธ | |
| pos_mask = max_iou >= pos_iou | |
| labels[pos_mask] = gt_for_prop[pos_mask] # ๊ฐ์ฒด ํด๋์ค ๋ถ์ฌ | |
| pos = torch.where(pos_mask)[0] | |
| neg = torch.where((max_iou < neg_iou_hi) & (max_iou >= neg_iou_lo))[0] | |
| pos, neg = _sample(pos, neg, num_samples, pos_frac) | |
| samp = torch.cat([pos, neg]) | |
| sel_prop = proposals[samp] | |
| sel_labels = labels[samp] | |
| reg_targets = encode_boxes(matched_gt[samp], sel_prop) | |
| is_pos = torch.zeros((samp.numel(),), dtype=torch.bool, device=device) | |
| is_pos[:pos.numel()] = True | |
| return sel_prop, sel_labels, reg_targets, is_pos | |
| # --------------------------------------------------------------- | |
| # RoI ์์ค | |
| # --------------------------------------------------------------- | |
| def roi_loss(head, feat, stride, proposals, gt_boxes, gt_labels): | |
| sel_prop, labels, reg_targets, is_pos = assign_roi_targets( | |
| proposals, gt_boxes, gt_labels) | |
| cls_logits, reg = head(feat, sel_prop, stride) # [S,C], [S,C*4] | |
| # --- ๋ถ๋ฅ ์์ค (์ ์ฒด ์ํ) --- | |
| cls_loss = F.cross_entropy(cls_logits, labels) | |
| # --- ํ๊ท ์์ค (positive๋ง, ํด๋น ํด๋์ค์ 4๊ฐ ์ขํ๋ง) --- | |
| if is_pos.sum() > 0: | |
| S = reg.shape[0] | |
| reg = reg.reshape(S, -1, 4) | |
| pos_idx = torch.where(is_pos)[0] | |
| pos_labels = labels[pos_idx] | |
| reg_pos = reg[pos_idx, pos_labels] # ํด๋น ํด๋์ค ์ขํ | |
| reg_loss = F.smooth_l1_loss(reg_pos, reg_targets[pos_idx], beta=1.0) | |
| else: | |
| reg_loss = torch.tensor(0.0, device=feat.device) | |
| return cls_loss + reg_loss | |