ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YOLOv5工业视觉实战:钢轨缺陷检测系统代码全解析

YOLOv5工业视觉实战:钢轨缺陷检测系统代码全解析

我们做工业视觉的人,其实天天都在和"脏、乱、差"的数据打交道。钢轨表面缺陷检测就是其中特别典型的一个场景——目标小、形态杂、环境光干扰大、误报代价又高。这几年深度学习方案逐步落地,YOLOv5因为工程生态成熟、部署链路短,成了很多团队的第一选择。这篇内容我把自己拆过的项目代码完整过一遍,从工程结构、训练逻辑到推理部署的坑,尽量讲得细一些。如果你正打算做类似的工业检测项目,或者拿到一份YOLOv5检测代码但看得一头雾水,这篇应该能帮你节省不少摸索时间。

先说清楚这个系统到底解决什么问题。钢轨在轧制和服役过程中,表面会出现裂纹、划伤、结疤、麻点、掉块等缺陷,这些缺陷如果漏检,轻则影响钢轨寿命,重则直接威胁行车安全。传统的人工目视检查效率低、漏检率高,而基于深度学习的视觉检测系统,可以架在产线或巡检车上,对钢轨表面图像做实时分析,直接输出缺陷类别和位置信息。YOLOv5作为当前最成熟的一阶段目标检测算法之一,在精度和速度之间取得了很好的平衡,非常适合这种既要检得准、又要跑得快的工业场景。

这套系统的代码量不算大,但麻雀虽小五脏俱全。核心包括数据加载与增强、模型结构构建、损失函数计算、训练流程控制、测试评估,以及最后的模型导出和推理预测。理解这套代码,本质上就是理解一条完整的算法落地流水线。我下面按照一个典型项目的标准结构,逐个模块给你拆开讲。

1. 系统整体设计与代码结构拆解

1.1 为什么工业落地首选YOLOv5

先聊几句选型逻辑。工业项目选模型,我们一般不看谁刷榜刷得高,而是看三件事:第一,能不能在产线的硬件条件下跑实时,钢轨检测通常要求处理速度不低于30FPS,老旧的工控机甚至只有CPU推理,YOLOv5的轻量级模型配合TensorRT加速完全够用。第二,技术栈熟不熟,团队里随便一个工程师都读过YOLOv5源码,遇到问题好排查,这点在项目维护期尤其重要,因为工业项目的痛点从来不是"模型跑不起来",而是"模型跑起来了但没人会改"。第三,工程生态是否完整,YOLOv5自带的超参数进化、多种数据增强、自动锚框计算、模型剪枝蒸馏工具链,基本覆盖了从训练到部署的所有环节。

对比当时其他方案,Faster R-CNN精度上限可能略高,但推理速度在工业场景下有点吃紧;EfficientDet系列调参复杂度高,训练慢;YOLOv5则是一个"上手就能跑、踩坑也有人陪"的方案。所以很多做钢轨缺陷检测的团队,比如我接触过的几个项目组,第一版都是拿YOLOv5源码改的,先跑通流程,再针对缺陷数据做定向优化。

1.2 工程目录结构与模块职责

一套规范的YOLOv5钢轨缺陷检测工程,目录结构大致长这样:

RailDefect_YOLOv5/ ├── data/ # 数据集配置与标签管理 │ ├── defect.yaml # 类别定义、数据路径 │ └── rail_defect/ # 原始图像与标注文件 ├── models/ # 模型结构定义 │ ├── yolov5s.yaml # 小模型配置 │ ├── yolov5m.yaml # 中等模型配置 │ └── common.py # 基础模块实现 ├── utils/ # 工具函数库 ├── train.py # 训练入口 ├── detect.py # 推理入口 ├── val.py # 评估入口 └── export.py # 模型导出脚本

不要小看这个目录结构,它本身就是一套最佳实践。data、models、utils三者解耦,意味着你可以不动模型代码就更换数据集,也可以不动数据逻辑就切换模型规模。钢轨缺陷检测项目在迭代过程中,最频繁的改动就是加类别、调数据、换模型结构,这套结构能让每次改动的影响范围最小化。

1.3 系统工作流程全景

