ARTICLE DETAIL

资讯详情

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

969张车辆图像带你跑通YOLOv8训练全流程

969张车辆图像带你跑通YOLOv8训练全流程 简介面向YOLO系列算法研究者的车辆目标检测数据集涵盖汽车、自行车、公共汽车三类常见交通目标适合用于模型训练、验证与测试等完整流程。压缩包内共计两千个文件包含文本标签与XML标注各九百六十九份配套六十一张JPG图像另附一份数据配置文件整体大小约五十七点零八兆字节。数据已经预先划分好训练集与验证集并提供YOLO、VOC两种标签格式能够直接适配YOLOv5至YOLO11等主流版本无需额外转换。标签记录目标类别的索引以及归一化后的中心点坐标、宽度和高度完全符合YOLO标准训练要求。已有一百六十四人学习/下载无论是初学目标检测的读者还是需要快速启动车辆识别实验的开发者都能从中节省数据准备时间专注模型调参与算法对比。1. 这 969 张带标签车辆图像能让 YOLO 项目从零开始跑通以“yolo算法”为关键词找数据的人多半不是缺模型结构图而是缺一批能直接喂给训练脚本的、带标签的图。这个车辆数据集恰好卡在入门最舒服的位置969 张图像汽车、自行车、公共汽车三个类别标注文件随图附带。它解决的实际问题是不用再花一下午从 OpenImages 或 COCO 里筛标签、转格式、按类别拆分解压后就能开始训练你自己的车辆检测模型。适合正在跑 YOLOv8 入门项目、需要交一份课程作业或者想在本地快速验证自己推理脚本的人。它不适合拿来刷极致精度但足够让你把训练到推理的整条链路跑通。2. 从数据集到可训练的 YOLO 项目标注格式先统一2.1 标签文件里到底装了什么TXT 格式与类别编号大多数带标签的车辆数据集在解压后会给出两类文件图片文件.jpg/.png和同名 TXT 标签文件。比如img_0001.jpg对应img_0001.txt这个 TXT 就是 YOLO 训练要求的标注格式。每一行代表一个目标框五个字段依次是类别编号、归一化后的中心点 x 坐标、中心点 y 坐标、框宽度、框高度全部用空格或制表符分隔。拿到手的第一件事不是急着开训而是先把标签读出来看一眼。以下脚本能把某个标签文件的内容按行解析让你确认类别编号和坐标范围是不是正常import os label_path datasets/vehicle/labels/img_0001.txt with open(label_path, r, encodingutf-8) as f: lines f.readlines() for line in lines: parts line.strip().split() class_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) width float(parts[3]) height float(parts[4]) print(f类别: {class_id}, 中心点: ({x_center:.3f}, {y_center:.3f}), f宽: {width:.3f}, 高: {height:.3f}) assert 0 x_center 1, 坐标超出归一化范围这段代码做的是基础体检把类别编号分离出来把坐标转成浮点数并断言中心点坐标保持在 0 到 1 之间。YOLO 系列使用的坐标全部是相对图像宽高的比例值不是像素坐标所以看到 x_center 0.512 这类数值是正常的如果你发现某个坐标值远大于 1那标签可能来自 VOC 格式没转干净训练时会直接报错。另一个关键点是类别编号和类别名称的对应关系。结合这个数据集的目录名“汽车-自行车-公共汽车”常见做法是约定0汽车、1自行车、2公共汽车。这个顺序不是数据集自带的硬规则而是你自己写在 YAML 配置文件里的。如果实际标注文件里 0 代表的是自行车而你按上面顺序写训练出来的模型会一脸茫然。2.2 用脚本把数据集切成 train / val先定随机种子数据集只给了一批图像并不保证已经把训练集和验证集分好所以需要自己按比例切分。常见做法是 8:2 划分也就是大约 775 张做训练、194 张做验证。切分脚本要满足一个前提图片和同名标签文件必须一起移动不能出现图有标签或标签有图的情况。import os import random import shutil random.seed(42) # 固定种子保证每次切分结果一致 image_dir datasets/vehicle/images label_dir datasets/vehicle/labels train_img_dir datasets/vehicle/train/images train_lbl_dir datasets/vehicle/train/labels val_img_dir datasets/vehicle/val/images val_lbl_dir datasets/vehicle/val/labels for d in [train_img_dir, train_lbl_dir, val_img_dir, val_lbl_dir]: os.makedirs(d, exist_okTrue) images [f for f in os.listdir(image_dir) if f.lower().endswith((.jpg, .png))] random.shuffle(images) val_count int(len(images) * 0.2) val_images images[:val_count] train_images images[val_count:] for img in train_images: stem os.path.splitext(img)[0] shutil.copy(os.path.join(image_dir, img), os.path.join(train_img_dir, img)) shutil.copy(os.path.join(label_dir, stem .txt), os.path.join(train_lbl_dir, stem .txt)) for img in val_images: stem os.path.splitext(img)[0] shutil.copy(os.path.join(image_dir, img), os.path.join(val_img_dir, img)) shutil.copy(os.path.join(label_dir, stem .txt), os.path.join(val_lbl_dir, stem .txt)) print(f训练集 {len(train_images)} 张验证集 {len(val_images)} 张)random.seed(42)这行值得单说种子值一旦固定shuffle 的结果就是确定的。今天跑一遍是这 775 张进训练集明天重跑还是这 775 张。这对排查问题很重要不然你改了一句增强代码重新切分后训练集变了指标波动你会分不清是代码引起的还是数据引起的。另一个细节是复制而非移动原文件这样即使切分出错原始数据还在相当于给自己留了后悔药。3. 从零训练一个 YOLOv8 车辆检测模型配置、命令与验证3.1 写一个极简的 vehicle.yaml路径、类别数、类别名YOLOv8 系列训练时需要一个 YAML 文件告诉框架数据在哪、要预测哪几类。这个文件很小但写错路径或者写错类别顺序后面所有工作白费。这里给出一个可直接套用的模板# vehicle.yaml path: datasets/vehicle # 数据集根目录 train: train/images # 训练集图片相对于 path val: val/images # 验证集图片相对于 path names: 0: car 1: bicycle 2: buspath建议写相对路径而不是绝对路径。绝对路径的问题在于你换一台机器训练路径就失效了数据集在 U 盘里插到别的电脑上也要改来改去。相对路径配合项目目录结构解压后放到约定位置就能直接跑。names的顺序严格对应标签文件里的类别编号0 对应 car 还是 bicycle 完全是这里说了算所以写完 YAML 后回头再核对一下第一节的读标签脚本。类别名建议统一用英文。YOLO 的日志、混淆矩阵、导出 ONNX 时的输出层名称都会拿这个名字当作标签中文名虽然能写进 YAML但在导出模型或者画混淆矩阵时容易出现编码问题。至于“yolo算法讲解ppt”里常见的网络结构图这里完全不用操心——你现在只是调用训练框架不是从零实现网络。3.2 用命令行启动训练img、batch、epochs 的关键设置配置写好以后训练本身是单条命令。以下命令适合大多数个人电脑yolo detect train \ datavehicle.yaml \ modelyolov8n.pt \ epochs100 \ img640 \ batch16 \ device0 \ patience20参数拆开看modelyolov8n.pt表示从 COCO 预训练权重开始微调这比从头训练收敛快得多对 969 张这样的小数据集几乎是必选项img640是输入分辨率越大越吃显存但小目标检测效果更好batch16要看显存调节8GB 显存建议先试 162GB 或 4GB 就降到 8patience20表示连续 20 轮验证指标没提升就提前结束省下空转的时间。训练过程里会出现一堆看不懂的指标新手最容易盯着的还是那个 mAP。前 20 轮里 mAP 从 0 涨到 0.7 以上都算正常如果 50 轮还在 0.1 附近打转问题大概率不在训练参数上而在标签文件或数据切分。把epochs设成 100 不是因为 100 轮一定最好而是小数据集在这个轮数附近基本收敛再多就是过拟合。训练结束后runs/detect/train目录里会留下 weights 文件夹里面的best.pt就是验证集上表现最好的权重。3.3 训练完成后怎么验证map50、map50-95、混淆矩阵训练结束不是看 Log 里的 loss 归零就算完你要打开runs/detect/train/results.png看两组曲线。第一组是box_loss和cls_loss的下降趋势loss 持续下降说明模型在学到东西第二组是mAP50和mAP50-95的上升趋势。训练结束时 mAP50 能达到 0.85 以上对这个规模的数据集属于正常表现mAP50-95 通常比 mAP50 低 0.2 到 0.3这是正常现象因为后者对框位置更严格。混淆矩阵是判断“三类之间到底哪里分不清”的最直接工具。训练完后在runs/detect/train/confusion_matrix.png里能看到横轴是真实类别纵轴是预测类别对角线越亮越好。我见过不少跑出来的模型汽车和公共汽车全部正确但自行车一堆被检成汽车——这类问题看指标曲线看不出来看混淆矩阵一眼定位。如果 diagonal 附近的格子颜色偏浅就得回到第 4 章去查标注边界。4. 三类车辆检测的数据集细节标注边界与数据增强4.1 汽车、自行车、公共汽车的标注边界和易混淆类别拿到 969 张车辆图像不能假设标注都完美无缺。车辆检测最常见的质量问题不是漏标而是标错类别公交车和厢式货车长得像自行车和摩托车更是重灾区。你需要在训练前把易混淆样本挑出来最直接的办法是用 OpenCV 把标签框画回图上人眼过一遍。import cv2 img_path datasets/vehicle/train/images/img_0020.jpg label_path datasets/vehicle/train/labels/img_0020.txt class_names {0: car, 1: bicycle, 2: bus} colors {0: (0, 255, 0), 1: (255, 0, 0), 2: (0, 0, 255)} img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r, encodingutf-8) as f: for line in f: cls, xc, yc, bw, bh line.split() cls int(cls) x1 int((float(xc) - float(bw) / 2) * w) y1 int((float(yc) - float(bh) / 2) * h) x2 int((float(xc) float(bw) / 2) * w) y2 int((float(yc) float(bh) / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), colors[cls], 2) cv2.putText(img, class_names[cls], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[cls], 2) cv2.imwrite(check_img.jpg, img)代码中把归一化坐标乘回图像宽高得到真实像素框。这里有个细节值得注意float(bw) / 2这种写法容易丢精度如果标注框极小差几个像素在可视化时看不出毛病但训练时网格匹配会受影响。稳妥做法是先把五个值全部转成 float再统一参与运算。一套可视化脚本跑遍全部训练图后把明显标成公共汽车的厢式货车挑出来手动改掉类别编号。这个改标签的过程虽然耗时却是小数据集提精度最有效的手段之一。数据集中汽车类别占比远大于自行车和公共汽车时易混淆类更值得仔细检查因为它们在损失函数里话语权本来就弱。4.2 面向小数据集的增强参数取舍hsv、flip、mosaic969 张图对深度学习来说不算多所以增强策略对最终效果影响明显。YOLO 自带一批在线增强参数但全开不一定是好事。以下是一份对小数据集比较稳妥的参数起步值# 在训练命令中追加这些超参数 hsv_h0.015 hsv_s0.7 hsv_v0.4 fliplr0.5 mosaic0.8hsv_h、hsv_s、hsv_v分别控制色相、饱和度、亮度的随机扰动幅度。车辆检测对颜色变化不敏感但适度调低饱和度扰动可以让模型不那么依赖天空和路面的颜色特征。fliplr0.5是水平翻转车辆左右对称这项增强几乎无副作用。mosaic把四张图拼成一张训练变相放大了样本量但对小物体密集场景容易导致目标裁切所以不要设成 1.0留一点概率让模型看到完整原图。与在线增强相对的是离线增强。在线增强在训练时实时做不动原始数据集离线增强则事先把图片复制 N 份、各自加亮度扰动、旋转、裁剪再写进训练集。对 969 张的数据集我一般不推荐先做离线增强原因很简单你无法控制增强后的标签分布旋转 30 度后框可能超出图像边界处理起来很麻烦。先把在线增强调好跑一轮看验证集表现再决定要不要加离线版本。训练日志里 augmentation 相关参数会和 loss 一起打印方便你确认配置是否生效。5. 用 969 张车辆图像训练时的常见坑从标注报错到过拟合排查5.1 坑标签文件里出现非数字字符训练第一轮就崩现象执行训练命令后不到 10 秒程序报ValueError: could not convert string to float有时还会带label file ... not found的提示。原因标签文件是从某些转换工具导出的里面残留了逗号、空行、不可见字符或者别的格式的标注比如 VOC 的 XML 内容被直接改成 txt。YOLO 要求每行五个数字多了少了一个标点都会直接失败。解决写脚本遍历所有标签文件逐行尝试split()后转 float失败就打印路径。处理时把行首尾空白去掉、空行跳过如果发现整行是类似(0.5, 0.4, 0.1, 0.2)的带括号格式说明是从 LabelMe 一类工具导出后没转换干净需要重新转一次而不是手工替换括号。5.2 坑类别编号越界mAP 曲线突然归零现象训练前 30 轮一切正常mAP 稳步上升某轮开始 mAP 突然跌到接近 0 并且再也没起来。原因标签里出现了类别编号 3 或更大而你 YAML 里只定义了 0、1、2 三个类别。YOLO 系训练脚本对越界编号不会直接崩而是把这个样本的损失计算异常拉崩整个 batch 的梯度。解决训练前先跑统计脚本检查全部标签文件中class_id的最大值是否小于len(names)。把出现异常编号的文件打印出来单独看那张图里到底是什么车。如果是不小心把一辆摩托车编成了 3改回 1 即可如果是别的杂类车辆干脆删掉这张图和对应标签别让一个脏样本污染全部梯度。5.3 坑数据量太少训练集 mAP 99% 但验证集只有 40%现象训练日志里 mAP50 一路飙到 0.9 以上但验证集上的表现远差于训练集两者差距超过 0.2。原因模型把训练集的背景纹理和车辆细节背下来了没有学到“车辆”的泛化特征本质是 775 张训练图过拟合。网络参数远多于样本量时这种现象尤其明显。另一个隐蔽原因是训练集和验证集划分时没固定随机种子导致验证集出现分布偏差。解决先确认切分脚本里用了random.seed(42)。如果已经固定种子优先按第 3 章增加增强强度、降低模型复杂度把yolov8n.pt换成yolov8n从头训练过度拟合缓解有限或者直接把epochs从 100 降到 60配合patience15提前停止。经验值是小数据集上早停比多加增强更有效因为过拟合是轮数累积出来的。5.4 坑图像尺寸和 EXIF 方向不一致标注框画错位置现象训练时 loss 降不下来把标签画回图上发现框和车辆位置完全对不上比如垂直方向的汽车框成了水平方向。原因很多数据集里的图片来自手机或相机JPEG 文件内嵌 EXIF 旋转信息。Windows 相册会自动按 EXIF 显示图片但 OpenCV 和训练框架读图时默认忽略 EXIF于是出现“显示出来是正的读进来是横的”的局面。标注工具一般按显示方向标坐标坐标和实际图像内容也就错位了。解决训练前统一把所有图片洗一遍用 OpenCV 读取后按 EXIF 方向旋转再重新写回。处理完的图片要在可视化脚本里抽查至少 20 张确认框贴着车辆边缘而不是乱飘。这个坑最容易出现在手机拍摄较多的数据集中在看清原图之前不要怀疑训练参数。5.5 坑数据集混入无关图片低置信度误检满天飞现象训练结束时指标不错但实际测试时报出一堆置信度只有 0.3 左右的检测框框的是路边指示牌、垃圾桶之类的东西。原因数据集中存在少量不属于这三类车辆的图片有些图甚至完全没有车辆目标却在标签文件里保留了一行“空检测”或一个错误框。模型把这些背景特征学进了特征提取层推理时自然把类似纹理的东西当成车辆候选。解决训练前统计每张标签文件的框数量把框数为 0 或框面积异常小的样本单独列出来人工确认。正常的车辆标签分布里汽车框面积占总图面积的 5% 到 30% 比较多如果你看到大量框占总面积不足 0.5%大概率是标注失误或缩略图残留。无条件保留的图就删标签不要让模型硬学噪声。这类样本数量只要占 3%就足以让验证集 mAP 下降 5 个百分点以上。6. 用训练好的检测模型做推理一个可直接改的脚本技巧训练好的best.pt除了看指标最终还是要跑推理。这里给一个带类别过滤的推理脚本适合处理单张车辆图片from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest.jpg, conf0.25, iou0.45, classes[0, 1, 2], saveTrue, projectruns/detect, nameinference, verboseTrue ) for r in results: boxes r.boxes for box in boxes: cls int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f{model.names[cls]} 置信度 {conf:.2f} 坐标 {xyxy})conf0.25是置信度阈值低于这个值的框会被丢弃。推理场景里如果车辆密集遮挡建议把这个值降到 0.15反过来如果误检太多提到 0.4 也不心疼。iou0.45控制 NMS 的窗口大小值越大保留的重叠框越多。classes[0, 1, 2]是一个经常被忽略的开关限制模型只输出这三类预测能挡掉一批在 COCO 预训练阶段学会的无关类别输出。我把这个脚本当作最后的验证关卡拿训练集里没有出现过的照片跑一遍重点看自行车这类小目标。如果自行车在远处模糊背景下检不出就先不要去调 NMS 和置信度回到第 4 章看看是不是自行车标注框画得太紧。说到第 4 章一个我踩过很多次的教训是拿到这类数据集先看 50 张可视化图再动手训练比任何参数调优都节省时间。有人说标注数据集的机器标注时代早来了自己标又累又慢但 969 张图的小数据集人工过一遍也就两三个小时换来的是后面几十个小时不折腾。希望能帮到你少走这段弯路。本文还有配套的精品资源点击获取
返回列表