| """ |
| 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 |
|
|
|
|
| |
| |
| |
| 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) |
|
|
| if gt_boxes.numel() > 0: |
| ious = box_iou(anc, gt_boxes) |
| max_iou, argmax = ious.max(dim=1) |
| labels[max_iou < neg_iou] = 0 |
| labels[max_iou >= pos_iou] = 1 |
| |
| 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]) |
|
|
| |
| 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 |
|
|
|
|
| |
| |
| |
| 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) |
| 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 |
|
|
|
|
| |
| |
| |
| 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) |
|
|
| |
| cls_loss = F.cross_entropy(cls_logits, labels) |
|
|
| |
| 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 |
|
|