ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于22600张YOLO数据集的驾驶员行为检测实战:从数据清洗到部署调优

基于22600张YOLO数据集的驾驶员行为检测实战:从数据清洗到部署调优 驾驶员行为检测这几年在智能驾驶圈子里热度一直没降过尤其是分心驾驶、打电话、抽烟、喝水这些动作的识别几乎是每家有座舱监控需求的团队都要啃的硬骨头。我手上这个项目用的是22600张标注好的YOLO格式数据集覆盖了多种驾驶员行为类别从数据清洗、类别平衡、模型选型到训练调参、部署验证整套流程走下来踩了不少坑也积累了一些在公开文档里不太容易找到的经验。这篇文章不打算写成一份冷冰冰的说明书而是把我实际做这个数据集训练时遇到的真实问题、做过的取舍、验证过的参数原原本本摊开来讲。不管你是刚接触目标检测的新手还是已经跑过几个YOLO项目想找找思路的老手应该都能从里面捞到点有用的东西。1. 驾驶员行为检测这个任务到底难在哪1.1 22600张数据集背后的类别分布真相拿到一个标注好的数据集很多人第一反应是直接丢进模型开跑。我一开始也这么干过结果训练到第30个epoch发现某些类别mAP死活上不去回头一查类别分布问题一目了然。22600张图听起来不少但分摊到各个行为类别上差距可能非常悬殊。正常驾驶的样本往往占了大头而像低头看手机双手离开方向盘这类危险行为样本量可能只有前者的几分之一。这种长尾分布带来的直接后果是模型会倾向于把大多数样本预测成高频类别因为这样损失函数下降得最快。你看到整体mAP还行但一拆开看每个类别的混淆矩阵低频类别的召回率惨不忍睹。我的做法是先统计每个类别的实例数量和图像数量做成一张表然后针对性地做处理。类别图像数实例数占比正常驾驶8200910036.3%打电话4100430018.1%抽烟2800295012.4%喝水2400250010.6%低头看手机190020008.4%双手离开方向盘150016006.7%调收音机100010504.4%回头说话7007503.1%从这张表能看出来最高频和最低频类别之间差了将近12倍。这种比例如果不做处理模型基本等于只学了大头。我试过直接训练和做过采样后再训练前者低频类别的mAP只有0.31后者能拉到0.52左右差距非常明显。1.2 行为类别的类间相似性陷阱驾驶员行为检测有个特别恶心的地方很多动作在视觉上极其相似。比如打电话和手放在耳边这两个动作如果只看手部位置几乎一模一样区别在于有没有手机这个物体。再比如喝水和拿杯子如果杯子被手挡住模型很难判断到底是在喝还是只是拿着。我在标注阶段就发现不同标注员对低头看手机和低头调空调的判定标准不一致导致同一张图在不同批次里可能被标成不同类别。这种标注噪声对模型的影响比想象中大尤其是当类别本身边界模糊的时候。后来我专门做了一轮标注一致性检查把那些模棱两可的样本挑出来重新审核该合并的合并该剔除的剔除。提示如果你的数据集里存在大量边界模糊的类别不要急着训练先花时间做一轮标注清洗。清洗带来的收益往往比换模型、调参数大得多。1.3 驾驶员姿态的类内多样性同一个行为不同驾驶员的姿态差异可能非常大。打电话这个动作有人用左手有人用右手有人把手机贴在左耳有人贴在右耳还有人用免提但手依然举着。抽烟也是有人叼在嘴里有人夹在手指间有人刚点着还没抽。这些类内差异要求模型有足够强的特征提取能力同时也意味着数据增强策略要更有针对性。我试过用标准的随机翻转、随机裁剪、色彩抖动效果一般。后来加了随机旋转±15度和随机遮挡模拟手部或方向盘遮挡低频类别的召回率提升了大概8个百分点。原因很简单这些增强方式更贴近真实驾驶场景中会出现的姿态变化和遮挡情况。2. YOLO版本选型与骨干网络取舍2.1 为什么最终选了YOLOv8而不是v5或v7YOLOv5和YOLOv7我都跑过v5胜在生态成熟、文档多、社区活跃v7在精度上比v5有提升但部署起来稍微麻烦一点。最终选YOLOv8主要基于三个考虑。第一YOLOv8的C2f模块相比v5的C3模块在特征复用上更充分对于驾驶员行为这种需要同时关注手部细节和整体姿态的任务多尺度特征融合的效果更好。第二YOLOv8的Anchor-Free设计省去了聚类锚框的步骤对于这种类别形状差异大的数据集少了一个需要调的超参数。第三Ultralytics的工程化做得确实好训练、验证、导出、部署一条龙省了很多自己写脚本的时间。当然YOLOv8也不是没有缺点。它的模型体积比v5同级别稍大在边缘设备上推理速度会慢一些。如果你的部署平台算力有限v5n或者v8n仍然是更务实的选择。我实测下来在相同输入尺寸640x640下v8s比v5s的mAP高约2.3个百分点但推理耗时多了大概15%。这个取舍要看你的实际场景更看重精度还是速度。2.2 骨干网络替换的尝试与教训我试过把YOLOv8的骨干换成EfficientNet和MobileNetV3想看看能不能在精度和速度之间找到更好的平衡点。EfficientNet的复合缩放策略理论上很美好但实际替换后发现在驾驶员行为数据集上精度提升并不明显反而因为特征图通道数变化导致颈部网络需要重新设计训练不稳定BN层崩溃了好几次。MobileNetV3替换后推理速度确实快了在同等精度下大概能快20%左右但小目标检测能力下降明显。驾驶员行为检测里手机、烟、杯子这些关键物体往往只占图像很小一块区域骨干网络的下采样倍数太高会导致这些小目标在深层特征图上几乎消失。后来我放弃了骨干替换回到原生YOLOv8把精力放在数据增强和损失函数调优上收益反而更大。注意骨干网络替换不是万能药。如果你的数据集里小目标占比高换轻量骨干之前一定要先评估小目标检测指标否则可能捡了速度丢了精度。2.3 预训练权重的选择与微调策略YOLOv8官方提供了在COCO上预训练的权重直接拿来微调是最省事的做法。但我发现COCO数据集里人这个类别的特征和驾驶员行为检测里的人有差异COCO里的行人多是全身、远景、站立姿态而驾驶员是半身、近景、坐姿。直接用COCO权重微调前几个epoch损失下降很快但后面容易过拟合到COCO的分布上。我的做法是先用COCO权重做初始化然后冻结骨干网络的前几层只训练颈部网络和检测头学习率设小一点1e-4跑10个epoch让模型先适应新数据集的分布。之后再解冻全部层用余弦退火学习率从1e-3降到1e-5跑100个epoch。这个两阶段微调策略比直接端到端训练最终mAP高了大概1.8个百分点而且训练曲线更平滑不容易出现早期过拟合。3. 数据增强与类别平衡的实操细节3.1 Mosaic和MixUp在驾驶场景下的适配Mosaic增强是YOLO系列的标配把四张图拼成一张能显著提升小目标检测能力和模型鲁棒性。但在驾驶员行为检测里Mosaic有个副作用拼接后的图像里驾驶员可能被切得只剩半个身子手部位置关系完全乱掉模型学到的特征反而有噪声。我试过把Mosaic的概率从默认的1.0降到0.5同时把MixUp的概率从0调到0.1。MixUp是把两张图按透明度叠加对于驾驶员行为这种需要精确判断手部与物体关系的任务MixUp的叠加会引入大量模糊样本低频类别的精度反而下降了。最后我的配置是Mosaic 0.6、MixUp 0.05配合关闭最后10个epoch的Mosaicclose_mosaic10让模型在训练末期看到更多真实分布的数据。3.2 针对低频类别的过采样与增强组合对于样本量低于2000的类别我做了两件事。第一是在数据加载器里给这些类别更高的采样权重让每个batch里低频类别的出现概率提升。第二是针对这些类别做定向增强抽烟类别增加随机遮挡模拟手挡住烟、打电话类别增加随机旋转模拟不同持机角度、低头看手机类别增加亮度变化模拟不同光照下的屏幕反光。这些定向增强不是拍脑袋想的而是先看了大量误检样本分析模型到底在哪些情况下容易出错然后针对性地构造增强。比如抽烟类别模型经常把手放在嘴边误判成抽烟我就专门增加了一批手放在嘴边但没有烟的负样本让模型学会区分。增强策略适用类别参数设置效果随机遮挡抽烟、喝水遮挡比例10%-30%召回率6.2%随机旋转打电话±15度召回率4.8%亮度抖动低头看手机0.6-1.4倍召回率3.5%过采样所有低频类权重2-3倍mAP5.1%3.3 验证集划分的坑别让同一驾驶员出现在训练和验证集这个坑我踩得比较深。一开始我是随机划分训练集和验证集结果验证集mAP虚高部署到实际视频流上效果差很多。后来分析发现同一个驾驶员的多张图像被分到了训练集和验证集里模型其实是在认人而不是认行为。因为同一个人的体型、穿着、车内环境在训练和验证集里都出现过模型学到了这些身份特征换个人就失效了。正确的做法是按驾驶员ID划分确保验证集里的驾驶员在训练集中从未出现过。如果数据集里没有驾驶员ID标注至少也要按拍摄场景或时间段划分避免同一段视频的帧被分到两个集合。我重新划分后验证集mAP从0.78降到了0.71但这个0.71才是真实的泛化能力。提示目标检测数据集的划分不能只看图像数量要看数据来源的独立性。同一来源的样本必须整体划分到同一个集合。4. 训练参数调优与损失函数观察4.1 学习率与批次大小的配合逻辑YOLOv8默认的学习率是0.01批次大小16。这个配置在COCO上没问题但在22600张的驾驶员行为数据集上我建议根据实际显存调整。我用的是单卡24G显存批次大小开到32学习率相应调到0.02。这里有个经验公式学习率大致与批次大小的平方根成正比。批次翻倍学习率乘以1.4左右比较稳妥。但也不是越大越好。我试过批次64、学习率0.04训练初期损失震荡很厉害BN层的统计量不稳定mAP波动超过3个百分点。后来回到批次32、学习率0.02配合warmup 3个epoch训练就稳多了。warmup的作用是让模型在初期用小学习率慢慢适应数据分布避免一开始就大步长导致梯度爆炸。4.2 损失函数三项的监控与异常判断YOLOv8的损失由三部分组成边界框回归损失box_loss、分类损失cls_loss、分布焦点损失dfl_loss。训练时我习惯把这三项分开看而不是只看总损失。box_loss下降慢说明边界框回归有问题可能是锚框设置不合理虽然v8是Anchor-Free但回归目标的设计仍有影响或者标注框质量差。cls_loss下降慢说明类别区分度不够可能是类别不平衡或者类间相似性太高。dfl_loss是v8特有的分布焦点损失它反映的是边界框预测的分布学习情况如果dfl_loss震荡大通常是学习率太高或者批次大小太小。我遇到过cls_loss在30个epoch后突然反弹的情况排查后发现是数据加载器里的一个bug某些低频类别的图像在过采样时被重复加载导致模型在这些类别上过拟合验证集分类损失上升。修复后重新训练曲线就正常了。4.3 早停与模型保存策略YOLOv8默认每10个epoch保存一次检查点训练结束后取最佳mAP的模型。但在实际项目中我建议把保存间隔改成5个epoch同时开启早停patience30。驾驶员行为检测的训练曲线有时候会出现平台期mAP连续十几个epoch不涨然后突然又涨一波。如果patience设太小比如10很容易在平台期就停了错过后面的提升。另外我习惯在训练结束后把最后10个epoch的模型都拿来做一次验证有时候最佳mAP出现在倒数第3个epoch而不是最后一个。YOLOv8的best.pt保存的是历史最佳但如果你中途改了学习率策略或者数据增强历史最佳可能不是当前策略下的最优。手动对比一下更保险。5. 模型评估与误检漏检的排查链路5.1 混淆矩阵暴露的类别混淆问题训练完成后第一件事是看混淆矩阵。驾驶员行为检测的混淆矩阵通常有几个固定的混淆对打电话和手放耳边、抽烟和手放嘴边、喝水和拿杯子。这些混淆对的根源是手部与物体的空间关系没有被模型充分学到。我的排查步骤是这样的先从混淆矩阵里找到混淆最严重的两个类别然后从验证集里把这两个类别的误检样本全部挑出来逐张看。如果误检样本里手部被遮挡的比例很高说明模型过度依赖手部特征需要增加遮挡增强。如果误检样本里物体本身就不清晰说明标注质量有问题需要回头清洗数据。5.2 小目标漏检的定位与解决驾驶员行为检测里手机、烟、杯子这些关键物体往往只占图像的5%以下面积属于典型的小目标。小目标漏检的排查可以从三个层面入手。第一看输入分辨率。YOLOv8默认640x640如果原始图像是1920x1080下采样后小目标可能只剩几个像素。我试过把输入尺寸提到960x960小目标召回率提升了约7个百分点但推理速度慢了将近一倍。折中方案是保持640训练但在推理时用1280的输入尺寸利用YOLO的多尺度推理能力。第二看特征金字塔。YOLOv8的P3层负责小目标检测如果P3层的特征图分辨率不够小目标就检测不到。可以通过修改模型配置文件增加P2层来提升小目标检测能力但计算量会增加不少。第三看数据增强。Mosaic增强会缩小图像中的目标如果Mosaic概率太高小目标在训练时变得更小反而有害。我最后把Mosaic概率降到0.6小目标召回率反而比1.0时高了。5.3 误检样本的归因分析方法误检比漏检更难处理因为误检的成因更复杂。我总结了一套归因分析方法把误检样本按置信度分三档0.5-0.6、0.6-0.8、0.8以上分别看。置信度0.5-0.6的误检通常是模型不确定导致的可以通过提高置信度阈值来过滤但会牺牲一部分召回。置信度0.6-0.8的误检往往是类间相似性导致的需要针对性增强。置信度0.8以上的误检基本可以判定是标注错误或者数据分布问题必须回头查数据。我遇到过一批高置信度误检模型把调收音机误判成打电话置信度0.9以上。查了原始图像发现这些样本里驾驶员的手确实放在中控台附近和打电话的手部位置有重叠。后来我把这批样本重新标注并在训练时增加了中控台区域的负样本误检率降了一半。6. 部署落地时的工程化考量6.1 模型导出与推理加速训练完的PyTorch模型直接部署效率不高通常要导出成ONNX或TensorRT。YOLOv8支持一键导出但有几个细节要注意。导出ONNX时opset版本建议用12或13太低不支持某些算子太高某些推理引擎不兼容。导出TensorRT时FP16精度通常能带来1.5到2倍的速度提升精度损失在1个百分点以内性价比很高。如果部署在边缘设备上比如Jetson系列建议用TensorRT的INT8量化。但INT8量化需要校准数据集校准集的分布要和实际推理数据一致。我试过用训练集的一部分做校准结果在实际视频流上精度掉了5个百分点后来改用实际场景采集的未标注视频帧做校准精度只掉了1.5个百分点。6.2 视频流推理的帧间一致性处理驾驶员行为检测通常是视频流输入单帧检测会有抖动。比如打电话这个动作可能第10帧检测到第11帧没检测到第12帧又检测到这种闪烁在实际产品里体验很差。我的做法是加一个简单的跟踪器比如ByteTrack把帧间检测结果关联起来只有连续3帧以上检测到同一行为才输出否则视为噪声。这个后处理逻辑看起来简单但对实际体验提升很大。误报率降低了约40%而且行为持续时间的统计也更准确。如果你不做跟踪至少也要加一个滑动窗口投票用最近5帧的检测结果做多数表决。6.3 实际路测中的光照与遮挡挑战实验室里跑得再好上路测试总会遇到新问题。我印象最深的是夜间红外补光下的检测训练集里夜间样本少模型在夜间召回率只有白天的60%左右。后来补采了一批夜间数据并增加了亮度抖动和对比度增强夜间召回率才拉到85%以上。另一个挑战是遮挡。驾驶员的手可能被方向盘挡住脸可能被遮阳板挡住这些遮挡在训练集里如果覆盖不够模型就会漏检。我的经验是遮挡增强的比例要占到总增强的20%以上而且遮挡的位置要随机不能只遮固定区域。提示实际路测的数据一定要回流到训练集里。我每跑一次路测都会把误检漏检的样本挑出来标注后加入下一轮训练。迭代三轮之后模型在真实场景的mAP比初版高了12个百分点。7. 一些零散但值钱的经验数据集清洗永远比模型调参重要。我见过太多人花几周时间调网络结构却不愿意花两天时间清洗标注。22600张图里如果有5%的标注错误模型精度至少掉3到5个百分点。清洗一遍比换任何先进模型都管用。类别不平衡的处理要趁早。不要等到训练完发现低频类别不行再回头处理应该在数据准备阶段就做好过采样和增强策略。我现在的习惯是拿到数据集第一件事就是画类别分布图低于1000实例的类别全部标记为需要特殊处理。验证集的划分要按来源独立。同一驾驶员、同一场景、同一时间段的样本必须整体划分否则验证集指标没有参考价值。这个坑我踩过两次每次都是部署后才发现问题。训练日志要看得细。不要只看mAP要看每个类别的precision、recall、AP要看损失曲线的形状要看学习率的变化。异常往往藏在细节里比如某个类别的AP突然下降可能是数据加载器出了问题也可能是标注文件损坏。模型导出后一定要做一致性验证。PyTorch模型和ONNX模型的输出可能有细微差异TensorRT的FP16和INT8差异更大。导出后拿一批验证集数据对比导出前后的检测结果确保精度损失在可接受范围内。我遇到过ONNX导出后某个算子不支持导致检测框全部偏移的情况如果没做一致性验证上线就是事故。最后说一个关于数据集的体会。22600张听起来是个不小的数字但在驾驶员行为检测这个任务上真正决定模型上限的不是图像数量而是场景覆盖度。白天、夜间、晴天、雨天、不同车型、不同驾驶员、不同摄像头角度这些维度的覆盖比单纯堆图像数量重要得多。我宁愿要10000张覆盖20种场景的图也不要20000张只覆盖5种场景的图。场景覆盖度上去了模型的泛化能力自然就上去了部署到新车上、新场景里表现也不会差太多。
返回列表