多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Faster-RCNN车辆行人及交通信号检测实战:从Anchor调参到mAP验证

Faster-RCNN车辆行人及交通信号检测实战:从Anchor调参到mAP验证 简介基于Faster-RCNN的车辆、行人及交通信号目标检测算法Python实现是一份面向目标检测学习与实战的完整资源包。内容覆盖数据集处理、模型训练到验证预测全流程适合具备Python与深度学习基础、希望掌握Faster-RCNN原理及PyTorch实现细节的开发者。包内共89个文件以Python源码、pyc编译文件、JPG示例图片居多另有txt配置、Markdown说明、PDF项目报告及JSON类别文件backbone模块提供ResNet50、MobileNetV2、VGG等特征提取网络network_files实现RPN与RoIHead等核心组件train_utils封装训练验证与COCO评估工具目录层次清晰便于按需查阅。压缩包仅3.61MB便于快速获取目前已有226人学习下载。附带详细注释和项目报告可帮助读者理解关键算法逻辑多GPU训练与混合精度配置降低了实操门槛从数据划分到模型输出均有对应脚本适合作为课程设计或算法复现参考。1. 从“毕业设计标配”到可复现的交通检测工程这份 Faster-RCNN 源码包该怎么用拿到“基于 Faster-RCNN 网络模型的车辆行人及交通信号目标检测算法”这套 Python 源码你其实拿到了一条完整的目标检测落地链路模型结构、数据集、训练脚本、评估报告和逐行注释都在里面了。和现在更流行的 YOLO 系单阶段检测器相比Faster-RCNN 在行人遮挡、信号灯小目标这类场景反而更稳这也是它至今仍被大量课程设计和工程交付选用的原因。这篇笔记不是泛泛讲原理而是按你实际复现这个项目时的操作顺序来写先弄懂它在检测什么、为什么这么设计再走通数据、训练、排错最后落到迁移学习和 mAP 验证上。适合正在做课程设计或毕业设计的学生也适合想用两阶段检测器处理交通场景的开发者。2. Faster-RCNN 调参地图RPN、Anchor、Backbone、ROI Head 各自决定什么2.1 RPN 与 Anchor真正决定检测上限的候选框层Faster-RCNN 的核心贡献是把“生成候选框”这件事也变成了一个可训练的网络也就是 Region Proposal Network区域建议网络。它会在特征图的每个位置上预先铺设一组不同尺寸、不同长宽比的矩形框这些框就是 Anchor锚框。RPN 的任务是对这些锚框做二分类——判断“框里有没有目标”同时做回归——把框的坐标修正到更贴近真实物体。当你面对的是“车辆、行人、交通信号灯”三类目标时Anchor 的预设几乎决定了模型的上限。车辆的宽高比通常是 1.5:1 到 2:1行人是典型的 0.4:1 到 0.6:1 的瘦高形信号灯则是 40×40 到 80×80 像素的小方块。如果沿用通用检测里默认的 Anchor 参数比如常见的ratios[0.5, 1.0, 2.0]、scales[8, 16, 32]小尺寸的信号灯几乎不会被 RPN 采到正样本后面的 ROI Head 自然也就学不到信号灯特征。import numpy as np # 以 backbone 输出 stride16 的特征图为例生成 anchor 的基础尺寸 base_size 16 ratios [0.5, 1.0, 2.0] # 宽高比矮宽 / 方形 / 高瘦 scales [8, 16, 32] # 相对基础尺寸的缩放倍数 def generate_base_anchors(): anchors [] for scale in scales: for ratio in ratios: area (base_size * scale) ** 2 w np.sqrt(area / ratio) h w * ratio anchors.append([-w / 2, -h / 2, w / 2, h / 2]) return np.array(anchors, dtypenp.float32) print(generate_base_anchors())这段代码演示的是锚框在图像坐标下的宽度和高度如何由 scale 与 ratio 组合计算出来。注意 RPN 是在特征图上做滑窗所以每个 anchor 对应到原图时实际感受野会被 stride 放大。改参数时你要观察本项目中数据集里三类目标的像素分布如果样本里行人高度普遍在 200 像素以上而 anchor 最大边只有 512 像素那就是浪费了 RPN 的表达能力反之信号灯多数小于 64 像素而最小 anchor 是 128小目标就永远没有正样本。实践里我一般会写一个脚本统计训练集标注框的 width/height再反推合适的 ratios 和 scales。RPN 的训练还涉及正负样本的定义与真实框 IoU 大于 0.7 的 anchor 视为正样本小于 0.3 视为负样本处于中间的一律不参与训练。这个阈值是经典设定一般不需要动。真正值得调的是每张图采样 anchor 的数量常见是 256以及正负样本比常见 1:1。如果你的数据里小目标很多256 个采样里纯随机抽样会漏掉大量小目标正样本这时建议把采样数提到 512并把正样本比例放宽到 1:2。2.2 Backbone 与 ROI Head检测精度瓶颈到底在哪一层RPN 只负责“找到可能是目标的地方”真正对候选框做分类和精确回归的是 ROI Head。ROI Head 的输入来自 backbone 提特征后RPN 从特征图上裁剪出的候选区域。这个过程里有两个常见误区一个是盲目换更深的 backbone另一个是不理解 ROI 池化对目标尺寸的敏感性。这套源码如果用的是 ResNet50 FPN 的组合那它的多尺度能力是够用的。FPN特征金字塔网络的价值在于信号灯这类小目标在高分辨率浅层特征上检测行人车辆靠深层语义特征不同尺寸走不同分支。你去看代码里的fpn相关实现时会发现RPN 和 ROI Head 都不是只吃一层特征而是在P2到P5这四层上分别做预测然后再汇总。如果你只是把 backbone 从 ResNet50 换成 ResNet101 而不动 FPN得到的提升往往有限因为检测头才是吃参数的大户。# 常见的目标检测框架里ROI Head 的分类与回归输出层设计 # self.cls_score 负责分类出类别数 背景self.bbox_pred 负责回归框坐标 import torch.nn as nn num_classes 4 # 背景 车辆 行人 交通信号灯 box_features_dim 1024 cls_score nn.Linear(box_features_dim, num_classes) bbox_pred nn.Linear(box_features_dim, num_classes * 4) # 每个类别一组 (dx, dy, dw, dh)这里bbox_pred输出维度是num_classes * 4意味着每个类别都有一组边界框回归参数。训练时只有真实类别对应的那组回归损失会被计算其他类别的输出会被 mask 掉。很多第一次读源码的人在这里纠结“为什么框回归要按类别分开”其实就是为了让模型学到“行人的框调整方式和车辆的框调整方式不同”——车辆偏横向扩展行人偏纵向。当你后面做迁移学习只想检测车辆和行人时把num_classes改成 3 的时候这一层的输出维度必须同步改否则加载预训练权重会报形状不匹配。池化方式同样影响小目标。经典 RoIPooling 有两次量化误差一次是把候选框坐标映射到特征图时取整一次是区域划分时取整。对 30×30 像素的信号灯来说一个像素的偏差可能就是 10% 的面积偏差。ROIAlign 改用双线性插值去消掉这两次量化对小目标更友好。你可以在源码里搜roi_align或RoIAlign确认这套工程包用的是哪一版如果代码里还是RoIPooling建议直接替换成torchvision.ops.RoIAlign这个改动对信号灯检测的收益非常直接。2.3 Faster-RCNN vs YOLO 系为什么这个工程不选 YOLOv8/v11 这类热门方案既然现在 YOLOv8、YOLOv11 的生态和部署工具链更成熟为什么还会有工程选 Faster-RCNN因为检测场景不一样。交通视频里行人之间互相遮挡、信号灯在远处只有十几个像素单阶段检测器在密集小目标场景下的漏检率通常高于两阶段。Faster-RCNN 先“粗筛候选框”再“细分类”的结构天然对遮挡和小目标更友好代价是推理速度慢一个量级——但课程设计、离线分析、交通违章取证这类场景根本不要求实时。维度Faster-RCNN两阶段YOLOv8/v11单阶段小目标检测强ROI 细分类兜底弱依赖多尺度融合遮挡场景强候选框二次筛选中漏检率偏高推理速度慢不适合实时视频流快轻松跑 60 FPS训练难度前置依赖多调参点分散开箱即用生态完善部署友好度中等需处理 ROI 算子高NCNN/TensorRT 支持好我的建议是如果你后续要做的产品是实时路口分析那就把这个工程当学习材料真正部署还是换 YOLO 系但如果你要做的是“证据留存、判定是否越线/闯灯”这类离线任务Faster-RCNN 的精度收益值得保留。这个选择没有谁吊打谁只有谁匹配你的约束条件。热词里那些“YOLOv8 训练自己的数据集”教程满天飞反而说明两阶段检测器在工业级精度敏感场景里始终有它的生态位。3. 数据集与标注把车辆、行人、信号灯装进 VOC 格式的完整流程3.1 VOC 格式目录结构xml 里哪些字段必须对齐大多数目标检测工程包都会采用 PASCAL VOC 的组织方式因为 mmdetection、Detectron2 和 torchvision 的 detection 接口全都默认读这套结构。你拿到这套源码后第一步不是急着跑训练而是确认它的数据目录是不是这样的dataset/ ├── Annotations/ # 每张图像对应一个同名的 .xml 标注文件 ├── JPEGImages/ # 原始图像jpg 或 png └── ImageSets/ └── Main/ ├── train.txt # 训练集图像文件名不带扩展名 ├── val.txt # 验证集图像文件名 └── test.txt # 测试集图像文件名可选Annotations 里的每个 xml 文件至少要有size、object和bndbox三组核心信息。size 记录图像宽高和通道数object 节点里 name 是类别名bndbox 是左上角和右下角坐标。交通信号灯场景还有一个容易丢的字段是difficult表示这个目标是否因遮挡、太小或模糊而难以辨认。训练时一定不要把 difficult 目标当作正样本参与训练否则会让模型在模糊目标上产生高置信度的错误预测验证时也要排除它否则 mAP 会被拉低。annotation filenameimg_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object nametraffic_light/name difficult0/difficult bndbox xmin1560/xmin ymin312/ymin xmax1608/xmax ymax360/ymax /bndbox /object /annotation注意一个细节name是字符串最终传给模型的类别索引由训练脚本里的CLASSES列表按顺序映射。如果工程包自带的CLASSES (__background__, vehicle, person, traffic_light)那 xml 里的小写命名必须严格一致。你的原始标注如果是从其他工具导出的很可能类名是中文或带空格这会在dataset.py里查表时静默失败图像被白白跳过。遇到这种问题优先看日志里skip的统计数而不是逐张检查图片。还有一类常见情况是数据集的标注格式并不是 VOC。比如车牌检测的 CCPD 数据集是纯文件名编码标注遥感舰船检测的 HRSC2016 是 XML 加旋转框坐标DOTA 更是连多边形顶点都给了。遇到这类公开数据集你要写一个转换脚本把坐标转成 bndbox 的 xmin/ymin/xmax/ymax 格式并丢弃旋转信息或单独存角度字段。标题里说这份源码包“带数据集”大概率是已经帮你做好了对齐如果你后续要自己扩充数据这一节就是你绕不开的功课。3.2 训练/验证集划分脚本随机种子与类别均衡的双重考量划分数据集的朴素做法是random.shuffle后按比例切一刀但交通场景这么做很容易翻车。路口视频连续帧之间高度相关如果某一秒的 30 帧里恰好有 20 帧被随机分进了训练集、另外 10 帧分进了验证集验证集就和训练集“长得很像”测出来的指标虚高换一段真实数据立刻现原形。正确做法是先按视频片段分组再以片段为单位划分。import os import random from collections import defaultdict random.seed(42) # 固定种子保证每次划分结果一致 annotation_dir dataset/Annotations main_dir dataset/ImageSets/Main # 按视频片段前缀分组避免同一片段的连续帧同时出现在训练集和验证集 groups defaultdict(list) for fname in os.listdir(annotation_dir): if not fname.endswith(.xml): continue # 假设文件名格式为 clip01_frame0037.xml前缀取 clip01 prefix fname.split(_)[0] groups[prefix].append(fname.replace(.xml, )) all_clips list(groups.keys()) random.shuffle(all_clips) split_idx int(len(all_clips) * 0.8) # 80% 片段进训练集 train_files [img for clip in all_clips[:split_idx] for img in groups[clip]] val_files [img for clip in all_clips[split_idx:] for img in groups[clip]] os.makedirs(main_dir, exist_okTrue) with open(f{main_dir}/train.txt, w) as f: f.write(\n.join(train_files)) with open(f{main_dir}/val.txt, w) as f: f.write(\n.join(val_files)) print(ftrain: {len(train_files)} images, val: {len(val_files)} images)这段脚本的核心是defaultdict按前缀分组再对“片段”而不是“单帧”做随机切分。random.seed(42)保证了任何人按相同代码执行都得到相同结果这对复现项目报告里的指标非常关键——很多报告只写“train 800/val 200”不写划分逻辑就是给自己留了随时改数据的后门。参数上80/20 是交通检测场景比较稳的起点如果样本总量小少于 2000 张我会改成五折交叉验证工程包里报告里的数值才有说服力。除了按时间片段划分类别均衡也要检查。车辆在数据里可能占 70%行人 20%信号灯只有 10%。如果信号灯样本里红灯、黄灯、绿灯又各占一半实际每个类别的有效样本就很少了。划分完数据集后写个小循环去打印每个 txt 里每类目标的统计数量确保训练集和验证集的类别分布大致相同。否则验证集里信号灯特别多、训练集里特别少模型会直接假装看不见信号灯val loss 再低也是假的。3.3 数据增广的边界交通场景哪些操作会帮倒忙数据增广是缓解样本不足最直接的手段但交通场景里很多按惯性加上的增广操作其实是负优化。先列几个安全的选择水平翻转、亮度对比度扰动、小角度旋转。这三个操作都在模拟真实路口摄像头的环境变化。晚上和白天的光照差别巨大训练集里如果全是白天数据亮度扰动几乎决定了模型夜间能不能用。再列几个要谨慎的随机裁剪最危险因为信号灯往往只有几十像素裁剪时极易把灯体本身裁掉一半相当于给模型喂了大量”背景无目标“的负样本训练出来的分类器会普遍低置信度马赛克拼接这类针对密集目标的增广只对 YOLO 系效果好Faster-RCNN 的 ROI Head 对拼接边界的伪轮廓很敏感反而会诱导 RPN 去关注拼接缝处的假边缘垂直翻转在交通场景等于把天和地倒过来模型学到的几何先验被打乱推理效果会明显变差。还有一个经常被忽略的点增广要和标注同步变换。很多初学项目在代码里直接调用 OpenCV 对图像随机翻转却忘了对 bndbox 坐标做同样的变换结果歪斜的框被当成正样本训练模型回归头直接学歪。这个问题在自定义 Dataset 的处理函数里反复出现每加一种增广都要在可视化脚本里抽查 20 张增广后的图像确认框和目标是严格贴合的状态。4. 训练配置与代码阅读路径超参、断点续训与项目报告怎么配套看4.1 拿到源码后的五个阅读入口别先从 train.py 开始读工程包的价值在于“详细注释”但注释多不等于结构清晰。我拿到这类代码包后一般不会从 train.py 读起那是最杂的文件。先按顺序看五个文件比较不容易迷失第一是模型构建文件常见命名model.py或network.py确认 backbone、FPN、RPN、ROI Head 用的是哪个框架实现比如是 torchvision 自带的fasterrcnn_resnet50_fpn还是 mmdetection 的FasterRCNN第二是配置文件config.py或options.py看学习率、batch size、anchor 参数、类别列表在哪里可以改第三是数据加载文件dataset.py重点确认 VOC 读取逻辑和 transform 的顺序第四是训练入口只看训练轮数和 checkpoint 保存逻辑第五才是项目报告它应该写清楚数据来源、训练超参、实验结果和结论这份报告就是你判断整个工程可信度的凭据。读项目报告有个实用技巧先看“实验设置”部分有没有写 backbone 的预训练权重来源、优化器参数、训练多少轮。如果报告只贴了 PR 曲线和 mAP 数字不给中间的实验表格和参数配置那这份报告的参考价值要大打折扣——因为任何检测模型只要给足训练时间都能在训练集上刷出好看的曲线关键是报告里敢不敢公布验证集的数值。你后期写自己的报告时也要按这个标准要求自己。4.2 训练超参的推荐起点学习率、batch size、epoch 与 warmup基于 Faster-RCNN 的工程包最常出现的优化器是 SGD 加 momentum 0.9。下面是针对交通场景数据量在 5000 到 20000 张之间的推荐起点这些值来自大量类似项目的真实运行结果不是空想。超参数推荐起点调整方向初始学习率0.001batch size 8 时显存小/震荡大就降 0.0005Warmup 步数500 步数据越杂warmup 越长Momentum0.9通常不动Weight decay0.0001数据少可以降到 0.00005训练轮数24 epoch看 val loss 是否有平台期验证间隔每 2 个 epoch 一次越早发现过拟合越好学习率是整个训练里最玄学但最值得花时间的参数。SGD 的学习率直接乘在梯度上如果 baseline 是 0.001你换成 Adam 系列反而容易在小目标检测上掉点。warmup本质上是在开始的 500 步里把学习率从很小的值线性升到目标值防止 RPN 在初始阶段被大量高 loss 的背景 anchor 带偏。如果你用的框架带有warmup_ratio参数通常设 0.1 到 0.33 起步。batch size 的影响比大多数人以为的大得多。Faster-RCNN 里的 batch size 指的是“每张图上有多少 anchor 参与 RPN 训练”而不是传统分类里的“一次喂几张图”。你看到batch_size8只是图像输入数量真正的 RPN 采样数量要看num_samples512这类参数。如果显存不够优先减小图像的输入尺寸比如从 1333×800 改成 1000×600而不是动 batch size。图像尺寸直接关联小目标在特征图上的像素数信号灯太小的时候再减尺寸等于自废武功。4.3 断点续训与日志记录让三天训练变成可回滚的操作训练断点是交通检测这类长周期训练的基本生存技能。一轮跑完可能要半小时到一小时停电、显存溢出、后台 OOM 都可能让进度归零。源码包里如果带了 checkpoint 机制你要检查保存内容是否包含四件套模型权重、优化器状态、学习率调度器状态、当前轮数。只存模型权重的断点续训相当于重新投胎前功尽弃。# 保存断点的最小完整结构这里的四件套缺一不可 checkpoint { epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), scheduler_state_dict: scheduler.state_dict(), val_mAP: best_mAP, } torch.save(checkpoint, fcheckpoints/epoch_{epoch}_mAP_{best_mAP:.3f}.pth)加载断点时需要注意一个顺序问题先加载模型权重再加载优化器状态最后加载调度器状态。如果反了optimizer 里的学习率记录会和当前配置冲突继续训练时可能出现学习率跳变的诡异现象。另一个实践技巧是保留“最好的模型”和“最后一轮模型”两个存档两者用途不同最后几轮 loss 降低了不代表验证 mAP 更高用过拟合灾难的人都懂。日志记录同样重要。我习惯每轮都记录 train loss、val loss、lr、mAP 四个指标到 CSV 文件再叠加 tensorboard 的曲线可视化。这样做最大的好处是训练结束后你可以回放整条曲线判断“是第几轮开始过拟合的”而不是对着一个孤立的结果文件发呆。特别是当你改了损失函数或数据增广后对比前后两轮训练的曲线形状往往比单看最终 mAP 更能说明问题。5. 避坑指南训练不收敛、漏检、信号灯误报、显存溢出的四类高频问题5.1 损失曲线不降反升先怀疑学习率再怀疑主干网络现象前 500 步 loss 从 2.0 涨到 5.0 以上或者 loss 在 1.5 附近震荡死活下不去。原因绝大多数情况是初始学习率太大SGD 在 loss landscape 上反复横跳。少数情况是预训练权重没加载对——backbone 的state_dict加载失败被静默忽略了。还有一种是学习率没有 warmupRPN 在开头把梯度和 anchor 的坐标回归更新带偏了。解决先看学习率0.001 不收敛就降一个数量级试 0.0003不要小步慢慢调。然后在配置文件里检查pretrainedTrue是否真的生效打印模型第一层的权重数值是否符合典型预训练分布比如 ResNet 的 conv1 权重均值接近 0 但方差明显非零。最后给学习率加上 500 步 warmup曲线基本都能在那个点之后开始往下走。5.2 车辆行人漏检严重anchor 尺寸与 NMS 阈值的连锁反应现象训练 loss 看起来正常但可视化预测时发现远处的行人完全没有框车辆只检到一半信号灯时有时无。原因这类问题往往不是模型没学到特征而是 RPN 就没把对应的候选框生成出来。远距离行人在 1920×1080 图像里像素高度大约 80 到 120如果你的 anchor 最小尺寸是[32, 64, 128]最低一层只能复盖 32 和 64 像素的框中间档 80 到 120 会成为“盲区”。另一个常见原因是 NMS非极大值抑制阈值太高比如 0.7导致密集行人区域里高 IoU 的框被合并过多最终只保留了一个。解决写一个统计脚本打印训练集真实框的宽高分布然后按分位数重设 anchor 尺寸。NMS 阈值一般设 0.5 到 0.6 之间交通场景的密集行人区建议往 0.6 方向调。注意RPN 和 ROI Head 的 NMS 是两套参数改的是 RPN 的nms_thresh还是最终检测的nms_thresh源码注释里通常都会标明不要一次改两个地方导致无法定位问题。5.3 信号灯误报频发类别不均衡与难例挖掘的正确打开方式现象模型把远处的红色招牌、尾灯、交通标志都识别成信号灯精确率低得没法看。原因信号灯在图像里是“小色块”和很多背景元素在颜色纹理上高度相似。当训练数据里信号灯样本占比很低比如只有 5% 的图像包含信号灯时ROI Head 的分类器会把所有“小而亮”的区域都贴成高概率信号灯因为独立判断的特征不充分。这就是典型的正负样本不均衡。解决优先做难例挖掘hard negative mining。在训练的中后期每隔 5 个 epoch 用当前模型去跑训练集里不包含信号灯的图像把预测置信度最高的 false positive 区域作为负样本补充到下一轮训练里。代价是实现复杂一点但提升最显著。轻量做法是把分类阈值的默认值从 0.05 提到 0.3虽然牺牲召回率但能先把精确率救回来让报告里的曲线不至于完全没法看。最后别忘检查训练集里信号灯标注是否齐全漏标率超过 10% 会使模型把已标注的灯当反例学习目标直接被污染。5.4 显存不够时的自救顺序batch、尺度、FP16、梯度累积现象程序跑起来不到十个 step直接弹出CUDA out of memory。原因Faster-RCNN 是出了名的显存大户。backbone 的特征图、RPN 的几百个候选框在 RoI 池化时的中间变量、ROI Head 的分类特征三者叠加让显存占用比同等参数量的分类模型高出好几倍。解决按顺序试四步每一步都能显著减少显存占用。第一步把 batch size 减半从 8 降到 4 通常就够了第二步减小输入图像尺寸从 1333×800 减到 1000×600显存可能直接降到原来的六成第三步开混合精度训练FP16在 torch 里就是加一个 GradScaler 的事显存减半且速度提升第四步如果还想维持大 batch 的稳定性就做梯度累积每 4 个 step 的梯度平均后再更新一次参数代价是训练时间变长。# 混合精度训练的标准写法能有效缓解显存压力 from torch.cuda.amp import autocast, GradScaler scaler GradScaler() for images, targets in dataloader: with autocast(): loss_dict model(images, targets) total_loss sum(loss_dict.values()) scaler.scale(total_loss).backward() scaler.step(optimizer) scaler.update() optimizer.zero_grad()注意这里scaler.step(optimizer)是反直觉的它先判断梯度里有没有 inf 或 nan有的话会跳过这次参数更新并缩小 scale避免loss 爆掉。如果你用过固定写法loss.backward(); optimizer.step()换成这段代码后整个训练的稳定性会明显提升。最后强调一句混合精度并不是万能药如果你发现训练 loss 一直不降且数据本身是半精度下容易溢出的场景FP16 加不加要权衡别迷信。6. 进阶技巧迁移学习压缩训练成本 用 mAP 脚本量化验证6.1 迁移学习的正确姿势冻住哪几层最省力如果你手里的工程包自带数据集太小或者你只想检测“车辆和行人”两类任务那就别从头硬训。拿 ImageNet 或 COCO 预训练权重做初始化然后冻结不同层级的策略直接影响收敛速度和最终精度。我的经验是小数据集小于 2 千张时冻结 backbone 的 stem 和前两个 stage只训练 FPN、RPN 和 ROI Head数据量中等5 千到 1 万张时全部解冻但把 backbone 学习率乘以 0.1数据量很大时直接全部解冻正常训。时间复杂度也是迁移学习的考量。全套解冻在多卡机器上可能跑两天冻结主干只训练检测头一个小时代就能看到可信的验证结果。这也符合项目报告的试验节奏——先快速验证思路再逐步解冻刷精度。6.2 一个可复用的 mAP 评估脚本最后给你一套 mAP 计算的骨架代码替换成实际预测结果就能用。这里按 VOC 的 0.5 IoU 标准算单类 AP多类别的平均就是整体 mAP。# 简化版单类别 AP 计算输入预测框列表和真实框列表 def voc_ap(recalls, precisions): 按 11 点插值法计算 AP也可换成积分法 ap 0.0 for t in np.linspace(0, 1, 11): mask recalls t if mask.any(): ap np.max(precisions[mask]) / 11 return ap # 实际使用时的循环每张图按置信度排序预测框用 IoU 匹配真实框 # IoU 0.5 记为 TP否则 FP漏检的真实框为 FN评估脚本里的关键参数是 IoU 阈值。VOC 历来用 0.5COCO 用 0.5:0.95 的平均值两个指标放在同一张报告表里时数值差距巨大写报告前先搞清楚自己用的是哪把尺子。我当年第一次做交通检测报告时只写了“准确率 97%”被打回来重写之后每次训练开始前先把评估脚本写好、把指标口径写清楚才避免重复劳动。现在拿到任何一份目标检测工程包我的固定习惯是先跑一个 epoch 验证数据加载和 loss 计算链路再去看报告里的最终指标最后才动 anchor 和迁移学习。这套流程帮我避开了无数次“训了三天发现类别列表顺序错了”之类的翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表