YOLO夜间车辆检测实战:从数据集准备到训练避坑指南
简介YOLO夜间车辆检测数据集专注于夜间低照度环境中的车辆识别任务面向目标检测初学者、课程设计者以及自动驾驶视觉算法工程师。数据集中包含真实夜间场景下拍摄的高质量车辆图片5000张整体场景丰富多样、贴近实际道路环境使用LabelImg工具人工标注框选准确度高并提供VOC格式xml、COCO格式json、YOLO格式txt三类标签文件分别存放于独立目录下载后可直接接入YOLOv5、YOLOv8等主流检测框架训练。压缩包共2000个文件包含xml标签、html教程、txt列表和Python脚本等多种类型整体大小约209MB。配套资料包括Linux与Windows双平台YOLO环境搭建教程、基于案例修改训练自有数据集的完整指导以及训练集验证集测试集划分脚本支持读者按需重组数据。目前已有448人学习下载便于快速上手真实夜间车辆检测项目。1. 夜间车辆检测数据质量比模型结构更值得先花钱做夜间车辆检测的人多半都经历过这种尴尬白天mAP刷到0.85的模型一到晚上直接掉到0.6换backbone、调anchor、堆trick都救不回来。问题往往不在模型而在训练数据——夜间图像光照低、车灯过曝、目标边缘模糊普通数据集根本喂不饱这个场景。这套YOLO夜间车辆检测数据集的核心价值就在这里5000张真实夜间道路图片全部用LabelImg人工标注同时提供VOC的xml、COCO的json、YOLO的txt三种格式标签拿到手不用转换就能直接开训。适合正在做车载视觉、智能交通、安防监控的从业者也适合刚入门YOLO但不想在数据准备上浪费两周的人。这篇文章会把这套资源里的数据组织、划分脚本、环境搭建和训练流程逐个拆开讲重点放在你照着做时最容易翻车的几个地方。2. 数据集真实构成5000张夜间图片与三种标注格式并存的底层逻辑2.1 图片场景与标签分布先搞清楚你拿到的到底是什么从标题和文档清单来看这套资源的核心是5000张夜间车辆检测图片。所谓夜间不是简单把白天图片调暗而是真实拍摄的夜晚道路场景包含城市道路、高速公路、十字路口、停车场等不同环境。这点很重要因为夜间检测的难点不在暗而在光照分布极端不均匀——对面来车的远光灯、路灯下的高亮区域、阴影里的车身轮廓这些才是真正考验模型泛化能力的地方。标注质量方面资源说明中提到使用LabelImg人工标注标注框质量高。我拿到这类资源的第一步永远是验标签不是看几张图觉得标得不错就完事。先统计每张图的标注框数量、类别分布、框面积分布再随机抽几十张图叠上标注框肉眼检查。5000张图说多不多说少不少如果标注质量不行后面训练出的模型会有明显的系统性偏差而且这种偏差在验证集上还看不出来。类别方面虽然资源描述没写具体类别清单但从车辆检测这个定位看大概率覆盖car、bus、truck、motorcycle这类常见车辆类别。单类别数据集训练起来省心但如果你要做的是区分具体车型需要自己对着xml或json检查类别名称是否满足需求。养成习惯拿到数据集第一件事不是训练是统计和抽样。2.2 voc、coco、yolo三种格式并存为什么需要三种标签而不是一种这套资源的标签组织方式是VOC的xml、COCO的json、YOLO的txt分别存放在不同文件夹下。不用自己写转换脚本这确实省事但如果你不理解三种格式的组织逻辑后面做数据增强、多数据集融合时会踩坑。VOC格式是一张图对应一个xml文件xml里包含object的名字和bndbox坐标坐标是绝对值。COCO格式是整个数据集对应一个json文件标注内容包含images列表、annotations列表和categories列表每个annotation有image_id、category_id、bbox和area等字段bbox坐标是绝对值。YOLO格式是一张图对应一个txt文件每行内容为class_id x_center y_center width height坐标是归一化到0到1的相对值。三种格式的差异决定了它们的用途xml适合单张图片的快速查看和人工校验因为跟图一一对应出问题容易定位json适合做训练集统一切分、统计分析但一个json文件里数据量大时读取和写入都慢txt是YOLO系列模型直接读取的格式不需要任何额外的转换。拿到三种格式后我的建议是平时用xml做可视化检查做统计分析用json训练直接用txt。不要觉得三种格式重复它们各有用处尤其是你要把这份数据跟自己手上的其他数据合并时通常需要统一成某一种格式这时候三种格式随便你挑省很多事。2.3 拿到的第一件事先做三项基础检查我认为数据验证比模型架构选择更影响最终效果。检查顺序如下第一确认图片和标签的数量是否一一对应。YOLO训练时如果某个txt缺失或损坏训练会直接报错而且错误信息定位成本高。用下面的命令可以快速比对# 统计图片数量和标签数量 ls /path/to/images/*.jpg | wc -l ls /path/to/labels/*.txt | wc -l # 找出没有对应标签的图片 for img in /path/to/images/*.jpg; do base$(basename $img .jpg) if [ ! -f /path/to/labels/${base}.txt ]; then echo Missing label: $base fi doneVOC和YOLO格式都是按文件一一对应的COCO格式则统一在一个json里数量检查需要分别做。这一步建议认真执行我遇到过一次数据集压缩包解压后部分txt文件损坏的情况幸好提前检查否则训练中途崩溃排查会很痛苦。第二检查标注坐标是否越界。YOLO格式的坐标是归一化的正常范围应该是0到1但有些标注工具导出的框边缘刚好卡在边界上或者因为裁剪操作导致部分坐标超出范围。写成脚本批量扫描发现越界的框做裁剪到边界或者整条删除。import os label_dir path/to/labels issues [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: issues.append((fname, wrong_field_count)) break try: vals list(map(float, parts[1:])) except ValueError: issues.append((fname, non_numeric)) break if not all(0.0 v 1.0 for v in vals): issues.append((fname, out_of_range)) break print(fFound {len(issues)} files with issues) for issue in issues[:20]: print(issue)这个检查代码很简单但我经历过因为边界框坐标是负数导致loss变成nan的情况排查半天才发现是数据问题不是模型问题。数据集的坑往往都藏在看不见的地方。第三检查类别id的连续性。如果用YOLO格式训练类别id必须从0开始连续编号不能跳号。部分标注工具导出的id可能不连续这会导致模型输出维度与实际类别数不匹配训练时各种奇怪的报错。3. 划分脚本怎么选按文件复制还是按ImageSets生成txt3.1 脚本差异与适用场景三个划分脚本各有各的用途压缩包里有三个划分脚本名字看起来相近作用完全不同。如果分不清直接用可能生成的数据集结构跟你预期的不一样浪费一晚上。第一个是训练集、验证集、测试集划分脚本图片标签划分写入新文件夹.py这个脚本做的是一次性三分把图片和对应的标签文件按比例拆成三份然后复制到新的文件夹里。适合的场景是下一步直接训练不希望中间再有多余步骤。复制出来的文件夹结构是自包含的比如train文件夹下同时有图片和标签后面接YOLO训练时直接改路径就行。第二个是训练集、验证集划分脚本图片标签划分写入新文件夹.py只做两分没有测试集。这个适合你打算训练完自己手动挑测试图片的情况。因为自动驾驶或安防场景下测试集的构建往往有特定的场景要求比如专门挑雨天、隧道、强光等特殊条件脚本自动划分的测试集不具备这种倾向性所以只分train和valtest自己搭。第三个是split_train_val生成ImageSets下txt文件划分脚本.py这个跟前面两个完全不同。它不复制文件而是生成VOC风格的txt文件里面写的是图片文件名列表。这是VOC数据集的经典组织方式配合那些需要先从ImageSets读取文件列表再加载数据的训练框架使用。如果你用的是老版本的一些检测框架或者想自己控制数据加载流程这个脚本更合适。三个脚本不是哪个更好的关系而是适配不同的工作流。最稳的做法是先用第三个脚本生成txt列表确认划分比例没问题再用第一个或第二个脚本做物理文件复制这样即使脚本有BUG也不会直接破坏原始数据。3.2 划分比例怎么设从实际经验看不是固定的712资源自带的脚本里默认比例很可能写的是712或811我可以直接说这个比例对夜间车辆检测这个任务不一定合理主要需要看你的数据和任务特点。5000张图片如果数据分布相对均匀场景变化不大那么811是可以的训练集4000张足够。但夜间车辆检测的场景差异非常大城市道路、高速、乡村道路的光照条件完全不同。如果某个场景的图片只占数据集的很小比例随机划分可能导致这个场景在训练集中占比过低模型学不好更严重的是验证集或测试集里这个场景样本太少评估结果不可信。我的经验做法是先按场景分好类然后每个场景单独按比例划分最后合并。这叫做分层抽样比直接shuffle好用得多。但脚本是通用的不支持分层所以你手工分也行。操作上你会用到两个参数一个是划分比例一个是随机种子。随机种子很重要固定seed保证每次运行结果一致方便复现。import random import os import shutil random.seed(42) # 固定随机种子 image_dir path/to/images label_dir path/to/labels train_img_dir path/to/train/images train_lbl_dir path/to/train/labels val_img_dir path/to/val/images val_lbl_dir path/to/val/labels all_images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(all_images) train_ratio 0.8 val_ratio 0.1 train_count int(len(all_images) * train_ratio) val_count int(len(all_images) * val_ratio) for idx, img_name in enumerate(all_images): base os.path.splitext(img_name)[0] src_img os.path.join(image_dir, img_name) src_lbl os.path.join(label_dir, base .txt) if idx train_count: dst_img_dir train_img_dir dst_lbl_dir train_lbl_dir elif idx train_count val_count: dst_img_dir val_img_dir dst_lbl_dir val_lbl_dir else: dst_img_dir path/to/test/images dst_lbl_dir path/to/test/labels shutil.copy(src_img, os.path.join(dst_img_dir, img_name)) shutil.copy(src_lbl, os.path.join(dst_lbl_dir, base .txt))逻辑不复杂但需要注意这行代码里 copy 而不是 move是为了防止划分错了导致原始数据被破坏。很多人在这里用move结果执行完发现原始数据被拆得七零八落想重新划分都没法恢复。划分脚本这种操作务必用copy宁可磁盘多占一份空间也不要给自己留后悔药。验证集比例取0.1是因为夜间检测任务的验证集主要看整体loss和mAP趋势不需要太大。测试集0.1是底线再小的话测试结果方差会很大模型A和模型B差0.2个点你都分不清是真差距还是随机波动。如果数据量小到3000张以下建议改成0.850.10.05把更多数据留给训练。3.3 划分后的目录结构一个会被反复踩的路径问题划分脚本把图片和标签写入新文件夹后目录结构一般是这样的train/ images/ # 或者直接放图片 labels/ # 或者直接放txt val/ images/ labels/YOLO训练时的数据配置文件需要指定图片路径和标签路径常见配置写法是这样的path: /path/to/dataset train: train/images val: val/images test: test/images nc: 1 names: [car]这里最大的坑在于这个path字段的根目录路径必须和train字段的相对路径拼起来后能直接访问到图片文件。很多人把path写错或者train写错报错说找不到图片然后就开始怀疑数据集坏了。另一个需要注意的点是如果你的YOLO版本是v5或v8标签目录是自动推导的images目录下如果有jpglabels目录会自动定位到同级目录下的labels文件夹并把jpg替换成txt。所以如果划分脚本把图片和标签放到不同层级可能导致训练时自动推导失败。解决方法是严格按照YOLO默认的目录结构来摆放dataset/ images/ train/ xxx.jpg val/ xxx.jpg labels/ train/ xxx.txt val/ xxx.txt这种images与labels分顶层、train与val分二级的结构是YOLO系列最通用的组织方式新版本YOLO也兼容这种结构。划分脚本默认生成的可能不是这种结构拿到手后需要自己调整一下目录层级不要嫌麻烦直接训练。4. 环境搭建与训练改造Windows和Linux各自的流程与差异点4.1 Windows环境搭建CUDA版本和torch版本必须匹配这个压缩包里附带了YOLO环境搭建的Windows版本教程和Linux版本教程还有Linux的Ubuntu安装教程。看起来是照顾了两类用户但实际训练我发现Windows和Linux的环境差异还是挺大的。Windows环境搭建第一条就是CUDA和PyTorch版本匹配问题。YOLO官方默认要求的PyTorch版本是1.8以上具体看你的YOLO版本v5要求1.7v8要求1.8而CUDA版本直接决定你能不能调用GPU。我遇到过一个案例某用户CUDA装的是11.8PyTorch装的是1.13.1cpu版本训练时系统跑CPU5000张图一个epoch要跑两个小时他还以为是正常的。# 查看CUDA版本 nvidia-smi # 查看PyTorch能否调用GPU python -c import torch; print(torch.cuda.is_available())如果torch.cuda.is_available()返回False说明PyTorch装的不是GPU版本或者CUDA版本不匹配。安装GPU版PyTorch的标准命令长这样pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118CUDA 11.8对应cu118CUDA 12.1对应cu121一定要跟本机驱动支持的CUDA版本对齐。这是环境搭建里最浪费时间的玄学问题百分之八十的环境问题都出现在这里。4.2 Linux环境搭建Ubuntu下别用系统自带PythonLinux环境搭建教程用的是Ubuntu系统我一般不建议直接用系统自带的Python 3.8或3.10。Ubuntu的Python是系统组件很多系统工具依赖它你如果在上面装各种pip包极容易搞坏系统环境。更安全的方式是用conda# 创建独立的conda环境 conda create -n yolo python3.9 conda activate yolo # 安装依赖 pip install -r requirements.txt用conda的好处是环境隔离之后不管怎么折腾都不会影响系统。注意这里YOLO项目对Python版本有一定要求v5支持3.8到3.10v8支持3.8到3.11过高或过低都可能出现奇怪的依赖冲突。建议按照教程里给的Python版本来建环境不要用最新的Python 3.12。Linux下还有几个坑一是OpenCV的依赖Ubuntu 22.04上经常因为缺少libGL.so.1报错安装一下就行sudo apt update sudo apt install libgl1 -y二是权限问题如果你用的是root用户训练深度学习模型一些缓存目录会出现权限混杂。建议训练时用普通用户或者把所有输出目录的所有权给到当前用户sudo chown -R $USER:$USER /path/to/project4.3 训练教程的核心改数据配置文件和模型配置文件资源里附带的YOLO训练教程是根据案例修改训练自己的数据集这种思路需要修改两个文件数据配置文件data yaml和模型配置文件model yaml。数据配置文件上面已经给过示例模型配置文件则是根据你的类别数调整输出层的结构。比如用YOLOv5s在models/yolov5s.yaml里修改nc为你数据集的实际类别数。不过这行代码本身很简单真正的坑在于如果改了模型配置文件预训练权重和模型结构对不上加载权重时会报错。解决方案有两种# 方案一不加载预训练权重从头训练 # 训练命令中不加 --weights 参数 # 方案二加载预训练权重但忽略类别数不匹配的层 model model # 需要在训练代码里处理YOLO官方代码通常自动处理了但有些版本会直接报错我自己训练数据集的经验是如果你不是从零开始做研究一般来说是加载预训练权重然后微调。YOLOv5s或YOLOv8n的预训练模型在COCO上学习过通用特征对夜间车辆的纹理、轮廓特征有一定的迁移能力。加载预训练权重后再训练自己的数据集收敛速度快很多最终精度也更高。训练命令中加 --weights 参数即可。python train.py --data data.yaml --cfg models/yolov5s.yaml --weights yolov5s.pt --epochs 300 --batch-size 16 --img-size 640训练时几个关键参数的建议值--batch-size根据显存来定16G显存可以168G显存建议8干不动就4--img-size用640如果目标车辆在画面中很小建议改成1024或1280但相应的显存占用会大幅增加--epochs先用100跑通流程确认能收敛再加到300。5. 训练避坑BN崩溃、类别不均衡与夜间目标的漏标问题5.1 现象训练到一半loss变成nanBN层数值爆炸第一次训练这5000张夜间图时很多人会遇到epoch 30左右loss突然变成nan进程崩溃。这是因为夜间图像噪声大光照突变剧烈模型的Batch Normalization层在统计均值和方差时被极端值干扰导致数值不稳定。原因有两个层面一是夜间图像中车灯区域过曝像素值接近255而周围区域很暗这种极端分布让BN层的running_mean和running_var出现偏移二是batch_size设置太小比如4或8导致BN统计的样本量不够方差估计偏差大。解决方法是调整训练参数最直接的是增大batch_size到16以上让BN统计更稳。如果显存不够冻结前几层的BN参数不让它更新# 在训练代码中冻结前10层的BN for name, module in model.named_modules(): if isinstance(module, torch.nn.BatchNorm2d) and int(name.split(.)[0]) 10: module.eval()这种方法可以让低层BN保持预训练时的统计分布不被夜间图片的极端值带偏。另一个方案是使用SyncBN多卡训练时BN统计跨卡计算效果也很好。YOLOv8的代码里已经内置了SyncBN支持开启就行。5.2 现象类别不均衡导致白天车辆检测正常夜间频繁漏检单类别数据集一般不存在类别不均衡问题但这个数据集虽然是车辆检测如果类别列表中轿车占比90%货车和摩托车只占5%和2%模型很自然会偏向学习样本量大的类别。夜间场景下摩托车尾灯是小目标且特征不明显漏检率会非常高。解决的方向很明确一方面收集更多稀少类别的夜间图片补充数据另一方面在训练时给稀少类别增加损失权重。YOLO的loss函数中类别损失权重在配置文件里可以调整# YOLOv5的loss计算中类别损失部分的加权 loss_cls self.BCEcls(pxy, py, weightcls_w)不过实际操作中这个类别的加权系数需要反复试改起来很麻烦。我更推荐的做法是数据层面的增强对稀少类别的样本做额外的复制或数据增强比如对摩托车图片做轻度色彩抖动和对比度增强让模型多看到这个类别的样本。这个在数据预处理阶段做不用动模型代码。5.3 现象训练loss正常下降验证集mAP也正常但夜间实拍效果一塌糊涂这是最气人的一种情况训练时一切正常验证集的mAP可能也有0.8但拿到夜间实际场景一测频繁漏检和误检。原因出在数据集构建的时候训练集、验证集、测试集划分脚本是随机划分的同场景的图片可能同时出现在训练集和验证集中。夜间车辆检测数据虽然场景多但如果你是在同一批连续拍摄的视频帧里取的数据相邻帧高度相似模型在验证集上见过这些场景评估自然虚高。这说明评估方式有问题。我验证具体做法是拿模型去跑完全不相关的夜间视频片段比如另一个城市、另一组光照条件的夜间道路视频统计检测效果。如果只有训练数据和验证数据都来自同一采样源看不出泛化能力。另外一个相关问题是漏标。5000张图中如果存在漏标例如有些图片里车灯反光中被标注为车辆但阴影里的车身部分完全没有被框出来模型会学到一个错误的特征看到两个车灯就认为是车而不是看到完整的车身轮廓才认为是车。夜间场景下这个错误特征被放大因为夜间车辆最明显的特征就是车灯而白天车辆特征是完整的轮廓。这个问题很难自动检测只能抽样人工检查标注质量。3600帧抽1帧检查5000张图抽500张人工看一遍花一两个小时是值得的。5.4 现象编码格式问题导致标签解析错乱训练loss无法收敛指示灯框标注的图片在Windows和Linux之间传输txt文件的编码和换行符不同部分脚本在读取标签时会把\r\n里的\r一并读进class id导致class id变成一个很奇怪的数字模型训练时类别数和真实类别数对不上训练完全无法收敛。# 用Python检查标签文件是否有异常字符 with open(label.txt, rb) as f: content f.read() if b\r in content: print(CRLF line endings detected, need to convert)解决方法是用dos2unix工具或者直接在Python中统一转换。这种东西如果不提前检查和批次处理足够让你浪费一整天排查训练不收敛的原因。这是典型的代码写对了、数据也没标错、训练就是不行的玄学问题最后发现是换行符的锅。6. 训练效果自检从loss曲线到夜间场景实测的验证闭环模型训练完最终确认效果能不能用之前我一般强制自己走一遍完整的验证流程缺一步都不踏实。第一步看loss曲线。训练过程输出的loss曲线应该是一路下降后趋于平缓且训练集和验证集loss差距不大。如果训练loss持续下降但验证loss回升是过拟合如果两个loss都在高位震荡大概率是学习率太大或数据有问题。YOLOv8的TensorBoard或wandb日志里可以直接看这两条曲线不要只盯着最后的mAP数字。第二步看混淆矩阵。YOLO训练结束后会输出混淆矩阵这个能直观看出哪些类别之间容易互相误检。夜间场景里轿车和SUV的混淆概率高于白天因为夜间轮廓模糊车灯位置相似模型区分不了车型。如果混淆矩阵里两个类别的互误率超过15%可以考虑合并类别或者补充更典型的样本。第三步做夜间场景实测。拿训练好的模型去跑几段跟训练集完全不同的夜间视频注意三个点远光灯直射场景下能不能保持检测、多个车灯重叠时会不会漏检、雨天或反光路面会不会误检。这三个点分别对应目标遮挡、目标重叠和环境干扰是夜间检测最容易暴露问题的地方。第四步尝试模型轻量化。如果模型要部署到嵌入式设备或车载边缘设备上YOLOv5s或YOLOv8n的参数量可能还是太大。可以用YOLO自带的pruning或蒸馏工具做压缩也可以用TensorRT做推理加速。夜间检测的落地场景通常对实时性要求高精度差一点可以通过后处理弥补速度上不去就是架构问题。我一般先把模型跑通FP16精度再决定要不要做int8量化int8量化会让mAP掉1到3个点夜间小目标可能会掉更多需要实测。从那以后每次做目标检测项目我都强制自己走完这条验证链路数据质检、分层划分、环境复现、训练监控、独立场景实测。前面省掉的每一步最后都会在某个深夜以诡异bug的形式还回来。希望这些经验能帮你在夜间车辆检测这个任务上少走弯路直接把这套资源的价值发挥出来。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →