
简介铁路轨道故障图像识别数据集提供约380张已标注的轨道图像按‘损坏/未损坏’两类组织面向轨道视觉检测与图像分类任务适合作为深度学习入门或巡检场景的基准数据。数据已划分训练集、验证集与测试集并附JSON标注文件说明类别另提供Python可视化脚本便于直接运行查看样本分布。压缩包内共387个文件以jpg/jpeg图像为主368张jpg、15张jpeg及1张webp搭配py脚本、png示例与json标注整体大小约159.73MB结构清晰便于按需取用。该数据集也可配合CNN分类项目或YOLOv5分类教程进行模型训练与效果对比当前已有192人学习下载适合希望快速上手轨道故障分类任务的学习者。1. 铁路轨道故障图像识别数据集380张已标注图的价值与局限做轨道巡检的人最头疼的往往不是算法而是数据。钢轨裂纹、扣件缺失、轨枕破损这些故障现场拍回来一大堆但能用的大概只占一成——角度歪的、过曝的、被杂草挡住的全得人工筛掉。再往后是标注一张轨道图里可能有几十个扣件、几条裂纹框到哪儿、框多大质检成本比拍摄还高。所以当出现一个“铁路轨道故障图像识别数据集【已标注约380张数据】”时它解决的不是“有没有图”的问题而是“有没有干净图、有没有规范框”的问题。380张不算多但它能让你把模型先跑起来、把流程理顺再拿这个底子去扩充自己的数据。这篇文章就围绕这套数据讲透三件事怎么验收它、怎么训练它、以及它到底撑不撑得住真实巡检场景。2. 读懂标注数据故障类型、标注格式与质量验收清单2.1 先搞清楚这380张里装的是什么任务类型拿到任何数据集第一件事不是打开训练脚本而是确认任务定义。铁路轨道故障图像识别数据集如果标注给的是矩形框那它就是目标检测任务不是分割、不是分类。目标检测的意思是模型要同时回答“哪里有故障”和“这是什么故障”输出一堆框和类别。如果你的最终目标只是判断“这段轨道有没有问题”分类模型也能做但工程上你很快会发现分类模型无法告诉你故障在哪个枕木、哪个扣件上维护人员还得拿着地图去比对效率极低。另一个任务是确认故障类别。按轨道巡检的常见诉求这类数据集通常覆盖三类到六类钢轨裂纹、扣件缺失或断裂、轨枕破损、道床异物侵入。我一般建议拿到数据后先写两行代码把类别名打出来而不是看文档描述。原因很实际很多数据集标注时把“扣件缺失”和“扣件断裂”分成两类但训练时你会发现这两类视觉差异极小模型反复在这两个类别上打架最终还得合并成一个大类“扣件异常”。这种事提前看一眼类别分布就能避免。还有一点值得留意这套数据只有约380张但每张图里很可能有多个目标。铁路轨道画面有很强的重复性一个扣件在一张图里出现三四次很常见。所以380张不等于380个样本实际参与训练的标注框数量可能是它的五到十倍。这个数字决定了你可以尝试的目标检测网络规模也决定了你要不要做后面的数据增强。2.2 三种标注格式VOC、COCO、YOLO怎么选标注格式是数据集的“方言”。同一个数据集可能换了个目录结构就导致训练脚本直接报错。目前轨检类数据集最常见的是三种Pascal VOCXML文件、COCOJSON文件、YOLOTXT文件。它们各有优缺点我直接列个对比表。格式文件形态坐标表示优势劣势Pascal VOC每张图对应一个XML左上角x、y和右下角x、y可读性好标注工具默认导出文件多解析慢COCO所有标注汇总进一个JSON左上角x、y和宽高自带类别、分割等扩展字段JSON嵌套深新手容易取错字段YOLO每张图对应一个TXT中心点x、y和宽高全部归一化到0-1训练时读取最快坐标是浮点肉眼检查不方便如果你自己标注标注工具输出的往往是VOC格式因为labelImg这类工具默认就存XML。但主流的检测框架比如YOLO系列吃的是TXT格式。所以拿到数据集后的第一个常规动作就是格式统一。2.3 质量验收训之前先做三道检查这套数据号称“已标注”。但“已标注”这件事本身值得怀疑——模型训练翻车一半原因是标注质量不过关。我拿到数据集后会先跑一个验收脚本核心看三件事有没有损坏文件、类别分布是否失衡、标注框是否离谱。import os import cv2 from glob import glob # 检查图片能否正常打开 img_dir images bad_files [] for p in glob(os.path.join(img_dir, *)): img cv2.imread(p) if img is None: bad_files.append(p) print(损坏或无法读取的图片:, bad_files) # 检查标注框是否超过图像边界 anno_dir labels overflow_count 0 for p in glob(os.path.join(anno_dir, *.txt)): with open(p) as f: for line in f: cls, x_center, y_center, w, h map(float, line.split()) # 归一化坐标理论上应在0-1之间 if not (0 x_center 1 and 0 y_center 1 and 0 w 1 and 0 h 1): overflow_count 1 print(坐标越界的标注行数:, overflow_count)这段代码的逻辑不复杂第一段用OpenCV逐个读图读不到就说明文件损坏或格式不支持这类图混进训练集只会让训练中断或产生无效loss第二段检查TXT里的坐标YOLO的归一化坐标一旦出现大于1或小于0的值说明标注工具导出时就出了错训练时模型会学到凭空出现的框。这两道检查不过后续训练全白做。关于类别分布建议再用一段代码统计每个类别的框数量这一步直接用Python的Counter就行。如果某个类别只有十几条标注后面数据增强时要重点给它做扩样否则模型大概率会忽略这个类别。提示如果数据给的是VOC的XML而不是TXT上面的脚本要把解析部分换成xml.etree.ElementTree第二个检查则要判断框的右下角是否超出了图片宽高。3. 用YOLOv8训练轨道故障检测VOC转YOLO与三个必调参数3.1 目录结构提前按YOLO约定的方式铺好图像识别数据集的目录组织没有玄学完全跟着框架走。Ultralytics YOLO系列期望的数据目录长这样track_fault_dataset/ ├── images/ │ ├── train/ │ │ ├── img_0001.jpg │ │ └── ... │ └── val/ │ ├── img_0015.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── img_0001.txt │ │ └── ... │ └── val/ │ ├── img_0015.txt │ └── ... └── data.yaml注意images和labels下的子目录名必须完全对应。你可以用脚本按比例切分数据集常见做法是train和val按8:2分但如果380张里某些类别的样本集中在某几张图一定要先把类别分布统计出来再切否则会出现val集里根本没有裂纹样本的情况验证指标全部失真。3.2 把VOC转成YOLO格式转换脚本与边界坑如果数据是VOC标注转换这一步躲不掉。YOLO的TXT每一行代表一个目标格式是“类别ID x中心坐标 y中心坐标 框宽 框高”除类别ID外全部归一化到0-1。下面是转换核心逻辑import os import xml.etree.ElementTree as ET from glob import glob class_list [crack, fastener_missing, sleeper_broken, foreign_object] def convert_voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) out_lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_list: continue cls_id class_list.index(cls_name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 防止标注框超出边界导致坐标大于1 x1 max(0, min(x1, img_width)) x2 max(0, min(x2, img_width)) y1 max(0, min(y1, img_height)) y2 max(0, min(y2, img_height)) # 转归一化中心坐标 x_center (x1 x2) / 2 / img_width y_center (y1 y2) / 2 / img_height w (x2 - x1) / img_width h (y2 - y1) / img_height out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) base_name os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, base_name .txt), w) as f: f.write(\n.join(out_lines)) xml_list glob(annotations/*.xml) for xml_path in xml_list: convert_voc_to_yolo(xml_path, labels)转换脚本里最容易出错的地方是坐标裁剪。很多标注工具在图片边缘画框时会把框的边界画到图片外直接归一化会得到大于1的坐标模型训练时要么报错要么产生一个永远学不对的样本。上面用max(0, min(...))把坐标压回图片边界内这一行看起来不起眼但能省掉后续大量报错排查。另一个坑是类别名对齐XML里的类别名必须和class_list一致如果一个XML里出现了没写进class_list的类别脚本会直接跳过它建议加上一行打印提醒。3.3 写data.yaml三个必调参数YOLO训练不读你的数据集说明书只读data.yaml。下面是模板train: track_fault_dataset/images/train val: track_fault_dataset/images/val nc: 4 names: [crack, fastener_missing, sleeper_broken, foreign_object]nc是类别数量names顺序必须和转换脚本里的class_list一致。这里有个隐蔽的坑如果训练时报“class index out of range”通常是TXT里的类别ID比nc大也就是转换脚本和yaml里的类别顺序对不上。训练命令本身不复杂但在380张数据的前提下有三个参数必须手工调不能全用默认值yolo detect train \ modelyolov8n.pt \ datatrack_fault_dataset/data.yaml \ epochs200 \ imgsz960 \ batch16 \ patience30 \ device0epochs不能用默认的100数据量小模型需要更多轮次才能稳定收敛但又不能无限加后面要配patience早停。imgsz是重中之重轨道故障目标尤其是钢轨裂纹往往只有几十个像素640的输入尺寸会把小目标压成几个像素点我通常从960起手。如果你的显卡显存只有12Gimgsz960加batch16可能会爆显存那就降到batch8先跑起来再说。device指定用哪张GPU如果你是Apple Silicon的Mac改成devicemps也能跑。3.4 380张数据训练时的预期管理用这样的小数据量训练千万不要指望第一轮就出漂亮指标。按我的经验前50轮loss下降很快但val的mAP0.5会在某个点突然停滞甚至回退这说明模型开始过拟合训练集了。此时先检查patience有没有触发早停没触发就手动停掉把最后一次权重拿来测通常这比硬跑完200轮的结果好。另外我习惯把save_period10加上每隔10轮存一次权重这样后面评估时可以试试不同轮次的模型找到验证集表现最好的那个而不是只盯着最后一轮——这是小数据训练最实用的“后悔药”。4. 数据增强把380张扩成可用规模的最少操作4.1 为什么380张必须走增强这条路380张原始图像喂给任何深度学习模型都偏少。目标检测模型动辄上千万参数几百张图只能让它记住训练集的样子换个角度、换个光照立刻翻车。数据增强不是可选项而是小数据集项目能不能落地的分水岭。增强的本质是给模型增加“先验知识”轨道永远有两条平行线、扣件永远在钢轨下方、裂纹的灰度特征和背景差异极大——这些规律靠变换图片让模型自己学出来。4.2 在线增强改参数就有收益YOLO的Ultralytics框架内置了在线增强训练时在内存里实时变换图片不额外占用磁盘这是最优先用的增强方式。下面是一组适合轨道场景的增强配置放在训练命令参数里yolo detect train \ modelyolov8n.pt \ datatrack_fault_dataset/data.yaml \ epochs200 \ imgsz960 \ batch16 \ hsv_h0.015 \ hsv_s0.5 \ hsv_v0.4 \ degrees10 \ translate0.1 \ scale0.2 \ fliplr0.5 \ mosaic1.0这些参数的用意要讲清楚hsv_h控制色调偏移轨道场景里钢轨是灰黑色扣件是深色金属色调变化不会扭曲它们的本质特征但能让模型不依赖某个固定的颜色值。degrees10是允许图片旋转10度铁轨在画面里不可能永远水平巡检车过弯道时图片会偏转。fliplr0.5是水平翻转轨道图翻转后仍是一条轨道这是最安全的增强。mosaic1.0表示全部启用Mosaic把四张图拼成一张训练模型每次迭代能看到更多上下文。4.3 针对稀缺类别的离线增强裂纹只有40张怎么办自带的在线增强对每个类别“一视同仁”但现实是380张数据里各类别数量极不平衡——扣件可能有上千个框裂纹可能只有四五十个框。这时候只靠在线增强稀有不少类别还是学不好。我一般会写离线增强脚本专门把稀有类别的图片多生成几份扩充到训练集里再开训。用albumentations库做最稳import cv2 import albumentations as A from glob import glob # 只对包含rare_class标注的图片做增强 rare_class 0 # 假设类别0是裂纹 image_dir images/train label_dir labels/train transform A.Compose([ A.RandomBrightnessContrast(p0.5), A.GaussNoise(var_limit(10, 50), p0.3), A.ShiftScaleRotate(shift_limit0.05, scale_limit0.1, rotate_limit5, p0.6), A.CLAHE(p0.3), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels])) def augment_and_save(img_path, label_path, out_dir, num_copies6): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: lines f.readlines() boxes [] class_labels [] contains_rare False for line in lines: cls, xc, yc, bw, bh map(float, line.split()) boxes.append([xc, yc, bw, bh]) class_labels.append(int(cls)) if int(cls) rare_class: contains_rare True if not contains_rare: return for i in range(num_copies): transformed transform(imageimg, bboxesboxes, class_labelsclass_labels) aug_img transformed[image] aug_boxes transformed[bboxes] aug_cls transformed[class_labels] base os.path.splitext(os.path.basename(img_path))[0] cv2.imwrite(f{out_dir}/images/{base}_aug{i}.jpg, aug_img) with open(f{out_dir}/labels/{base}_aug{i}.txt, w) as f: for bbox, cls in zip(aug_boxes, aug_cls): f.write(f{int(cls)} {bbox[0]:.6f} {bbox[1]:.6f} {bbox[2]:.6f} {bbox[3]:.6f}\n) for img_path in glob(f{image_dir}/*.jpg): label_path os.path.join(label_dir, os.path.basename(img_path).replace(.jpg, .txt)) if os.path.exists(label_path): augment_and_save(img_path, label_path, augmented)albumentations的bbox_params里formatyolo表示输入输出都是归一化的中心坐标格式这跟数据集标注格式一致不用来回换算。这段脚本的核心逻辑是读图后先看这张图里有没有满是稀缺类别的框没有就直接跳过有才扩成6份副本。它不会触碰那些本来就够用的图片避免把训练集无谓地撑大。4.4 增强的边界别把轨道增强成“异物”增强也不是越猛越好这是我踩过最多的坑。轨道场景的特殊性在于它的负样本判断依赖强背景一致性——钢轨是暗色的、扣件是对称的、道床是规则的碎石纹理。如果你把hsv_v拉得过大让钢轨曝光成白色模型可能把正常的白色钢轨识别成异物如果把rotate_limit设成30度图片里的轨道已经不像轨道了模型学到的是“随机纹理配矩形框”。我的建议是增强参数以“人不看标注也能认出这是轨道”为底线增强后的图随机抽20张打上框人眼跑一遍看到哪张离谱就调低对应参数。这个习惯能省掉很多训练完才发现方向错了的时间。5. 避坑轨道故障模型训练中最常见的5个翻车点5.1 类别不平衡稀有类别从未出现在预测结果里现象训练完成val mAP整体看起来有0.6但打开预测结果一看裂纹一个没检出扣件全是框。原因数据集中扣件几千个框、裂纹只有几十个框模型在训练时梯度被大量扣件样本主导学会了扣件就没动力学裂纹。损失函数里每个样本权重相同稀缺类别对loss的贡献几乎可以忽略。解决两条路并行。第一按前面离线增强的方法把稀缺类别扩到至少两百条标注第二训练时给稀缺类别加权重Ultralytics支持在配置里传class_weights或者直接对稀缺类别的loss乘以一个放大系数让模型哪怕只有几十个样本也能收到足够的梯度。5.2 小目标漏检裂纹在960分辨率下仍然丢失现象imgsz640训练时大面积的轨枕破损全部检出但细裂纹基本全军覆没。原因钢轨裂纹的目标尺寸常常只有20像素×5像素在640分辨率下压缩成几个像素特征在卷积过程中被池化层不断削弱。解决把imgsz升到960或1280这是最直接的答案。显存不够时先减少batch、再考虑关掉mosaic——Mosaic把四张图缩小拼接会让小目标更小反而起反效果。另外把模型从yolov8n换成yolov8s或yolov8m更大网络的特征图分辨率更高对微小裂纹的保留能力更强但训练时间会成倍增加。5.3 标注框不贴合模型学到的是“圈里有两根铁轨”现象推理时框的位置总是偏的检测框比目标大一圈像是把目标周围的背景也框进来了。原因原始标注时标注员图省事框得粗糙矩形框把裂纹周围的背景噪声一起包了进去。模型学到的特征是“这一片区域的纹理”而不是“裂纹本身的纹理”。解决对标注框做收缩处理。把每个标注框的宽高乘以0.8让框更贴合目标。我写过一个批量脚本遍历所有TXT文件对w和h直接乘系数然后重新调整x_center和y_center。这么做会让模型被迫去学目标本身而不是学背景。代价是标注框和真实目标的IoU下降但对检测任务来说框紧一点远好于框松一点。5.4 训练集和验证集泄漏指标虚高但不泛化现象train和val的loss都降到很低val mAP0.5高达0.85可一到测试视频就原形毕露漏检一片。原因380张图随机切分时同一处轨道病害的不同角度照片被拆进了训练集和验证集验证集里出现了训练集的近亲图片。模型本质上是“记住”了这个病害而不是“学会”了检测。解决切分时按故障实例分组而不是按图片随机分。如果时间段某一段轨道拍摄的多张连续照片属于同一个故障实例把它们全部归入训练集或全部归入验证集。判断方法很简单看同一病害有没有在不同图片中重复出现。重新切分后指标虽然会下降但那才是真实水平。5.5 过拟合交叉点判断失误只看最后epoch权重成了盲人摸象现象训练日志上loss还在降但val mAP已经连续十几轮不动你还在等它“奇迹般上涨”。原因验证集指标已经触及天花板但训练loss持续下降说明模型在疯狂记住训练集细节。日志里第一个指标不一定是最终指标。解决我每个项目都会先看patience是否触发。Ultralytics的patience30表示验证指标连续30轮不提升就自动停这是最可靠的保护机制。但不要在训练结束后只看best.pt要看last.pt和best.pt在真实图片上的表现差距。若是差距大说明best.pt本质上还是过拟合的。这个差距本身就是数据量和增强充分度的体检指标——差距超过15个点说明你的增强做得不够。6. 决定这套数据集值不值的两个验证动作训练结束模型权重在电脑里安静地躺着。在投入下一个迭代之前先做两个验证动作它们才决定这套数据的真实价值。第一个动作是拿没见过的视频跑一轮推理。我有一次前后花了三天调参val mAP漂亮得感人结果把训练时没用过的夜巡视频丢进去阴影部分疯狂误报——模型把道床的影子当成了裂缝。从此以后我给自己立了个规矩训练完当天不评估第二天拿一段与训练集完全不同的场景视频做测试固定置信度阈值0.25数一遍误报和漏报。视觉上数两百帧比看十个指标曲线都有用。第二个动作是导出部署格式实测推理速度。轨道巡检的推理盒子性能有限best.pt在训练GPU上跑得飞快不代表在嵌入设备上能用。我习惯用下面命令先转成ONNXyolo export modelbest.pt formatonnx imgsz960 opset12然后开个简陋的脚本循环跑100次推理量一下单帧耗时。巡检车运行速度通常不高于60km/h每秒处理两帧就能覆盖10米左右的间隔所以单帧推理时间控制在300毫秒以内是底线。超过这个值要么换更小的模型结构要么把imgsz砍回640两害相权取其轻。这两个动作做完你才真正知道这套数据集值不值。值就继续扩充它——把拍摄角度从水平改为俯角把天气从晴天补充阴天把380张滚到800张不值问题也不在数据而在标注质量和场景覆盖回炉重标比追加训练更划算。我第一次拿这种规模的数据集训练轨道故障模型时满脑子都是跑出高mAP结果现场视频第一帧就让我明白指标是给论文看的误报率才是给工务段看的。这套流程我后来在每个项目里都重走一遍每次都能提前拦下至少一轮无效训练。记住这个顺序先验数据再拉基线后调增强最后压测部署。铁路安全的容错率极低宁可前期多花两天做上述检查也不要在测试线上用一个你没验证过的权重。希望帮到你。本文还有配套的精品资源点击获取