尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Fast R-CNN深度解析:核心原理、端到端训练与完整复现指南

发布时间:2026/9/30 0:36:35

资讯中心
01
ARTICLE

Fast R-CNN深度解析:核心原理、端到端训练与完整复现指南

Fast R-CNN深度解析:核心原理、端到端训练与完整复现指南
先说结论如果你的研究方向是目标检测或者正准备入门检测算法的演进史那Fast R-CNN是你绕不开的一个模型。它发表于ICCV 2015作者是Ross Girshick也就是R-CNN的原班人马。它的出现直接把R-CNN的训练速度提升了约9倍、测试速度提升了约200倍而且在PASCAL VOC 2007上mAP从R-CNN的66%左右直接拉到70%。更关键的是它第一次把目标检测的训练流程真正变成一个端到端可微分的整体这件事对后续Faster R-CNN、Mask R-CNN、Cascade R-CNN等一堆模型产生了直接影响。这篇文章我会从设计思路、核心原理、训练复现、踩坑实录四个角度把它讲透。不求你把每一行代码背下来但希望你看完之后能明白RoI Pooling到底在干嘛多任务损失为什么能work复现的时候哪些参数不能乱改这些才是真正值钱的东西。1. 项目概述Fast R-CNN在目标检测演进中的位置1.1 从R-CNN到Fast R-CNN解决的核心痛点目标检测这个任务简单说就是两件事找到目标在哪里定位判断目标是什么分类。在R-CNN之前主流做法还停留在滑动窗口加手工特征的老路上计算量大、精度低。R-CNN算是第一个把深度学习大规模引进目标检测的里程碑但它有一个致命问题速度太慢。原因很直接R-CNN对每张测试图片先生成约2000个候选区域然后把这2000个候选区域逐个缩放到固定尺寸再分别送入卷积神经网络提取特征、做分类和回归。也就是说一张图过一遍网络实际上要跑2000次前向传播。我当时第一次跑R-CNN的时候测一张图等了好几秒当时就意识到这玩意儿在生产环境根本没法用。Fast R-CNN的思路很聪明既然2000个候选区域来自同一张图那何不先把整张图只过一遍卷积网络得到完整的特征图然后在这张特征图上根据候选区域的位置信息去“抠”出对应的特征块再做分类和回归这样一来一次前向传播就把所有候选区域的特征提取都解决了。这个看似简单的思路实际上把检测速度从“候选区域数量×单次前向耗时”压缩到“1次前向2000次轻量级RoI处理”量级差异直接决定了它能不能落地。1.2 核心贡献与关键指标一览Fast R-CNN有三个核心贡献我用三句话概括多任务损失把分类损失和边框回归损失放在同一个网络里联合优化训练不再分阶段梯度可以同时回传到共享的卷积层。RoI Pooling层把不同尺寸的候选区域特征统一池化成固定尺寸让网络可以接受任意尺寸输入并输出定长特征。SVD分解加速全连接层用两个小子矩阵替代大的权重矩阵把全连接层的计算量降一个数量级。在PASCAL VOC 2007测试集上Fast R-CNN的mAP达到70.0%比R-CNN的66.0%高了4个点同时单张图像测试时间不含候选区域生成大约0.2秒左右相比R-CNN的十几秒是质的飞跃。这里提一句VOC 2007的mAP评估方式是把20个类别的APAverage Precision做平均理解这个指标的底层逻辑对后续调参很有帮助后面我会专门说。1.3 适合谁读这篇博文这篇文章适合三类人。第一类是刚接触目标检测、想理清算法演进脉络的学生或转行工程师你可以把Fast R-CNN当一个“解剖样本”理解它就能更顺滑地理解后续所有两阶段检测器。第二类是要在科研或工作中复现Fast R-CNN的开发者我会给出环境搭建、数据准备、训练评估的完整流程和参数细节。第三类是正在做算法选型的人你需要知道Fast R-CNN的优势和瓶颈在哪里以及什么时候该用它、什么时候应该直接上Faster R-CNN。2. 整体设计与思路拆解2.1 R-CNN三阶段流程的尴尬之处要理解Fast R-CNN的设计必须先把R-CNN的痛点解剖清楚。R-CNN的完整流程是先用Selective Search生成约2000个候选框然后把每个候选框裁剪出来并缩放到227×227分别送入AlexNet或VGG16提取特征最后用SVM做分类、用线性回归做边框修正。这个流程有三个明显问题。第一重复计算严重2000个候选框之间存在大量重叠区域这些区域的特征被反复计算效率极低。第二训练复杂度高CNN特征提取、SVM分类器、边框回归器三部分是分开训练的每个阶段都需要单独调参和保存中间结果非常繁琐。第三CNN的输入尺寸固定因为全连接层要求输入特征维度固定所以所有候选框都必须缩放到同一尺寸这会造成物体形变和信息丢失。Fast R-CNN把这些痛点逐个击破。它把特征提取统一到整图一次前向计算中用RoI Pooling解决尺寸固定问题用softmax分类器取代SVM把分类和回归放进同一个损失函数中联合训练。这一步设计上的“合并同类项”在逻辑上是非常漂亮的。从工程角度看它少了很多中间文件管理从算法角度看共享卷积特征让梯度能同时更新底层特征提取器特征本身也变得更有判别力。2.2 端到端训练思路的成立条件所谓端到端就是从原始图像输入到最终分类和回归输出中间所有模块都在同一个优化目标下联动更新。但这里有个隐含前提候选区域的生成必须是独立的。Fast R-CNN并没有把候选区域生成纳入网络内部它仍然依赖Selective Search这类外部算法。所以严格来说Fast R-CNN算是“半端到端”。注意这个区别很重要Faster R-CNN后面做的就是把这个外部模块也塞进网络里才真正实现了完全端到端。端到端训练为什么效果好而且训练快因为梯度可以一路回传到共享的卷积层让特征提取器直接为分类和回归两个任务服务。R-CNN阶段特征提取网络的训练目标只是图像分类ImageNet预训练它并不知道下游要做检测框回归所以提取出来的特征未必是检测任务最优的。Fast R-CNN用多任务损失联合优化后VGG16的卷积层能同时感知分类和定位需求相当于让“眼睛”为“双手”服务任务对齐度更高了。2.3 为什么选择RoI Pooling而不是直接全局池化当时很多人在处理不定长特征时会直接用全局平均池化或者把候选框直接缩放到固定patch。Fast R-CNN选择RoI Pooling的原因有两个。一是保持空间对应关系。直接缩放整图会破坏物体比例全局平均池化则丢失了空间位置信息而RoI Pooling是在候选区域对应的小块特征图内部做网格化池化既保留了相对空间布局又能输出固定长度。二是让反向传播可以精确地回到RoI对应的特征图区域这一点对端到端训练至关重要。我用一个生活化类比解释RoI Pooling想象你有一堆大小不一的照片想统一放进同样大小的相册格子。RoI Pooling不是把整张照片拉伸变形而是先把照片按目标位置裁出来再在每个格子里取一个“最有代表性的像素块”然后用这些块拼成统一尺寸。这样既保住了目标的位置结构又不破坏比例。3. 核心细节解析与实操要点3.1 RoI Pooling的数学原理与实现细节RoI Pooling层的输入有两部分一是整图通过卷积网络得到的特征图其尺寸为C×H×W二是N个候选区域在原始图像上的坐标每个候选区域用五元组表示(batch_index, x1, y1, x2, y2)。操作分三步。第一步把候选区域坐标从原始图像尺度映射到特征图尺度。假设原始图像尺寸为img_w×img_h特征图尺寸为feat_w×feat_h网络总步长为stride那么映射关系是x1_feat x1 / stride。VGG16在普通输入尺寸下总步长为16所以一个原图上160像素的坐标映射到特征图上就是10个像素。第二步把映射后的矩形区域划分成H×W的网格。比如输出要7×7的固定尺寸那就把这个RoI区域均匀切成7行7列共49个格子每个格子的尺寸约为(h_roi/H) × (w_roi/W)。第三步对每个格子内的特征值做最大池化得到单一个值。49个格子对应49个值按顺序排列就得到了C×7×7的输出特征。这里的通道数C保持不变VGG16最后卷积层输出512个通道所以RoI Pooling输出就是512×7×7。实现上有两个容易踩坑的地方。一是坐标取整。如果RoI边界落在像素之间通常用最近邻取整这会造成轻微的位置偏移但实践中影响不大。二是量化误差。因为池化网格边界也需要对整数像素位置取整会丢失部分空间精度。后来Mask R-CNN提出的RoIAlign就是专门解决这个量化问题的你可以理解为它用了双线性插值取代取整精度更高。复现Fast R-CNN时你直接用取整版本没有问题但心里要清楚误差来源在哪里。3.2 多任务损失函数的设计逻辑Fast R-CNN的损失函数由两部分组成分类损失加边框回归损失写成表达式就是L L_cls λ * L_reg分类损失用的是softmax交叉熵。注意这里Fast R-CNN并没有保留R-CNN的SVM分类器而是直接用全连接层加softmax输出21个类别的概率以VOC为例20个物体类加1个背景类。为什么换成softmax因为这样分类和特征提取可以在同一个反向传播流程中优化SVM是外部模型梯度根本回不到网络里。回归损失使用的是smooth L1损失。它的定义是当|x| 1时0.5 * x^2否则|x| - 0.5smooth L1的好处是对离群点不敏感。如果直接用L2损失一个标注框轻微标注偏差就可能导致梯度爆炸训练非常不稳定。而smooth L1在误差较大时梯度绝对值恒定为1可以避免震荡。在我实际训练中这个设计确实让训练稳定很多尤其是在数据标注质量不高的时候smooth L1明显比L2更能扛。边框回归的目标不是直接预测坐标值而是预测相对偏移量。具体来说候选框坐标为(x, y, w, h)真实标注框为(x*, y*, w*, h*)回归目标定义为t_x (x* - x) / wt_y (y* - y) / ht_w log(w* / w)t_h log(h* / h)也就是说模型学习的是候选框到真实框的归一化平移量和缩放量。为什么要归一化因为不同尺寸的候选框其像素偏移的绝对值差异巨大直接预测绝对像素值会让大框和小框的回归难度不一致。归一化之后模型只需要学一个相对位移尺度不变性大大增强。3.3 SVD分解如何给全连接层提速Fast R-CNN里还有一个不太起眼但很实用的工程细节用SVD分解加速全连接层。VGG16全连接层权重矩阵非常大fc6是4096×25088fc7是4096×4096。推理时2000个RoI都要过这两个全连接层计算量可想而知。SVD的思想是把一个大权重矩阵W近似分解成三个小矩阵乘积W ≈ U·S·V^T然后只保留前k个奇异值。这样原来的一次大矩阵乘法W·x就变成了两次小矩阵乘法U·(S·(V^T·x))计算量从m×n降为k×(mn)。在实验中当k取128左右时全连接层计算量大约降低一个数量级而mAP只下降约0.1到0.3个百分点。如果想在CPU上部署模型这个技巧非常实用。上面这些问题想清楚了你对Fast R-CNN的理解就已经超过很多人了。接下来进入动手环节。4. 环境搭建与数据准备4.1 依赖环境与版本选择复现Fast R-CNN第一件事不是写代码而是把环境理清楚。因为这是一个2015年的模型现在的深度学习框架版本差异很大如果直接拿最新的PyTorch跑老代码大概率会遇到API不兼容的问题。我给出一套我在实践中验证过比较稳妥的组合组件推荐版本说明Python3.8/3.93.10以上某些老库可能编译失败PyTorch1.10~1.131.x系列API稳定对RoI Pooling支持好torchvision0.11~0.13对应PyTorch版本的官方扩展CUDA11.3左右与PyTorch版本匹配即可OpenCV4.x图像读取、候选框可视化数据集PASCAL VOC 2007/2012经典检测数据集训练验证方便有人可能会问直接用MMDetection这类工具箱复现不就行了我的回答是可以但我不建议你一上来就用。MMDetection把Fast R-CNN封装得太好你很难感受到核心模块到底是怎么串起来的。我建议第一次复现至少手动写一遍数据加载、RoI Pooling和损失计算哪怕跑得慢一点理解深度完全不一样。当然你完全可以用开源库的RoI Pooling实现比如torchvision.ops.roi_pool没必要从零写CUDA算子。4.2 数据集准备与标注格式说明PASCAL VOC是一个经典的目标检测数据集2007版有9963张图片标注了20个物体类别包括人、鸟、猫、牛、狗、马、羊、飞机、自行车、船、巴士、汽车、摩托车、火车、瓶子、椅子、餐桌、盆栽植物、沙发、电视。每张图片对应一个XML文件记录了每个目标的类别和边界框坐标。XML标注格式大致是这样的annotation filename000001.jpg/filename size width500/width height375/height /size object namedog/name bndbox xmin100/xmin ymin50/ymin xmax300/xmax ymax250/ymax /bndbox /object /annotation下载数据集后目录结构应该是这样的VOCdevkit/ └── VOC2007/ ├── Annotations/ # 存放XML标注 ├── ImageSets/ │ └── Main/ # 存放train.txt、val.txt等划分文件 ├── JPEGImages/ # 存放JPG图片 ├── SegmentationClass/ └── SegmentationObject/在训练前我们需要把XML标注解析成模型可用的格式通常是一个字典或张量列表。这里的关键一步是生成候选区域。Fast R-CNN自己不做候选区域生成你需要额外用Selective Search产生。在复现中可以直接用selectivesearch这个Python库import selectivesearch import cv2 img cv2.imread(000001.jpg) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) _, proposals selectivesearch.selective_search( img_rgb, scale500, sigma0.9, min_size10) # proposals是多个候选框字典提取坐标即可 candidate_boxes [] for prop in proposals: x, y, w, h prop[rect] candidate_boxes.append([x, y, x w, y h])这里要注意Selective Search生成的候选框数量通常在1000到2000个左右Scale参数越大候选框越多但计算量也越大。训练时我们用2000个测试时用2000个保持一致性。4.3 数据加载器的设计思路训练Fast R-CNN时我们不是把一张图的全部候选框都塞进一个batch而是精心挑选。原论文的做法是一个mini-batch包含2张图像每张图像取64个RoI其中正样本与某个标注框IoU大于等于0.5最多占25%也就是16个其余48个为负样本IoU在0.1到0.5之间。为什么要这样做因为如果负样本太多模型会严重偏向背景类导致检测不到物体。这个数据均衡策略在检测任务里是基本功后续几乎所有两阶段检测器都在沿用。我自己写数据加载器时的代码逻辑是def load_train_batch(img_path, rois, gt_boxes, gt_labels): # rois是候选框gt_boxes是真实标注框 # 计算所有候选框与真实框的IoU ious compute_iou(rois, gt_boxes) # 每个候选框分配一个真实框标签找最大IoU的那个 max_iou, max_idx ious.max(dim1) labels gt_labels[max_idx] labels[max_iou 0.5] 0 # 背景类设为0 # 挑选正负样本保证正样本比例 pos_idx (max_iou 0.5).nonzero() neg_idx (max_iou 0.5).nonzero() ...这里有个细节值得注意负样本的IoU上限一般设在0.5以下但如果IoU介于0.1到0.5之间视为“难负样本”保留下来训练会更好。如果IoU低于0.1说明候选框和任何真实目标都几乎不重叠这种样本太简单对训练帮助不大干脆直接丢弃。这个“难负样本挖掘”的思想在现代检测器里也非常常见。5. 完整复现流程与核心代码5.1 构建Fast R-CNN网络结构Fast R-CNN的主干网络可以是AlexNet、VGG16甚至是ResNet。原论文实验里最强的是VGG16。我们来搭建一个简化版的Fast R-CNN主干用VGG16的卷积部分后面接RoI Pooling和两个全连接分支。import torch import torch.nn as nn import torchvision.ops as ops class FastRCNN(nn.Module): def __init__(self, num_classes21): super().__init__() # 使用VGG16的特征提取部分到conv5为止 from torchvision.models import vgg16 vgg vgg16(pretrainedTrue) self.backbone nn.Sequential(*list(vgg.features)[:30]) self.stride 16 # RoI Pooling的输出尺寸 self.roi_size 7 # 经过RoI Pooling后特征维度 512 * 7 * 7 self.fc6 nn.Linear(512 * 7 * 7, 4096) self.fc7 nn.Linear(4096, 4096) self.cls_score nn.Linear(4096, num_classes) self.bbox_pred nn.Linear(4096, num_classes * 4) self.relu nn.ReLU(inplaceTrue) self.dropout nn.Dropout(p0.5) def forward(self, x, rois): # x: [B, 3, H, W] 输入图像 # rois: [N, 5] 每个值是 (batch_index, x1, y1, x2, y2) features self.backbone(x) # [B, 512, H/16, W/16] # RoI Pooling pooled ops.roi_pool(features, rois, output_size(self.roi_size, self.roi_size), spatial_scale1.0 / self.stride) # pooled: [N, 512, 7, 7] pooled pooled.flatten(start_dim1) fc6_out self.dropout(self.relu(self.fc6(pooled))) fc7_out self.dropout(self.relu(self.fc7(fc6_out))) cls_scores self.cls_score(fc7_out) bbox_deltas self.bbox_pred(fc7_out) return cls_scores, bbox_deltas这段代码里需要特别留意的是roi_pool输入的坐标系。PyTorch的ops.roi_pool要求rois的坐标是相对于输入图像的绝对坐标内部会根据spatial_scale参数做缩放。这里spatial_scale1/16是因为VGG16卷积部分的总步长是16也就是说特征图上的一个像素对应原图16个像素。另外注意bbox_pred的输出维度是num_classes * 4也就是每个类别都预测一组偏移量。为什么要对每个类别单独预测因为不同类别的物体长宽比和偏移模式差异可能很大比如“人”通常是窄高的“汽车”通常是宽扁的。让模型为每个类别学习独立的回归参数可以提升定位精度。实际使用时我们只需取预测概率最大类别对应的那组偏移量即可。5.2 损失函数实现与训练循环损失函数的实现直接对应论文里的公式。分类部分用交叉熵回归部分只计算正样本。def fast_rcnn_loss(cls_scores, bbox_preds, labels, bbox_targets, num_classes21, lambda_reg1.0): # cls_scores: [N, 21] # labels: [N] 每个RoI的类别标签背景为0 # bbox_preds: [N, 84] # bbox_targets: [N, 84] 只对正样本位置有效 # 分类损失 cls_loss nn.functional.cross_entropy(cls_scores, labels) # 回归损失只对非背景样本计算 pos_mask labels 0 if pos_mask.sum() 0: # 为每个样本选择对应类别的回归预测 rois_batch torch.arange(labels.shape[0], devicelabels.device) selected_bbox_pred bbox_preds[rois_batch, labels * 4:(labels 1) * 4] selected_bbox_target bbox_targets[rois_batch, labels * 4:(labels 1) * 4] reg_loss smooth_l1_loss(selected_bbox_pred, selected_bbox_target) else: reg_loss torch.tensor(0.0, devicecls_scores.device) total_loss cls_loss lambda_reg * reg_loss return total_loss有几个实现细节我想特别强调。第一bbox_targets在数据准备阶段怎么算。给定候选框(x, y, w, h)和它匹配的真实框(x*, y*, w*, h*)偏移目标的计算遵循我在3.2节给的公式。这些目标要在训练前预先算好注意只对正样本填充有效值负样本位置可以全填0反正回归损失不会计算它们。第二SVD加速在训练时不需要用只在推理时把fc6和fc7替换成两个小矩阵相乘即可。训练时正常反向传播SVD会破坏梯度的连贯性。第三学习率设置。原论文用SGD初始学习率0.001每经过一定迭代轮次后降低为原来的十分之一。批量大小2张图每张图64个RoI相当于一个batch有128个RoI参与训练。如果显存不够可以适当减少RoI数量到32但精度会有轻微下降。5.3 训练中的关键参数与技巧汇总我把复现过程中需要重点关注的参数整理成表格这些不是我拍脑袋编的都是从论文实验和实际调试中得到的经验。参数推荐值说明主干网络VGG16特征能力强但显存占用大RoI Pooling输出尺寸7×7在精度和计算量之间平衡每张图RoI数量64论文标准设定正负样本比例1:3保证正样本不至于过少初始学习率0.001微调预训练主干时常用学习率衰减每几轮降10倍让损失后期稳定下降动量0.9SGD标配权重衰减0.0005防止过拟合迭代轮数30k~40k视数据集大小调整类别数21VOC是20类背景我实际调试时有个体会不要一上来就追求完整复现论文的每一点细节先把VOC2007的train集跑通、mAP跑到60以上再逐步调整数据增强、学习率策略和RoI采样比例。先跑通再调优这条路径比一开始就想全部拉满稳得多。5.4 推理流程与可视化检测结果训练完成后的推理流程和训练时很相似但有一些细节不同。完整流程是这样的读入图片用Selective Search生成候选框整图过一遍网络提取特征对每个候选框做RoI Pooling再过全连接层得到类别分数和边框偏移。此时对每个候选框先根据预测的偏移量修正坐标然后用NMS非极大值抑制去掉重叠的框。NMS的阈值一般设在0.3到0.5之间越高保留的框越多越低去除越狠。def infer(net, img, proposals, conf_thresh0.5, nms_thresh0.3): net.eval() with torch.no_grad(): # 图片预处理、转tensor等略 img_tensor preprocess(img) rois torch.tensor(proposals, dtypetorch.float32) cls_scores, bbox_deltas net(img_tensor.unsqueeze(0), rois) probs torch.softmax(cls_scores, dim1) # 对每个类别做NMS只取置信度大于阈值的 final_boxes [] for cls_id in range(1, net.num_classes): mask probs[:, cls_id] conf_thresh if mask.sum() 0: continue cls_boxes proposals[mask] cls_scores_cur probs[:, cls_id][mask] # 利用预测的bbox_deltas修正候选框 cls_deltas bbox_deltas[mask, cls_id * 4:(cls_id 1) * 4] refined_boxes apply_deltas(cls_boxes, cls_deltas) keep nms(torch.tensor(refined_boxes), cls_scores_cur, nms_thresh) ... return final_boxes这里NMS的输入特别容易写错。NMS的目的是去掉“同一个目标上的重复框”所以必须按类别分别做不能把所有类别的框放在一起做。否则一个行人和一辆汽车如果靠得近可能互相压制导致漏检。我在第一次写的时候就是没按类别分开NMS结果明明检测到了目标可视化时却发现很多框不见了排查了半天才找到这个原因。关于置信度阈值和NMS阈值怎么选我给一组经验值。在VOC数据集上置信度阈值设在0.5NMS阈值设在0.3通常能得到比较干净的可视化结果。但要注意这组参数只是视觉效果友好并不一定对应最高mAP。做评估时我们要用更严谨的mAP计算流程这个放到下一节讲。6. 常见问题与排查技巧实录6.1 训练显存溢出跑Fast R-CNN最常见的问题就是显存溢出尤其是用VGG16主干。VGG16的卷积层中间特征图非常大一张640×480的图片经过VGG16前几层后通道数128时特征图尺寸还是320×240一张图就要占用不少显存。再加上每个batch两张图、每张图64个RoI显存消耗很容易爆。我的排查思路是这样的先降到单图、单RoI测试显存占用逐步加大看哪一步消耗最多。如果瓶颈在主干卷积可以减小输入图片的短边尺寸比如从600缩到500。如果瓶颈在RoI Pooling后的大量RoI并行计算可以减少每张图的RoI数。还可以把torchvision.ops.roi_pool换成RoIAlign它的显存占用更低精度还更高一举两得。如果显存实在不够最后的手段是用梯度累积。即每轮只处理少量样本梯度先累积几个batch再统一更新参数。虽然训练时间变长但至少能跑起来。6.2 分类收敛但回归不收敛这是一个非常隐蔽的问题。如果你发现分类损失掉得很快但回归损失怎么也降不下去先别急着调学习率检查一下bbox_targets的计算是不是有问题。最容易被忽视的是坐标归一化t_x和t_y是用原图宽度和高度归一化t_w和t_h是对数缩放。如果代码里把宽高搞混了回归目标就会出现系统性错误。另外回归分支的输出层通常不接激活函数直接线性输出。如果你在bbox_pred后面加了ReLU或Tanh会限制输出范围导致模型学不出大的偏移量。我在调试时见过有人把全连接层的初始化默认值改成了0结果回归分支的梯度直接消失损失纹丝不动。检查一下这些基础细节比盲目调参有效得多。6.3 mAP评估时容易踩的坑最后说一下评估指标的问题。热词里反复出现“目标检测评价指标”“红外小目标检测中的一些评价参数”说明大家都很关心怎么算mAP。我这里讲三个最核心的点。第一mAP计算需要按类别分别统计。对每个类别先把所有测试图片上该类的检测结果按置信度从高到低排序然后逐一计算precision和recall画PR曲线曲线下的面积就是该类的AP。20个类的AP取平均就是mAP。这个计算过程代码不算复杂但要小心排序和去重的细节建议直接用标准工具库别自己造轮子。torchvision.ops.box_iou和pycocotools都可以处理。第二IoU阈值不同mAP数值完全不一样。VOC用的IoU0.5COCO用的是一系列从0.5到0.95的IoU阈值取平均。所以你在比较不同模型的mAP时一定要先确认评估协议是否一致。Fast R-CNN论文里的70.0%是基于VOC协议拿去和COCO协议的mAP对比没有任何意义。第三评估时要不要做NMS要但是NMS对最终mAP影响比较微妙。NMS阈值设得太低重叠度大的同类别目标会被抑制掉召回率下降AP降低。NMS阈值设得太高会出现大量重复框precision下降。在VOC上0.3到0.5之间都有合理表现。如果你发现某个类别的AP特别低先单独可视化一下这个类别的检测结果看看是漏检还是重复框太多再决定调阈值还是调模型。我个人的习惯是先不调任何后处理参数用论文默认的conf_thresh0.05、nms_thresh0.5跑一遍观察各类别AP分布。然后再根据各类问题做针对性调整。这么做的好处是你可以把模型能力和后处理调优分开看待不会把两者混为一谈。6.4 快速排查速查表为了方便你排查问题我把常见症状和对应思路整理成了一张表。症状可能原因排查与解决损失一直不降学习率过大/过小尝试0.01、0.001、0.0001对比分类正常但回归损失高bbox_targets计算错误检查坐标归一化、宽高取对数逻辑检测框位置偏RoI坐标映射错误检查spatial_scale是否与主干步长一致mAP过低但训练损失正常评估协议不一致确认IoU阈值、类别顺序、NMS逻辑推理速度慢全连接层计算量大用SVD分解或减小RoI数量显存溢出batch过大/图片过大缩小输入尺寸、减少RoI、用梯度累积检测大量重复框NMS阈值太高或未按类NMS降低阈值、确认按类别分别处理这表里的经验基本覆盖了我自己复现过程中遇到的大部分问题。当然代码版本不同、数据不同具体表现会有差异但排查思路是通用的。遇到问题先聚焦在“数据流对不对”上再看“模型结构对不对”最后才会怀疑“优化参数行不行”这个顺序能帮你少走很多弯路。尾声一点实际操作后的体会Fast R-CNN这个模型说实话在今天已经不是最佳实践了你真正上线做检测大概率会选Faster R-CNN、YOLO或者基于Transformer的DETR系列。但我依然建议你有时间就手动复现一遍。因为它是理解两阶段检测器最合适的“最小完备系统”候选区域、特征图共享、RoI Pooling、多任务损失、NMS这些概念在几乎所有的现代检测器里都会以不同形式出现。你把这个模型吃透了后面看Faster R-CNN就是加一个RPN、看Mask R-CNN就是加一个分割分支学习曲线会平缓很多。回看我自己的复现过程最大的收获不是把mAP跑到67%的成就感而是通过手动调参真正理解了为什么候选区域的采样比例要设1:3、为什么回归损失要用smooth L1、为什么NMS要按类别处理。这些经验是读一百篇论文都换不来的。希望这篇博文能帮你少踩几个坑也能让你在动手的时候心里多一份笃定。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。