整个系统运行起来是个什么流程?我用最直白的话给你串一遍。训练阶段,你的钢轨表面图像和对应的缺陷标注框会被送入数据加载器,加载器做随机增强(翻转、旋转、色彩抖动、马赛克增强),增强后的图片进入YOLOv5模型前向传播,输出预测的边界框和类别概率,然后与真实的标注框计算损失,反向传播更新权重。这个过程反复进行,直到损失收敛。推理阶段就简单了,一张钢轨图像输入模型,经过归一化处理后前向推理,输出原始检测框,再用NMS(非极大值抑制)去掉重复框,最后把置信度满足阈值的检测框绘制在图上并输出缺陷类别与坐标。

这套流程里的每一个环节都有不少工程细节,尤其是数据增强和损失计算这两块,直接决定了钢轨缺陷检测的最终精度,后面的章节我逐步展开。

2. 数据模块的精髓:钢轨缺陷数据的加载与增强

2.1 钢轨缺陷数据的痛点与应对

先摸清数据底牌。钢轨表面缺陷数据有几个让人头疼的特征:一是缺陷目标尺寸差异巨大,有的裂纹宽度只有几个像素,有的掉块能占到半幅图像,YOLOv5的多尺度特征融合就是为此设计的;二是正负样本极度不平衡,一张正常的钢轨图像里往往只有一小块区域是缺陷,背景占了绝大多数;三是缺陷形态与背景纹理相似,比如轻微划伤在灰度上和正常的轧制纹路几乎一样,这也是钢轨缺陷检测普遍比通用目标检测难的重要原因。

数据处理层面,我们首先要规范标注格式。YOLOv5使用的是YOLO格式的txt标注文件,每一行对应一个目标,格式为"类别id 中心点x 中心点y 框宽度 框高度",四个坐标值都是相对于图像宽高的比例。举个例子,一张1280x720的图像上,某个裂纹目标的左上角在(512, 360)、右下角在(640, 720),那么中心点坐标就是(576, 540),归一化后是(0.45, 0.75),宽高归一化后是(0.1, 0.5),标注文件里就是"0 0.45 0.75 0.10 0.50"。

注意:标注坐标归一化这一点极其重要,很多人漏检问题最后查出来是标注坐标换算错误。建议在数据准备阶段就写一个可视化脚本,把标注框画到图上人工检查。可视化这一步绝对不能省,否则错误会潜移默化地影响所有后续结果。

2.2 数据集配置文件的编写要点

看一段典型的defect.yaml:

train: ../data/rail_defect/train.txt val: ../data/rail_defect/val.txt nc: 4 names: ['crack', 'scratch', 'shelling', 'dropped_block']

nc表示缺陷类别数,这里我有四个类别,分别是裂纹、划伤、剥落、掉块。需要提醒的是,类别名称尽量用具体的英文单词,避免用class1这种无意义命名。你后面做错误分析时,一张图看到crack和看到class2的感受完全不同。

train.txt和val.txt里存的是所有训练和验证图片的绝对路径或相对路径,一行一张图。YOLOv5源码会根据图片路径自动找到同名的txt标注文件。这里又有个容易踩的坑:训练集和验证集划分的时间点要在第一次训练前就确定好,而且一旦确定就不要再动,否则验证集"漏题"会导致精度虚高,到了部署现场原形毕露。

2.3 数据增强策略的选择与调参

YOLOv5自带一套相当复杂的数据增强流水线,在训练时实时执行。核心增强包括马赛克增强、随机仿射变换、HSV色彩空间扰动、随机水平翻转。马赛克增强是把四张图拼成一张大图再随机裁剪,这个策略对小目标检测效果非常好,因为相当于把多张图的信息浓缩到一张图里,变相增加了batch size。钢轨缺陷目标很多是小目标,所以默认开启马赛克增强是合理选择。

但马赛克增强在训练后期建议关闭。YOLOv5源码中有自适应机制,在最后几个epoch会自动关闭马赛克增强,原因很简单——马赛克产生的图片和真实场景分布有偏差,后期训练需要"回归真实",否则模型学到的特征会带着人为拼接痕迹。

HSV扰动对钢轨检测有特殊意义。不同钢厂、不同光照条件下的钢轨表面颜色差异很大,合理的色彩抖动范围可以提升模型的鲁棒性。但要注意,如果缺陷形态比较依赖颜色纹理(比如结疤区域的颜色发暗),过强的色彩扰动反而会模糊掉关键特征。我给几个常用配置参考:

