0Curious0 commited on
Commit
0bae036
Β·
verified Β·
1 Parent(s): 86b8c1f

Upload README.md

Browse files
Files changed (1) hide show
  1. README.md +158 -0
README.md ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Faster R-CNN From Scratch (PyTorch)
2
+
3
+ A from-scratch PyTorch implementation of Faster R-CNN (Ren et al., 2015), aiming to closely β€” not exactly β€” reproduce paper-level results on Pascal VOC under real compute constraints.
4
+
5
+ ## Dataset
6
+
7
+ - **Training**: VOC2007 trainval + VOC2012 trainval ("07+12" protocol), 5,011 + 11,540 = 16,551 images, read from each dataset's `ImageSets/Main/trainval.txt` (not `Segmentation` or `Layout` β€” an early bug in this project pointed at the wrong subfolder and silently shrank the dataset to ~1,446 images).
8
+ - **Evaluation**: VOC2007 test. VOC2012 test is not used, since its ground truth requires official evaluation-server submission.
9
+
10
+ ## Backbone
11
+
12
+ - **ResNet-50** (deviation from the paper's ResNet-101, for compute reasons β€” expect a modest mAP gap as a known, accepted trade-off).
13
+
14
+ - Initialized from ImageNet-pretrained weights.
15
+
16
+ ## Image Preprocessing
17
+
18
+ - Resize so the shorter side = 600px, longer side capped at 1000px, aspect ratio preserved (matches the paper's protocol; an earlier fixed-224Γ—224 resize was replaced after confirming this in the paper text).
19
+ - Images are padded with max_dimensions in the batch to create a batch image tensor
20
+
21
+ ## Anchors
22
+
23
+ - 9 anchors per grid location: 3 scales (128Β², 256Β², 512Β²) Γ— 3 ratios (1:1, 1:2, 2:1).
24
+
25
+ ### Boundary handling (train vs. test)
26
+
27
+ - **Training**: cross-boundary anchors are excluded entirely from the loss (not labeled positive/negative β€” ignored).
28
+ - **Testing**: no exclusion; decoded proposals are clipped to the image boundary instead.
29
+
30
+ ## Anchor Labeling (IoU-based)
31
+
32
+ Convention used: positive = `1`, negative = `-1`, ignore = `0`.
33
+
34
+ - **Positive**: (i) the anchor(s) with the highest IoU for a given GT box, OR (ii) any anchor with IoU > 0.7 with any GT box.
35
+ - **Negative**: IoU < 0.3 with all GT boxes.
36
+ - **Ignore**: neither of the above β€” excluded from the loss.
37
+ - Condition (i) is applied last, so it can override a negative/ignore label.
38
+
39
+
40
+ ## RPN Loss
41
+
42
+ - 256 anchors sampled per image, ~1:1 positive:negative ratio (padded with negatives if fewer than 128 positives are available).
43
+ - Classification: cross-entropy over the sampled anchors (2-class: background/foreground).
44
+ - Regression: Smooth L1 on `(t_x, t_y, t_w, t_h)` deltas, positive anchors only, normalized by positive count.
45
+ - Combined: `loss = cls_loss + Ξ» * reg_loss`, Ξ» = 10.
46
+
47
+ ## Proposal Generation (decode β†’ clip β†’ filter β†’ NMS β†’ top-N)
48
+
49
+
50
+ 1. Decode anchors + predicted deltas β†’ boxes
51
+ 2. Clip to image boundary (test-time; training excludes cross-boundary anchors upstream instead)
52
+ 3. Filter boxes smaller than 16px
53
+ 4. Select Pre-NMS-Top-N Boxes
54
+ 4. NMS, IoU threshold 0.7
55
+ 5. Keep Post-NMS-Top-N Boxes
56
+
57
+
58
+ ## RoI Pooling
59
+
60
+ - `RoIPool` projects each proposal (corner format, absolute px) onto the shared feature map using `stride = image_dim // feature_map_dim`, then max-pools each projected region to a fixed 7Γ—7 output β€” the original Fast R-CNN "RoI Pooling" (quantized max-pool over per-bin `floor`/`ceil` boundaries), not the later RoIAlign (Mask R-CNN's bilinear-interpolated variant).
61
+ - Two implementations of the same operation: a manual `"loop"` mode (default) that computes each bin's boundaries explicitly (every bin covers β‰₯1 pixel even when a proposal is smaller than the output size), and an `"adaptive"` mode via `nn.AdaptiveMaxPool2d`.
62
+
63
+ ## Detection Head (Fast R-CNN)
64
+
65
+ - Reuses `conv5_x` of an ImageNet-pretrained ResNet-50 as the region classifier β€” the shared backbone is split at `conv4_x`/`conv5_x`: `conv4_x`'s output is the shared/RPN feature map, `conv5_x` becomes the per-RoI head. BatchNorm affine params are frozen and `.train()` is overridden to keep those BN layers in `eval()` mode (freezing `requires_grad` alone doesn't stop `.train()` from reactivating BN running-stat updates).
66
+ - Global average pool over `conv5_x`'s output, then two sibling `nn.Linear` heads:
67
+ - Classification: `num_classes + 1` logits (VOC's 20 object classes + 1 background class).
68
+ - Regression: `num_classes * 4` box deltas β€” **class-specific**, unlike the RPN's class-agnostic deltas.
69
+
70
+ ## Detection Loss
71
+
72
+ - Classification: cross-entropy over the sampled proposals (21-way: 20 VOC classes + background).
73
+ - Regression: Smooth L1, computed only on the positive proposals' predicted deltas **for their own ground-truth class**, gathered out of the class-specific `[N, num_classes, 4]` delta tensor β€” summed, then divided by the number of *sampled* proposals for that image (not just the positive count).
74
+ - Regression targets are `(t_x, t_y, t_w, t_h)` deltas (same form as the RPN's), normalized by `delta_std = (0.1, 0.1, 0.2, 0.2)` β€” the Fast R-CNN paper's convention for zero-mean/unit-variance targets. **Unlike the RPN's unnormalized deltas** β€” any code decoding detection-head deltas back into boxes must multiply by `delta_std` first, or the decoded boxes come out silently near-zero-offset.
75
+ - Combined: `loss = cls_loss + Ξ» * reg_loss`, Ξ» = 1 (Fast R-CNN's default balancing weight β€” unlike the RPN's Ξ» = 10).
76
+
77
+ ## Detection Net (Inference Decode)
78
+
79
+ - Wraps a trained `DetectionHead` for test-time use: given `RegionProposalNetwork` proposals and their `RoIPool`-ed features, runs the batched detection head once, then per image:
80
+ 1. Every `(proposal, foreground class)` pair whose softmax probability exceeds `score_thresh` (default 0.3) is emitted as a candidate β€” **not** just the argmax class. One proposal can therefore produce several detections, and a proposal whose highest-scoring class is background still contributes its foreground classes.
81
+ 2. Each candidate's box deltas for **its own emitted class** are gathered out of the class-specific delta tensor, un-normalized by `delta_std`, and decoded back to boxes with the same center-format inverse transform as the RPN's decoder.
82
+ 3. Boxes are clipped to the image's pre-padding size, then boxes that clipping collapsed to under `min_box_size` in either dimension are dropped.
83
+ 4. Per-**class** NMS (`torchvision.ops.batched_nms`, IoU `nms_iou_thresh`, default 0.3), then a top-`max_detections_per_image` cap by score (default 100).
84
+
85
+ ### Why argmax was replaced
86
+
87
+ The original decode kept only the argmax class per proposal and dropped the proposal when that was background. Measured on VOC2007 test, that emitted 16,819 detections against 14,976 GT boxes β€” 1.12 per object, where a standard Fast R-CNN emits 10–100Γ— more β€” and capped mean recall at 0.613 while the RPN was supplying 80% proposal recall. Because 11-point AP scores `p_interp(t) = 0` for every `t` above the achieved recall, mAP was pinned at 0.5389 against a ceiling of 0.6046 that the recall alone imposed; precision was already running at 89% of that ceiling. The loss was objects that never became detections at all, not objects ranked badly.
88
+
89
+ Per-class NMS (rather than class-agnostic) matters for the same metric: a `person` box must not suppress an overlapping `horse` box.
90
+
91
+ ## Training Protocol (4-Step Alternating Training, per the paper)
92
+
93
+ | Step | What's trained | Backbone |
94
+ |---|---|---|
95
+ | 1 | RPN (backbone + RPN head, end-to-end) | ImageNet-pretrained, fine-tuned |
96
+ | 2 | Fast R-CNN detector, using Step-1 RPN's *frozen* proposals as fixed input | Fresh ImageNet-pretrained, fine-tuned (separate from Step 1's) |
97
+ | 3 | RPN again, backbone now frozen (shared, from Step 2) | Frozen |
98
+ | 4 | Fast R-CNN unique layers only | Frozen |
99
+
100
+ **Step 1 hyperparameters** (from the paper): SGD, momentum 0.9, weight decay 0.0005, lr 0.001 for the first ~60k mini-batches then 0.0001 for ~20k more (paper's batch-size-1 framing). This project's realized schedule: batch size 2 (a deliberate deviation for GPU throughput), 10 total epochs over 07+12 (~82,760 iterations) β€” 8 epochs at lr 0.001, 2 at lr 0.0001.
101
+
102
+
103
+ ## Results (VOC2007 test)
104
+
105
+ ### RPN Proposal Recall
106
+
107
+ | IoU band | Recall | GT boxes recalled |
108
+ |---|---|---|
109
+ | β‰₯ 0.5 | 83.91% | 219 / 261 |
110
+ | 0.3 – 0.5 | 9.20% | 24 / 261 |
111
+ | < 0.3 | 6.90% | 18 / 261 |
112
+
113
+ ### Detection Net (Fast R-CNN head)
114
+
115
+ | `score_thresh` | `nms_iou_thresh` | mAP @ IoU 0.5 |
116
+ |---|---|---|
117
+ | 0.1 | 0.3 | **~63%** (best result; per-class AP not separately recorded for this config) |
118
+ | 0.3 | 0.3 | 62.52% (full per-class breakdown below) |
119
+
120
+ Per-class breakdown, `score_thresh=0.3`, `nms_iou_thresh=0.3`:
121
+
122
+ | Class | AP | rec[-1] | AP_ceil | n_det | n_gt | n_diff |
123
+ |---|---|---|---|---|---|---|
124
+ | aeroplane | 0.6838 | 0.7333 | 0.7273 | 897 | 285 | 26 |
125
+ | bicycle | 0.6937 | 0.7953 | 0.7273 | 852 | 337 | 52 |
126
+ | bird | 0.6364 | 0.7211 | 0.7273 | 1359 | 459 | 117 |
127
+ | boat | 0.5217 | 0.6540 | 0.6364 | 1951 | 263 | 130 |
128
+ | bottle | 0.2293 | 0.3838 | 0.3636 | 1934 | 469 | 188 |
129
+ | bus | 0.7971 | 0.9014 | 0.9091 | 869 | 213 | 41 |
130
+ | car | 0.6796 | 0.7502 | 0.7273 | 4172 | 1201 | 340 |
131
+ | cat | 0.8025 | 0.8966 | 0.8182 | 764 | 358 | 12 |
132
+ | chair | 0.3560 | 0.6389 | 0.6364 | 7047 | 756 | 618 |
133
+ | cow | 0.6692 | 0.7623 | 0.7273 | 805 | 244 | 85 |
134
+ | diningtable | 0.6474 | 0.8447 | 0.8182 | 941 | 206 | 93 |
135
+ | dog | 0.7892 | 0.8875 | 0.8182 | 1171 | 489 | 41 |
136
+ | horse | 0.7778 | 0.8391 | 0.8182 | 1022 | 348 | 47 |
137
+ | motorbike | 0.6966 | 0.7877 | 0.7273 | 890 | 325 | 44 |
138
+ | person | 0.5916 | 0.6879 | 0.6364 | 10485 | 4528 | 699 |
139
+ | pottedplant | 0.2850 | 0.4729 | 0.4545 | 3228 | 480 | 112 |
140
+ | sheep | 0.5681 | 0.6736 | 0.6364 | 880 | 242 | 69 |
141
+ | sofa | 0.6941 | 0.8912 | 0.8182 | 2019 | 239 | 157 |
142
+ | train | 0.7728 | 0.8688 | 0.8182 | 1163 | 282 | 20 |
143
+ | tvmonitor | 0.6119 | 0.7792 | 0.7273 | 2071 | 308 | 53 |
144
+
145
+ **mAP @ IoU 0.5: 0.6252** β€” mean recall[-1]: 0.7485 β€” total detections: 44,520 β€” total GT (non-difficult): 12,032 β€” difficult GT excluded: 2,944.
146
+
147
+ ## Known Deviations From the Paper (Summary)
148
+
149
+ | Deviation | Reason | Expected effect on mAP |
150
+ |---|---|---|
151
+ | ResNet-50 instead of ResNet-101 | Compute constraint | Weaker features than ResNet-101, likely costing several mAP points β€” probably felt most on small/textured classes like `bottle`/`pottedplant`, this project's weakest. Not isolated by a ResNet-101 run. |
152
+ | Batch size 2 instead of 1 | GPU throughput | Paper's lr schedule (per-image, batch size 1) reused unscaled, changing gradient noise per step. Not isolated. |
153
+ | No horizontal flip augmentation | Not implemented | Paper's VOC recipe uses flipping as a free 2Γ— augmentation; skipping it likely costs some mAP, more on sparser classes. Not isolated. |
154
+ | BatchNorm frozen from Step 2 onward | Batch size 2 is too small for stable BN statistics β€” standard practice, not ad hoc | Expected neutral-to-beneficial vs. unfrozen (paper's VGG16 has no BN to compare against). Step 1 is the exception β€” its backbone trains with BN unfrozen. |
155
+
156
+ None of these were isolated by a controlled ablation β€” the ~63% mAP reflects their combined effect, not any single deviation's contribution.
157
+
158
+