Recognition & Detection
Feature Pyramid Network
A network design that turns a convolutional backbone's feature maps at strides 4 to 32 into a pyramid with strong semantic features at every scale, through a top-down pathway and lateral connections, so a detector can find small and large objects from one image scale.
intermediate
A feature pyramid network (FPN) builds a multi-scale set of feature maps inside a convolutional neural network, one map per resolution, each with strong semantic features. It takes the maps a backbone already computes at strides 4 to 32 and runs a cheap top-down pathway that upsamples the deep, coarse maps and adds them, through lateral connections, to the finer ones. Lin et al. introduced it at CVPR 2017 for object detection, where it replaced image pyramids at little extra cost; it has since served as the multi-scale stage of two-stage and one-stage detectors and of segmentation models.
Core Idea
Objects in one image appear at very different sizes, and a classifier or box regressor looking through a fixed-size window handles only a narrow range of them. Lin et al. compare four ways of covering the full range (their Fig. 1):
- Featurized image pyramid. Compute features separately on every level of an image pyramid. Every level is semantically strong, and a change of object size becomes a change of level. But the authors cite a fourfold increase in inference time, and end-to-end training on all levels does not fit in memory, so image pyramids were used only at test time, inconsistently with training.
- Single feature map. Predict from the network’s last map only, as Fast and Faster R-CNN did by default: fast, but a stride-16 or stride-32 map covers small objects with very few cells.
- Pyramidal feature hierarchy. Predict from several of the backbone’s own maps, as SSD (Liu et al., 2016) did. The shallow, high-resolution maps carry low-level features, poor for recognition. The authors note that SSD avoided them by starting its pyramid high in the network, at VGG’s
conv4_3, and adding new layers, which left out the finer maps they found important for small objects. - Feature Pyramid Network. Keep the backbone’s maps at every resolution, enrich each with semantics passed down from coarser levels, and predict independently on every level, as on a featurized image pyramid, at a cost close to designs 2 and 3.
Predicting on every level separates FPN from earlier top-down designs with skip connections, such as U-Net and SharpMask, which predict only from the finest map (the paper’s Fig. 2).
Building Blocks
Bottom-up pathway. The ordinary forward pass of the backbone. Layers producing maps of one size form a stage, and the pyramid takes the output of each stage’s last layer, which should carry its strongest features. For a ResNet (He et al., 2016), these are the outputs of the last residual block of conv2 to conv5, named , with strides of 4, 8, 16 and 32 pixels. The conv1 output is left out because of its large memory footprint.
Lateral connections. A convolution maps each to channels; all levels need the same width because they share prediction heads.
Top-down pathway. Starting from the projection of , each coarser map is upsampled by 2 with nearest-neighbor interpolation (chosen for simplicity) and added element-wise to the lateral projection of the next finer stage, down to stride 4.
Output convolutions. A convolution on each merged map, meant to reduce the aliasing of upsampling, produces to , the same sizes as to . For the region proposal network (RPN), is a stride-2 subsampling of , added only for the largest anchors.
The extra layers have no nonlinearities, which the authors found to matter little; residual blocks in place of single convolutions were only marginally better.
How Data Flows
Written out, the merged maps and outputs are
where is the lateral convolution and is 2× nearest-neighbor upsampling. thus contains contributions from all four stages: top-down terms from units with very large receptive fields, and a lateral term whose activations are better localized because they were subsampled fewer times.
The NumPy example imitates this on a image with a large and a small square. In this toy, “semantic strength” is just the edge evidence a unit has gathered over its window: the bottom-up pathway applies 2×2 average pooling and a averaging layer per level to a Sobel edge map, the lateral convolutions become scalar normalizations, and the output convolutions averages.
import numpy as np
from scipy import ndimage
# A 256 x 256 image with a large (64 x 64) and a small (8 x 8) bright square.
img = np.zeros((256, 256))
img[40:104, 40:104] = 1.0
img[176:184, 184:192] = 1.0
large, small = (slice(40, 104), slice(40, 104)), (slice(176, 184), slice(184, 192))
inner = (slice(56, 88), slice(56, 88)) # the large square's interior, away from its edges
# Low-level features: Sobel gradient magnitude (an edge map) at stride 1.
edges = np.hypot(ndimage.sobel(img, 0), ndimage.sobel(img, 1))
def avgpool2(x):
return x.reshape(x.shape[0] // 2, 2, x.shape[1] // 2, 2).mean(axis=(1, 3))
def box3(x):
"""A 3 x 3 averaging layer: each unit gathers evidence from a wider window."""
return ndimage.uniform_filter(x, 3, mode="constant")
def region(F, k, sl):
"""Values of the stride-2^k map F over the image region sl (pixel slices)."""
s = 2 ** k
return F[sl[0].start // s:(sl[0].stop - 1) // s + 1, sl[1].start // s:(sl[1].stop - 1) // s + 1]
def fires(F, k, sl):
"""A toy detector fires where the response reaches half the level's maximum."""
return bool(region(F, k, sl).max() >= 0.5 * F.max())
# Toy bottom-up pathway: C2..C5 at strides 4, 8, 16, 32; a unit of level k sees about 3 * 2^k pixels.
C, x = {}, edges
for k in range(1, 6):
x = avgpool2(x)
if k >= 2:
C[k] = box3(x)
print("level stride small peak large peak small/large")
for k in range(2, 6):
ps, pl = region(C[k], k, small).max(), region(C[k], k, large).max()
print(f" C{k} {2**k:>4} {ps:9.3f} {pl:10.3f} {ps / pl:10.2f}")
print("C5 fires on large:", fires(C[5], 5, large), "| on small:", fires(C[5], 5, small))
print(f"C2 mean inside the large square: {abs(region(C[2], 2, inner).mean()):.3f}")
# Top-down pathway with lateral connections: scale each level (the 1x1 lateral conv),
# add the 2x nearest-neighbor upsampled coarser map, smooth the sum (the 3x3 output conv).
def up2(x):
return x.repeat(2, axis=0).repeat(2, axis=1)
lateral = {k: C[k] / C[k].max() for k in C}
merged = lateral[5]
P = {5: box3(merged)}
for k in (4, 3, 2):
merged = lateral[k] + up2(merged)
P[k] = box3(merged)
far = ndimage.binary_dilation(img > 0, iterations=24)[::4, ::4] # stride-4 mask, objects plus margin
print(f"P2 small peak {region(P[2], 2, small).max():.3f} | large interior mean "
f"{region(P[2], 2, inner).mean():.3f} | background mean {P[2][~far].mean():.3f}")
print("P2 fires on large:", fires(P[2], 2, large), "| on small:", fires(P[2], 2, small))
Output:
level stride small peak large peak small/large
C2 4 1.216 0.861 1.41
C3 8 0.417 0.437 0.95
C4 16 0.104 0.276 0.38
C5 32 0.026 0.220 0.12
C5 fires on large: True | on small: False
C2 mean inside the large square: 0.000
P2 small peak 2.105 | large interior mean 1.464 | background mean 0.094
P2 fires on large: True | on small: True
Going up, the small square’s response falls from 1.41 times the large square’s to 0.12 times, and at it no longer reaches the threshold. has the opposite weakness: it responds only at edges, and is zero inside the large square. After the top-down merge, responds strongly inside the large square (1.46, against 0.09 for the background) and keeps the small square’s sharp peak, so both objects are found on one fine map.
In a real network the lateral and output convolutions are learned. The module below, run with PyTorch 2.14.1, applies them to random tensors with the channel counts of ResNet-50’s stages (256, 512, 1024, 2048) for an input.
import torch
import torch.nn as nn
import torch.nn.functional as F
class FPN(nn.Module):
"""Top-down pathway with lateral connections over backbone maps C2..C5 (Lin et al., 2017)."""
def __init__(self, in_channels=(256, 512, 1024, 2048), d=256):
super().__init__()
self.lateral = nn.ModuleList(nn.Conv2d(c, d, 1) for c in in_channels)
self.output = nn.ModuleList(nn.Conv2d(d, d, 3, padding=1) for _ in in_channels)
def forward(self, feats): # feats = [C2, C3, C4, C5], finest first
merged = self.lateral[-1](feats[-1]) # start from C5
outs = [self.output[-1](merged)] # P5
for i in range(len(feats) - 2, -1, -1): # C4, C3, C2
top_down = F.interpolate(merged, size=feats[i].shape[-2:], mode="nearest")
merged = self.lateral[i](feats[i]) + top_down
outs.insert(0, self.output[i](merged)) # 3x3 conv reduces upsampling aliasing
p6 = F.max_pool2d(outs[-1], kernel_size=1, stride=2) # stride-2 subsampling of P5
return outs + [p6] # [P2, P3, P4, P5, P6]
torch.manual_seed(0)
H, W = 800, 1216 # shorter side 800, as in the paper's training
C = [torch.randn(1, c, H // s, W // s) for c, s in zip((256, 512, 1024, 2048), (4, 8, 16, 32))]
fpn = FPN()
with torch.no_grad():
P = fpn(C)
print("torch", torch.__version__)
for name, t in zip(["C2", "C3", "C4", "C5"], C):
print(name, tuple(t.shape))
for k, t in enumerate(P, start=2):
print(f"P{k}", tuple(t.shape))
print("FPN parameters:", sum(p.numel() for p in fpn.parameters()))
Output:
torch 2.14.1
C2 (1, 256, 200, 304)
C3 (1, 512, 100, 152)
C4 (1, 1024, 50, 76)
C5 (1, 2048, 25, 38)
P2 (1, 256, 200, 304)
P3 (1, 256, 100, 152)
P4 (1, 256, 50, 76)
P5 (1, 256, 25, 38)
P6 (1, 256, 13, 19)
FPN parameters: 3344384
Every level has 256 channels at its stage’s resolution; the added layers hold 3.3 million parameters, 2.4 million of them in the convolutions.
Mathematical Formulation
Heads on every level. The RPN attaches one head, a convolution with two sibling convolutions, to every level from to , each with anchors of a single scale labeled by IoU as in Faster R-CNN (Ren et al., 2015); ground-truth boxes reach a level only through its anchors. Shared head parameters did about as well as separate ones, which the authors read as evidence that all levels have similar semantic strength.
Assigning regions to levels. A Fast R-CNN detector pools features for each region of interest (RoI), so with a pyramid it must choose a level. Treating the feature pyramid as if it came from an image pyramid, Lin et al. assign an RoI of width and height (input-image pixels) to level
Here 224 is the canonical ImageNet pre-training size, and sends a RoI to , matching the map of ResNet-based Faster R-CNN; halving an RoI’s size moves it one level finer. RoI pooling then extracts features for two shared 1,024-dimensional fully connected layers, a lighter head than the ResNet baseline’s conv5 stage, which FPN already uses in the pyramid.
import numpy as np
def fpn_level(w, h, k0=4, canonical=224, k_min=2, k_max=5):
"""Pyramid level for an RoI of w x h pixels, Eq. (1) of Lin et al. (2017), clipped to P2..P5."""
k = np.floor(k0 + np.log2(np.sqrt(w * h) / canonical))
return np.clip(k, k_min, k_max).astype(int)
boxes = np.array([[20, 20], [60, 40], [110, 110], [112, 112], [120, 120], [100, 400],
[224, 224], [300, 200], [448, 448], [640, 480]], float)
levels = fpn_level(boxes[:, 0], boxes[:, 1])
for (w, h), k in zip(boxes, levels):
print(f"{w:4.0f} x {h:<4.0f} sqrt(wh) = {np.sqrt(w * h):6.1f} -> P{k} (stride {2 ** k:2d}, "
f"RoI spans {np.sqrt(w * h) / 2 ** k:5.1f} cells)")
Output:
20 x 20 sqrt(wh) = 20.0 -> P2 (stride 4, RoI spans 5.0 cells)
60 x 40 sqrt(wh) = 49.0 -> P2 (stride 4, RoI spans 12.2 cells)
110 x 110 sqrt(wh) = 110.0 -> P2 (stride 4, RoI spans 27.5 cells)
112 x 112 sqrt(wh) = 112.0 -> P3 (stride 8, RoI spans 14.0 cells)
120 x 120 sqrt(wh) = 120.0 -> P3 (stride 8, RoI spans 15.0 cells)
100 x 400 sqrt(wh) = 200.0 -> P3 (stride 8, RoI spans 25.0 cells)
224 x 224 sqrt(wh) = 224.0 -> P4 (stride 16, RoI spans 14.0 cells)
300 x 200 sqrt(wh) = 244.9 -> P4 (stride 16, RoI spans 15.3 cells)
448 x 448 sqrt(wh) = 448.0 -> P5 (stride 32, RoI spans 14.0 cells)
640 x 480 sqrt(wh) = 554.3 -> P5 (stride 32, RoI spans 17.3 cells)
The clipping is ours, not stated in the paper: the detector uses only to . Between the ends, an RoI spans 14 to 28 cells of its level whatever its size, which is the point of the rule. But the boundaries are hard: the nearly identical 110- and 120-pixel squares are pooled from different levels.
Why It Works
Lin et al. ran their ablations with ResNet-50 on COCO minival, training on trainval35k at a shorter side of 800 pixels.
- Proposals. FPN raised the RPN’s average recall at 1,000 proposals from 48.3 (single-scale baseline) to 56.3, and by 12.9 points on small objects (32.0 to 44.9).
- Ablations. Without the top-down pathway (an SSD-style bottom-up pyramid), average recall fell to 49.5, on par with the baseline, and Fast R-CNN AP on fixed proposals fell from 33.9 to 24.9; the authors conjecture that semantic gaps between levels are the cause. Without lateral connections, recall was 46.1, which they attribute to locations blurred by repeated down- and upsampling. Using only , with all anchors on it (750k instead of 200k), gave 51.3: more anchors alone did not close the gap.
- Detection. With Faster R-CNN, AP rose from 31.6 to 33.9 (+2.3) and AP@0.5 from 53.1 to 56.9 (+3.8) over the authors’ own baseline.
With ResNet-101, the minival AP was 35.0, or 35.2 when the RPN and detector shared features. Training the detector twice as long raised it to 35.6 (without shared features). That model, submitted to the COCO evaluation server, scored 36.2 AP and 59.1 AP@0.5 on test-dev (18.2 AP on small objects), and 35.8 AP on test-std. It used a single input scale, without image pyramids, and surpassed the single-model entries of the 2015 and 2016 COCO competition winners listed in the paper. With shared features, inference took 0.165 s per image (ResNet-50) and 0.19 s (ResNet-101) on an NVIDIA M40 GPU, against 0.32 s for the single-scale ResNet-50 baseline; the arXiv revision (v2) updates these to 0.148 s and 0.172 s.
Training Considerations
Because the pyramid comes from one image scale, the system trains end-to-end and runs identically at training and test time. The backbone is pretrained on ImageNet and fine-tuned; the detector’s fully connected layers start from random weights, since ResNets have no pretrained fc layers. Unlike Faster R-CNN, the RPN experiments kept anchors crossing the image border.
Variants
- RetinaNet (Lin et al., 2017, focal loss) uses levels to . It drops for computational reasons, computes by a stride-2 convolution on , and adds (ReLU, then another stride-2 convolution) for large objects.
- PANet (Liu et al., 2018) adds a bottom-up path augmentation after FPN. From , each new level adds a stride-2 convolution of the finer one to the matching level and applies another convolution, giving low-level localization signals a path of fewer than 10 layers to the top instead of the backbone’s 100+. It also pools each proposal from all levels (adaptive feature pooling) instead of using the hard rule above.
- NAS-FPN (Ghiasi et al., 2019) searches the cross-scale connections with neural architecture search, using a reinforcement-learning controller, inside RetinaNet. The resulting cell mixes top-down and bottom-up connections and can be stacked repeatedly.
- BiFPN (Tan et al., 2020, EfficientDet) simplifies a PANet-like bidirectional network. It removes single-input nodes, adds same-level input-to-output edges, repeats the bidirectional block, and replaces plain addition with learned, normalized nonnegative weights, because inputs at different resolutions contribute unequally.
Use in Computer Vision
FPN is a component of other models. In Faster R-CNN it serves both the RPN and the detector. Mask R-CNN (He et al., 2017) offers a ResNet-FPN backbone, reported to give gains in both accuracy and speed; on COCO minival, ResNet-50-FPN reached 33.6 mask AP against 30.3 for ResNet-50-C4. RetinaNet predicts densely on every level. Panoptic FPN (Kirillov et al., 2019) adds to Mask R-CNN’s shared FPN a semantic segmentation branch that merges all levels at 1/4 resolution. The original paper also generated segment proposals with FPN (), with over 8.3 points higher average recall than the image-pyramid methods DeepMask and SharpMask, and faster.
Limitations
- Hard level assignment. PANet’s authors note that two proposals differing by 10 pixels can be pooled from different levels, and that high-level context could help small proposals too.
- One-way, equal fusion. Information flows only top-down, and inputs are summed with equal weight; PANet, NAS-FPN and BiFPN revisit these choices.
- Cost of the finest level. has 16 times as many positions as ; RetinaNet drops it.
Related
- Faster R-CNN
A two-stage object detector in which a region proposal network, sharing the detector's convolutional features, replaces hand-designed region proposals, making proposal generation nearly free and the whole detector one network.
- One-Stage Object Detectors
Object detectors that predict class scores and box offsets densely at every position of a feature map, or of a feature pyramid, in a single network pass, with no region-proposal stage.
- Anchor Boxes
Reference boxes of fixed sizes and aspect ratios, tiled at every feature-map position, that a detector classifies and refines, and that decide which of its predictions are trained on which objects.
- Image Pyramid
A multi-scale representation of an image built by repeatedly smoothing and subsampling it, so that each level holds a coarser copy at half the resolution of the one below.
- Receptive Field
The region of the input image that can affect one unit of a convolutional network, how kernel size, depth, stride, pooling and dilation make it grow, and why the effective, gradient-weighted region is much smaller than the theoretical one.
- Deep Residual Learning for Image Recognition
The 2016 CVPR paper by He, Zhang, Ren and Sun of Microsoft Research that introduced residual networks (ResNets), trained them to 152 layers on ImageNet, and won the ILSVRC 2015 classification task.
References
- Lin, T.-Y., Dollár, P., Girshick, R., He, K., Hariharan, B. & Belongie, S. (2017). Feature Pyramid Networks for Object Detection. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 936–944.
- Lin, T.-Y., Dollár, P., Girshick, R., He, K., Hariharan, B. & Belongie, S. (2017). Feature Pyramid Networks for Object Detection. arXiv:1612.03144v2.
- He, K., Zhang, X., Ren, S. & Sun, J. (2016). Deep Residual Learning for Image Recognition. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 770–778.
- Ren, S., He, K., Girshick, R. & Sun, J. (2015). Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks. Advances in Neural Information Processing Systems 28 (NIPS).
- Liu, W., Anguelov, D., Erhan, D., Szegedy, C., Reed, S., Fu, C.-Y. & Berg, A. C. (2016). SSD: Single Shot MultiBox Detector. European Conference on Computer Vision (ECCV), Lecture Notes in Computer Science, 21–37.
- Lin, T.-Y., Goyal, P., Girshick, R., He, K. & Dollár, P. (2017). Focal Loss for Dense Object Detection. IEEE International Conference on Computer Vision (ICCV), 2999–3007.
- He, K., Gkioxari, G., Dollár, P. & Girshick, R. (2017). Mask R-CNN. IEEE International Conference on Computer Vision (ICCV), 2980–2988.
- Kirillov, A., Girshick, R., He, K. & Dollár, P. (2019). Panoptic Feature Pyramid Networks. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 6392–6401.
- Liu, S., Qi, L., Qin, H., Shi, J. & Jia, J. (2018). Path Aggregation Network for Instance Segmentation. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 8759–8768.
- Ghiasi, G., Lin, T.-Y. & Le, Q. V. (2019). NAS-FPN: Learning Scalable Feature Pyramid Architecture for Object Detection. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 7029–7038.
- Tan, M., Pang, R. & Le, Q. V. (2020). EfficientDet: Scalable and Efficient Object Detection. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 10778–10787.