增强参数推荐范围说明
hsv_h0.01-0.02色相扰动,不宜过大
hsv_s0.5-0.7饱和度扰动
hsv_v0.3-0.5亮度扰动,兼顾暗光场景
degrees5-15旋转角度,过大会改变缺陷方向统计
translate0.1-0.2平移幅度
scale0.5-1.5缩放范围,模拟远近场景
fliplr0.5水平翻转概率

我习惯把scale范围调大一些,因为钢轨检测相机安装高度、角度各异,模型需要能适应不同尺度的缺陷。但translate不建议调太大,钢轨表面图像中缺陷通常不会偏离中心太远,过大的平移会引入大量无效背景。

2.4 从零开始训练时的锚框计算

锚框(anchor)是目标检测里绕不开的概念。简单理解,锚框就是一组预设的初始包围盒,模型基于它们做修正预测。YOLOv5有两个模式:如果数据集是标准COCO这类公开数据集,直接用预置锚框即可;如果是钢轨缺陷这种自定义数据集,建议开启自动锚框计算。

在训练命令中加上--noautoanchor可以关闭自动计算,但如果关闭,模型就要从COCO的锚框出发去适配你的缺陷数据,相当于一步没走对,后面全靠模型硬扛。我建议第一次训练时务必让YOLOv5自动计算锚框,源码会使用K-Means聚类算法分析你的标注框尺寸分布,生成适合当前数据集的锚框组合。

实操中还有个经验:如果数据集特别小(比如只有几百张),自动锚框计算出来的结果可能不稳定,此时可以手动收集所有标注框的宽高,画一个散点图,观察分布是不是集中。通常钢轨缺陷标注框会集中在几个长宽比上,比如裂纹是长条形的,宽高比很大;掉块是块状的,接近正方形。锚框计算本质是在拟合这个分布。

3. 模型结构解析:YOLOv5检测钢轨缺陷的核心原理

3.1 从配置YAML看模型整体架构

YOLOv5的模型结构定义藏在models/yolov5s.yaml这类配置文件里。我看到很多初学者一打开YAML就懵了,其实你只需要抓住三个部分:backbone(骨干网络)、head(检测头)、nc(类别数)。配置文件里的数字列表分别代表每层的输入通道数、输出通道数、模块类型和参数。

以yolov5s为例,backbone部分由Focus结构、CSP模块和SPP模块组成。Focus模块通过切片操作将输入图像的尺寸缩小四分之一、通道数扩展四倍,本质上是一种无损下采样。CSP模块(Cross Stage Partial)将特征分成两路,一路经过卷积处理,一路直接连接,最后拼接合并,这种设计可以减少计算量的同时保持特征表达能力。SPP模块使用不同尺寸的池化核提取多尺度特征,对钢轨缺陷这种多尺度目标的检测非常关键。

head部分采用PANet(Path Aggregation Network)结构,自底向上和自顶向下两条路径融合多层特征。为什么要做特征融合?因为不同尺寸的缺陷需要不同的特征分辨率——大掉块在浅层特征中细节更多,小裂纹在深层语义特征中更容易被识别。PANet把浅层的边缘纹理信息和深层的语义信息融合起来,让模型在多个尺度上都有足够的判别能力。

3.2 不同模型规模的选型思考

YOLOv5系列提供了n/s/m/l/x五档规模,从轻到重。钢轨缺陷检测用哪个?我的建议是分阶段考虑。

  • 快速验证阶段用s甚至n,先把流程跑通,确认数据标注和训练链路没问题;
  • 精度优化阶段用m或l,通过提升模型容量来压精度;
  • 部署落地阶段回到s或n,配合TensorRT量化压缩,保证产线上的实时性。

这个顺序其实就是项目开发的真实节奏。不要一上来就上x大模型,训练慢、部署难、过拟合风险高。钢轨缺陷数据往往只有几千张,大模型在这个量级的数据上很容易直接把训练集的噪声背下来,验证集上表现不错,一到现场就露馅。

我在项目中实际对比过,yolov5s在钢轨缺陷验证集上mAP@0.5大概89%,yolov5m能到92%左右,yolov5l反而只提升了不到1%,却牺牲了将近一倍推理速度。这说明在这个任务上,yolov5m基本就是性能和效果的平衡点。当然这个数字只能说明特定数据集的相对趋势,你的数据分布不同结论可能不同。

