ARTICLE DETAIL

资讯详情

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

火车轨道检测数据集实战:3900张COCO标注图训练YOLOv8模型

火车轨道检测数据集实战:3900张COCO标注图训练YOLOv8模型 简介这份火车轨道检测数据集面向计算机视觉开发者、轨道交通智能化研究者及深度学习实践者用于训练和评估轨道区域与障碍物的目标检测模型可支撑列车前方障碍预警、轨道巡检自动化等场景。资源包共2000个文件以1995张jpg图像为主体另含3个json标注文件和2个txt说明文件压缩包约471.42MB图像均采用COCO格式标注共3900张原始图片便于直接接入YOLO、Detectron2等主流检测框架。据描述模型识别准确率可达93.7%覆盖火车轨道与障碍物两类核心目标适合作为算法验证与迁移学习的基线数据。目前已有2063人学习下载具备一定社区验证基础。读者可获得完整标注数据、清晰的目录组织以及可复现的训练素材快速开展轨道检测实验、对比模型性能并迭代优化检测精度。1. 火车轨道检测数据集3900 张 COCO 标注图能撑起什么铁路巡检这件事真正难的不是把摄像头架上去而是让模型在复杂背景下把轨道和障碍物分开。轨道本身是两条细长、低对比度的线遇到道砟、阴影、雨渍、枕木纹理边缘很容易糊成一团障碍物又可能是落石、倒伏树枝、施工遗留物、行人尺度跨度极大。我拿到这个标题的第一反应是3900 张 COCO 标注的原始图片标称识别准确率 93.7%这套火车轨道检测数据集到底能不能直接用于训练自己的模型还是只能当个演示样本先说结论3900 张在目标检测里属于中小规模但如果标注质量过关、场景覆盖够杂它完全能撑起一个可用的轨道与障碍物检测基线。关键在于你怎么用它——是直接拿来做迁移学习的起点还是当成验证自己数据管线的试验田。COCO 格式意味着标注是 JSON 结构包含 images、annotations、categories 三段bbox 用 [x, y, width, height] 绝对坐标这对熟悉 YOLO 系列、MMDetection、Detectron2 的人来说几乎没有转换门槛。93.7% 这个数字要拆开看它大概率是在特定测试集上的 mAP 或准确率换到你的线路、你的相机、你的光照条件掉十几个点很正常。这篇笔记就按「数据集怎么读 → 怎么转格式 → 怎么训练 → 怎么避坑 → 怎么验证」的顺序把这条链路走一遍。2. 拆开 COCO 标注3900 张图里到底有什么2.1 COCO 的 JSON 结构与轨道检测的类别设计COCO 格式的核心是一个大 JSON 文件顶层通常有四个键info、images、annotations、categories。images里每条记录包含id、file_name、width、heightannotations里每条包含id、image_id、category_id、bbox、area、iscrowdcategories定义类别名和 id 的映射。对火车轨道检测来说类别设计通常分两类思路一类是「轨道 障碍物」二分类简单直接另一类是把障碍物再细分为落石、树枝、人员、车辆等。标题里说的是「可识别火车轨道和障碍物」我倾向于至少保留三个类rail、obstacle、background背景类不参与训练只用于负样本管理。先写一段脚本把 JSON 读进来看看类别分布和 bbox 尺度分布这一步决定了后面 anchor 怎么设、图像要不要切分。import json import numpy as np from collections import Counter # 加载 COCO 标注文件假设文件名为 annotations.json with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) # 类别映射category_id - name cat_map {c[id]: c[name] for c in coco[categories]} print(类别列表:, cat_map) # 统计每个类别的标注数量 cat_counter Counter(ann[category_id] for ann in coco[annotations]) for cid, cnt in cat_counter.items(): print(f类别 {cat_map[cid]}: {cnt} 个标注) # 统计 bbox 宽高分布判断目标尺度 ws, hs [], [] for ann in coco[annotations]: _, _, w, h ann[bbox] ws.append(w) hs.append(h) ws, hs np.array(ws), np.array(hs) print(bbox 宽度 中位数/均值/最大:, np.median(ws), ws.mean(), ws.max()) print(bbox 高度 中位数/均值/最大:, np.median(hs), hs.mean(), hs.max()) # 统计每张图的标注数量判断是否有多目标密集场景 img_ann_cnt Counter(ann[image_id] for ann in coco[annotations]) counts list(img_ann_cnt.values()) print(每图标注数 最小/中位/最大:, min(counts), np.median(counts), max(counts))这段代码的逻辑很直白先建立类别 id 到名字的映射避免后面看到数字不知道对应什么然后统计每个类的标注量如果obstacle只有几百个而rail有上万就说明类别不平衡训练时要么加权采样要么用 focal loss。bbox 宽高分布用来判断目标尺度——轨道通常是极宽极扁的框障碍物则接近方形这两类目标的 anchor 设计要分开考虑。每图标注数则告诉你场景密度如果中位数只有 2 到 3 个说明大部分图是稀疏场景模型容易学到「大部分区域是背景」的先验部署到密集场景会漏检。参数上iscrowd字段要特别注意。COCO 里iscrowd1表示该区域是密集人群或难以逐个标注的群体训练时通常忽略或单独处理。轨道检测里如果遇到大面积遮挡的障碍物群标注者可能标了iscrowd你在转格式时要决定是丢弃还是保留为一个大框。2.2 从 COCO 转 YOLO 格式脚本与四个边界坑YOLO 系列要求每张图对应一个.txt文件每行是class_id x_center y_center width height全部归一化到 0 到 1。转换本身不难但边界条件容易翻车。我一般会写一个带校验的转换脚本把越界框、零面积框、类别 id 不连续的问题一次性暴露出来。import json import os from pathlib import Path def coco_to_yolo(coco_json, img_dir, out_dir): with open(coco_json, r, encodingutf-8) as f: coco json.load(f) # 建立 image_id - 文件信息 的索引 img_info {img[id]: img for img in coco[images]} # 建立 category_id - 连续 class_id 的映射 cat_ids sorted(c[id] for c in coco[categories]) cat2cls {cid: idx for idx, cid in enumerate(cat_ids)} # 按 image_id 聚合标注 ann_by_img {} for ann in coco[annotations]: ann_by_img.setdefault(ann[image_id], []).append(ann) os.makedirs(out_dir, exist_okTrue) skipped 0 for img_id, anns in ann_by_img.items(): info img_info[img_id] W, H info[width], info[height] lines [] for ann in anns: x, y, w, h ann[bbox] # 坑1零面积或负面积框直接丢弃 if w 1 or h 1: skipped 1 continue # 坑2越界框裁剪到图像范围内 x1, y1 max(0, x), max(0, y) x2, y2 min(W, x w), min(H, y h) if x2 x1 or y2 y1: skipped 1 continue # 归一化中心点与宽高 xc (x1 x2) / 2.0 / W yc (y1 y2) / 2.0 / H nw (x2 - x1) / W nh (y2 - y1) / H cls cat2cls[ann[category_id]] lines.append(f{cls} {xc:.6f} {yc:.6f} {nw:.6f} {nh:.6f}) # 坑3没有有效标注的图也要生成空 txt否则训练时索引错位 txt_path Path(out_dir) / (Path(info[file_name]).stem .txt) with open(txt_path, w) as f: f.write(\n.join(lines)) print(f转换完成跳过无效框 {skipped} 个) coco_to_yolo(annotations.json, images, labels)逻辑说明先建立image_id到图像信息的索引这样每张图的宽高随手可得类别 id 重新映射成从 0 开始的连续整数因为 YOLO 要求 class_id 从 0 开始且连续COCO 的 category_id 经常是跳跃的比如 1、3、7。按图聚合标注后逐框处理四个坑分别是零面积框、越界框、空标注图、类别 id 不连续。空标注图必须生成空 txt 文件否则 YOLO 在按文件名匹配时会找不到对应标签直接报错或静默跳过导致训练集和验证集划分错乱。参数上w 1 or h 1这个阈值可以按需调整有些标注工具会留下 0.5 像素的噪声框直接丢弃比强行保留更干净。归一化保留 6 位小数足够YOLO 内部会再处理。转换完建议跑一遍校验统计生成的 txt 行数是否等于有效标注数随机抽几张图用可视化脚本画框确认坐标没偏移。3. 用 YOLOv8 训练轨道检测模型配置、参数与显存账3.1 数据集 YAML 与训练命令的最小闭环YOLOv8 的数据集配置是一个 YAML 文件指定训练、验证、测试集的路径和类别名。3900 张图按 7:2:1 划分大约 2730 张训练、780 张验证、390 张测试。如果某些场景类别在验证集里太少可以做分层抽样保证每个类在三个集合里都有代表。# rail_dataset.yaml path: /data/rail_dataset train: images/train val: images/val test: images/test names: 0: rail 1: obstacle训练命令用 YOLOv8 的 CLI 或 Python API 都行我习惯用 Python 脚本方便记录参数和回调。from ultralytics import YOLO # 加载预训练权重做迁移学习 model YOLO(yolov8s.pt) results model.train( datarail_dataset.yaml, epochs120, imgsz640, batch16, device0, workers4, optimizerAdamW, lr00.001, lrf0.01, warmup_epochs3, cos_lrTrue, patience30, augmentTrue, mosaic1.0, mixup0.1, hsv_h0.015, hsv_s0.7, hsv_v0.4, fliplr0.5, flipud0.0, degrees0.0, translate0.1, scale0.5, save_period10, projectruns/rail, nameexp1 )逻辑说明yolov8s.pt是中小模型在 3900 张图上比yolov8n更稳又比yolov8m省显存。imgsz640是默认值但轨道是细长目标如果原始图片分辨率很高比如 1920×1080直接缩到 640 会让轨道变成几个像素宽建议要么用imgsz1280要么把大图切块训练。batch16在 8GB 显存上跑 640 分辨率基本够用如果开 1280 就要降到 4 或 8。optimizerAdamW配合cos_lrTrue余弦退火在小数据集上比 SGD 收敛更平滑。patience30表示 30 轮验证指标不提升就早停避免过拟合。数据增强里fliplr0.5水平翻转对轨道检测是安全的因为轨道左右对称但flipud必须设为 0垂直翻转会让轨道出现在天上语义完全错误。degrees0.0也是同理旋转会让轨道的透视关系失真。mosaic1.0和mixup0.1能提升小目标召回但 mixup 太强会让轨道和障碍物的边界变模糊0.1 是个保守值。3.2 轨道细长目标的 anchor 与损失调参YOLOv8 是无 anchor 的直接预测中心点和宽高但损失函数里分类损失和回归损失的权重仍然影响很大。轨道这类极宽极扁的目标CIoU 损失在宽高比差异大时梯度不稳定可以换成 SIoU 或 EIoU。在 YOLOv8 的配置里可以通过修改box损失的 gain 来调整。# 在训练前覆盖损失权重 model.train( datarail_dataset.yaml, epochs120, box7.5, # 回归损失增益默认 7.5轨道细长可提到 8.5 cls0.5, # 分类损失增益默认 0.5 dfl1.5, # 分布焦点损失增益默认 1.5 # ... 其他参数同上 )box增益提高意味着模型更关注框的位置精度适合轨道这种对定位要求高的目标。但提太高会导致分类置信度下降障碍物漏检。我一般先在默认值上跑 20 轮看 loss 曲线如果box_loss下降慢而cls_loss已经很低就适当提box反之如果障碍物误报多就提cls。另一个关键是正样本分配。YOLOv8 用 TaskAlignedAssigner根据分类和回归的联合分数分配正样本。轨道框很扁中心点落在轨道上的像素很少如果topk设得太小正样本不够召回上不去。可以在model.train里通过overlap_mask和mask_ratio间接影响但更直接的办法是增大imgsz让轨道在特征图上有更多像素。显存账要算清楚640 分辨率、batch 16、yolov8s大约占 6 到 7GB如果开 1280batch 要降到 4否则 OOM。训练时间上单卡 3090 跑 120 轮大约 3 到 4 小时3900 张图不算大但增强后的 epoch 时间会拉长。4. 避坑与排查轨道检测训练里最容易翻车的五件事4.1 现象验证集 mAP 很高实拍视频漏检严重原因训练集和验证集来自同一批图片的随机划分场景高度相似模型学到了「这条线路的轨道长这样」而不是「轨道的一般特征」。3900 张图如果只覆盖少数几个路段、几种光照泛化能力会很差。解决按场景划分数据集比如按路段、按天气、按时间段分组确保验证集里有训练集没见过的场景。如果数据不够至少做一次「留一线路交叉验证」每次留一条线路做验证其余训练看 mAP 波动有多大。波动超过 10 个点说明场景偏差严重需要补数据。4.2 现象轨道框预测得断断续续一条轨道被拆成好几段原因轨道在图像里是连续的但标注时可能因为遮挡、阴影被分成多个框。模型学到的是「有标注的地方才是轨道」没标注的连续区域被当成背景。另外NMS 的 IoU 阈值如果设得太低相邻的轨道框会被互相抑制。解决检查标注是否连续遮挡处是否应该合并成一个框。训练时把 NMS 的 IoU 阈值从默认 0.7 提到 0.8 或 0.9减少误抑制。推理时可以用agnostic_nmsFalse保留类别信息避免轨道和障碍物互相抑制。4.3 现象障碍物类别召回率极低几乎全漏原因类别不平衡。轨道标注可能有上万条障碍物只有几百条模型倾向于把所有区域预测成轨道或背景。另外障碍物的定义模糊标注者可能把一些小石块、阴影也标成障碍物噪声标签让模型学不到稳定特征。解决对障碍物类别做过采样或者在损失里给障碍物更高的cls权重。更彻底的办法是两阶段先检测轨道区域再在轨道区域内做障碍物分类缩小搜索范围。标注规范也要统一明确什么算障碍物、最小尺寸是多少。4.4 现象训练 loss 震荡mAP 忽高忽低原因学习率太大或者 batch size 太小导致梯度噪声大。3900 张图如果 batch 只有 4 或 8BN 层的统计量不稳定loss 会跳。另外数据增强太强比如 mosaic 加 mixup 加旋转会让每轮看到的分布差异过大。解决先把lr0降到 0.0005warmup_epochs提到 5让模型慢慢进入状态。batch 尽量开到显存允许的最大值如果只能开 4就用梯度累积模拟大 batch。增强策略先关掉 mixup 和旋转只留翻转和色调抖动等 loss 稳定后再逐步加回。4.5 现象推理时框的位置整体偏移或者框比实际大一圈原因COCO 转 YOLO 时坐标归一化用错了宽高比如把x w当成了右下角但实际标注是[x, y, w, h]且x, y是左上角。或者推理时imgsz和训练时不一致letterbox 的填充比例没还原。解决转格式后随机抽 10 张图用 OpenCV 画框和原图叠加肉眼确认。推理时确保imgsz和训练一致YOLOv8 的predict会自动处理 letterbox但如果自己写后处理要记得把坐标映射回原图尺寸。检查conf阈值太低会出很多小框看起来像偏移。5. 验证 93.7% 这个数字分层评估与部署前检查标称 93.7% 的准确率我一般不会直接信而是自己跑一遍分层评估。把测试集按场景分组——白天、夜间、雨天、弯道、直线、有障碍物、无障碍物——分别算 mAP50 和 mAP50-95看哪个场景掉得最狠。轨道检测里夜间和雨天通常是重灾区如果这两个场景的 mAP 低于 70%那 93.7% 大概率是白天直线场景的数字部署到全线会翻车。from ultralytics import YOLO import json model YOLO(runs/rail/exp1/weights/best.pt) # 按场景分组评估假设测试集有 scene 标签文件 scenes { day: [img_001.jpg, img_002.jpg], night: [img_101.jpg, img_102.jpg], rain: [img_201.jpg, img_202.jpg], } for scene, imgs in scenes.items(): results model.val(datarail_dataset.yaml, splittest) # 实际使用时可以用 model.predict 逐图推理再按场景聚合指标 print(f场景 {scene} 的 mAP50: {results.box.map50:.4f})更细的做法是用model.predict逐图输出 TP、FP、FN再按场景聚合精确率、召回率、F1。精确率和召回率的取舍取决于业务巡检场景宁可误报也不能漏报所以召回率优先conf阈值可以降到 0.2 到 0.3让更多候选框进入后处理再用规则过滤。如果误报太多导致人工复核成本高就提conf到 0.5 以上牺牲召回换精确。部署前还要做三件事一是用不同分辨率的视频流测试看模型对缩放是否鲁棒二是测推理速度640 分辨率下 yolov8s 在 T4 上大约 5 到 8ms 一帧如果要做实时检测帧率要匹配三是检查类别 id 映射训练时的names和部署时的解析必须一致否则会把轨道标成障碍物。我自己的习惯是任何标称准确率超过 90% 的模型先在自己的验证集上跑一遍掉点不超过 5 个才考虑上线。轨道检测这件事数据比模型重要3900 张图如果场景覆盖够广配上合理的增强和调参完全能做出可用的基线。希望帮到你。本文还有配套的精品资源点击获取
返回列表