
简介基于Faster-RCNN网络模型的车辆、行人及交通信号目标检测算法是一份面向计算机视觉初学者与目标检测开发者的完整实战资源。资源涵盖完整Python源码、配套数据集、项目报告及详细注释可直接用于交通场景下多类别目标的检测训练与推理演示。压缩包共89个文件以30个py源码和36个pyc编译文件为核心代码辅以jpg测试图像、txt说明文本、md文档、pdf项目报告及json类别映射文件整体大小仅3.61MB下载与部署非常轻量。代码严格按功能模块划分包含backbone特征提取网络支持ResNet50、MobileNetV2、VGG、network_files中的Faster R-CNN与RPN核心模块、train_utils训练验证工具以及自定义数据集读取脚本并提供了train_res50_fpn.py、predict.py、validation.py等一键运行入口注释细致适合对照学习。目前已有226人学习浏览若在Ubuntu/CentOS系统配合GPU环境使用Pytorch1.6及以上版本即可顺利运行是入门Faster-RCNN并动手实践目标检测的不错参考。1. 把 Faster-RCNN 跑通车辆行人交通信号检测这份源码包到底值不值得下目标检测这个方向YOLO 系列现在确实火但在交通场景里Faster-RCNN 依然是很多工程项目的主力。原因很直接两阶段检测在精度上比单阶段稳尤其对行人这种小目标、密集目标RPN 先提候选框再分类回归误检率控制得好。这份资源提供的是完整可跑的 Faster-RCNN 实现支持 resnet50、mobilenetv2、vgg 三种特征提取骨干自带数据集切分脚本、训练验证脚本、多 GPU 训练脚本和详细注释还附了项目报告和 mAP 记录。不是那种只丢几个 .py 的残缺包是直接能动手训练一版模型出来的完整工程。适合谁想学 Faster-RCNN 原理但不想从零手写网络的人做课程设计需要可复现项目的学生以及要在自有数据集上做车辆/行人/交通信号检测的工程入门者。我拆完整个包之后先把它的文件结构和核心实现逻辑理清楚再把训练到验证的完整流程走一遍最后把最容易翻车的坑列出来——这几个坑我几乎每个都踩过。2. 网络结构拆解RPN、RoIHead 和骨干网络的分工与选型2.1 从 network_files 看懂 Faster-RCNN 的模块划分这个包的network_files目录是我最先看的地方因为它直接对应 Faster-RCNN 的经典结构RPN 在做区域提议RoIHead 在做分类和回归image_list 和 boxes 负责数据组织。值得先说的是rpn_function.py里的RegionProposalNetwork类它完成了 anchor 的生成、前景背景二分类和 bounding box 回归。RPN 的核心逻辑简单说就是在特征图的每个位置上铺一组不同尺度和长宽比的 anchor然后预测这些 anchor 是前景还是背景同时回归出更精确的候选框。# rpn_function.py 核心逻辑简化 proposals self.rpn_head(anchors, pred_obj_logits, pred_bbox_deltas) # self.rpn_head 是 RPNHead 实例 # anchors 来自 AnchorGenerator负责生成不同尺度/长宽比的 anchor # pred_obj_logits 是所有 anchor 的前景分数 # pred_bbox_deltas 是所有 anchor 的坐标修正量这里的proposals就是 RPN 输出的候选区域后面会经过filter_proposals按分数排序、做 NMS、限制数量。我一般把 RPN 的输出数量关注在 2000 附近训练时多一些没关系推理时可以降到 300 左右提速。image_list.py里的ImageList类处理的是批量图片尺寸不一致的问题它把所有图 resize 到同一尺寸再做后续计算这是从 torchvision 参考实现里继承下来的做法理解这个结构对调试 shape 不匹配的问题很有帮助。2.2 三种骨干网络的选型逻辑resnet50 是默认首选backbone目录提供了三份特征提取网络resnet50_fpn_model.py、mobilenetv2_model.py、vgg_model.py。配套的feature_pyramid_network.py实现 FPN 多尺度特征融合。在交通目标检测场景里我的建议是直接用 resnet50 FPN 作为首选配置因为车辆和行人的尺寸差异大交通信号灯又是极小目标FPN 的多尺度特征能同时照顾到不同尺寸的目标。mobilenetv2 适合嵌入式或推理速度要求高的场景vgg 在三者中精度和速度都不占优。# train_res50_fpn.py 中的骨干加载部分 backbone resnet50_fpn_backbone() # 返回的是带 FPN 的 ResNet50 特征提取网络 model FasterRCNN(backbonebackbone, num_classesargs.num_classes 1) # num_classes 需要 1因为 Faster-RCNN 默认有背景类这里有个容易忽略的点num_classes必须加上背景类。数据集的类别是车辆、行人、交通信号三类那num_classes要传 4。这个包在pascal_voc_classes.json里维护类别映射切数据的时候也是按这个文件走的两个地方必须保持一致不然训练出来类别索引是错位的。2.3 定制数据集读取my_dataset.py 的标注格式约定my_dataset.py是自定义 Dataset负责把图片和标注文件读进来。这个工程的数据集是 PASCAL VOC 格式每张图对应一个 XML 标注文件。默认的split_data.py会把数据集按比例切分成训练集和验证集我拆包的时候看它默认是 8:2 切分这个比例对交通场景数据集来说够用。# split_data.py 核心逻辑 train_percent 0.8 # 按 8:2 切分训练集和验证集 # 生成 train.txt 和 val.txt 两个索引文件 # 每一行是图片路径不含扩展名如果你要换自己的数据集核心工作是把标注转成 VOC 格式然后用split_data.py重新生成索引文件。常见做法是先用 LabelImg 标注输出 PASCAL VOC 格式的 XML然后放到VOCdevkit对应的目录结构里。这个包没有给你标注工具但数据组织方式是标准的照着 VOC 的目录规范摆放就行。2.4 训练入口参数说明从 train_res50_fpn.py 看完整训练流程训练脚本的参数集中在train_res50_fpn.py的命令行参数里。我把它拆开后列成了参数表方便新手直接照抄配置。参数默认值说明--data-path默认指向 VOC 数据集目录训练数据根目录--num-classes按数据集实际类别填写不含背景类脚本内部会 1--epochs通常 20~30训练轮数--batch-size2~4受显存限制8G 显存建议 2--lr0.01 左右初始学习率--output-dir默认 save_weights模型保存目录--pretrainedTrue加载 ImageNet 预训练权重强烈建议开启# 单卡训练示例 python train_res50_fpn.py --data-path /path/to/VOCdevkit --num-classes 3 --epochs 30 --batch-size 2 --lr 0.005训练过程中模型权重会按 epoch 保存到save_weights目录每轮的 loss 曲线可以通过plot_curve.py画出来。record_mAP.txt记录了每个 epoch 的验证 mAP我每次训练完第一件事就是看这个文件里 mAP 是否稳步上升而不是只看 loss 下降——loss 下降但 mAP 震荡的情况我遇到过太多次了。3. 环境配置与数据准备从零把训练链路跑通3.1 版本组合的选择Python 3.8 Pytorch 1.6 以上是安全区环境这块项目说明里写得很明确Python 3.6/3.7/3.8Pytorch 必须 1.6.0 或以上因为混合精度训练是 1.6 才正式支持的。我实测下来 Python 3.8 Pytorch 1.8 的组合最稳。操作系统建议 UbuntuWindows 能跑但 pycocotools 的安装会多出不少事。Windows 用户直接用pip install pycocotools-windows这个包不需要额外装 Visual Studio 就能编过。提示GPU 训练是硬性建议。CPU 训练这个工程不是不能跑但一个 epoch 可能要几小时完全没有实战意义。显存 8G 以下的卡建议 batch-size 设 2不然显存溢出直接 OOM。# requirements.txt 安装 pip install -r requirements.txt # 核心依赖pytorch1.6.0, torchvision, pycocotools, opencv-python, Pillow装 pycocotools 是训练脚本里 coco_eval 的硬依赖不装的话训练能跑但验证阶段会报 ImportError。Linux 上直接pip install pycocotools就行如果编译报错先装sudo apt install python3-dev再试。3.2 数据集目录摆放VOC 格式必须严格对齐数据集目录结构必须严格遵循 PASCAL VOC 的规范这个包里的 dataset 已经按照正确的结构组织好了。如果你要换自己的数据目录长这样VOCdevkit/ ├── VOC2007/ │ ├── JPEGImages/ # 所有原图 │ ├── Annotations/ # 所有 XML 标注文件 │ ├── ImageSets/ │ │ └── Main/ # train.txt, val.txtmy_dataset.py读取数据时就是按这个结构找JPEGImages和Annotations的。最容易翻车的地方是路径拼接错一层——训练时日志里的图片数量为 0或者读到不存在的文件基本就是目录层级不对。3.3 训练启动到产物生成从命令行到 save_weights 目录训练脚本跑起来之后产物主要在save_weights目录每个 epoch 结束会保存一份.pth权重文件另外record_mAP.txt会实时追加这个 epoch 的验证结果。我在实际项目中习惯把脚本改成只保留最后一个 epoch 的权重避免训练几十轮后磁盘被占满。# 查看训练过程输出 tail -f record_mAP.txt # 每行格式类似: [epoch: 15] mAP: 0.782, lr: 0.002验证阶段的输出也值得留意validation.py会逐类打印 AP 值我的习惯是分车辆、行人、交通信号三个类别分别看 AP不要只看整体 mAP。行人 AP 明显低于车辆是很常见的这提示你可能需要增加行人样本或调整 FPN 中小目标的特征融合权重。4. 推理预测与模型验证predict.py 的使用和可视化输出4.1 加载权重做单图检测predict.py 的两种写法predict.py是单张图片推理脚本也是我第一次跑通整个项目的关键步骤。模型的加载逻辑和训练脚本保持一致用create_model创建网络结构然后load_state_dict加载权重。# predict.py 核心推理逻辑 model.eval() # 切换到 eval 模式关闭 dropout 和 BN 的 training 统计 with torch.no_grad(): # 推理阶段不计算梯度 predictions model(image_list) # predictions 包含 boxes, labels, scores # boxes 是预测框坐标scores 是置信度分数推理结果会通过draw_box_utils.py画到原图上保存为_result.jpg文件。test目录里已经放好了几张测试图和对应的结果图我拆包时直接用训练好的权重跑了一遍测试集输出结果和包内自带的result_faster.txt基本一致说明权重和代码版本是匹配的。4.2 置信度阈值和 NMS 参数怎么调推理时有两个参数直接影响可视化效果置信度阈值和 NMS 的 IoU 阈值。默认配置里置信度阈值是 0.5低于这个分数的框会被过滤掉。交通信号灯是小目标检测分数普遍偏低如果发现红绿灯经常漏检把阈值降到 0.3 试试代价是误检会多一点。# draw_box_utils.py 中的阈值设置 threshold 0.5 # 低于此分数的目标将被过滤 # NMS 的 IoU 阈值通常在 0.4~0.5 之间 # 值越小重叠框被抑制得越狠这里有一个我在工程里反复遇到的教训NMS 阈值不要乱调。0.4 和 0.5 之间对最终结果的影响远小于置信度阈值。如果出现两个同类目标重叠导致漏检优先调置信度而不是 NMS。行人在密集场景下本来就互相遮挡NMS 阈值调太低会把相邻的行人框并掉一个。4.3 用 validation.py 在测试集上做客观验证光看图不够要客观评估模型好坏得跑validation.py。它会在验证集上逐类计算 AP最后汇总 mAP。包内record_mAP.txt记录了一份训练过程中的 mAP 数据我看了下数值在默认数据集上训练到 30 个 epoch 左右整体 mAP 在 0.7 以上是正常的其中车辆的 AP 通常最高交通信号灯最低——这个现象和交通信号灯尺寸小、样本数量少直接相关。python validation.py --weights save_weights/model_15.pth --num-classes 3验证脚本输出的逐类 AP 表格我建议每次训练完都截图保存。对比不同版本权重的逐类 AP能看出来你调参改的是哪个类别的效果比单看一个 mAP 数字有用得多。5. 常见问题与避坑训练翻车现场排查手册5.1 GPU 显存溢出OOM现象训练到第几个 batch 就报CUDA out of memory程序直接退出。反复调整batch_size也没有根本改善。原因交通场景的原始图片分辨率普遍偏高例如 1920x1080 的输入图经过 FPN 多尺度处理后中间特征图占用非常大。batch_size2在 ResNet50 FPN 结构下也是比较极限的配置根因是输入尺寸太大不是网络本身的问题。解决我采用的方案是先把训练图片最长边缩放到 960 左右。这个包的transforms.py里有 resize 逻辑修改max_size参数即可。代价是检测精度略有损失但显存占用大幅下降训练速度提上来之后整体收益是正的。5.2 训练 loss 降不下去现象loss 在前几个 epoch 快速下降后进入平台期后续训练 mAP 始终不涨。原因最常见的是学习率设置偏大或数据集类别不均衡。交通场景里车辆和行人的样本量可能是交通信号灯的几十倍模型把大部分精力花在了车辆上。解决先把lr从 0.01 降到 0.005 试跑 5 个 epoch观察 loss 曲线是否继续下降。类别不均衡的问题可以给交通信号灯类别增加样本或使用train_multi_GPU.py里的多卡训练来加速迭代实验。我一般不推荐上来就用进阶的损失函数修改先调 lr 和 epochs效果不够再加数据集增强。5.3 预测结果全部是背景类现象推理时 labels 全部为 0背景输出的框几乎没有有效目标。原因90% 是类别数设置错了。--num-classes 3传入后脚本内部会 1 变成 4但你的数据集实际只有 2 类那第 3 类就永远不会出现实体目标模型会把所有预测归到背景或错位类别。解决先确认pascal_voc_classes.json里的类别列表和数据集标注完全一致再确认传给训练脚本的num_classes等于列表长度。我习惯先打印一次训练集的类标签分布确认每个类别都有足够的样本再开训练。5.4 pycocotools 安装失败现象Linux 上报gcc: error: x86_64-linux-gnu-gcc: No such fileWindows 上报编译错误。原因pycocotools 需要编译 Cython 扩展系统里缺少编译工具链。Windows 上很多人卡在这一步是去折腾 Visual Studio Build Tools其实没必要。解决Linux 上执行sudo apt install build-essential python3-dev后重装。Windows 上直接用pip install pycocotools-windows这个包已经是编译好的二进制不需要本地编译环境。装完验证方式python -c from pycocotools.coco import COCO不报错即成功。5.5 自定义数据集训练时读不到图片现象训练日志显示样本数量为 0或者报FileNotFoundError。原因my_dataset.py里的路径是基于root参数拼接的VOC 目录结构少一层就会找不到 JPEGImages 和 Annotations。另一个常见原因是split_data.py生成的是相对路径如果你移动了数据集目录索引文件里的路径就失效了。解决把数据集的绝对路径传给--data-path参数千万别传相对路径。移动数据集后重新跑一遍split_data.py重新生成索引不要手动编辑 train.txt 和 val.txt。6. 把训练结果导出成可用的检测服务一个实用的部署思路训练出满意的权重之后真正要落到项目里需要把它封装成对外接口。我自己习惯做一个简单的推理类把模型加载、预处理、后处理封装成一个detect()方法这样不管是接摄像头流还是批量处理图片都方便。# 部署推理类封装示例 import torch import numpy as np from PIL import Image class TrafficDetector: def __init__(self, weights_path, num_classes, devicecuda): self.device torch.device(device if torch.cuda.is_available() else cpu) self.model create_model(num_classes).to(self.device) self.model.load_state_dict(torch.load(weights_path, map_locationself.device)) self.model.eval() self.classes [background, vehicle, person, traffic_light] def detect(self, img_path, score_threshold0.5): # 预处理读图、resize、转 tensor img Image.open(img_path).convert(RGB) img_tensor self.transform(img).unsqueeze(0).to(self.device) with torch.no_grad(): pred self.model(img_tensor)[0] # 按置信度阈值过滤 keep pred[scores] score_threshold boxes pred[boxes][keep].cpu().numpy() labels pred[labels][keep].cpu().numpy() scores pred[scores][keep].cpu().numpy() return boxes, labels, scores一是模型加载和推理在__init__完成后就把网络结构固定下来了后续调用detect()不需要再碰网络逻辑性能损耗最小。二是map_location参数保证了服务器上没有 GPU 也能用 CPU 做推理这在部署阶段很关键。三是推理循环里一定要包torch.no_grad()不包的话显存会被计算图慢慢拖满跑批量检测到后面就会 OOM。调用这个类的方式很简单对着测试图直接调detect()就能拿到框坐标。出来的boxes可以直接丢给 OpenCV 画框也可以转成 JSON 供 Web 接口返回。我一般在接入摄像头时把视频帧按固定间隔抽帧传入detect()里做目标检测再把结果叠加到原始帧上。要注意的是推理耗时——ResNet50 骨干在 GPU 上单帧大概 50-80 毫秒CPU 上会到 1-2 秒想实时得用 TensorRT 或 ONNX 加速这是后话。最后说我从这个项目里沉淀下来的一个习惯每次跑一个新的数据集我会先跑 5 个 epoch 做一个快速实验确认 loss 曲线正常走了、mAP 表格能出数、推理脚本能画出结果图才敢放心跑完整的 30 个 epoch。因为一次完整训练在单卡上可能要花几个小时前面哪个环节断了都白等。这个做法让我少熬了很多个通宵从那以后我每次拿到新的目标检测工程项目都强制走一遍「小 epoch 冒烟 → 完整训练 → 逐类 AP 对比」的流程希望帮到你。本文还有配套的精品资源点击获取