3.3 配置文件深度定制

实际项目中nc、depth_multiple、width_multiple是你最常动的三个参数。depth_multiple控制网络层数的缩放比例,width_multiple控制每层通道数的缩放比例。yolov5s就是在这两个系数上取0.33和0.50,yolov5m取0.67和0.75。

如果钢轨缺陷的类别数和目标特征差异很大,可以手动微调检测头层的输出。举个例子,如果缺陷类别只有两类(裂纹和划伤),且你的计算资源非常紧张,可以在YAML里把head部分最后几层的输出通道数改小,减少模型参数量。但我不建议上来就魔改结构,先跑通默认配置,再逐步裁剪。

3.4 预训练权重的关键作用

YOLOv5官方提供了在COCO数据集上预训练的权重,后缀为.pt文件。训练时加上--weights yolov5s.pt,代码会自动加载预训练权重,再在你的钢轨缺陷数据上微调。这个策略叫迁移学习,原理是预训练模型已经学会了通用的底层特征(边缘、纹理、形状),你只需要在高层特征上适配缺陷类别。

千万别小看这一步。从零训练一个目标检测模型需要海量数据和大量算力,而迁移学习可以在几千张缺陷图上就训出可用模型。我试过同样条件下,加载预训练权重比从零训练收敛快3倍以上,最终mAP高5到8个百分点。原因很好理解,钢轨缺陷的"裂纹"特征和COCO数据集里"裂缝""树枝"等类别的边缘纹理特征有一定的底层相似性,预训练权重相当于给了模型一个很好的起点。

4. 训练核心代码逻辑:损失计算与训练策略

4.1 损失函数的构成与优化目标

YOLOv5的损失函数由三部分组成:分类损失、置信度损失、边界框回归损失。训练时模型的输出会与真实标注计算这三类损失并反向传播。我分别展开说。

分类损失用的是二分类交叉熵(BCEWithLogitsLoss),对每个锚框、每个类别计算预测概率与真实标签的交叉熵。钢轨缺陷检测中,各类别外观相似度较高,分类损失会引导模型学会区分细微纹理差异。

置信度损失是判断"这个锚框里有没有目标",同样使用BCE损失,但有个重要细节:YOLOv5对无目标的锚框会乘以一个权重系数(默认0.4)来减小背景样本的影响。钢轨缺陷场景背景占比极大,这个系数对训练稳定性影响很大,数值太小会让模型对背景过于敏感,太大则会让模型忽视真正的缺陷。

边界框回归损失计算预测框与真实框之间的CIoU(Complete IoU)损失,CIoU在IoU基础上考虑了中心点距离和长宽比,对精确定位特别重要。钢轨缺陷检测对定位精度的要求比通用目标检测高得多,比如裂纹的边界如果偏差几个像素,就可能影响后续的修复决策,所以CIoU损失的实际贡献非常关键。

4.2 训练超参数的选择与调整

钢轨缺陷检测训练中最关键的超参数包括初始学习率、batch大小、训练轮数和权重衰减。

先聊batch size。它决定了每一步训练看到多少样本。工业数据量小的情况下,batch size通常设在8到32之间。batch太小(比如2),梯度更新方向抖动剧烈,训练不稳定;batch太大(比如128),显存和内存占用高,而且小数据下容易陷入过拟合。我用12GB显存的卡训练yolov5m,batch设16比较合适。

学习率方面,YOLOv5默认初始学习率是0.01,但实际训练中我会建议用0.001到0.005起步。因为钢轨缺陷数据量小,偏高的初始学习率容易让模型在预训练权重附近剧烈震荡,把一个好起点摔得粉碎。配合余弦退火调度器,训练后期学习率逐渐降到很低的值,做精细收敛。

训练轮数的选择要看曲线,而不是拍脑袋。我一般会在tensorboard里盯三个指标:训练loss、验证loss、验证mAP。当验证loss停止下降甚至回升时,说明模型开始过拟合,立即停止训练就行。完整项目里钢轨缺陷数据大概3000张,yolov5m训练100到150个epoch就能收敛,再多也没意义。

4.3 类别不平衡问题的处理策略

钢轨缺陷数据里各类别的样本量往往差异很大,比如划伤可能有三万张,掉块只有五百张。直接训练的话,模型会严重偏向样本多的类别。处理方案有几个层次:

