简介:面向智慧工地与工程车辆识别场景,这份数据集提供了五千零六十七张图片的完整标注,覆盖混凝土运输车、挖掘机、叉车、装载机、压路机、卡车、工人七类目标,可直接用于目标检测模型的训练与验证。压缩包内共两千个文件,以一千九百九十九个Pascal VOC格式的xml标注文件为主体,另附一份使用说明文本,整体大小三百二十一点八六兆字节,便于使用者快速了解目录结构。目前已有五百八十六人学习下载。数据同时包含Pascal VOC与YOLO两种主流标注格式,省去手工转换环节,可无缝对接YOLO、SSD等常见检测框架。对于需要扩充工程车辆样本、开展工地安全监测或算法调优的开发者和研究人员,这份资源能提供标准、统一的数据支撑,减少数据准备时间,提升项目迭代效率。此外,标注框信息完整,类别划分清晰,既适合初学者学习数据标注规范,也能作为生产环境下的预训练数据补充,是一份兼顾易用性与实用性的数据集。
1. 挖掘机叉车工程车辆检测数据集:为什么我建议先核对双格式标注
做施工现场安全监测的同行应该都有体会:模型结构从来不是瓶颈,数据才是。挖掘机叉车工程车辆检测数据集这套资源,拆开 7z 之后是 5067 张 JPG,配了 5067 个 VOC 格式 XML 和 5067 个 YOLO 格式 TXT,覆盖混凝土运输车、挖掘机、叉车、装载机、压路机、卡车、工人这 7 个类别。它解决的核心问题不是“怎么训”,而是“标注格式已替你转换好,省掉自己写转换脚本和对齐坐标的活”。适合谁?做工地安全帽之外第二道防线——工程车辆闯入识别、车辆类型统计的算法工程师,以及刚入门目标检测、想在真实标注数据上验证训练流程的学生。我拿到这种双格式数据集,第一件事永远是写脚本扫一遍 XML 和 TXT 是否一致,而不是急着开训练。
2. 数据集内部结构与格式:VOC 和 YOLO 双标注怎么对齐才算真正可用
2.1 解压后你到底拿到了什么:三个同步文件组
拆开这套 7z 压缩包,我先不看内容,直接用命令行统计文件数量。不少朋友解压完就开始配环境,结果训练时发现缺标注文件,回头再查压缩包,浪费的时间比下载还多。
7z x 挖掘机叉车工程车辆检测数据集VOC+YOLO格式5067张7类别.7z -o./dataset find ./dataset -name "*.jpg" | wc -l find ./dataset -name "*.xml" | wc -l find ./dataset -name "*.txt" | wc -l7z x表示解压并保留目录结构,-o./dataset指定输出目录,注意-o后面不要跟空格。三个find命令分别统计 JPG、XML、TXT 文件数量,正常结果都应该是 5067。如果某个数量少了几百,一般是压缩包下载不完整,或者解压过程中磁盘空间不足导致静默失败,这时候别急着训练,先确认文件齐全再说。
这套资源的文件组织方式是每个xxx.jpg配一个同名xxx.xml和同名xxx.txt。XML 是 Pascal VOC 标准标注,里面记录图片尺寸、目标类别、边界框的xmin/ymin/xmax/ymax像素坐标;TXT 是 YOLO 检测格式,每行一个目标,依次是类别编号、归一化中心点 x、归一化中心点 y、归一化宽度 w、归一化高度 h。两者坐标体系完全不同,XML 是绝对像素值,TXT 是相对比例值,所以拿到手必须核对两边是否真的对应。
另外要提醒一句:这份资源标题里写的是“VOC+YOLO 格式”,但注意它只有检测框的 TXT,不包含 YOLO 分割格式的路径文件。如果你是想做实例分割才下载的,可能会失望——它只适合目标检测和分类任务,不适合直接当分割数据集用。下载前先想清楚自己要做什么,避免白费功夫。我把文件类型整理成一张表,方便对照:
| 文件后缀 | 数量 | 格式说明 | 用途 |
|---|---|---|---|
| .jpg | 5067 | 普通 JPEG 图片 | 训练输入 |
| .xml | 5067 | Pascal VOC XML | 保留完整标注信息 |
| .txt | 5067 | YOLO 检测标注 | 直接供 YOLO 系列使用 |
| 使用前必读.txt | 1 | 文本说明 | 标注类别顺序和注意事项 |
2.2 7 个类别的编号映射:YOLO TXT 第一行数字代表什么
打开使用前必读.txt,类别顺序是["ConcreteTruck", "Excavator", "Forklift", "Loader", "Steamroller", "Truck", "Worker"]。这个顺序直接决定 YOLO TXT 里每行开头的类别编号,训练时的data.yaml必须和它完全一致。
| 编号 | 英文名 | 中文名 | 典型出现场景 |
|---|---|---|---|
| 0 | ConcreteTruck | 混凝土运输车 | 工地出入口、搅拌站 |
| 1 | Excavator | 挖掘机 | 基坑开挖、土方作业 |
| 2 | Forklift | 叉车 | 仓库、材料堆放区 |
| 3 | Loader | 装载机 | 砂石料场、道路施工 |
| 4 | Steamroller | 压路机 | 路面铺设、碾压施工 |
| 5 | Truck | 卡车 | 土方运输、渣土清运 |
| 6 | Worker | 工人 | 施工现场流动人员 |
很多人第一次训练时习惯把自己的类别重新排序,结果模型把工人识别成挖掘机,这不是模型笨,而是编号错位了。YOLO 系列训练时只认数字,不认英文名,数字一旦对应错,后面所有指标都会乱。我拿到数据集后第一件事,就是写脚本检查每个 TXT 里的类别编号是否超出范围:
import os def check_yolo_labels(label_dir, num_classes=7): bad_files = [] for f in os.listdir(label_dir): if not f.endswith('.txt'): continue filepath = os.path.join(label_dir, f) with open(filepath, 'r', encoding='utf-8') as fp: for line in fp: parts = line.strip().split() if not parts: continue cls_id = int(parts[0]) if cls_id >= num_classes or cls_id < 0: bad_files.append((f, cls_id)) if bad_files: print("发现越界类别编号:", bad_files[:10]) else: print("全部标注类别编号正常") check_yolo_labels("dataset/labels")这段脚本扫描整个 labels 目录,逐行解析 TXT,检查第一列的类别编号是否在 0 到 6 之间。num_classes=7是固定的类别总数,如果有文件的编号是 7 或负数,说明标注文件损坏或者转换工具有 bug,留着这种数据训练,轻则 loss 异常,重则训练直接崩溃。跑一遍只要几秒钟,但能拦住一半以上的训练异常。
2.3 VOC 和 YOLO 坐标体系差异:从 xmin/ymin 到归一化 cx/cy/w/h
虽然这份数据集已经帮你转换好了,但训练过程中你很可能需要自己补充标注,或者把别人给的 VOC 标注并进来。这时候理解坐标换算关系就非常重要。
VOC 的 XML 里,每个<bndbox>给的是xmin, ymin, xmax, ymax,即目标框左上角和右下角的像素坐标。YOLO 格式需要的是归一化后的中心点坐标和宽高,换算公式为:
import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, img_width, img_height, class_id): tree = ET.parse(xml_file) root = tree.getroot() lines = [] for obj in root.findall('object'): bndbox = obj.find('bndbox') xmin = int(float(bndbox.find('xmin').text)) ymin = int(float(bndbox.find('ymin').text)) xmax = int(float(bndbox.find('xmax').text)) ymax = int(float(bndbox.find('ymax').text)) cx = (xmin + xmax) / 2.0 / img_width cy = (ymin + ymax) / 2.0 / img_height w = (xmax - xmin) / float(img_width) h = (ymax - ymin) / float(img_height) lines.append(f"{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") return "\n".join(lines)其中img_width和img_height是整张图片的宽高,不是边界框的宽高。cx先算出边界框中心的像素坐标,再除以图片宽度得到归一化值;w是边界框宽度占图片宽度的比例。YOLO 的所有坐标都应在 0 到 1 之间,如果转换结果出现负数或大于 1,说明 XML 里的xmax或ymax超出了图片边界,这类脏数据必须在训练前过滤,否则损失函数直接变成 NaN。cx和cy保留 6 位小数就够用了,训练精度已经足够,没必要写更多位数。
3. 从 7z 解压到跑通 YOLOv8:完整训练流程的四个关键步骤
3.1 Linux 和 Windows 下解压 7z:工具链选择和目录整理
拿到压缩包后,第一步是解压,但解压工具选不对,后面会出一堆莫名其妙的坑。Windows 下我推荐直接用 7-Zip 官方最新版,不要用国产第三方压缩软件。原因不是兼容性问题,而是这套数据里有几千个 XML 小文件,第三方工具解压时容易丢失文件属性或静默跳过部分文件,等你训练到一半才发现缺数据,排查成本极高。
Linux 服务器上则使用 p7zip 命令行工具:
# Ubuntu / Debian 系安装 p7zip sudo apt-get install -y p7zip-full # 解压到指定目录 7z x 挖掘机叉车工程车辆检测数据集VOC+YOLO格式5067张7类别.7z -o./dataset # 查看解压结果 du -sh ./dataset解压完成后,我习惯先把文件归类。JPG 单独放一个目录,XML 一个目录,TXT 一个目录,后面划分数据集时操作起来更清晰。如果在同一个目录里混着放,脚本处理时要不断判断文件类型,容易出错。
cd ./dataset mkdir -p images xmls labels mv *.jpg images/ mv *.xml xmls/ mv *.txt labels/注意mv命令只移动当前目录下的文件,如果解压后有子目录结构,需要先确认文件实际位置。归类完成后,再跑一次数量统计,确认三个目录都是 5067 个文件,这一步做完才算是真正做好了训练前的准备。
3.2 数据集划分:按图片切分,同步移动标签文件
训练 YOLO 模型之前,必须把数据分成训练集和验证集。常见做法是 8:1 划分,如果还需要测试集,就按 8:1:1 切分。关键原则是:只能按图片文件做划分,然后同步移动对应的 XML 和 TXT,不能把图片和标签分别随机划分,否则会造成信息泄漏,评估结果虚高,现场部署立刻翻车。
import os import random import shutil random.seed(42) image_dir = "images" label_txt_dir = "labels" label_xml_dir = "xmls" train_dir = "yolo_dataset/train" val_dir = "yolo_dataset/val" test_dir = "yolo_dataset/test" # 创建目录结构 for d in [train_dir, val_dir, test_dir]: os.makedirs(f"{d}/images", exist_ok=True) os.makedirs(f"{d}/labels", exist_ok=True) os.makedirs(f"{d}/xmls", exist_ok=True) images = [f for f in os.listdir(image_dir) if f.endswith('.jpg')] random.shuffle(images) train_cnt = int(len(images) * 0.8) val_cnt = int(len(images) * 0.9) for idx, img in enumerate(images): name = os.path.splitext(img)[0] if idx < train_cnt: dest = train_dir elif idx < val_cnt: dest = val_dir else: dest = test_dir shutil.copy(f"{image_dir}/{img}", f"{dest}/images/{img}") shutil.copy(f"{label_txt_dir}/{name}.txt", f"{dest}/labels/{name}.txt") shutil.copy(f"{label_xml_dir}/{name}.xml", f"{dest}/xmls/{name}.xml")这里random.seed(42)固定随机种子,保证每次划分结果一致,方便复现实验结果。train_cnt和val_cnt分别是 80% 和 90% 的边界下标,前 80% 进训练集,中间 10% 进验证集,最后 10% 当测试集。我用复制而不是移动,这样原始文件保留一份备份,划分逻辑写错了随时重来,不用重新解压。生产环境里强烈建议保留原始数据,因为你永远不知道后面是否会需要回头重置标注。
3.3 data.yaml 和训练命令:YOLOv8 跑这套数据的标准配置
YOLOv8 训练需要两个东西:一个data.yaml描述数据和类别,一个模型配置文件或预训练权重。data.yaml的写法有固定格式,我直接给出这套数据对应的配置:
path: /home/user/dataset/yolo_dataset train: train/images val: val/images test: test/images nc: 7 names: 0: ConcreteTruck 1: Excavator 2: Forklift 3: Loader 4: Steamroller 5: Truck 6: Workerpath是数据集根目录,建议写绝对路径。Windows 上如果路径含中文,训练时很容易读到一半报错找不到文件,最好把数据集放到纯英文路径下。train、val、test后面填的是相对于path的图片文件夹路径。YOLO 会自动去图片同级的labels目录找同名 TXT,所以目录结构必须保持train/images/x.jpg对应train/labels/x.txt,不能把标签文件夹放在别处。
训练命令一条就够了:
yolo train data=data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0解释几个关键参数:model=yolov8s.pt是加载 COCO 预训练权重做迁移学习,比自己从零训练收敛快得多,对 5000 多张图的数据集尤其友好;imgsz=640是训练分辨率,工程机械这类大目标 640 够用,如果画面中工人占的像素很少,可以改成 800 或 960,代价是显存占用上升;batch=16根据显卡显存调整,显存小的改成 8 或 4,不要硬撑。如果训练中途 loss 出现 NaN,第一时间检查标注文件,九成是坐标越界或空 TXT。
3.4 断点续训和日志监控:训练中断怎么接着跑
YOLOv8 训练时默认会在runs/detect/train下保存last.pt和best.pt,但很多人不知道程序中断后怎么续训。直接再执行yolo train会从头开始,前面的时间全部白费。正确的续训命令是:
yolo train data=data.yaml model=runs/detect/train/weights/last.pt epochs=100注意用last.pt续训时,epochs填的是总轮数,不是剩余轮数,这一点非常容易搞混。如果你已经跑了 40 轮,想再补 60 轮,应该填epochs=100而不是60,否则会提前停止或者多跑。ultralytics 在命令行日志里会打印当前 epoch,自己心里算好再填。
训练过程中还需要留意results.png里的损失曲线。正常情况下train_loss和val_loss都应该是平滑下降趋势。如果val_loss先降后升,说明过拟合了,提前用best.pt做推理而不是等训练跑完;如果两个损失都下不去,说明学习率不合适或标注噪声过大,先检查数据质量再调参。
4. 常见坑排查:解压报错、标注错位与类别编号问题一次说清
4.1 7z 解压报密码错误或 CRC 校验失败
现象:7z 解压到一半弹出 “Wrong password” 或者 “CRC Failed”,压缩包解出来文件不全,训练时缺 JPG 或 TXT。
原因:绝大多数情况是下载过程中文件损坏,特别是用浏览器断点续传或者网盘客户端下载时,容易出现字节错位。也有小概率是解压工具版本太老,不支持高压缩率算法。
解决:先对比压缩包的 SHA-256 校验值和源站提供的是否一致,对不上就重新下载。解压工具换成最新版 7-Zip 或 p7zip 16.02 以上版本。如果压缩包设置了密码,建议复制粘贴而不是手动输入,避免特殊字符被输入法替换成全角。解压完成后,用find | wc -l确认三个数量都是 5067 再继续。
4.2 训练时报错 “All labels are empty” 或图片尺寸小于输入分辨率
现象:训练日志里报某个 batch 没有标签,或者提示Image is smaller than 640,训练直接中断。
原因:数据集中混入了少量低分辨率图片,可能来自监控截图或手机拍摄的缩略图。YOLOv8 默认开启 mosaic 增强,把四张图拼接在一起,如果其中一张尺寸过小,拼接后目标严重变形,标签计算就会出错。
解决:训练前写脚本过滤低分辨率图片,并同步删除对应的标签文件:
from PIL import Image import os def filter_small_images(image_dir, label_txt_dir, min_size=320): removed = [] for f in os.listdir(image_dir): if not f.endswith('.jpg'): continue img_path = os.path.join(image_dir, f) with Image.open(img_path) as img: w, h = img.size if w < min_size or h < min_size: name = os.path.splitext(f)[0] os.remove(img_path) os.remove(os.path.join(label_txt_dir, name + '.txt')) removed.append(f) print(f"过滤掉 {len(removed)} 张低分辨率图片: {removed[:5]}") filter_small_images("images", "labels")这段脚本用 PIL 读取图片宽高,低于min_size阈值的图片连同同名 TXT 一起删除。运行之前最好备份一份原始目录。如果过滤掉的图片数量超过几百张,说明原始数据集里低分辨率占比高,这时候应该把训练分辨率调低到 480 而不是继续删数据。少几张被删掉影响不大,损失函数不再报错才是关键。
4.3 类别编号错位:模型把工人识别成挖掘机
现象:训练过程顺利,损失函数正常下降,但推理时把工人识别成挖掘机,或者把压路机识别成装载机。
原因:这不是模型的问题,而是data.yaml里的类别顺序和 TXT 标注里的编号不一致。比如你在标注工具里看到的类别顺序是“挖掘机、工人、叉车”,但 TXT 里的编号是“0 挖掘机、6 工人”,模型训练时会把编号 6 的工人学到“挖掘机”的位置上。
解决:先用 2.2 节的校验脚本检查所有 TXT 的类别编号最大值,确认没有越界。然后把data.yaml的names列表和使用前必读.txt里的类别顺序逐一对照。这里要提醒的是,不要以为用 labelImg 打过标就高枕无忧,labelImg 的 classes.txt 顺序和最终训练用的顺序必须完全一致,稍不留神就会错位。最好在训练前打印几个 TXT 的前几行,人工确认一下数字和类别名对应关系,这一步比任何调参都重要。
4.4 标签文件里出现空 TXT,造成静默漏检
现象:数据检查脚本发现某些图片的 TXT 是空文件,或者 XML 和 JPG 数量一致,但 TXT 数量少了一些。
原因:原始标注工具可能漏导出部分文件,或者转换脚本遇到文件名中包含空格、特殊字符时跳过了。空心 TXT 不报错,但图片没有标签,训练时被当作背景,模型就会漏检该图片里的目标。
解决:用集合做三向对齐检查:
import os def check_alignment(image_dir, label_dir): imgs = {os.path.splitext(f)[0] for f in os.listdir(image_dir) if f.endswith('.jpg')} labels = {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith('.txt')} missing = imgs - labels extra = labels - imgs print(f"缺标注的图片: {len(missing)}") print(f"无图片的标注: {len(extra)}") if missing: print("示例:", list(missing)[:5]) check_alignment("images", "labels")集合差集运算能快速找出缺标签的图片名。对缺标注的图片,要么人工补标,要么直接删除图片。对无图片的 TXT,多半是源图被删了但标签没删,留着会影响 dataloader 的文件对齐,直接删掉。如果缺失量只有几十张,删除对整体影响不大;如果超过三百张,建议重新下载压缩包对比文件完整性。
4.5 导出 ONNX 后推理结果全乱:预处理不一致的典型翻车
现象:PyTorch 训练时验证效果正常,导出 ONNX 后用 ONNX Runtime 部署,检测框偏移或者类别全错。
原因:训练时 YOLO 做了 letterbox 缩放和归一化,但推理代码里没有做相同处理。最常见的错误是填充比例对不上、BGR 和 RGB 通道顺序颠倒、没有除以 255。这三个问题任何一个都会导致结果全乱,而且难以排查。
解决:导出命令加上固定参数:
yolo export model=best.pt format=onnx opset=12 imgsz=640推理时不要自己手写 resize,直接用 ultralytics 的YOLO类加载 ONNX。如果必须手写预处理,记住三件事:输入色彩空间转成 RGB,像素值除以 255 归一化,letterbox 填充值用 114。这三条是 ONNX 部署最常见的坑,缺一个结果就乱。每次部署完先用一张训练过的原图做冒烟测试,确认检测框位置正常再接入业务系统。
5. 数据质量与调优思路:7 类工程车辆标注里的边界情况
5.1 类别分布不均衡:先统计框数量再谈训练策略
拿到这套数据后,第一个要做的不是直接训练,而是统计每个类别的标注框数量。施工现场场景天然存在不均衡问题:混凝土运输车和卡车常年在工地出入口出现,样本量通常远大于压路机和叉车。如果不做任何处理,模型会偏向高频类别,导致低频类别漏检严重。
统计方法很简单,遍历所有 TXT 文件,统计每行第一个数字的出现次数。如果最大类和最小类的数量差距超过 10 倍,就需要采取干预措施。常见做法有三种:第一,对少量类别做复制粘贴增强,把该目标的标注框连同像素抠出来,随机粘贴到其他背景图上;第二,在损失函数里给少量类别更高的分类权重;第三,对多类别做欠采样。我个人更推荐前两种组合使用,欠采样会浪费大量数据,对于只有 5067 张图的数据集不太划算。
训练后还要看各类别的 AP 值是否均衡。如果发现某个类别的 AP 明显低于整体平均,说明它要么是样本少,要么是标注质量差。这时候不要急着加数据,先抽二十张该类别的图看标注有没有问题,很多情况下是漏标和错标造成的。
5.2 小目标检测:“Worker”这一类为什么容易漏
“Worker”是 7 个类别里唯一的人类目标,也是检测难度最高的一个。在 640 分辨率下,一个站在挖掘机旁边的工人可能只有二三十个像素高,经过 YOLOv8 骨干网络的多次下采样后,这个目标在深层特征图上只剩下一两个像素,信息量严重不足。
针对小目标问题,三种做法按性价比排序。第一,训练分辨率从 640 提到 960,工人目标像素面积接近翻倍,代价是训练速度变慢和显存占用升高;第二,开启多尺度训练,ultralytics 里设置mosaic=1.0和scale=0.5,让模型在训练过程中看到各种尺寸的目标;第三,把工人目标单独抠出来做增强,随机复制到其他图片上,变相增加小目标样本数量。
我不推荐为了小目标直接换大模型。从yolov8s换到yolov8x,参数量提升十倍,但小目标检测提升有限,反而训练时间大幅增加。工程车辆检测场景里,提分辨率永远是最直接的方案。如果你的显存不够,吃不满高分率,那就集中在画面下采样次数最少的特征层上做检测头增强,但这属于进阶玩法,新手先把分辨率提上去就行。
5.3 相似类别混淆:叉车/装载机、卡车/混凝土运输车的后处理兜底
叉车和装载机在二维图像里确实难分,车身结构相似,都有前臂和货叉,尤其是侧视角下,连人眼都容易看错。卡车和混凝土运输车同理,混凝土运输车本质上就是带罐体的卡车。这种情况下,光靠网络结构很难进一步压榨精度,需要引入先验知识做后处理。
我的做法是在推理代码里加规则:当两个相似类别的置信度都超过 0.3 时,根据检测框的长宽比做二次判断。装载机的铲斗通常更大,框整体更宽;叉车的货叉细长,框的长宽比更极端。用这个规则做兜底,能救回一部分误判,但不建议把阈值设得太高,否则会把正确检测的框也改掉。
还有一个更简单的调参与 Limiter:把 NMS 的 IoU 阈值从默认的 0.45 调低到 0.3,减少两个相近类别框之间的互相抑制。视觉上会保留更多冗余框,但至少不会漏掉真实目标。后处理规则属于工程技巧,不影响训练过程,可以在模型迭代时保持稳定,等换了一版更强的模型后再评估是否还需要保留。
5.4 标注质量抽检:数据集的隐藏边界
双格式数据集最容易被忽略的问题是标注噪声。VOC 和 YOLO 的转换是自动的,但原始标注如果是人工标的,难免存在框偏大、框偏小、漏标、错标。尤其“工人”这个类别,小目标加遮挡,标注最容易出问题。
我习惯用训练好的best.pt对训练集做一次回灌推理,把预测框和真实标注框的 IoU 小于 0.5 的图片批量抽出来,人工看一遍。这个操作听起来很玄学,但实际操作中总能发现一批漏标样本。比如某张图上明明有三台挖掘机,标注只框了一台,模型因为“多学”了这个目标,验证集的 Precision 反而下降。把这些漏标样本补上后,指标通常立刻回升。数据集的价值不只是数量,标注准确率同样重要。
6. 验证数据集的最后一步:可视化标注与混淆矩阵交叉检查
数据集能不能真用,不是看文件数量,而是看标注是否贴合训练需求。我每次拿到新数据集,都强制自己走两遍验证:第一遍是可视化画框,第二遍是训练后检查混淆矩阵。
可视化验证用 OpenCV 在图片上画框,把 VOC 和 YOLO 各画一遍:
import cv2 import xml.etree.ElementTree as ET img = cv2.imread("images/firc_pic_101.jpg") tree = ET.parse("xmls/firc_pic_101.xml") for obj in tree.getroot().findall("object"): box = obj.find("bndbox") x1 = int(box.find("xmin").text) y1 = int(box.find("ymin").text) x2 = int(box.find("xmax").text) y2 = int(box.find("ymax").text) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, obj.find("name").text, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite("check_voc.jpg", img)这段代码读取 XML 的边界框,在原图上画绿框并标注类别名。跑完随机抽几十张图,肉眼确认框的位置是否贴合目标边缘,尤其注意有没有框偏移半个车身的情况。再用同样思路读取 TXT 画一遍 YOLO 框,两张输出图理论上应该完全重合,如果有对不上的,说明格式转换存在隐蔽问题,必须回到坐标换算逻辑排查。
训练结束后,打开runs/detect/train目录下的confusion_matrix.png和results.png,重点看混淆矩阵对角线的亮度。对角线越亮,说明各类别区分得越好。如果发现 Forklift 被大量识别成 Loader,说明这两类需要做针对性增强,而不是盲目调学习率。
我自己有个习惯,把可视化脚本固定命名为check_data.py放在项目根目录,每次换新数据集或更新标注后,先跑一遍这个脚本再开训练。这个习惯帮我挡掉过很多次因为标注错位导致的无效迭代。这套挖掘机叉车工程车辆检测数据集我验证下来格式完整、坐标对齐,可以放心拿去跑 YOLOv8 训练。希望这篇拆解能帮你在处理双格式数据集时少走几步弯路,也欢迎在实际复现时对照这份流程自查,祝训练顺利。希望帮到你。
本文还有配套的精品资源,点击获取