资讯动态

YOLOv5小目标车辆检测实战:从数据优化到部署的完整方案

发布时间:2026/10/17 12:11:11 来源:尧图企业网站定制
做小目标检测和做大目标检测完全是两码事。前一阵帮A同学调试一个基于YOLOv5的小目标交通工具检测系统类别只有三类轿车、卡车和公交车。一开始以为套个预训练模型标一批数据训练出来就能跑实际推上去才发现远处车辆只有几十个像素大小轮廓模糊特征被背景吞掉直接用默认参数根本不够用。折腾了数据增强、模型结构、输入分辨率和推理阶段之后才把漏检率压下来。这篇文章把项目从需求拆解、数据准备、模型调整、训练评估、部署再到配套论文和讲解文档的组织思路完整梳理一遍希望能给正在做类似毕业设计、课程设计或交通场景落地开发的朋友提供一份能直接参考的经验。1. 项目需求拆解与整体方案设计1.1 用户真正要的是什么样的系统这种带“源码论文部署文档讲解”的交付型项目要求往往比单纯写一段代码更复杂。硬性功能很明确输入一张道路监控图或视频帧输出三类车辆的边界框、类别和置信度类别是轿车、卡车、公交车。看起来只要训练一个检测器就行但实际交付时要考虑的维度更多。我会先把这个项目拆成四个层面来看。第一是检测效果小目标召回率不能太低尤其是远处车辆第二是代码可读性工程要结构清晰别人拿到之后能快速跑通第三是扩展性后续要换数据类别、调推理接口不能伤筋动骨第四是文档完整性论文、部署文档、讲解材料要覆盖项目全生命周期。很多时候用户并不真的需要一个“最先进”的模型而是需要一个“能跑、能讲、能改、能交付”的完整案例。所以系统的核心设计目标不只有精确率还包括稳定性和可复现性。数据集划分、随机种子、训练参数、评估指标都必须能复盘论文里的每一张图都来自真实实验记录而不是事后编造。1.2 为什么选YOLOv5而不是更新的模型YOLOv5在这类项目里的地位很稳虽然新版本不断出现但它仍然是资料最全、社区问题沉淀最充分的目标检测框架之一。选择它有几个非常现实的原因。首先是代码结构清晰。模型定义用配置文件描述训练评估入口固定对刚接触目标检测的人比较友好。论文里要写技术路线YOLOv5的CSPDarknet骨干网络、PANet特征融合结构都有对应公开资料讲解起来容易形成完整逻辑链。其次是部署链路短。训练得到的PyTorch权重可以轻松导出为ONNX或TensorRT格式CPU场景还能用OpenCV的DNN模块加载。这个特性对“部署文档”交付特别重要因为我不会希望为了一个简单接口引入一堆重型依赖。第三个原因是硬件门槛适中。YOLOv5s版本在主流显卡上训练成本可控比起更大规模的模型更符合学生项目和中小型工程场景的实际情况。1.3 系统模块怎么划分整个系统我习惯拆成四个独立模块模块之间只通过目录和接口通信。数据模块负责整理图片、标签、类别映射关系输出标准YOLO格式的训练集和验证集。训练模块读取数据配置执行训练、验证、可视化输出best.pt权重和训练日志。推理模块负责加载权重对图片、视频或视频流做检测绘制结果并统计数量。服务模块把推理能力封装成HTTP接口方便前端调用或接入到更大的业务系统中。这种拆分的好处是调试时能快速定位。推理结果不对先检查数据标注和预处理再检查模型权重和后处理不用在混乱的代码里来回翻。论文里的架构图也直接按这四个模块去画逻辑清楚答辩时也容易讲。2. 小目标车辆检测的难点与选型分析2.1 小目标到底难在哪里先说一个基本标准。在目标检测社区里像素面积小于32x32的目标通常被视为小目标。道路监控画面中一辆位于画面远端或边缘的轿车宽度往往只有20到30个像素这就是很典型的小目标。小目标难检的核心原因有两个。第一是信息量不足车辆缩小之后变成一团模糊色块车牌、车灯、车窗等判别特征全部丢失。第二是背景干扰一个模糊色块可能是车也可能只是路面的阴影、井盖、隔离墩或者绿化带的一部分。深度学习模型本质上是靠特征做决策当目标特征本身已经很少模型就需要更强的浅层细节和上下文信息来辅助判断。常规目标检测能轻松识别的大卡车在版画边缘可能变成一小条深色区域很容易被当成背景忽略。所以项目能不能成很大程度上取决于对小目标问题的重视程度而不是单纯比谁的网络更强。2.2 YOLOv5里的组件对小目标检测的影响YOLOv5的网络结构由骨干网络、特征融合和检测头三部分组成。骨干网络负责提取不同尺度的特征特征融合层把深层语义信息和浅层位置信息结合起来检测头在三个特征层上输出预测结果。默认配置会带来一个问题小目标主要依赖浅层特征但浅层特征经过多层下采样后位置细节被逐步压缩。YOLOv5的输出层通常是8倍、16倍、32倍下采样分别对应80、40、20的网格尺寸。大目标在低分辨率网格上效果很好小目标则更需要80×80这层特征。如果输入分辨率只有640再经过32倍下采样一个20像素的车在深层特征图上可能只剩1个点检测头基本无计可施。针对这个结构常见的解决办法是提高输入分辨率让目标在下采样后仍然保持足够像素或者增加更浅层的检测头让模型直接在4倍下采样特征上预测。这两个方向我都会在后面的优化章节里详细展开。2.3 轿车、卡车、公交车三类的特征差异三类车的长宽比和外表有明显区别。轿车通常扁而长车顶轮廓低尾部接近地面卡车车身高大方正很多时候是矩形轮廓货箱尺寸大公交车也很方正但整体长度比卡车更长车窗分布规律侧面线条更连续。但在实际监控视角中如果拍到的是正后方视角卡车和公交车会特别容易混淆。两者都是高大的矩形只有长度、高度比例和细节上的差别。轿车的问题更多是漏检而卡车与公交车的问题则主要是互相误判。所以我在标注阶段就会约定清楚集装箱半挂类、货箱类统一标成卡车车身长、有明显车窗排布的客运车辆标成公交车普通家用车标成轿车。边界框要贴合每类车辆的可见轮廓被遮挡时框出可见主体不要框出半个车壳再加一截背景。3. 数据准备、标注与增强3.1 数据来源与数据量规划数据来源主要采用公开道路数据加自己采集的监控视频帧截图。这里不推荐直接用网上下载的混杂图片因为容易出现大量重复帧、标注格式混乱、场景单一的问题。在数据量规划上我建议三类合计至少5000到10000张图片最低不要低于5000张。5000张情况下还要特别关注类别均衡轿车样本多、公交车样本少时模型很容易对公交车视而不见。数量不足时宁可每类各1500张也不要轿车4000张加公交车500张的组合。划分比例用8:1:1比较稳妥。训练集负责学习特征验证集用来早停和挑选最优权重测试集在最后做一次公正评估。要注意验证集和测试集中的视频片段不能和训练集来自同一段连续画面否则相同场景几乎一样评估出来的指标会虚高真实场景一跑就露馅。3.2 标注工具与小目标标注要点标注工具方面可以选用支持本地部署的开源标注工具例如带自动跟踪和预标注能力的那类。车辆目标比较规整半自动标注能显著提高效率。但自动标注结果一定要二次检查尤其是小目标边界框差几个像素都会影响训练质量。小目标标注有一条实战经验放大图像到200%再框不要盯着缩略图随意拉框。如果目标像素小于10×10认真判断是否保留这条标注比如确实能看出车顶轮廓就标只剩一团无法分辨的色斑就宁可删掉这张图。标签里面混入大量噪声框会让模型对“什么是目标”产生极端错误的理解。YOLO格式的标签相对简单每行是“类别 中心点x 中心点y 宽度 高度”坐标值都除以图像宽高做了归一化。检查标签时重点看宽高是否为0、坐标是否越界、类别编号是否和配置文件对应。这些低级错误会直接导致训练损失异常。3.3 面向小目标的数据增强策略YOLOv5自带的Mosaic增强对小目标不完全友好。Mosaic会把四张图拼接后随机裁剪如果裁剪区域避开小目标那么这几张小目标实际上就被丢弃了。我的做法是降低Mosaic概率或把小目标占比高的图像排除在Mosaic之外。复制粘贴增强是一个非常有效的手段。把标注好的车辆区域从原图中抠出来随机旋转、缩放后粘贴到其他背景图上同时自动生成新的标签。这样既增加了小目标数量又增加了目标出现的环境多样性。要注意粘贴时不要让车辆悬空在明显不可能的位置例如跨到天空或完全遮挡在树冠里。多尺度训练也属于增强。训练过程中每固定轮数切换一次输入分辨率比如从640切到960再切到1280模型对不同大小目标的变化会更鲁棒。不过这对显存有一定要求后续会提到怎么降低训练开销。4. 环境准备与YOLOv5训练全流程4.1 环境配置与依赖安装我习惯用独立虚拟环境进行训练避免把系统Python环境搞乱。推荐使用Python 3.9或3.10配套安装PyTorch 2.x版本的稳定版显卡驱动已适配的机器直接用CUDA版本。创建环境的基本命令如下conda create -n yolo python3.9 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt安装完成后不要急着训练先用一张图片跑通前向推理确认CUDA设备能被PyTorch正常访问。很多问题不是出在模型而是显存驱动不匹配、依赖版本冲突或者路径错误。花十分钟验证链路能省下后面一整天的排查时间。4.2 数据集配置与目录检查数据目录建议按官方默认结构组织datasets/vehicle/ images/ train/ val/ labels/ train/ val/labels目录里每张jpg对应一个同名txt文件。图片放在images的train目录下标签放在labels的train目录下YOLOv5训练时会自动匹配文件名。在datasets/vehicle目录下新建一份vehicle.yaml文件path: datasets/vehicle train: images/train val: images/val nc: 3 names: [car, truck, bus]类别顺序必须严格和标注文件里的编号一致。训练之前写一个小脚本统计每类目标数量和标签格式发现某个类别数量异常低或标签文件为空就要先处理数据而不是直接启动训练。4.3 训练命令与关键参数解释训练入口是YOLOv5的train.py我的基础命令如下python train.py \ --data data/vehicle.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch 8 \ --epochs 120 \ --device 0 \ --name vehicle_small \ --hyp data/hyps/hyp.scratch-low.yaml这里几个参数对小目标影响很大。--img 1280意味着输入分辨率从默认的640提高到了1280图像被放大后小目标的像素信息显著增加。代价是显存占用上升所以batch只能从16降到8。--weights yolov5s.pt用到预训练权重。迁移学习让模型从一开始就具备基础视觉特征比从零训练收敛快很多。如果显存特别紧张可以选yolov5n.pt或者yolov5tiny检测精度会略有下降但节省显存效果明显。--epochs不需要一直写很高。模型通常在几十轮达到平台期后面容易过拟合。配合早停参数观察验证集指标即可。4.4 训练过程监控与结果评估训练启动后要重点看三个曲线训练损失是否下降、验证集mAP是否上升、分类损失是否异常集中。YOLOv5会自动保存best.pt和last.ptbest.pt根据验证集mAP挑选。训练结束后执行一次完整评估python val.py \ --data data/vehicle.yaml \ --weights runs/train/vehicle_small/weights/best.pt \ --img 1280评估输出会包括mAP0.5和mAP0.5:0.95。mAP0.5是IoU阈值0.5下的平均精度适合快速判断模型是不是“能用”mAP0.5:0.95对边界框定位要求更高更贴近严格工程场景。如果mAP0.5已经不错但全值区间偏低说明检测框整体偏大或偏小需要检查数据标注质量和后处理参数。5. 针对小目标检测的优化组合拳5.1 提高输入分辨率是最直接的提升从640提高到1280是见效最快、成本也最直观的优化。分辨率翻倍后远处车辆占用的像素区域变大模型能观察到车灯、车窗结构召回率提升明显。我用同一份数据做过对比1280输入在mAP0.5上的涨幅通常比960输入高2到5个百分点。代价是显存占用接近翻倍推理速度下降也明显。工程化的折中方案是分两阶段训练先用640或960把模型收敛再用1280做一次小fine-tune。这样既享受高分辨率的收益又避免从头到尾都用高分辨率导致训练时间过长。而推理阶段可以根据业务需求选择960或1280不必要的话就用960。5.2 增加浅层检测头的判断标准默认YOLOv5有三个检测头最小步长是8倍下采样。面对超小目标可以在骨干网络浅层再增加一个4倍下采样的检测头让模型直接在更高分辨率的特征图上做预测。修改思路是在模型配置里加入一个来自骨干网络早期层的新输出分支并调整检测头输出的网格尺寸。实现时要注意特征融合部分的通道数要和新增层匹配训练时正样本分配逻辑也要支持多检测头。这里给一个清晰的提醒增加检测头不是万灵药。参数量和推理时间会上升有时小目标召回率提升不明显反而大目标指标轻微下降。我的建议是先试输入分辨率和数据增强如果小目标还是大量漏检再考虑加检测头。实际上很多道路场景用1280分辨率加切片推理已经能解决问题不一定非改网络结构。5.3 切片推理是部署阶段的大杀器切片推理的原理很简单把大图切块每一块放大来检测最后合并结果。前文说的“大目标小目标”问题直接分摊到每张切片里一个小目标原本可能只有20像素切成4块之后在单块里可能变成40像素以上检测能力立刻提高。具体操作是把输入按例如640×640切片切片之间保留一定重叠确保目标不会被切面分割成残块。每块独立推理后用NMS对重叠区域去重。代价是推理时间成倍增加所以适合离线分析、卡口抓拍、监控区段回放等实时性要求不高的场景。如果项目要做成实时视频流建议在采集端做ROI裁剪或仅在关键区域启用切片其他区域用普通单帧推理。这样既保留了小目标检测能力又不会让GPU占用率直接爆表。5.4 锚框、阈值与后处理调节YOLOv5的默认锚框来自通用目标集合直接用在车辆场景不一定最优。训练时开启锚框自适应程序会对标注数据做聚类生成更适合当前数据集的三组锚框。如果改过检测头结构锚框数量必须和网格输出匹配否则训练会直接报错。推理阶段的置信度阈值默认是0.25这个值对小目标偏高。小目标因为特征弱置信度天然低于大目标所以把阈值放到0.15甚至0.1能明显减少漏检。代价是误检数量上升所以要和NMS阈值配合调整。NMS的IoU阈值我建议保持0.5左右太高会把多个真实目标合并成一个框太低会把同一个车切碎成多个框。下面这张表是我在项目中实际摸索出来的优化手段对比优化手段收益成本适合场景提高输入分辨率高显存和耗时上升所有小目标场景增加浅层检测头中网络复杂训练变慢超小目标且分辨率已经到头切片推理高推理时间成倍增加离线分析、监控录像回查降低置信度阈值中误检增加需配合NMS漏检问题远大于误检问题6. 部署与API封装6.1 模型导出为ONNX或TensorRT训练结束后的PyTorch权重不能直接投到生产环境因为部署服务器的Python环境不一定装PyTorch复现。统一做法是导出为标准格式。导出命令python export.py \ --weights runs/train/vehicle_small/weights/best.pt \ --include onnx engine \ --img 640应该先用640输入导出做通道验证导出1280输入会增大模型体积并拖慢推理速度。如果GPU是NVIDIA且追求低延迟TensorRT引擎格式在推理性能上优势明显。导出后建议用网络可视化工具检查输入和输出维度确保和后面写的后处理代码一致。6.2 使用ONNX Runtime做推理我把推理独立成一个类避免业务代码和模型加载耦合在一起。简单的加载方式如下import onnxruntime as ort import numpy as np import cv2 class YOLOv5ONNX: def __init__(self, onnx_path): self.session ort.InferenceSession( onnx_path, providers[CUDAExecutionProvider, CPUExecutionProvider] ) self.input_name self.session.get_inputs()[0].name def preprocess(self, img, size640): h, w img.shape[:2] r size / max(h, w) new_w, new_h int(w * r), int(h * r) resized cv2.resize(img, (new_w, new_h)) canvas np.full((size, size, 3), 114, dtypenp.uint8) canvas[:new_h, :new_w] resized blob cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB) blob blob.astype(np.float32) / 255.0 blob np.transpose(blob, (2, 0, 1))[None] return blob预处理时注意保持宽高比用灰色填充。省略缩放步骤会让检测框坐标完全错位。后处理需要解析输出张量完成置信度过滤和NMS后再映射回原始图像坐标。6.3 封装成HTTP接口为了让部署文档更完整我通常会再包一层HTTP服务。上传图片后返回JSON响应包括类别、置信度和边界框坐标也可以返回画好检测框的图片。服务启动时一次性加载ONNX模型到内存避免每次请求都重新初始化。并发不高的场景用同步请求处理就够了。并发高时要把推理放到独立线程池避免阻塞Web服务的事件循环。接口响应里除了检测框我还会带上统计结果比如当前帧轿车数量、卡车数量、公交车数量这样接业务系统时少一次二次开发。这一步对论文的“系统实现”章节很有帮助。架构图里可以画成“图片输入→HTTP服务→模型推理→结果返回”的完整链路答辩演示也更直观。7. 常见问题与避坑记录7.1 小目标漏检严重怎么办首选检查输入分辨率。默认640跑漏检明显的情况下换成1280经常有立竿见影的效果这是最优先的debug步骤。分辨率提高后还是漏就把置信度阈值调低开启后处理合并试切片推理。还有一个容易被忽略的点夜间或逆光场景下画面整体偏暗细胞车辆对比度极低。这时候在网络之外加一个对比度拉伸或白平衡预处理可以有效提升小目标可见度。不要指望模型在这种数据下还保持很强的表现预处理增强才是性价比最高的办法。7.2 卡车与公交车互相误判这套系统里最容易出的“工程病”。卡车和公交车在正后方视角下高度相似很多标注样本里货箱式卡车和城市公交车长度接近模型分不清很正常。解决思路有三步。第一步保证训练集里公交车样本充足至少和卡车样本量在一个数量级。第二步是增加难例挖掘去数据里找那些特别容易被混淆的图片单独圈出来参与训练让模型学习细微差异。第三步是后处理规则修正比如长宽比明显偏长的矩形更可能是公交车但这只能做辅助不能当核心逻辑。7.3 训练损失不降或过拟合训练损失不降先检查数据路径。标签全部为空或路径写错时模型等于在一片空白数据上学习损失自然下不去。可以先在训练开始时打印一个batch的标签统计确认里面有真实目标。学习率设置太高也会震荡。YOLOv5默认学习率在0.01附近可以改成0.001看是否变得稳定。过拟合的表现是训练损失持续下降、验证指标不动或下降解决办法是增加数据增强、删除脏样本、降低epoch数或者换更小尺寸的模型。不要一上来就加正则硬扛数据层面的修正更本质。7.4 显存和推理速度不够显存不足优先减小batch而不是减小输入分辨率因为分辨率是影响小目标的核心变量。batch降到1之后还爆显存就打开梯度累积参数让多个小batch的梯度累计后再更新。推理速度不够时优先考虑TensorRT和量化。INT8量化之前一定要用代表性训练数据做校准否则检测框精度会明显下降。对部署场景来说有时候CPU上跑半精度ONNX也能接受关键是保证小目标不被后处理过滤掉。8. 配套论文、部署文档与讲解内容的组织思路8.1 配套论文主体怎么写这类项目交付时通常会要求一份配套设计论文核心不是堆技术名词而是把“题目、方案、实现、实验、结论”讲清楚。论文骨架一般分成绪论、相关技术介绍、系统设计、实验与结果分析、总结与展望。系统设计章节放总体架构图并逐一说明数据、训练、推理、服务四个模块。实验章节必须放训练损失曲线、PR曲线、混淆矩阵和不同优化方案的对比表。答辩最看重的就是实验有没有完整记录所以在训练阶段就应当固定好对照实验保存每个对照组的权重和日志。到写论文时无非是把这些真实记录转成图表。8.2 部署文档怎么组织部署文档的目标是让另一个人照着做就能跑起来。开头先放项目目录结构把源码、权重、数据、文档的位置在树状目录中说明。然后写环境安装步骤包括Python版本、依赖库、CUDA版本和安装命令。随后写数据准备说明重点写目录格式、标签格式和data.yaml配置。训练和评估部分放目标命令即可。每个命令后面可加一到两行参数说明让读者明白这些参数在控制什么。最后写接口调用示例包括请求格式和响应格式。如果能配一张实际测试效果图文档会比干巴巴的文字有说服力得多。8.3 讲解视频或现场演示怎么准备讲解时不要从第一行代码开始念而是要围绕“小目标为什么难”和“我做了哪些优化”这两条主线来展开。演示流程可以固定为启动服务、传入一张图片、展示检测结果、再跑一段视频流。要提前准备一张问题清单常见问题包括“为什么选YOLOv5”“mAP怎么解释”“增加检测头为什么没效果”“漏检怎么办”等。A同学第一次试讲时就被问到“增加检测层之后为什么mAP没涨”当场愣了几秒。后来我让他把实验记录打印出来对着对照组表格回答效果立刻好很多。所以建议在正式讲解之前自己先做一次完整的问答模拟把每一条实验结果的意义提前理解清楚。最后再分享一点个人体会这类小目标项目最容易出成果的顺序是先把训练和评估流程跑通再决定要不要改模型结构。很多人一上来就修改配置文件结果输入输出对不上排错排了一整天。我实际做下来最有效的组合是“干净的数据集高一点的分辨率锚框自适配后处理阈值下调”三件套就能把几十像素车辆的识别率拉到很可用的水平浅层检测头这类结构改动只有前面手段都试完还达不到要求时才值得考虑。希望这篇经验整理能帮你在自己的项目里少走弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