第一层是数据层面,最简单的做法是做样本复制(过采样),把掉块这类小类别的样本重复复制几份,让它在训练中出现的频率提高。第二层是数据增强层面,对小类别样本加强增强力度,变相扩充样本量。第三层是损失函数层面,YOLOv5的BCE损失自带pos_weight参数,可以给小类别设置更高的正样本权重,让模型更加关注它们。

我实测下来,第一层和第三层组合使用效果最稳定。单纯过采样容易导致模型对小类别过拟合;单纯调损失权重又容易让训练不稳定。先把小类别所有样本完整复制一遍,让它们在数据流中出现次数翻倍,再配合损失权重微调,基本能让各类别mAP趋于均衡。

4.4 训练日志分析与调优方向

训练过程中,tensorboard里会实时显示各类指标。我习惯关注三张曲线:Box损失曲线(边界框回归质量)、Cls损失曲线(分类质量)、Objectness损失曲线(目标检测置信度质量)。三条曲线如果都在稳定下降,说明训练一切正常。

如果发现Box损失降低但Cls损失居高不下,大概率是类别特征太相似,需要检查标注是否存在大量错标,或者考虑增加数据多样性。如果Objectness损失一直不降,说明模型无法区分前景和背景,这种情况常见于缺陷对比度极低的场景,需要对图像做预处理增强(比如直方图均衡化)来提升缺陷与背景的差异。

调优方向不要凭感觉来,我推荐用对比实验的方式,每次只改一个变量,记录结果。比如这轮试验把马赛克增强关闭,下轮试验把损失权重系数调低,对比训练曲线和mAP,用数据说话而不是靠猜。

5. 推理与部署完整流程:从模型导出到产线接入

5.1 模型导出与格式选择

训练完成后,你手头是一份.pt格式的PyTorch权重文件,这对部署来说不够高效。我们需要把它导出成更适合推理的格式,常见选择有ONNX和TensorRT。YOLOv5提供了torch.hub导出工具,一行命令就可以完成导出。

ONNX是开放神经网络交换格式,好处是跨框架、跨硬件。TensorRT则是针对NVIDIA GPU做的极致优化,能将模型推理速度提升2到5倍。在钢轨检测这种对实时性要求很高的场景,TensorRT基本上是标配选项。

导出时有个关键参数叫--simplify,对应onnx-simplifier,可以简化计算图消除冗余算子。这个参数务必加上,它能减少模型大小并提升兼容性。另外,如果模型里有动态shape操作(比如不同尺寸的输入图像),导出时要么固定输入尺寸,要么设置动态维度,否则部署时容易踩位置错乱的坑。

5.2 推理脚本的关键过程拆解

看YOLOv5的detect.py源码,推理过程核心是这几步:

# 图像预处理 img = letterbox(img, new_shape=(640, 640))[0] # 等比例缩放+填充 img = img.transpose((2, 0, 1))[::-1] # HWC转CHW,BGR转RGB img = np.ascontiguousarray(img)

letterbox是必须理解的函数,它会将输入图像等比例缩放到目标尺寸,剩余区域用灰色填充。为什么要这样做?因为模型输入尺寸是固定的640x640,但产线相机采集的图像尺寸可能千差万别,直接拉伸会导致目标变形、降低检测精度。letterbox通过填充保持原始宽高比,是工程部署中几乎必用的处理方式。

推理输出经过NMS(非极大值抑制)后,得到最终的检测框列表,然后根据类别阈值过滤掉置信度低的框。NMS的作用是解决同一个目标被多个锚框同时检测到的问题,它先按置信度排序,保留最高置信度的框,再删除与该框IoU大于阈值的其他框。这个IoU阈值默认是0.45,如果钢轨缺陷互相靠近重叠,可以适当调高到0.6,避免漏检。

5.3 可视化输出与结果保存

detect.py运行结束后,会在runs/detect/目录下生成标注好的图像。包含检测框、置信度和类别名。我建议在项目交付时,把输出格式改成JSON,除了坐标信息外还要带上检测耗时、图像ID等元数据,这样方便上游系统对接。

还有一点很多人忽略:检测结果的可视化最好带上类别对应的中文名称映射。比如模型输出crack,展示时转成"裂纹",工程师和一线维护人员看着更直观。这类小细节在项目验收时往往比模型精度更让人满意。

5.4 部署到产线:CPU与GPU场景的取舍

