YOLO驾驶员行为检测实战:22600张数据集训练与调优全流程
发布时间:2026/10/1 4:03:34来源:尧图网络
驾驶员行为检测这几年在智能座舱和商用车队管理里越来越热但真正动手做项目的人都知道最卡脖子的往往不是模型结构而是数据。你要检测抽烟、打电话、喝水、双手离方向盘、扭头张望这些动作公开数据集要么类别对不上要么场景太单一要么标注质量堪忧。我最近拿到一份22600张的YOLO格式驾驶员行为检测数据集从整理、清洗到训练跑通完整走了一遍中间踩的坑比想象中多。这篇就把整个流程拆开讲清楚这份数据集到底包含什么、YOLO格式的目录该怎么组织、类别不平衡怎么处理、训练参数怎么调、以及那些只有真正跑过一遍才会知道的细节。不管你是刚接触目标检测的新手还是已经做过几个YOLO项目想切入智能驾驶场景的老手应该都能从里面找到能直接用的东西。1. 先搞清楚这份数据集到底装了什么拿到一个数据集最忌讳的就是直接丢进训练脚本开跑。我见过太多人这么干结果训练到一半发现类别对不上、图片损坏、标注越界白白浪费几个小时甚至一整天的GPU时间。所以在动手之前先把数据集的家底摸清楚这一步花二十分钟后面能省你半天。1.1 22600张图片的构成与场景分布这份数据集总共22600张图片全部是驾驶员行为相关的场景。从实际翻看的情况来看拍摄视角以车内固定摄像头为主大致可以分为几类正对驾驶员的面部视角、从副驾或A柱斜向拍摄的侧视角、以及少量从车顶向下俯拍的视角。这种多视角的构成其实挺重要因为实际部署时摄像头安装位置千差万别如果训练数据只有单一视角模型换个安装位置就废了。场景光照条件覆盖得还算全面白天自然光、夜间红外补光、隧道明暗交替、逆光强曝光这些都有涉及。我特意统计了一下夜间和弱光场景大概占三成左右这个比例对于驾驶员行为检测来说是合理的毕竟夜间疲劳驾驶和违规行为反而更高发。图片分辨率不统一有1920×1080的也有1280×720的还有一部分是640×480的这个后面预处理的时候要统一处理。从行为类别来看覆盖了驾驶场景下最常见的几类动作正常驾驶、打电话、抽烟、喝水/进食、双手离开方向盘、扭头与乘客交谈、低头看手机、打哈欠/疲劳。类别数量不算特别多但都是实际业务里真正需要检测的高频行为没有那种凑数的冷门类别。1.2 YOLO格式的目录结构与标注规范这份数据集是标准的YOLO格式目录结构大概是这样的dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml图片和标签文件一一对应文件名相同只是扩展名不同图片是.jpg标签是.txt。每个标签文件里每一行代表一个目标格式是class_id x_center y_center width height这里有个新手特别容易搞混的点x_center、y_center、width、height这四个值全部是归一化到0到1之间的相对值不是像素值。也就是说x_center 目标中心点x坐标 / 图片宽度。我刚开始做检测的时候就因为把像素值直接写进去训练了半天loss不降排查了好久才发现是标注格式的问题。data.yaml文件里定义了类别数量和类别名称大概长这样train: ./images/train val: ./images/val test: ./images/test nc: 8 names: [normal, phone, smoke, drink, hands_off, turn_head, look_down, fatigue]提示拿到数据集第一件事就是打开data.yaml确认nc和names然后随机抽十几个标签文件对照图片看看标注框位置对不对。这一步能帮你提前发现标注错位、类别编号错乱等致命问题。1.3 类别分布与不平衡情况摸底类别不平衡是目标检测里的老问题驾驶员行为检测尤其明显。正常驾驶的样本数量远远多于抽烟、打电话这些违规行为这是符合现实的但对训练很不友好。我实际统计了一下这份数据集的类别分布大致情况如下类别大致占比说明normal正常驾驶约35%数量最多容易导致模型偏向phone打电话约15%高频违规行为smoke抽烟约12%中等频率drink喝水进食约10%中等频率hands_off双手离盘约8%较难检测turn_head扭头约8%姿态变化大look_down低头约7%与疲劳有重叠fatigue疲劳打哈欠约5%数量最少这个分布意味着如果你不做任何处理直接训练模型会倾向于把大部分目标预测成normal因为这样蒙对的概率最高。后面我会专门讲怎么处理这个问题。2. 训练之前必须做的数据清洗与预处理很多人觉得数据清洗是脏活累活能跳过就跳过。但我可以负责任地说在驾驶员行为检测这个场景里数据清洗带来的收益往往比换一个更先进的模型结构还大。原因很简单车内场景的干扰因素太多了方向盘遮挡、安全带横穿、后视镜反光、乘客入镜这些都会让标注变得模糊而模糊的标注会直接教坏模型。2.1 标注越界与零面积框的批量排查YOLO格式要求所有坐标都在0到1之间但实际标注过程中标注员手一抖就可能标出越界的框。更隐蔽的是零面积框就是width或height等于0的情况这种框在训练时会直接导致loss计算出NaN。我写了一个脚本批量排查这两类问题import os import glob def check_labels(label_dir): issues [] for txt_file in glob.glob(os.path.join(label_dir, *.txt)): with open(txt_file, r) as f: lines f.readlines() for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: issues.append((txt_file, i, field_count, line.strip())) continue cls, x, y, w, h map(float, parts) if not (0 x 1 and 0 y 1): issues.append((txt_file, i, center_out, line.strip())) if w 0 or h 0: issues.append((txt_file, i, zero_area, line.strip())) if w 1 or h 1: issues.append((txt_file, i, size_out, line.strip())) return issues issues check_labels(./labels/train) print(f共发现 {len(issues)} 处问题) for item in issues[:20]: print(item)跑完这个脚本我在这份数据集里发现了大概一百多处问题主要集中在零面积框和中心点越界。处理方式很简单零面积框直接删除该行中心点越界的把坐标裁剪回0到1范围。但要注意如果一个标签文件里所有行都被删光了那这个文件对应的图片也要从训练集里剔除否则会出现有图无标签的情况某些训练框架会报错。2.2 重复图片与近似重复的识别数据集里存在重复图片是很常见的尤其是从视频抽帧生成的数据集。重复图片会导致训练集和验证集之间发生数据泄漏让验证指标虚高实际部署时性能大打折扣。识别完全重复的图片可以用MD5哈希但近似重复比如同一帧稍微裁剪了一下就需要用感知哈希或者特征相似度。我一般用imagehash库做感知哈希阈值设在5左右from PIL import Image import imagehash import glob def find_duplicates(img_dir, threshold5): hashes {} duplicates [] for img_path in glob.glob(f{img_dir}/*.jpg): img Image.open(img_path) h imagehash.phash(img) for existing_hash, existing_path in hashes.items(): if abs(h - existing_hash) threshold: duplicates.append((img_path, existing_path)) break else: hashes[h] img_path return duplicates这份数据集我跑下来发现近似重复的比例不算高大概2%左右主要集中在连续帧抽样的部分。处理策略是保留其中一张其余的从训练集移除。但这里有个细节如果重复图片分别落在训练集和验证集里一定要确保移除后两个集合没有交集否则验证结果不可信。2.3 图像尺寸统一与letterbox处理前面提到这份数据集图片分辨率不统一训练前需要统一尺寸。YOLO系列通常用640×640作为输入尺寸但直接resize会改变宽高比导致目标变形。正确做法是letterbox保持宽高比缩放然后用灰色填充到目标尺寸。这里有个容易忽略的点letterbox之后标注框的坐标也要跟着变换。如果你用的是Ultralytics的YOLOv8它的数据加载器会自动处理letterbox和坐标变换你不需要手动改标签。但如果你自己写数据加载器就必须同步更新坐标否则框会错位。我实测下来对于驾驶员行为检测640×640的输入尺寸基本够用因为驾驶员在画面里占的比例通常比较大。但如果你的摄像头安装位置比较远驾驶员在画面里很小那可能需要提到960甚至1280。这个要根据实际场景来定不能一概而论。3. 用YOLOv8跑通第一版基线模型数据清洗完之后先别急着调参优化第一步是跑通一个基线模型确认整个流程没问题。基线模型不需要多高的精度它的作用是给你一个参照点后面所有的改进都要跟它对比。3.1 环境搭建与依赖版本选择YOLOv8用Ultralytics的官方库是最省事的安装就一行命令pip install ultralytics但这里有个坑Ultralytics的版本更新非常快不同版本之间的API和默认参数可能有差异。我建议锁定一个稳定版本比如8.0.x系列避免今天跑通的代码明天就报错。另外PyTorch的版本要和CUDA版本匹配这个不用多说装之前先nvidia-smi看一下驱动支持的CUDA版本。我用的环境是Python 3.10 PyTorch 2.0 CUDA 11.8 Ultralytics 8.0.196这套组合实测比较稳。如果你用的是更新的50系显卡可能需要PyTorch 2.7以上才支持这个要提前确认。3.2 data.yaml配置与路径陷阱data.yaml看起来简单但路径配置是最容易出问题的地方。Ultralytics支持相对路径和绝对路径相对路径是相对于你运行训练命令时的工作目录不是相对于data.yaml文件本身。这个细节坑过很多人。我的建议是直接用绝对路径省得来回折腾train: /home/user/dataset/images/train val: /home/user/dataset/images/val test: /home/user/dataset/images/test nc: 8 names: [normal, phone, smoke, drink, hands_off, turn_head, look_down, fatigue]还有一个细节names列表的顺序必须和标签文件里的class_id严格对应。比如class_id为0的必须是normal如果顺序写反了模型学出来的类别就全乱了。这个错误很隐蔽因为训练loss看起来正常但推理结果完全不对。3.3 基线训练命令与关键参数解读基线训练命令其实很简单yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns/driver \ namebaseline这里几个参数值得展开说。model选yolov8n.pt是因为它最小最快适合先跑通流程。等流程验证没问题了再换yolov8s或yolov8m提升精度。epochs设100是起步值实际要看loss曲线什么时候收敛。batch16是显存和训练稳定性的折中显存够的话可以往上加但要注意学习率也要相应调整。注意第一次训练建议先用小规模数据比如从训练集里抽2000张跑10个epoch确认整个流程没问题再上全量数据。全量数据跑一次动辄几个小时流程有问题的话时间全浪费了。3.4 训练过程监控与loss曲线判读训练启动后重点看几个指标box_loss、cls_loss、dfl_loss以及验证集上的mAP50和mAP50-95。box_loss反映定位精度cls_loss反映分类精度dfl_loss是YOLOv8特有的分布焦点损失影响框的回归质量。正常情况下三个loss都应该稳步下降然后趋于平缓。如果box_loss下降但cls_loss不降说明定位学得还行但分类有问题可能是类别不平衡或者标注类别有误。如果loss震荡剧烈通常是学习率太大或者batch太小。我跑基线的时候前20个epoch loss下降很快30到60个epoch进入平台期60之后基本没什么变化。最终mAP50大概在0.82左右mAP50-95在0.55左右。这个成绩作为基线是合格的但离实际部署还有距离尤其是hands_off和fatigue这两个类别的召回率偏低。4. 类别不平衡与难例的针对性处理基线跑通之后就要开始解决具体问题了。这份数据集最突出的问题就是类别不平衡normal类别太多fatigue类别太少导致模型对少数类的检测能力很弱。另外hands_off和look_down这两个类别因为姿态变化大、与相邻类别界限模糊也是难啃的骨头。4.1 过采样、欠采样与focal loss的取舍处理类别不平衡有三条路过采样少数类、欠采样多数类、改损失函数。我三种都试过说说实际效果。过采样就是把fatigue和look_down这些少数类的图片复制多份让它们在训练集里的比例提上来。好处是简单直接坏处是容易过拟合因为同一张图反复出现模型会记住而不是学会。我的做法是配合数据增强一起用每次过采样的时候随机施加不同的增强这样每份副本其实都不一样。欠采样就是随机丢弃一部分normal类别的图片。这个方法的副作用是浪费数据而且normal类别里其实有很多边界情况比如手放在方向盘上但没完全握住丢掉可惜。我不太推荐。focal loss是我最终采用的方法。它通过降低易分类样本的权重让模型更关注难分类的样本。YOLOv8默认用的是BCE loss改成focal loss需要修改源码或者用支持自定义loss的版本。实测下来focal loss对fatigue类别的召回率提升明显从基线的0.61提到了0.74。4.2 针对hands_off与look_down的标注复核这两个类别的问题不完全在数据量而在标注一致性。hands_off的定义是双手离开方向盘但实际标注中单手离开算不算手搭在方向盘上但没握算不算这些边界情况如果标注标准不统一模型就学不明白。我的做法是抽了500张这两个类别的图片逐张复核标注把模糊的、有歧义的挑出来重新标。这个过程很枯燥但效果立竿见影。复核之后重新训练hands_off的mAP50从0.71提到了0.83。look_down的问题是和fatigue有重叠低头看手机和低头打瞌睡在视觉上很像。解决办法是在标注时严格区分看手机必须有手机入镜打瞌睡必须有闭眼或打哈欠的特征。如果两者都不明显就归到normal里宁可漏标也不要错标。4.3 数据增强策略的定制化调整YOLOv8默认的数据增强包括mosaic、mixup、随机翻转、HSV调整等。这些通用增强对驾驶员行为检测不一定都合适。比如随机翻转水平翻转后左手打电话变成右手打电话如果实际场景里两种都常见那没问题但如果你的应用只关心是否打电话这个二分类翻转反而增加了不必要的多样性。我针对这个场景做了几处调整关闭上下翻转驾驶员不会倒着开车降低mosaic的概率mosaic会把四张图拼在一起车内场景拼出来很违和增强HSV的饱和度扰动应对不同光照条件。这些调整都是基于对场景的理解没有放之四海皆准的答案。5. 模型选型与推理部署的实测对比训练出模型只是第一步真正落地还要考虑推理速度、模型大小、部署环境。这份数据集训练出来的模型最终要跑在车机或者边缘设备上所以模型选型不能只看精度。5.1 YOLOv8n/s/m在驾驶员行为检测上的精度速度权衡我把三个尺度的模型都训了一遍在同一台机器上测了推理速度结果如下模型mAP50mAP50-95参数量GPU推理(ms)CPU推理(ms)YOLOv8n0.820.553.2M6.885YOLOv8s0.870.6111.2M12.4210YOLOv8m0.890.6425.9M24.1480从数据看YOLOv8s是性价比最高的选择精度比n高了5个点速度还能接受。m的精度提升有限但速度慢了一倍除非你的硬件很充裕否则不太划算。如果部署在纯CPU设备上那只能选n而且还要做量化加速。5.2 ONNX导出与TensorRT加速的踩坑记录Ultralytics支持一键导出ONNXyolo export modelruns/driver/baseline/weights/best.pt formatonnx opset12但导出之后用onnxruntime推理经常遇到输出维度对不上的问题。这是因为YOLOv8的ONNX输出格式和YOLOv5不一样v8的输出是[1, 84, 8400]这种形状需要自己写后处理解码。我建议直接用Ultralytics的推理接口或者参考官方提供的后处理代码不要自己瞎写。TensorRT加速在NVIDIA设备上效果很明显FP16量化后速度能提升2到3倍精度损失在1个点以内。但TensorRT的版本兼容性很坑引擎文件不能跨版本使用换一台机器就要重新构建。如果部署环境固定TensorRT是首选如果环境多变ONNX更省心。5.3 实际车载场景下的误报与漏报分析实验室指标好看不代表实际好用。我把模型接到实际车载视频上跑了一遍发现几个典型问题。误报最多的是把调整后视镜误判成扭头因为两个动作的头部姿态确实很像。解决办法是加入时序信息单帧判断不了就看连续几帧的动作趋势。漏报最多的是低头看手机因为手机屏幕小、反光强有时候模型根本看不到手机。这个只能靠提高输入分辨率或者专门训练一个手机检测的子模型来辅助。还有一个容易被忽略的问题驾驶员戴墨镜时疲劳检测基本失效因为看不到眼睛。这种情况下只能依赖打哈欠的嘴部特征或者干脆把疲劳检测降级为长时间无动作的辅助判断。6. 从数据集到落地项目的几点经验做完这个项目有几个体会特别深都是文档里不会写、只有真正跑过一遍才知道的东西。第一数据集的类别定义比数量重要得多。22600张听起来不少但如果类别边界模糊再多数据也训不出好模型。我花在复核标注上的时间比调参的时间还多但我觉得值。第二不要迷信大模型。在驾驶员行为检测这个相对封闭的场景里YOLOv8s已经能到0.87的mAP50实际业务够用了。与其花时间堆模型规模不如把数据质量和后处理逻辑做好。第三时序信息是绕不开的。单帧检测永远解决不了扭头和调整后视镜这种动作相似的问题必须引入连续帧的分析。我后来加了一个简单的滑动窗口投票机制误报率直接降了一半。第四部署环境决定技术选型。如果你的目标平台是车机那模型大小和推理速度的优先级要高于精度如果是云端分析那可以放开手脚用大模型。这个顺序不能搞反。最后说一个具体的技巧训练的时候把验证集按场景分组比如白天一组、夜间一组、逆光一组分别看指标。这样你能清楚知道模型在哪种场景下弱针对性补数据。我一开始只看总体mAP觉得0.87挺好了分组一看才发现夜间场景只有0.72后来专门补了夜间数据才提上来。
网站建设高端定制企业官网