ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLOv5多尺度改造:交通灯小目标检测的优化实践

YOLOv5多尺度改造:交通灯小目标检测的优化实践 简介面向自动驾驶与目标检测领域的研究者、算法工程师及相关专业学生这份文档系统阐述一种针对交通灯尺度小、环境复杂等难题而提出的多尺度YOLOv5检测算法。内容完整覆盖核心设计思路采用复合数据增强增加输入信息量以多尺度训练替代固定尺度训练来均衡模型学习能力并构建4倍、8倍、16倍、32倍下采样信息融合的多尺度特征融合网络同时引入远跳链接提升小目标检测能力。文中还给出了数据集构建、消融实验及结果分析并附有中英文摘要与关键词便于快速把握研究脉络。资源为1个docx文档压缩包大小约1.6MB内容紧凑、结构清晰适合需要借鉴交通灯检测改进思路或开展相关实验的读者。目前已有194人学习具备一定参考价值。1. 为什么交通灯检测要把 YOLOv5 拆成多尺度交通灯是目标检测里一个非常特殊的小目标场景。城市道路上的红绿灯通常只有几十个像素宽一辆车在 50 米外看到的交通灯可能只占画面的 0.5%而同一张图里往往同时出现近处的高清灯头、中距离的模糊灯珠和远处近乎噪声的信号点。单模型、单尺度特征图很难同时覆盖这种目标尺寸分布。多尺度 YOLOv5 的交通灯检测算法核心就是在 YOLOv5 的 FPNPAN 结构上把特征层选择、锚框匹配和训练采样策略重新对齐到交通灯的实际尺度分布。它解决的不是「用 YOLOv5 跑通一个检测」而是让模型在小目标召回率和误检率之间找到可落地的平衡点。这篇文章从特征层与锚框的关系讲起给出一套可复现的多尺度改造方案覆盖数据增强、训练参数和部署阶段的优化。读者需要具备基础的 YOLOv5 使用经验我会把参数背后的动机和调法讲清楚不列无意义的默认值。2. 特征金字塔的尺度分配交通灯应该交给哪一层2.1 P3/P4/P5 与目标尺寸的对应关系YOLOv5 默认使用 P3、P4、P5 三层特征图做检测分别对应输入尺寸的 1/8、1/16、1/32。在 640×640 输入下P3 的每个网格对应原图 8×8 像素区域适合检测 8 到 32 像素的小目标P4 对应 16×16 区域适合 32 到 128 像素的中目标P5 对应 32×32 区域适合 128 像素以上的大目标。交通灯的灯头宽度在城市道路视频里通常落在 12 到 60 像素之间这正好压在 P3 和 P4 的边界上。直接把模型默认的锚框套上去会导致大量交通灯被分到偏大的锚框组里小目标的 IoU 很容易低于正样本阈值而被丢弃。# YOLOv5 默认锚框以 COCO 预训练为例 anchors: - [10, 13, 16, 30, 33, 23] # P3 小目标 - [30, 61, 62, 45, 59, 119] # P4 中目标 - [116, 90, 156, 198, 373, 326] # P5 大目标这段配置里 P5 的最小锚框是 116×90在交通灯场景里几乎不会匹配到任何真实框。多尺度改造的第一步不是加检测头而是重新检查锚框和特征层的匹配关系。2.2 用聚类重新生成锚框YOLOv5 提供了自动计算锚框的脚本但默认设置下聚类结果仍然偏向 COCO 的尺度分布。常见做法是单独统计交通灯数据集的真实框尺寸再做一次 K-Means 聚类。python utils/autoanchor.py --cfg models/yolov5s.yaml --data dataset.yaml运行之后脚本会在终端输出新的锚框并显示每个锚框与真实框的平均 IoU。如果平均 IoU 低于 0.7说明数据集中存在大量极端长宽比的目标需要检查标注框是否包含倒计时数字或 LED 灯珠这类细长区域。交通灯的标注策略容易踩坑把整个灯头框进去还是连黑底外壳一起标注我一般建议标注发光的灯珠区域去除灯壳和横杆的背景像素。这样能显著提高小目标的正样本占比聚类出来的锚框也更集中。聚类结果中如果 P3 层出现 8×8 甚至 6×6 的锚框不要觉得奇怪这恰好说明数据里有大量极小目标。注意autoanchor 脚本算出的锚框是基于原始图片尺寸统计的不是基于缩放后的输入尺寸。如果训练时用 640×640 输入但原始图片是 1920×1080锚框匹配时要保证换算方式一致否则会出现训练时匹配正常、推理时框偏大的问题。2.3 多尺度采样的实际效果光改锚框还不够。YOLOv5 默认启用了多尺度训练每 10 个 batch 随机从 0.5 到 1.5 倍之间缩放输入尺寸。交通灯数据集的原始分辨率普遍较高如果缩放系数下限太低灯头会被压到 4 像素以下标注框可能小于 1 个像素成为无法学习的噪声。输入尺寸最小灯头像素能否有效学习640约 8可以480约 6勉强320约 4不建议实际训练时我会在 data/hyps/hyp.scratch-low.yaml 中调整 scale 相关参数同时把随机裁剪的比例限制在 0.8 倍以上避免交通灯在训练过程中频繁丢失。# hyp.scratch-low.yaml 部分参数修改后 scale: 0.9 # 从默认 0.5 提高到 0.9 hsv_h: 0.015 # 交通灯颜色敏感不宜过度扰动色相 hsv_s: 0.5 # 饱和度扰动保持中等hsv_h 降到 0.015 是为了防止红绿黄三色在增强时互相混淆。城市交通灯的颜色集中在固定色相区间过强的色相抖动会让红灯变成黄灯干扰模型对颜色语义的建模。3. 网络结构调整在 YOLOv5 骨架上扩展检测层3.1 增加 P2 层的收益与代价默认的 P3/P4/P5 层级对极小目标覆盖不足常见的多尺度改造是在原结构上增加一个 P2 输出层。P2 对应 1/4 特征图在 640 输入下每个网格代表原图 4×4 像素可以覆盖 4 到 16 像素的极小目标。改造模型配置文件的主要步骤包括复制 YOLOv5 的 backbone在第 2 个 C3 模块之后引出 P2 特征。在 head 部分增加一个上采样/拼接分支让 P2 特征经过一次融合后进入检测头。更新 anchors 数组为 P2 层分配与它分辨率匹配的小尺寸锚框。修改检测头的 nc 类别数和 ch 通道数。# yolov5s_traffic.yaml 中 head 部分的关键改动 head: - [8, 1, Conv, [256, 3, 1]] - [14, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 6], 1, Concat, [1]] # 拼接 P3 之前的特征 - [C3, [256, False]] # P2 检测头 - [-2, 1, Conv, [128, 1, 1]] - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 2], 1, Concat, [1]] # 拼接 backbone 浅层特征 - [C3, [128, False]] - [17, 1, Detect, [nc, anchors, [64, 128, 256]]]这段配置中[[-1, 2], 1, Concat, [1]]里的2表示从 backbone 的第 2 层引出浅层特征这个索引值需要根据实际 backbone 结构对应调整。加完 P2 之后检测层从 3 个变成 4 个计算量增加约 40%显存占用也会同步上升。P2 层对交通灯这类小目标召回提升非常明显但代价是把大量背景纹理引入检测空间。尤其在树木阴影、车窗反光、广告牌上的红色圆形标志附近模型容易把类似颜色和形状的像素组合误判为交通灯。缓解手段有两种一是在损失函数中调整 P2 层的正样本权重二是依靠后处理 NMS 的置信度阈值过滤。3.2 浅层特征语义不足的补偿策略P2 来自网络的浅层特征图分辨率高但感受野小缺少上下文语义。交通灯检测极度依赖上下文灯头本身是圆形、颜色鲜明但要判断「这是一个交通灯」还是「一个红灯」需要知道它是否挂在道路上方、是否有横杆支撑。增加 P2 层之后必须让浅层特征具备一定的语义信息否则误检很难压住。常见做法是在 P2 分支的 C3 模块里增加注意力机制。不引入重模块的前提下我会在 P2 的 C3 后加一个简单的通道注意力层用全局平均池化生成通道权重与原始特征相乘。这种轻量改造在 Jetson 等边缘设备上也能满足实时性要求。class ChannelAttn(nn.Module): def __init__(self, channels, reduction8): super().__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.fc nn.Sequential( nn.Conv2d(channels, channels // reduction, 1, biasFalse), nn.ReLU(), nn.Conv2d(channels // reduction, channels, 1, biasFalse), nn.Sigmoid() ) def forward(self, x): b, c, _, _ x.size() w self.avg_pool(x).view(b, c) w self.fc(w).view(b, c, 1, 1) return x * w.expand_as(x)这个注意力模块插入 P2 分支的 C3 输出之后、检测头之前。8 倍的通道压缩不会带来明显的速度损失但能帮助模型聚焦发光灯珠区域忽略大面积深色背景。如果数据集中红灯和绿灯的数量严重不平衡注意力机制的稳定性会下滑需要在损失函数中配合类别权重使用。3.3 损失函数里的小目标权重调整YOLOv5 的损失由 box、cls、obj 三部分组成。多尺度结构下小目标在默认设置里贡献的梯度占比偏低因为小目标产生的正样本锚框数量少。为了让 P2 层真正学到东西需要在损失计算中按检测层分配权重。YOLOv5 的实现中通常通过hyp[box]、hyp[cls]、hyp[cls_pw]调节全局权重但要做到按尺度区分需要修改损失函数中的权重系数。实践中可以在训练时用 TensorBoard 观察三个检测层的 obj loss如果 P2 层的 obj loss 明显高于 P3/P4说明小目标没有被正确学习此时# 训练时开启日志观察各层损失 python train.py --data dataset.yaml --cfg models/yolov5s_traffic.yaml \ --weights yolov5s.pt --batch-size 16 --epochs 100 \ --log-imgs 8 --cos-lr观察到的val/obj_loss是各层平均还是合并值取决于版本实现。更直接的做法是别在损失函数里做太多定制先把 P2 的锚框匹配调准。很多场景下 P2 层学不好是因为锚框聚类时的目标尺寸范围没有按层切分干净而不是损失权重不对。4. 数据增强与训练配置小目标场景的专属参数4.1 Mosaic 增强的负作用YOLOv5 的 Mosaic 增强把四张图拼接成一张在常规目标检测中效果显著但对交通灯场景需要谨慎。四张 1080p 城市道路图缩放到 640×640 后单张图中交通灯可能已经缩到 3 像素。Mosaic 拼接还会破坏空间上下文交通灯和道路、车辆的位置关系被切割重排模型学到的是「一个亮色圆点」而不是「道路上方悬挂的信号灯」。我把 Mosaic 的概率从默认的 1.0 降到 0.5 左右同时保留 MixUp 的轻微扰动。这个改动在小规模交通灯数据集上通常能降低 2-3 个百分点的误检率。# hyp.scratch-low.yaml mosaic: 0.5 # 默认 1.0降低后保留真实布局比例 mixup: 0.2 # 轻微混合增强 copy_paste: 0.3 # 如果类别包括倒计时数字牌可以启用4.2 针对灯色的 HSV 增强边界交通灯检测中颜色是比形状更强的判别特征每个交通灯类别几乎绑定一个固定颜色。典型的处理方式是极度压缩 HSV 色相扰动范围但也不能完全关闭因为不同城市、不同厂商的灯珠色温存在肉眼可见的差异。一对合理的数值组合是 hsv_h 0.01、hsv_s 0.4、hsv_v 0.4。这个组合允许亮度和饱和度在合理范围内波动基本保持色相不变。如果把 hsv_h 设为 0模型会对日落后灯光泛白的场景非常脆弱设得过高绿色灯在训练集中可能被增强成灰色造成类别混淆。训练短帧视频时需要额外加入亮度抖动。城市道路上有大量逆光、树影、夜间远光灯干扰固定亮度的训练集在白天模型上部署夜间场景召回率会有断崖式下降。4.3 多尺度训练的尺度范围修正在前面的小节中已经涉及 scale 参数的调整训练配置里还有两个直接影响小目标效果的参数anchor_t和iou_t。anchor_t控制锚框与真实框的长宽比阈值默认是 4.0iou_t是正样本匹配的 IoU 阈值默认 0.2。小目标本身的面积很小即使锚框尺寸接近由于特征图分辨率有限IoU 天然偏低。YOLOv5 正样本分配时还依赖gr参数格点比例它决定真实框中心附近的网格有多少参与预测。对小目标场景我会把anchor_t从 4.0 放宽到 5.0增加正样本锚框数量。# train.py 中可选的参数覆盖示例 parser.add_argument(--anchor-t, typefloat, default5.0, helpanchor matching threshold, higher more matches)放宽anchor_t后负样本数量相对减少模型收敛速度会变快但也容易把背景中的圆形物体纳入正样本导致框输出不稳定。建议配合更严格的置信度阈值做推理或训练结束后再观察各类别的 PR 曲线决定是否回退。注意anchor_t放宽后如果训练 loss 曲线震荡严重首先检查是不是真实框标注有大量超出边界的矩形。交通灯数据集中很多标注框接近图片上边缘裁剪时容易丢失YOLOv5 对这类样本会直接丢弃标注导致有效正样本数不足。4.4 类别不平衡的采样处理交通灯数据集中红灯和绿灯往往远多于黄灯和左转箭头灯。黄灯因为亮灯时间短在视频抽帧中天然稀少箭头灯又常被框进同一个标注框里不单独标注。处理这个问题的落地顺序是先收集数据不行再做重采样最后才改损失权重。对静态图片数据可以按类别数量进行加权采样保证每个 epoch 中黄灯样本的重复出现概率更高对视频帧序列常见做法是定时抽帧而非连续抽帧避免相邻帧标注几乎相同导致模型对角度和遮挡鲁棒性不足。5. 部署阶段的推理优化输入尺寸、NMS 与 TensorRT 加速5.1 推理尺寸的选择逻辑训练时用 640×640 输入不代表部署推理时也用同样尺寸。交通灯场景需要同时检测远距离小目标和近距离大目标固定输入尺寸对两种目标都不友好。我一般会保留训练时的多尺度能力但在推理时按图像分辨率做一次预处理判断高分辨率输入1080p 以上采用 832×832 或 960×960 推理720p 以下采用 640×640。def select_infer_size(img_size, target_ratio0.8): 根据输入图像分辨率选择推理尺寸 h, w img_size max_dim max(h, w) if max_dim 1920: return 960 elif max_dim 1280: return 832 else: return 640把输入从 640 提高到 960 后小目标的特征像素数量约增加 2 倍P2 层的有效检测能力被显著释放。代价是推理耗时非线性上涨因为特征图尺寸增大后FPN/PAN 中的上采样和拼接操作都有额外开销。5.2 NMS 参数对不同尺度的差异化调整YOLOv5 推理时的 NMS 有两个核心参数conf_thres和iou_thres。在交通灯场景里这两个参数需要针对多尺度带来的误检做特殊处理。当模型包含 P2 层后小目标检测框数量大幅增加高置信度的背景误检也会变多。常见做法是把conf_thres从默认的 0.25 提高到 0.35 或 0.4但如果你部署的算力比较紧张可以换一个思路保持 0.3 的置信度阈值把iou_thres从 0.45 调低到 0.4。# 使用 detect.py 推理时传入参数 python detect.py --weights runs/train/exp/weights/best.pt \ --source test_videos/ --conf-thres 0.35 --iou-thres 0.40 \ --img 960 --device 0iou_thres降低会让重叠框更容易被合并消除但假设同一路口有两个距离很近的交通灯同时存在过低的 IoU 阈值会把其中一个框合并掉导致漏检。多尺度模型的输出框往往比单尺度模型更发散所以这里要按实际视频结果反复调整没有一套通用最优值。5.3 TensorRT 部署时的尺度抖动处理YOLOv5 官方提供了export.py导出的 TensorRT 引擎但在多尺度模型上直接导出容易遇到一个隐蔽问题固定输入尺寸的引擎在推理时对长宽比不是 1:1 的图像会做 letterbox 填充填充区域过大时原本的小目标会进一步缩小。解决这个问题的方法是把推理尺寸固定为 1280×736 这类与常见视频分辨率等比缩放后的尺寸而不是直接用 640×640 或 960×960。这样 letterbox 的填充面积最小小目标信息损失也最小。# 导出为固定宽高比的 TensorRT 引擎 python export.py --weights runs/train/exp/weights/best.pt \ --img 1280 736 --batch-size 1 --device 0 \ --include engine --half导出后建议用同一段视频对比 PyTorch 模型和 TensorRT 引擎的检测框差异。由于 TensorRT 的 FP16 精度可能在低像素区域产生数值误差我通常会在 Tiny 目标上做一次置信度分布对比如果 P2 层输出的低置信度检测框差异超过 10%考虑改回 FP32。6. 验证多尺度改造成果的 3 个维度多尺度模型的最终效果不能只看 mAP要同时关注小目标召回率、误检率和部署稳定性这三个维度。一个实际可行的方法是先做单层消融实验把 P2 分支单独开关对比训练结果确认性能提升确实来自新尺度特征而不是因为增加了参数量。具体操作时我一般会从模型测试集中按标注框大小切出三个子集小于 16 像素、16 到 48 像素、大于 48 像素分别计算 AP。如果小于 16 像素的 AP 没有明显提升先怀疑数据增强中的随机裁剪把太多小目标切掉了如果 16 到 48 像素区间的 AP 反而下降了需要检查 P3 层锚框分配是否受到了 P2 层挤压。推理阶段还有一个快速自检办法对比模型对同一段路况视频里「红灯变绿灯」瞬间的响应帧数。好的多尺度模型能在 2 到 3 帧内稳定切换检测结果而弱模型往往在目标亮度变化时丢失 5 到 10 帧。这是因为交通灯在切换瞬间存在短暂暗屏期多尺度模型可以利用更大感受野的上下文特征维持输出稳定。最后一件事值得记住多尺度改造不是只加一个 P2 层就结束了。锚框聚类、数据增强、推理尺寸和 NMS 参数是互相绑定的改了一个就必须重新审视另外三个。交通灯检测的难点从来不是模型表达能力不足而是小目标的信号强度与环境噪声太接近。多尺度结构提供的不只是更多像素而是让模型有机会把「这个亮色圆点」和「这条路口的信号灯」两件事分开来判断。本文还有配套的精品资源点击获取
返回列表