钢轨检测的部署环境大致分两类。一类是产线固定工位,有稳定的GPU服务器,这种直接上TensorRT,延迟可以压缩到毫秒级。另一类是巡检小车或便携设备,环境复杂,可能是CPU或嵌入式设备,这时候要用轻量模型加量化压缩。

CPU上跑YOLOv5,我推荐用OpenVINO工具套件(针对Intel CPU)转换模型,或者使用ONNX Runtime直接推理。实测yolov5n在普通工控机CPU上可以跑到15到20FPS,基本满足低速巡检需求。如果用的是ARM架构的嵌入式设备(比如Jetson系列),TensorRT同样支持,但需要根据设备的具体GPU型号选择对应的TensorRT版本。

部署时有个我踩过的坑:TensorRT生成的引擎文件与GPU型号和驱动版本强相关,换机器必须重新生成,否则模型直接报错。这个问题在项目交付时尤其麻烦,建议把转换脚本一并交付,让现场工程师在新机器上重新生成引擎。

6. 模型评估体系:不只盯mAP

6.1 评估指标怎么看

YOLOv5用val.py来做验证集评估,核心输出指标包括mAP@0.5(IoU阈值为0.5时的平均精度)和mAP@0.5:0.95(IoU阈值从0.5到0.95等间隔采样后的平均精度)。后者更严格,也更贴近工业场景对定位精度的要求。

钢轨缺陷检测不能只看mAP。我会额外关注每一类别的单独AP。比如划伤类AP高但裂纹类AP低,说明模型在裂纹这种细长目标上还有明显短板。另外,Precision高但Recall低,表示模型漏检严重,这在安全相关场景(钢轨缺陷直接关联行车安全)往往比误报更不能接受。工程上我通常卡一个原则:Recall优先,Precision其次,宁可多报不可漏报,因为漏报的缺陷可能直接埋下安全隐患。

6.2 误报与漏报的根因分析

如果模型在某个钢种或光照条件下误报特别多,我会先检查是不是数据分布问题。具体做法是:从验证集中误报的图片里挑出代表性样本,分析模型把什么当成了缺陷。常见原因有几类:

一是缺陷标注人员标准不一致,同一形态的图像,一个人标成裂纹,另一个人标成划伤,模型在不同标注之间摇摆,就会产生误报。这类问题需要用标注评审会的方式解决,统一标注规范后重新训练。

二是背景纹理和缺陷过于相似,比如钢轨表面的轧制纹路和细裂纹几乎无法区分。这种情况下单靠YOLOv5难以根治,需要在前处理阶段加入针对性图像滤波,突出裂纹的灰度对比特征,或者用更细粒度分割模型做二次确认。

6.3 提升模型精度的实战路线

当基础模型训练完成后,提升精度有几个优先级较高的方向。第一是收集更多难例:从产线连续采集一段时间图像,专门挑出模型预测错误的样本,交给标注人员补充标注后加入训练集,这个迭代通常能带来最大的精度提升。第二是针对性数据增强:如果裂纹在特定光照下容易漏检,就模拟这种光照做数据增强。第三是做模型集成:多个不同初始化条件下训练的模型做加权投票,可以提升稳定性,但推理成本也成倍增加,产线落地时酌情考虑。

我个人的经验是,数据迭代永远是性价比最高的提升手段。模型结构大改一次可能耗时两三周,但一次高质量的数据扩充分类才是稳定提升精度最可靠的路径。

7. 实际调试遇到的典型问题与解决记录

7.1 训练损失降不下去怎么办

训练过程中最让人头疼的就是loss卡在某个值死活不降。排查顺序我建议按下面来:

先看数据。用可视化脚本随机抽几张训练图像,检查标注框是否和缺陷对齐精确。钢轨图像本身高分辨率,标注人员如果只标注了大致区域,框偏了几个像素,模型学习到的边界就会模糊,损失自然降不下去。

再看增强。马赛克增强在训练前期是帮助,但如果数据本身图像尺寸很大(例如2448x2048),马赛克把四张图缩小拼接,缺陷在拼接后可能只剩十几个像素,这个难度对模型来说太大了。这种情况可以把mosaic关闭或降低拼接概率。

最后看学习率。如果loss在大幅度震荡,试试降低学习率,或者用更小的batch。学习率过高在训练初期表现是loss不降反升或剧烈波动。

