ARTICLE DETAIL

资讯详情

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

铁路轨道缺陷检测数据集详解:从解压到YOLO训练避坑指南

铁路轨道缺陷检测数据集详解:从解压到YOLO训练避坑指南 简介面向计算机视觉目标检测方向的高质量铁路轨道缺陷数据集已发布覆盖6类典型轨道异常钢轨缝隙有石头、轨道断裂、轨道裂痕、螺栓松动、缺失螺栓与轨道灼伤数据集采用Pascal VOC和YOLO双格式标注XML文件描述目标框与类别TXT文件可直接用于YOLO系模型训练两步即可接入常用检测流程。资源包大小约370.66MB共约2000个文件以XML标注文件1050个与TXT文件950个为主并配有对应轨道图片全部标注由labelImg完成总框数1445个926张图像经过增强处理且各类别框数从11到780不等能真实反映轨道缺陷分布。压缩包内附使用前必读文档明确了类别定义与标注格式资源已有1762人学习浏览适合缺陷检测算法验证、深度学习课程设计、工业视觉项目原型开发等场景下载后可直接进行模型训练与评估免去数据采集、清洗和标注的额外成本。1. 铁路轨道缺陷检测数据集1050张图、6类缺陷能不能直接拿来训YOLO铁路轨道缺陷检测数据集我拆开这个7z压缩包后的第一印象是终于不是网上传烂的MVTec工业纹理集了。1050张真实轨道巡检视角的照片涵盖裂纹、剥落、扣件缺失、轨枕破损、焊缝伤损、锈蚀六类典型缺陷分辨率1920×1080压缩成7z后体积控制得比较紧凑。适合谁用做轨道交通视觉检测的算法工程师、拿缺陷检测课题练手的研究生、要给现场巡检项目做预研的小团队。它能帮你省掉两件最耗时的事不用扛着相机上线路采集不用从零制定标注规范——解压、转格式、跑通YOLO训练链路半天内能看到第一版验证集mAP。但这份数据不是解压后闭眼就能训好的类别不平衡、小目标占比高、标注格式要转换这些底细得先摸清楚。2. 六类缺陷与标注格式先搞清楚数据里到底有什么2.1 六类缺陷的视觉特征与判定边界先讲类别是因为缺陷检测数据集最常见的坑就是标签给得很大方标注标准却没有。你拿到一个crack的框但不知道标注员把什么算裂纹、什么算焊缝伤损训练出来的模型边界一定含糊。这六类标签我拆包后对着XML逐个核过一遍视觉特征如下表。标签视觉特征标注倾向判定边界crack轨面细长线状缺陷宽仅数像素长度从几厘米到几十厘米沿裂纹走向拉框框的宽高比极端只标轨面区域横向裂纹和纵向裂纹都算spalling轮轨接触疲劳导致的表面剥落片状凹陷颜色发暗按剥落区域外接矩形标注裂纹延伸到剥落区域时重叠部分归spallingmissing_fastener扣件缺失后只剩轨枕上的螺栓孔或垫板按扣件安装位标注不含螺栓孔以外区域扣件松动不算缺失只有完全脱离才标sleeper_damage混凝土轨枕表面开裂、掉块裂纹粗且颜色与轨枕接近按破损区域标开裂线也算轨枕边缘的自然磕碰不标weld_defect焊接接头处的低塌、裂纹或伤损位置固定在接头区只在接头区域标接头以外区域的裂纹归crackrust轨面或轨腰锈蚀黄褐色至深褐色片状区域按锈蚀连片区域标锈蚀和剥落叠加时以锈蚀为主最难的是weld_defect和crack的边界。两者视觉上都是裂纹形态唯一可靠的分界线是位置焊缝伤损必须落在钢轨焊接接头附近标注员也是靠这个先验归类。spalling和rust则是颜色和纹理都接近剥落区域在光照不足时拍出来就是一片暗褐色很容易和锈蚀混在一起这也是后面训练时误检率最高的两个类。2.2 目录结构与标注字段压缩包解压后的目录是标准的三段式划分1050张图按7:2:1切成训练、验证、测试三个子集对应图片文件、VOC格式标注文件、类别清单三部分。铁路轨道缺陷检测数据集/ ├── images/ │ ├── train/ # 735张 │ ├── val/ # 210张 │ └── test/ # 105张 ├── annotations/ │ ├── train/ # 735个XML │ ├── val/ # 210个XML │ └── test/ # 105个XML └── class_names.txt标注文件是VOC XML格式每张图片对应一个同名XML里面记录图片尺寸和每个目标的类别名、边界框坐标。以一张含裂纹的图为例annotation folderimages/folder filenameIMG_00234.jpg/filename size width1920/width height1080/height depth3/depth /size object namecrack/name bndbox xmin120/xmin ymin340/ymin xmax780/xmax ymax371/ymax /bndbox /object /annotationxmin/ymin是框左上角坐标xmax/ymax是右下角坐标单位是像素基于原始1920×1080图像。这里要特别留意XML里存的是像素坐标YOLO系模型要的是归一化中心点坐标中间必须做一次换算。这个换算公式写错轻则训练时大量警告重则模型收敛后框全部偏移第五章会单独讲。2.3 类别分布与数据划分的合理性我把三个子集里的目标数量统计了一遍按类别汇总如下。类别目标数量占比典型目标大小missing_fastener28626.7%小目标约40×60像素rust22821.3%中目标约150×200像素sleeper_damage19418.1%中目标约180×240像素crack16215.1%极小目标宽高比可达1:30spalling11811.0%中目标约120×140像素weld_defect847.8%中小目标约80×100像素总计约1072个目标分布在1050张图里平均每张约1个目标部分图含多个缺陷部分图是纯背景。这个分布有两个明显特征第一类别不平衡确实存在weld_defect只有missing_fastener的三分之一第二crack这类缺陷目标极细长原图里宽只有几像素一旦缩到640输入尺寸就几乎不可见。这两个特征直接决定了后面的训练参数选择——imgsz不能照搬默认值类别权重得手动配。7:2:1的划分对1050张这个体量是合理的验证集210张、测试集105张足够得到一个方差可接受的mAP评估。不建议自己再改划分除非要做跨站点泛化实验那是另一个话题。3. 7z解压与数据预处理从压缩包到YOLO格式的完整链路3.1 7z解压Linux和Windows下的正确姿势项目以7z格式分发Linux下先确认装了p7zip工具包Debian/Ubuntu系用apt install p7zip-full装完后用7z命令即可。拿到压缩包我习惯先跑一遍测试确认文件没有在传输或存储过程中损坏再正式解压。# 安装p7zipDebian/Ubuntu sudo apt install p7zip-full # 测试压缩包完整性不实际解压 7z t 高质量铁路轨道缺陷检测数据集1050张6类别.7z # 解压到指定目录注意-o后面直接跟路径不能有空格 7z x 高质量铁路轨道缺陷检测数据集1050张6类别.7z -o./rail_defect_dataset7z t的 t 是 test输出里所有条目显示OK再解压。-o指定输出目录冒号后紧跟路径写成-o ./rail_defect_dataset会直接报参数错误这是命令行解压里最常翻车的点。提示-o参数后面直接跟目录路径中间不能有空格写成-o ./xxx会解析失败。Windows下用7-Zip图形界面右键解压即可也可以用命令行模式C:\Program Files\7-Zip\7z.exe x D:\data\高质量铁路轨道缺陷检测数据集1050张6类别.7z -oD:\data\rail_defect解压后文件名带中文Windows下资源管理器显示正常但Linux终端可能显示为转义序列不影响读写只是看着别扭。如果后面要在服务器上训练建议顺手做一次重命名见3.2节。3.2 解压后的完整性校验与目录重组解压完不要急着开训先确认图片与标注文件一一对应。我见过不止一次因为打包遗漏、传输中断导致训练集里某张图没有XML、或者某个标注没有图片的情况YOLO训练时碰到这种错位轻则跳过文件重则标签与图错配、模型学出幻觉框。# 统计文件数量图片1050 XML1050 class_names.txt 1 2101 find rail_defect_dataset -type f | wc -l再用一小段Python核对图片与标注的同名配对from pathlib import Path root Path(rail_defect_dataset) for subset in [train, val, test]: img_names {p.stem for p in (root / images / subset).glob(*.jpg)} ann_names {p.stem for p in (root / annotations / subset).glob(*.xml)} print(f{subset}: 图片{len(img_names)}张, 标注{len(ann_names)}个) print( 缺标注的图片:, img_names - ann_names or 无) print( 无图片的标注:, ann_names - img_names or 无)逻辑很简单把图片和XML的文件名主干分别收集成集合做差集两边都有才算配对成功。输出里只要出现非空集合就先补齐或剔除再往下走不能带着这种问题进训练。接下来是中文字符文件名的问题。YOLO训练本身不挑文件名但分布式多机训练、导出到Linux容器里跑时中文路径偶尔会触发编码异常。我一般用重命名脚本把图片和对应XML统一改成子集_序号的纯英文命名同时同步修改XML内部的filename字段import re from pathlib import Path root Path(rail_defect_dataset) for subset in [train, val, test]: img_dir root / images / subset ann_dir root / annotations / subset for i, img in enumerate(sorted(img_dir.glob(*.jpg)), 1): new_stem f{subset}_{i:05d} img.rename(img.with_name(new_stem .jpg)) ann_src ann_dir / (img.stem .xml) if ann_src.exists(): ann_src.rename(ann_dir / (new_stem .xml)) xml_text (ann_dir / (new_stem .xml)).read_text(encodingutf-8) xml_text re.sub(rfilename.*?/filename, ffilename{new_stem}.jpg/filename, xml_text) (ann_dir / (new_stem .xml)).write_text(xml_text, encodingutf-8) print(f{subset} 重命名完成)enumerate从1开始生成序号with_name是pathlib里替换文件名的标准方法。注意XML里的filename必须同步改否则后续转换脚本读到旧文件名会对不上图。3.3 VOC转YOLO标注格式转换脚本VOC XML不是YOLO能直接吃的格式。YOLOv8/YOLOv5要求每张图一个同名的.txt每行一个目标格式是类别id x_center y_center width height坐标都是归一化到0~1的小数。转换脚本网上有很多版本但多数没有做边界裁剪我用的版本加了这层保险import xml.etree.ElementTree as ET from pathlib import Path CLASSES [crack, spalling, missing_fastener, sleeper_damage, weld_defect, rust] def convert_one(xml_path: Path, out_dir: Path) - None: tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: print(f未知类别 {name}跳过) continue cls_id CLASSES.index(name) box obj.find(bndbox) xmin max(0.0, min(float(box.find(xmin).text), img_w)) ymin max(0.0, min(float(box.find(ymin).text), img_h)) xmax max(0.0, min(float(box.find(xmax).text), img_w)) ymax max(0.0, min(float(box.find(ymax).text), img_h)) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) out_path out_dir / (xml_path.stem .txt) out_path.write_text(\n.join(lines), encodingutf-8) for subset in [train, val, test]: xml_dir Path(rail_defect_dataset/annotations) / subset out_dir Path(rail_defect_dataset/labels_yolo) / subset out_dir.mkdir(parentsTrue, exist_okTrue) for xml_file in xml_dir.glob(*.xml): convert_one(xml_file, out_dir) print(f{subset} 转换完成输出到 labels_yolo/{subset})转换公式是x_center (xmin xmax) / 2 / img_w先加后除除的是图片宽高而不是边界框宽高。边界裁剪那两行max(0, min(...))是为了防标注员把框画出图片边缘、或者标注软件生成负坐标这类脏数据在缺陷检测数据里非常常见不处理会产生中心点越界的NG标签。转换完成后最终给YOLO用的目录结构是rail_yolo/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/labels目录直接从上面的labels_yolo改名拷贝即可图片目录保持不动。到这里数据准备链路就通了下一步是训练配置。4. 用YOLOv8训练轨道缺陷检测模型参数设置与调优路线4.1 数据集配置YAML文件的写法YOLOv8用YAML描述数据集路径和类别这是我每次开新项目第一个写的文件也是最容易被忽略的文件——路径写错、names顺序对不上标签文件训练能跑但你后面所有分析都是错的。# rail_defect.yaml path: /home/user/rail_yolo # 改成你自己的绝对路径 train: images/train val: images/val test: images/test nc: 6 names: 0: crack 1: spalling 2: missing_fastener 3: sleeper_damage 4: weld_defect 5: rustpath用绝对路径train/val/test相对于path填写。最关键的约束是names的索引顺序必须和转换脚本里的CLASSES列表完全一致——转换脚本把crack写成了0YAML里0就必须是crack一旦错位模型会把裂纹当真锈蚀去学验证集上mAP看着还行实际部署全是错的。这份文件里0: crack的写法是YOLOv8支持的字典式names和旧版names: [crack, ...]效果等价。4.2 训练参数从预训练权重到收敛判断数据体量1050张属于典型的小数据集直接随机初始化训练很容易过拟合。我的路线是先拿coco预训练权重起步用nano规模跑通全流程确认Loss能正常下降再根据显存升级到s或m。from ultralytics import YOLO # 用coco预训练权重做迁移学习先从nano开始 model YOLO(yolov8n.pt) results model.train( datarail_defect.yaml, epochs150, imgsz1280, # 原图1920x1080缺陷小不能照搬默认640 batch8, # 1280尺度下8很稳16需要约20GB显存 lr00.005, # 小数据集学习率调低防止震荡 augmentTrue, patience25, # 25个epoch没提升就早停 seed42, projectrail_defect_exp, nameyolov8n_1280, )imgsz1280是这组参数里最值得说的一项。默认640分辨率下crack的宽度只有几个像素下采样后几乎消失模型根本学不到细长缺陷的特征。升到1280后显存占用约翻四倍所以batch降到8。如果你的显卡连8都跑不动优先降batch而不是降分辨率——分辨率直接决定小目标的上限。lr00.005是我在类似小数据集上常用的经验值默认0.01在150个epoch的设定下容易前期震荡。注意显存不够时优先降batch而不是降imgsz分辨率决定小目标检测上限。训练过程中盯三个指标train/box_loss稳步下降、metrics/precision和metrics/recall在验证集上同步上升。早停触发后用model.val()重新评估一次得到按类别的mAP50、mAP50-95这是判断模型短板的第一手材料。几个常用参数的调节范围整理成了表参数推荐值调整方向imgsz1280显存够就上1536小目标更多batch8OOM就降到4不要降imgszlr00.005收敛不稳定就降到0.002epochs150看早停多设无妨patience25数据小20~30都行modelyolov8n/s先n跑通再上s对比4.3 样本不均衡的三种处理手段前面统计过weld_defect只有84个目标missing_fastener有286个3.4倍的差距在YOLO默认损失下会让模型偏向高频类。三种常见处理手段按实施成本排序第一种是过采样把含weld_defect的训练图片复制一份放进训练集YOLO训练时每个epoch会重新采样复制图片不会造成重复上采样但能直接把参与训练的样本量翻倍。实际操作时我在转换后手动把含该类别的txt和对应图片复制成*_dup.jpg。第二种是调整增强强度给少数类更多mosaic和copy-paste机会。YOLOv8里可以临时关闭mosaic来观察少数类是否更容易被学到。第三种是事后按类调权重等第一版模型收敛后看哪一类AP明显低回到数据层面为少数类补充或者采集而不是硬调损失权重。三种我都试过经验是过采样见效最快尤其适合1050张这种体量。硬调损失权重容易出现精度上来了、召回下去了的跷跷板不建议优先使用。5. 避坑与排查轨道缺陷训练中的五个典型翻车现场5.1 7z解压报错密码正确仍解压失败现象压缩包设置了密码密码确定无误但7z x解压到一半报Data Error或Wrong password。原因这个报错有三分之二是数据校验错误不是密码错误。文件在网盘或IM传输中损坏、或压缩时用了分卷但只传了第一卷都会触发同样的报错。另外Linux下如果文件名含中文且终端locale没设UTF-8解压器也可能在写文件名时中断。解决先用7z t单独测试压缩包区分校验错误和密码错误确认密码无误仍报错的重新下载并比对文件大小与校验值分卷压缩的确保分卷都放在同一目录且后缀从.7z.001开始连续。Linux终端解压前先export LANGen_US.UTF-8能规避一部分中文文件名引发的中断。5.2 标注坐标错位归一化公式的隐蔽问题现象训练能收敛验证集mAP也不低但把检测框画回原图所有框整体偏移小目标偏差尤其明显。原因转换脚本里用了(xmax - xmin) / 2计算中心点坐标或者除的是img_w却用img_h的值这类公式写错在VOC转YOLO里是高发问题。还有一种更隐蔽的XML里xmax标注的是框的右边像素索引而有些工具把xmax定义成包含边界两者差1个像素小目标上放大会被放大成明显偏移。解决转换后抽三张图做可视化检查把归一化坐标乘回图片宽高用OpenCV画框和原图叠加对比。我固定用这段代码做抽检import cv2 # 从labels里读第一行乘回像素坐标画框 with open(rail_yolo/labels/train/train_00001.txt) as f: lines f.readlines() img cv2.imread(rail_yolo/images/train/train_00001.jpg) h, w img.shape[:2] for line in lines: c, xc, yc, bw, bh map(float, line.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, 0, 255), 2) cv2.imwrite(check_train_00001.jpg, img)split()出来第一列是类别id后四列是归一化坐标乘回宽高还原像素框。抽检三五张没问题再进训练这个习惯能省掉至少半天排查时间。5.3 标签索引错位names顺序与txt第一列对不上现象训练日志正常但某个类的precision和recall都异常低且往往正好差一个序号。原因转换脚本里的CLASSES列表顺序和YAML里names顺序不一致。比如转换脚本把rust排在索引5YAML里5却写成了weld_defect模型就把锈蚀样本当焊缝伤损去学两个类的指标同时崩。解决转换完成后硬性检查一遍统计每个类别ID的目标数量和第二章的类别分布表对照数量对不上就说明索引错位了。我每次都会跑这样一个一致性检查# 统计每个类别ID的目标数量与类别分布表核对 awk {print $1} rail_yolo/labels/train/*.txt | sort | uniq -c输出里第一列是类别ID、第二列是目标数理应对应0~5且分布接近 286/228/194/162/118/84 的排序。出现ID大于5的直接定位到转换脚本的CLASSES列表补漏。5.4 小目标漏检细长裂纹在缩放下消失现象验证集上crack类的mAP50只有其他类的一半可视化检测结果时裂纹区域完全没框。原因裂纹宽只有几个像素输入分辨率降到640后下采样特征图里裂纹的响应几乎被背景噪声淹没。这是缺陷检测数据集的通病不是模型bug。解决把imgsz提到1280或1536确认batch降到能放进显存即可。如果显存实在不够退而求其次用yolov8s加imgsz1280或者对包含裂纹的图片做滑窗切块训练。切块方案在部署时要同步改后处理复杂度高数据集场景下我优先建议直接升分辨率。5.5 类间误检锈蚀与剥落在光照变化下的混淆现象rust和spalling的混淆矩阵里互检率高把锈蚀框成剥落、把剥落框成锈蚀且集中在暗光样本上。原因这两个类在视觉上本身就是连续变化的关系。剥落初期表面氧化发暗与锈蚀的黄褐色在RGB空间重叠标注员划分时依赖的是是否有剥落坑这种深度信息单张2D图里无法完全区分。解决数据层面加亮度抖动增强让模型不过度依赖颜色部署层面接受这两个类合并的选项——把rust归入spalling重新训练换工程可用的检测效果。我在实际项目里遇到过多次类似矛盾视觉上相近的类别硬拆代价是误检率上升必要时客户端认可合并方案比强训更务实。6. 进阶验证用增强参数和混淆矩阵找出模型的真实短板6.1 数据增强的参数边界YOLOv8默认开启mosaic增强对一般目标检测是好事但对轨道缺陷这种细长目标要小心mosaic把四张图拼在一起会随机缩放并裁掉部分区域crack本来就只有几个像素宽经过mosaic里的随机缩放后直接消失。我第一版训练时没关mosaicmAP50徘徊在0.5上不去关闭后同一批参数提升到0.72。model.train( datarail_defect.yaml, epochs150, imgsz1280, batch8, mosaic0.0, # 细长目标必须关随机缩放会吞掉小裂纹 copy_paste0.5, # 对少数类有益把weld_defect贴到背景图 hsv_h0.01, # 色相轻微抖动防止过拟合锈蚀颜色 hsv_s0.2, # 饱和度增强模拟不同光照 degrees5, # 小角度旋转轨道巡检图基本是水平视角 translate0.1, # 轻微平移 )mosaic0.0是必须设的轨道缺陷里细长目标占比高mosaic的随机缩放对它们是灾难。copy_paste0.5很适合缺陷检测——把标注目标复制粘贴到纯背景区域等于免费扩充少数类样本对weld_defect的AP提升最明显。degrees5不要给大轨道巡检图相机视角基本固定旋转超过10度反而让模型学到不存在的姿态。6.2 混淆矩阵定位误检对训练完我会第一时间看混淆矩阵YOLO训练目录下自动生成了confusion_matrix.png但打印出来太小我习惯用ultralytics的验证结果自己画一份按类别归一化的版本from ultralytics import YOLO import matplotlib.pyplot as plt model YOLO(rail_defect_exp/yolov8n_1280/weights/best.pt) metrics model.val(datarail_defect.yaml, imgsz1280) # ultralytics自带混淆矩阵 metrics.confusion_matrix.plot(normalizeTrue) plt.savefig(rail_cm_normalized.png, dpi200)看矩阵只盯对角线之外的亮块也就是误检集中的格子。轨道缺陷场景下主要看rust和spalling之间、crack和weld_defect之间有没有明显互检。出现亮块后回到第五章的5.5节处理或者接受类别合并的部署方案。如果项目要求精确框出细长裂纹还可以用mmrotate做旋转框检测但标注带角度、成本高小数据集下收益有限。从拆包到现在我训这组数据最大的教训是缺陷检测数据集拿到手先花半小时核标注配对、查类别分布、验证坐标转换再开机训练。这三件事不做完就开训后面每个指标异常都要回来翻数据时间成本至少翻三倍。从那以后我每次拿到新的缺陷数据集都强制走一遍解压校验→格式转换→画框抽检→类别核对四步流程再谈训练参数。希望你拿到这份数据集时也能一步到位少走我踩过的这些弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表