简介:面向YOLO系列目标检测任务的火车、轨道与手推车识别数据集,适合算法学习者、模型训练者以及工业视觉场景开发者直接使用。压缩包约236MB,共2000个文件,提供VOC格式xml与YOLO格式txt两套标签,标签按类别单独组织,便于切换训练框架。数据集已完成训练集、验证集与测试集划分,并附带data.yaml配置,可无缝接入yolov5、v7、v8、v9、v10、yolo11等主流算法,省去数据准备与格式转换时间。YOLO格式标签中每行包含目标类别索引、归一化的中心点坐标及宽高比例,信息完整,可直接用于模型训练、验证测试和迁移学习。结合火车、轨道、手推车三类目标的标注,适合轨道运输与施工现场相关检测研究。目前已有100人学习,适合需要快速获取带标注数据来调试模型或验证算法效果的开发者。
1. YOLO 火车轨道手推车数据集:3793 张带标签图像,从解压到训练先看清三件事
提到 YOLO,最烦的从来不是网络结构,而是数据集的可用性。这套火车、轨道、手推车数据集,讲究在“直接用”:3793 张图像,两部分标签都齐——YOLO 格式的 txt 和 VOC 格式的 xml 分文件夹存着,训练集、验证集已经拆好,还放了份 data.yaml 配置,YOLOv5/v7/v8/v9/v10/yolo11 都能吃下。对上要快速验证检测管线、对下要做轨道交通货运场景预研的人来说,它最大的价值是同时省掉标图、转格式两道最磨人的工序。
但“带标签”不等于开箱就能练出好模型。标签文件里坐标参照系、类别索引、图片尺寸三者一旦对不上,训练时会换成另一种方式冒出来:loss 不降、mAP 个位数、验证集看着挺好现场却漏检。下面我就把这套资源从目录结构、双标签格式、训练命令到常见报错完整拆一遍,新手照步骤能跑通,熟手也能直接拿去当检查清单用。
2. 数据集结构与双标注格式:txt 和 xml 两种标签怎么对应到同一张图
2.1 解压后目录应该长什么样
数据集解压后,核心文件按功能分成了四块:图像文件、YOLO 格式标签文件夹、VOC 格式标签文件夹和一份 data.yaml。常见目录结构如下,如果压缩包内结构略有出入,按文件后缀来找就不会认错:
dataset/ ├── images/ # 训练与验证图像,通常按 train/val 子目录分好 │ ├── train/ │ └── val/ ├── labels/ # YOLO 格式 txt 标签,每个 txt 与图像同名 │ ├── train/ │ └── val/ ├── voc_labels/ # VOC 格式 xml 标签,文件名与图像同名 │ ├── train/ │ └── val/ └── data.yaml # 训练时的数据集配置入口我一般拿到压缩包先看 data.yaml,它描述的路径、类别数、类名比任何说明文件都贴近训练逻辑。压缩包里的图像文件名类似img_0866_807.jpg,对应标签就是img_0866_807.txt和img_0866_807.xml,靠同名关系绑定。文件名末尾的数字后缀,有的来自原始抓帧序号,有的可能是类别编号,不建议靠文件名推测标注内容,一切以标签里的实际数值为准。
2.2 YOLO txt 标注的五个字段:中心点、宽高与归一化坐标
YOLO 格式每个 txt 文件里一行一个目标,五个数值依次是:
<class> <x_center> <y_center> <width> <height>第三个数开始容易看走眼:x_center和y_center是归一化后的目标框中心点坐标,width和height是归一化后的框宽高,归一化的基准是图像本身的宽和高。一张 640×480 的图,框左上角像素坐标为 (160,120),右下角为 (320,240),对应 txt 里的这一行就是:
0 0.375 0.375 0.25 0.25换算过程为x_center = (160+320)/2/640 = 0.375,y_center = (120+240)/2/480 = 0.375,width = (320-160)/640 = 0.25,height = (240-120)/480 = 0.25。这套换算关系后面检查脚本天天要用,只记格式不记参照系的话,转出来的框位置一定是飘的。
YOLO 为什么坚持归一化而不是直接存像素坐标?因为训练时图像会被 Resize 到固定尺寸,还伴随 Mosaic 等数据增强,图像实际尺寸随时在变。用归一化坐标,增强模块只需要按比例线性映射,不需要为每种增强重算坐标基准。反过来也提醒一点:如果你打算把 VOC 转成 YOLO,千万别先把图像缩放一遍再读 xml 尺寸,那样会把两个基准全部弄乱。
2.3 VOC xml 标注:像素坐标系、类别名与它的“后悔药”价值
VOC 格式的 xml 保存的是像素坐标和类别名称,一个目标一个object节点:
<annotation> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>hand_truck</name> <bndbox> <xmin>160</xmin> <ymin>120</ymin> <xmax>320</xmax> <ymax>240</ymax> </bndbox> </object> </annotation>xml 里类别是字符串,txt 里是索引,因此从 xml 生成 yolo 标签时,必须先建立「类别名 → 索引」的映射表。索引顺序没有全局统一标准,完全取决于 data.yaml 里names列表的排列。如果压缩包内没有附带类别文件,最稳的做法是扫描全部 xml,提取出现过的name去重后按序编号,再确认与 data.yaml 的names顺序严格一致。
同时保留 xml 格式的价值,不只是兼容旧 pipeline:txt 一旦被误改、误删或者编码损坏,xml 是完整的原始标注证据,可以用脚本重新生成 txt,相当于给标注上了层后悔药。做多人协作标注的项目,我通常会把 VOC 版本作为唯一事实源,txt 只当作训练时的派生产物。
2.4 data.yaml 是入口:类别数才是标签检核的基准
data.yaml 的内容通常长这样,具体类名与顺序以压缩包内实际文件为准,不要不核对就照抄:
train: ./images/train val: ./images/val nc: 3 names: ['train', 'rail', 'hand_truck']第一次训练前要检查两点。第一,train/val路径在你的机器上能否正确解析,相对路径会在命令行工作目录变化时失效;第二,nc必须和names长度一致,同时必须大于所有标签文件里出现过的最大类别索引。因为数据集已经划分好,划分比例不用你操心,真正要动手的是在打开训练命令之前,把标签的合法性检查一遍。
提示:修改 data.yaml 前先备份一份原文件,后面排查路径问题时,可以快速对照是数据集本身的问题还是你改配置改出来的问题。
类别名对应错位的后果往往滞后:训练过程不报错,loss 也下降,但预测时索引 0 实际指向的是names里的第一个名字,如果 txt 里索引 0 的语义在 xml 里根本不是这个类,模型学到的就是错位语义。这也是双格式数据集的一个隐藏优势——用 xml 去反查 txt 的类别语义,可以快速验证映射表有没有建对。
3. 训练前先做一次标注体检:坐标越界、类别超范围与可视化抽查
3.1 为什么要先检查,而不是直接训练
边界框坐标越界、类别索引超出nc、宽高为 0 这些错误,通常不会阻止训练启动,而是转化成损失曲线不收敛、mAP 稳定在零点几这类难定位的玄学问题。我处理新数据集的习惯是先扫描一遍全部 txt 字段,而不是急着敲训练命令。检查点主要落在五个方面:每行字段数量是否为 5、类别索引是否落在 [0, nc-1]、中心点坐标是否在 [0,1] 区间、宽高是否为正、中心点加减半宽高之后是否仍在图像范围内。
另一个常被忽略的点是空标签文件。YOLO 允许一张图没有标注,但一张空标签意味着该样本没有任何监督信息,如果空文件占比过高,训练时这部分图像只会贡献背景误差。扫描脚本里顺手统计空文件数量,低于 1% 基本不用管,高到 5% 以上就要回到原始 xml 确认是不是漏转了文件。
3.2 批量检查 YOLO txt 标注的 Python 脚本
import os from collections import Counter labels_dir = "labels/train" # 改成实际标签目录 nc = 3 # 与 data.yaml 的 nc 保持一致 err_count = 0 empty_count = 0 class_counter = Counter() with open("issue_report.txt", "w", encoding="utf-8") as out: for root, _, files in os.walk(labels_dir): for name in files: if not name.endswith(".txt"): continue path = os.path.join(root, name) with open(path, encoding="utf-8") as f: lines = f.readlines() if len(lines) == 0: empty_count += 1 out.write(f"{path} 空标签\n") for ln, line in enumerate(lines, 1): parts = line.strip().split() # 每个目标必须正好 5 个字段 if len(parts) != 5: out.write(f"{path}:{ln} 字段数={len(parts)}\n") err_count += 1 continue cls, xc, yc, w, h = parts cls, xc, yc, w, h = int(cls), float(xc), float(yc), float(w), float(h) # 类别索引必须小于 nc,同时不能是负数 if cls < 0 or cls >= nc: out.write(f"{path}:{ln} 类别越界 {cls}\n") err_count += 1 # 归一化坐标必须落在 [0,1] if not (0 <= xc <= 1 and 0 <= yc <= 1): out.write(f"{path}:{ln} 中心点越界 xc={xc} yc={yc}\n") err_count += 1 if not (0 < w <= 1 and 0 < h <= 1): out.write(f"{path}:{ln} 宽高非法 w={w} h={h}\n") err_count += 1 # 中心点加减半宽高不能超出图像范围 if xc - w / 2 < 0 or xc + w / 2 > 1 or yc - h / 2 < 0 or yc + h / 2 > 1: out.write(f"{path}:{ln} 框超出图像边界\n") err_count += 1 class_counter[cls] += 1 print("问题数量:", err_count) print("空标签数量:", empty_count) print("类别分布:", sorted(class_counter.items()))逻辑说明:脚本按行读取每个 txt,把五个字段解包后逐个做数值范围校验。类别越界和中心点越界是硬错误,代表标签本身已损坏,优先处理;框整体超出图像范围一般不阻止训练,但会引入错误监督,同样要定位。参数说明:labels_dir改成labels/val就能检查验证集,nc必须以你 data.yaml 里的实际值为准,脚本不会自动推断。
3.3 越界以后怎么办:过滤还是裁剪
issue_report.txt里如果出现“框超出图像边界”,先看数量占比。只有零星几个,说明标注源头的误差不大,直接过滤掉这些目标即可。如果成片出现,基本可以断定是某批 xml 的size字段与实际图像尺寸不一致,导致转换时计算基准错了。这时候不要靠裁剪坐标去“修补”标签,因为基准错的情况下裁剪出来的框仍然是偏的,正确做法是拿原图真实尺寸重新生成整份标签。
如果只是单条目标越界且你确实想保留它,裁剪逻辑可以这样写:
def clamp_yolo_line(xc, yc, w, h): x1 = max(0.0, xc - w / 2) y1 = max(0.0, yc - h / 2) x2 = min(1.0, xc + w / 2) y2 = min(1.0, yc + h / 2) if x2 <= x1 or y2 <= y1: return None # 裁剪后面积趋近 0,直接剔除 return (round((x1 + x2) / 2, 6), round((y1 + y2) / 2, 6), round(x2 - x1, 6), round(y2 - y1, 6))逻辑说明:先把归一化坐标还原成左右、上下边界,用min/max夹到 [0,1] 区间,再重新换算回中心点和宽高。参数说明:函数返回None时表示该目标被压没了,调用方应跳过这行;返回的四个浮点数保留了 6 位小数,足够满足 YOLO 训练精度。
3.4 把归一化坐标画回图像上做人工抽查
数值检查只能发现边界问题,无法确认框是否真的框在目标上,尤其是类别名容易张冠李戴。可视化抽查我一般每个类别抽 10~20 张,用 OpenCV 把检测框画出来:
import cv2 def draw_yolo(img_path, txt_path, class_names, out_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(txt_path) as f: for line in f: cls, xc, yc, bw, bh = map(float, line.strip().split()) x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[int(cls)], (x1, max(y1 - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(out_path, img)逻辑说明:归一化中心点先换算回像素坐标,x1 = (xc - bw/2) * w是框左边界,y1同理,绘制时类别文本放在框左上角偏上的位置。参数说明:class_names必须按 data.yaml 的names顺序传入,否则画出来的标签名是错位的;抽查阶段发现大量“框在但类别名不对”的情况,优先怀疑类别映射表,不是图像。
3.5 类别分布统计与少数类倾向
检查脚本最后输出的类别分布,是判断训练配置要不要调整的重要参考。像轨道这种长条形目标,在图像里可能框数量不少,但实际覆盖面积占比很小;手推车如果样本量明显偏少,默认权重下训练会出现漏检倾向。常见做法是先把各类别框数拉平看比例,当最小类别框数不足最大类别十分之一时,训练时可以考虑加大少数类的cls_loss权重,或者对少数类做简单的离线增强——水平翻转、随机亮度变化就够了。不过第一次训练以跑通为主,不用一上来就调权重,知道分布比例就行。
此外抽查时还要留意半标注现象,即图像里明显有目标但标签漏了。半标注是数据增强和损失权重都救不回来的,它会让模型把漏标目标当背景学。抽 5% 的图人工过一遍,如果漏标率高,这份资源的可用性就要打个折扣,需要自己补标后再进训练。
4. 从解压到跑通训练:data.yaml 改路径,用 YOLOv8 命令快速验证
4.1 解压与目录整理
mkdir -p ~/datasets/railway unzip YOLO算法-火车-轨道-手推车数据集-3793张图像带标签-火车-轨道-手推车.zip -d ~/datasets/railway/ cd ~/datasets/railway # 确认图像、标签、yaml 三个部分都在 find . -maxdepth 2 -type d | sort解压时留意路径里的中文。YOLO 工具链对中文路径的支持参差不齐,尤其 Windows 下容易出现编码报错,解压时直接放进纯英文目录,后续能少踩一堆坑。如果 zip 解压后文件名乱码,多半是压缩包内部使用 GBK 编码而解压工具默认用 UTF-8 解码,换支持编码选择的工具重解一次即可。
4.2 修改 data.yaml 的 train/val 路径
打开 data.yaml,把train和val改成绝对路径:
train: /home/you/datasets/railway/images/train val: /home/you/datasets/railway/images/val nc: 3 names: ['train', 'rail', 'hand_truck']写绝对路径是为了避免命令行工作目录切换后出现「No labels found」。names的顺序就是 txt 里类别索引的对应表,这里如果与实际标签不一致,训练能跑但指标会非常怪异,属于最难排查的那类问题。
提示:改路径前先备份原始 data.yaml,排查问题时可以直接 diff 两份配置,确认不是你改坏了。
4.3 安装并执行 YOLOv8 训练
pip install ultralytics yolo task=detect mode=train \ model=yolov8n.pt \ data=/home/you/datasets/railway/data.yaml \ epochs=100 imgsz=640 batch=16 device=0参数说明:epochs第一次跑可以设短一点,50 起步,目标是验证数据链路通不通;imgsz训练时会把图像 Resize 到这个尺寸,标签是归一化坐标所以不受影响;batch受显存约束,8G 显存跑 16 容易 OOM,先降到 8 更稳;device=0指定第一张 GPU,没有 GPU 就改成device=cpu,只是速度慢很多。yolov8n.pt是最小的主干模型,适合验证链路;如果数据集难度不大,n 模型收敛速度最快,后续再换 s/m/l 逐步提升精度。
4.4 训练日志该怎么看
训练一旦启动,重点盯住下表的几个指标:
| 指标 | 含义 | 我观察什么 |
|---|---|---|
| train/box_loss | 边界框回归损失 | 前 20 个 epoch 快速下降为正常,持续震荡不降则回查标签 |
| train/cls_loss | 分类分支损失 | 对应 YOLO 损失函数里的类别分量,类别错位时它会先异常 |
| val/mAP50 | IoU=0.5 时的平均精度 | 对长条形目标比 mAP50-95 更直观 |
| val/mAP50-95 | IoU 0.5~0.95 的积分平均 | 指标更严格,数值通常比 mAP50 低不少 |
新手先不用纠结推理速度和 mAP 的取舍,完整跑完一次训练,看到 val 指标正常出来,才算数据集在这台机器上真正“跑通”。另外注意 ultralytics 默认开了早停,patience控制连续多少个 epoch 验证指标不提升就停止训练。训练到 30 个 epoch 就停了但 mAP 很高,这不是 bug,是验证集上已经收敛。
4.5 同一份数据切到 YOLOv5 或 YOLOv10
这份数据集的核心价值在于双格式标签和 data.yaml 可以跨版本复用。切到 YOLOv5 时命令变为:
python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100切到 YOLOv10 或 yolo11 时,通常还是用 ultralytics 包,只是把model换成对应的权重文件名。yolo txt 格式是所有 YOLO 版本通用的底层存储结构,数据文件本身不需要改动。需要提醒的是,不同版本的数据增强策略不完全一致,v5 和 v8 在相同数据上跑出来的指标不能直接横向比,但这不影响数据文件的复用。第一次迁移时固定imgsz、batch和随机种子,才能判断指标差异是版本引起的还是数据引起的。
5. 训练数据集避坑与常见问题排查:五条「现象→原因→解决」记录
这一章是数据加载与训练阶段的踩坑记录,按「先现象、再原因、后解决」的顺序写,方便对应自己的报错直接跳读。
5.1 「No labels found」:训练启动即退出
现象:YOLOv8 启动训练后打印No labels found in ...,然后直接结束。
原因:yolo 默认会把images路径中的目录名替换成labels去寻找标签。如果解压后的标签目录不叫labels,或images和labels不在同级,就会找不到。另一种常见情况是 txt 后缀被系统隐藏显示成.txt.txt,或标签文件是 0 字节。
解决:先把标签目录统一调整为labels并保持与images同级;再执行ls -la看是否有隐藏字符;最后用前面章节的扫描脚本统计空文件数量。这三步做完基本能定位问题。
5.2 loss 直接 NaN 或数值爆炸
现象:第一个 epoch 的train/cls_loss直接是nan,或者 loss 数值大到离谱。
原因:标签里混入了非数值内容,比如某个 txt 中有一行是说明文字;或者坐标出现极端值,如 1e10 这种明显异常数据;也可能是类别索引为 -1,超出 YOLO 类别张量的合法范围。
解决:拿第 3 章的体检脚本全量扫一遍,重点看字段数不等于 5 的行,基本都是混入脏数据的文件。找到后对比同名 xml,把正确的标注重新写回。这种情况手动修不如从 xml 重新生成 txt 干净。
5.3 txt 与 xml 类别顺序不一致
现象:训练全程无报错,但验证集里某一类的 AP 极低,输出预测框发现框位置是对的,标签名却整体错位。
原因:txt 里的类别索引是按某个顺序生成的,而 data.yaml 的names列表顺序与它不一致。索引被整体平移,比如原索引 0 是火车,names 里索引 0 写了轨道,模型学的就是把火车当轨道。
解决:写脚本交叉核对,扫描所有 txt 中出现过的类别编号,再和 xml 里name的集合做映射比对。两者能对齐,修正 data.yaml 的names顺序;对不齐,直接用 xml 重新生成 txt。记住:先核对再训练,比训练后看指标猜原因要快得多。
5.4 框画在目标上但 mAP 始终很低
现象:可视化抽查时框都在目标周围,边界也基本贴合,但训练多个 epoch 后 mAP 仍上不去。
原因:很可能是部分图像的尺寸与 xml 的size字段不一致。比如原图被压缩过,但 xml 里还保留旧尺寸。归一化坐标是以 xml 尺寸为基准算的,实际图像变小后,框的位置就跟着漂移,和画面内容错开。
解决:批量读取每张图像的真实宽高,和同名 xml 的size做一致性比对;如果确实被批量压缩过,用图像真实尺寸重新生成整份标签。这里的教训是:不要只看框形状是否正常,要看框相对图像内容的位置是否准确。
5.5 验证集 mAP 很高,现场测试却漏检严重
现象:自带的验证集 mAP 有 70% 以上,但拿现场拍的十几张图去测,火车、手推车漏检明显。
原因:划分好的验证集和训练集来自同一拍摄批次,光照、角度、环境高度同源。验证集指标只能证明模型在这个数据分布内有效,不代表换到现场光照和遮挡条件下依然有效,这不一定是数据集的问题,而是验证结论有边界。
解决:从现场收集十到二十张图片组成一个很小的外部验证集,用训练好的模型跑一遍推理,以这组数据的结果判断能否部署,而不是只看自带的 val 指标。这是我在多个数据集上反复验证过的经验,前四条坑能靠脚本避开,这一条只能靠对场景的清醒认识。
6. 进阶验证技巧:固定随机种子重训两次,判断是训练问题还是数据问题
6.1 用同一份数据跑两次,看稳定性
拿到数据集并且训练跑通一轮之后,先别急着调参。我建议做一个低成本但信息量很大的验证:固定随机种子,把同一份数据用完全相同的超参数训练两次。Ultralytics 里直接加seed=42即可:
yolo task=detect mode=train \ model=yolov8n.pt \ data=.../data.yaml \ epochs=50 imgsz=640 batch=8 device=0 \ seed=42逻辑说明:YOLO 训练涉及数据增强随机性、权重初始化随机性、shuffle 随机性,不固定种子的话前后两次结果天然有波动。固定种子后如果两次训练出来的 mAP 相差 1~2 个百分点,说明数据集的一致性够好,后续调参可以放心;如果两次相差超过 5 个百分点,说明数据里存在影响收敛的噪声——这种噪声往往在标注层面,比如边界框不齐、类别边缘模糊、少数类样本太少。此时的首要任务不是调学习率,而是回头再看一眼第 3 章的检查报告。
再做细一点,同一个训练好的权重上跑yolo val,打印出每个类别的 AP。三个类别里最容易出问题的是轨道这类长条形目标:长宽比极端,默认 anchor 和 imgsz 对它并不友好。如果只有这一个类 AP 明显掉队,可以针对性地把imgsz提到 1280 重训,或者单独给这个类做裁剪增强,而不是整体换模型。
这类验证的意义在于把“模型效果差”归因到正确的位置。很多时候问题不在 YOLO 结构,也不在训练轮数,而在数据本身的一致性。3793 张图像属于中等规模数据集,适合做算法验证和预研,但要部署到真实场景,建议补充现场样本做一轮 fine-tune。
从那以后,我拿到任何新数据集,第一步都是先跑标注体检脚本,再做一次双种子稳定训练,全部通过才进入正式的调参循环。这套顺序帮我少走了不少冤枉路,希望帮到你。
本文还有配套的精品资源,点击获取