7.2 模型对现场图像检测效果差

模型在验证集上表现不错,一上产线就崩,这个问题的背后要么是数据分布不一致,要么是预处理流程不对。最典型的是图像尺寸:训练时如果输入是640x640,推理时却直接拿2448x2048的大图喂给模型,虽然letterbox会自动缩放,但如果代码里没有做,模型就傻了。另一个高频原因是色彩通道顺序:opencv读取的是BGR顺序,而PyTorch训练时通常用RGB,漏了转换的话,模型看到的图像颜色全变,缺陷特征被严重干扰。

钢轨表面在产线不同位置的光照差异很大,同一段钢轨在隧道内和室外阳光下可能差出几个曝光级别。建议在前处理流程中加入自适应直方图均衡化或灰度归一化,让模型看到的图像亮度分布保持稳定。

7.3 小目标缺陷漏检严重

钢轨表面的细小裂纹往往只有二三十个像素,这类目标在当前网络结构下很容易在多层下采样之后丢失。解决思路有几个方向:

第一个思路是提高输入分辨率,从640x640提升到960x960或1280x1280。这个是最直接的,但推理时间会同步增长,需要根据产线速度权衡。第二个思路是使用P2输出层,也就是在YOLOv5的head结构中加入更浅层的特征图,专门捕捉小目标。官方源码没有直接支持P2层,需要手工修改模型YAML,有一定工程量。第三个思路是在数据层面把小目标样本做针对性复制,让模型有更多机会学习小目标的特征。

我实际测试中,最有效的组合是"高分辨率输入+P2特征层",能把细小裂纹的Recall从70%拉到85%以上,代价是推理速度下降了大约40%。具体怎么选,关键看产线的节拍和可用算力。

7.4 过拟合的典型信号与对策

训练集loss持续下降,验证集loss却开始上升,这就是过拟合的标准信号。钢轨缺陷数据量小,这个问题非常容易暴露。

对策从容易到复杂排列:第一,加数据增强,尤其是色彩抖动和随机形变类增强,可以人为制造更多样化的训练样本。第二,降低模型容量,从yolov5m降到yolov5s,让模型没有足够能力背下训练集。第三,加正则化,YOLOv5支持--label_smoothing标签平滑参数,设为0.01可以稍微缓解过度自信,提升泛化能力。第四,提前停止训练,发现验证loss拐点就保存此权重,后面的epoch全白跑。

最根本的对策还是增加数据量,尤其是难例样本。把一个工业现场连续运行一周的数据收集下来,错检漏检的样本全是金子。

8. 个人实操体会与项目复盘心得

这套项目做下来,我个人体会最深的其实不是模型怎么改,而是怎么把"检测系统"这四个字落地。

算法层面,YOLOv5的成熟度让开发门槛低了不少,但钢轨缺陷本身的特殊性决定了通用方案必然要经过大量定制。比如同样的马赛克增强,别人在通用数据集上提升了2个点mAP,在钢轨数据上可能反而掉点。这类问题是藏在源码底下的,只有深入关键代码逻辑、分析自己数据的分布特点才能发现。

工程层面,最大的收获是必须把检测系统的输入处理、输出格式、硬件适配当成和模型同等重要的模块来对待。在开发周期中,有将近三分之一的时间花在了数据清洗和标注规范化上,这比调模型结构更花时间,但也更值得。

部署层面,我的建议永远是"在产线上跑通一个可用版本,再回来优化精度"。很多团队喜欢在实验室里追求极致精度,等到了现场发现推理延时超标、接口对接复杂、现场工程师不会操作,项目就僵住了。先让模型跑起来,哪怕最初精度只有80%,也要让整个链路里的人看到真实的检测效果。此时,大家的反馈会指出问题的方向,这比你自己闭门调模型有效得多。

最后再分享一个小技巧。钢轨缺陷检测这种场景,缺陷形态会随着钢种、产线工艺变化,没有任何模型训一次就能用一辈子。我建议在交付代码的时候,一定要把数据标注规范和模型迭代流程一并交付给客户的算法团队,让他们能在新钢种上线时自己完成数据扩充和模型更新。一套能自我迭代的系统,远比一个当前精度很高但改不动的系统值钱。

钢材轧制的所有表面质量问题,说到底都要靠检出来才能谈后续处理。这套检测系统的价值就在于此。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进