我第一次接触FPN是在处理一个工业质检项目时遇到的痛点:小尺寸缺陷检测总是漏检。传统CNN架构就像用放大镜看地图——低层特征能看清纹理但看不懂内容,高层特征知道是什么物体却看不清细节。FPN的巧妙之处在于它模拟了人类视觉系统的工作方式:先快速扫视全局定位目标,再聚焦细节观察局部特征。
FPN的核心创新点可以用"三个一"概括:
在Detectron2的实现中,FPN的构建过程特别值得注意:
python复制# Detectron2中的FPN构建代码片段
class FPN(Backbone):
def __init__(self, bottom_up, in_features, out_channels=256):
super().__init__()
# 横向连接的1x1卷积
self.lateral_convs = nn.ModuleList()
# 融合后的3x3卷积
self.output_convs = nn.ModuleList()
for idx, in_channels in zip(in_features, bottom_up.out_channels):
lateral_conv = Conv2d(in_channels, out_channels, 1)
output_conv = Conv2d(out_channels, out_channels, 3, padding=1)
self.lateral_convs.append(lateral_conv)
self.output_convs.append(output_conv)
FPN带来的性能提升非常直观。在COCO数据集上的对比实验显示:
| 架构 | mAP@0.5 | 小目标召回率 | 推理速度(FPS) |
|---|---|---|---|
| ResNet-50 | 68.3 | 42.1 | 26 |
| ResNet-50+FPN | 73.8 (+5.5) | 56.7 (+14.6) | 22 |
第一次实现ROI Pooling时,我遇到一个诡异现象:同一个物体在图像偏移几个像素后,检测框得分会出现剧烈波动。这个问题困扰了我们团队两周,直到深入研究ROI Align论文才找到根源——两次量化误差的蝴蝶效应。
假设有一个800x800的图像,其中665x665的目标框:
ROI Align的解决方案堪称优雅,它包含三个关键步骤:
在PyTorch中的实现核心:
python复制# 双线性插值核心代码
def bilinear_interpolate(grid, x, y):
x0 = torch.floor(x).long()
x1 = x0 + 1
y0 = torch.floor(y).long()
y1 = y0 + 1
# 边界处理
x0 = torch.clamp(x0, 0, grid.size(2)-1)
x1 = torch.clamp(x1, 0, grid.size(2)-1)
y0 = torch.clamp(y0, 0, grid.size(1)-1)
y1 = torch.clamp(y1, 0, grid.size(1)-1)
# 四个相邻点
Ia = grid[:, y0, x0]
Ib = grid[:, y1, x0]
Ic = grid[:, y0, x1]
Id = grid[:, y1, x1]
# 计算权重
wa = (x1-x) * (y1-y)
wb = (x1-x) * (y-y0)
wc = (x-x0) * (y1-y)
wd = (x-x0) * (y-y0)
return (Ia*wa + Ib*wb + Ic*wc + Id*wd).sum(0)
ROI Align带来的改进令人印象深刻:
| 指标 | ROI Pooling | ROI Align | 提升幅度 |
|---|---|---|---|
| box AP | 66.1 | 68.4 | +2.3 |
| mask AP | 58.1 | 60.3 | +2.2 |
| 小目标AP | 42.7 | 46.5 | +3.8 |
当我第一次拆解Mask R-CNN时,最惊叹的是它的"分而治之"设计哲学。整个架构可以看作三个智能体的协作:
在项目中尝试过多种Backbone组合:
Mask分支采用FCN结构而非全连接层,这是为了保留空间信息。关键配置:
python复制# Mask Head典型结构
mask_head = nn.Sequential(
Conv2d(256, 256, 3, padding=1), # 保持空间分辨率
nn.ReLU(),
Conv2d(256, 256, 3, padding=1),
nn.ReLU(),
Conv2d(256, 256, 3, padding=1),
nn.ReLU(),
Conv2d(256, 256, 3, padding=1),
nn.ReLU(),
Conv2dTranspose(256, 256, 2, stride=2), # 2倍上采样
nn.ReLU(),
Conv2d(256, num_classes, 1)
)
在部署Mask R-CNN时踩过不少坑,这里分享三个关键经验:
COCO默认配置:
python复制ANCHOR_SIZES = [32, 64, 128, 256, 512] # 面积
ASPECT_RATIOS = [0.5, 1.0, 2.0] # 宽高比
但在医疗影像项目中,发现需要调整:
python复制lr_scheduler = WarmupMultiStepLR(
optimizer,
milestones=[120000, 160000],
gamma=0.1,
warmup_factor=0.001,
warmup_iters=1000
)