ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLOv6工业视觉检测优化与部署实战:从能检出到敢上线

YOLOv6工业视觉检测优化与部署实战:从能检出到敢上线

工业视觉检测这个圈子有个挺有意思的现象:实验室里 mAP 跑到 0.92 的模型,搬到产线上一周就被人投诉到停线。原因不复杂——产线根本不看你那张 PR 曲线,它只关心三件事:节拍能不能跟上、误检能不能压住、连续跑三十天会不会莫名漂移。YOLOv6 工业视觉检测优化与部署实战这个题目,讲的正是从"能检出"到"敢上线"这一段最脏最累的路。YOLOv6 作为一款面向工业落地设计的单阶段检测器,靠着可重参数化主干、解耦检测头和多尺寸模型族,在推理延迟和精度之间给了一个相对务实的平衡点,特别适合节拍在几十到一百多毫秒、目标尺寸中等偏小的缺陷检测场景。这篇内容主要写给两类人:一类是已经把 YOLO 系列跑通过、准备往产线推的算法工程师;另一类是负责视觉系统集成的现场工程师,需要知道模型交付后怎么接、怎么调、怎么保。我不会只讲架构,更多是把数据、训练、导出、量化、流水线这几段里真实会咬人的地方拆开讲。

1. 先搞清楚工业检测要什么:需求拆解与模型选型逻辑

1.1 产线真正卡脖子的指标,跟论文里的完全不是一回事

做学术评测的时候,我们盯的是 mAP@0.5:0.95,多一个点能高兴半天。但产线验收单上写的往往是另一套东西:单件检测时间不超过 120ms、过杀率低于 0.5%、漏检率低于 0.1%、连续运行 72 小时无异常退出、换型后 30 分钟内可完成重新标定。这几条里,只有第一条跟模型算力直接相关,剩下的几乎全是数据质量和工程稳定性问题。

我印象很深的一次,某产线做金属件表面划伤检测,模型在测试集上 mAP 0.89,信心满满上线,结果第一天过杀率 6%。现场排查发现,测试集是白天在恒温实验室采的,产线是夜班、车间温度高、镜头前有油雾附着,图像整体对比度掉了将近一档,模型把正常的加工纹理当成了划伤。这件事之后我形成了一个习惯:任何模型的评估集里,必须包含至少 20% 的"恶劣工况样本"——不同班次、不同温湿度、镜头脏污程度不同的图。否则 mAP 就是个自欺欺人的数字。

工业场景和通用场景的差异,我习惯用一张表来对齐认知,跟现场同事沟通时特别管用:

维度通用检测场景工业视觉检测
首要指标mAP过杀率、漏检率、节拍
类别数量几十到上千类通常 1 到 10 类,但类内差异极大
样本分布相对均衡极端不平衡,缺陷样本可能只占千分之几
目标尺寸差异大常集中在小目标区间,8 到 64 像素
数据获取成本低,可爬取极高,需要停产、布光、人工挑选
对误检的容忍较高极低,一次误判可能导致整批返工
迭代周期以月计以周甚至以天计,换型频繁

看这张表就知道,选模型不能只看 benchmark 排名,得看它在这个约束集合里能不能活下来。

1.2 YOLOv6 的架构取舍,为什么正好卡在这个位置

YOLOv6 的几处设计,恰好踩在工业检测的痛点上,值得单独说说。

第一处是可重参数化主干。训练阶段用多分支结构(类似 RepVGG 的思路)提升表达能力,推理阶段通过结构重参数化把多分支折叠成单路 3x3 卷积。这意味着你训练时享受多分支带来的精度红利,部署时拿到的是纯单路的、对推理引擎极其友好的计算图。对工业部署来说这点很关键——单路卷积在 TensorRT 里的融合效率最高,算子调度开销最小,也不会出现某些分支算子不被支持而被迫回退的情况。

第二处是解耦检测头。分类分支和回归分支分开,各自走独立的卷积。工业缺陷往往"长得像但位置差一点",分类和回归的关注点本来就不一样,解耦之后对小目标的定位精度提升明显。我在一个连接器针脚歪斜检测的项目里对比过,同样的数据和解码方式,解耦头比耦合头的定位误差中位数低了约 18%。

第三处是多尺寸模型族。YOLOv6 提供了 N/T/S/M/L 几档,参数量从两百万级到五千万级不等。这个梯度对工业选型太友好了:边缘盒子跑 N 或 T,工控机加中端卡跑 S 或 M,只有极少数高精度场景才需要 L。我一般的做法是先用 S 跑通全流程拿到基线指标,再往下降尺寸看能压到多小,通常能省下 40% 左右的算力预算。

第四处是量化友好。这一点在 2.x 之后体现得更明显,配合重参数化结构,INT8 量化的精度损失控制在 1 个点以内是常见结果。工业现场大量使用低功耗推理卡,FP16 甚至 INT8 是刚需,模型本身对量化友好,等于省掉了一大块调优成本。

1.3 什么情况下别硬上 YOLOv6,该换思路

这话我得说在前面,因为踩过坑。有几种场景,YOLOv6 不是最优解,硬上只会浪费时间。

目标小于 8 像素的极微小缺陷。比如某些芯片表面的微裂纹,在原图里就几个像素。YOLOv6 最小下采样到 8 倍,P3 特征图上目标只剩一两个格子,召回率上不去。这种要么上切图推理(把大图切块分别检测再合并),要么直接换高分辨率输入加 P2 检测头,要么干脆用传统图像处理配合频域分析。

需要像素级轮廓而不是框的场景。缺陷面积占比、形状不规则度这类指标,框给不出来,得上分割模型。用检测框去估算面积,误差能到 30% 以上,客户不会认。

样本少于 50 张的冷启动场景。缺陷样本实在太少,监督学习训不动。这时候异常检测路线(比如基于特征嵌入的方法)往往比硬训检测器靠谱得多,先跑起来积累数据,等样本攒到几百张再切回检测。

规则极其明确的缺陷。比如"孔位偏移超过 0.2mm"这种,用卡尺算法或者模板匹配几毫秒就出结果,又稳又可解释,没必要上深度学习。我见过有团队为了一个规则明确的尺寸测量硬训了两个月模型,最后精度还不如传统方法,纯粹是路径依赖。

提示:选型阶段花两天做可行性验证,比训两周模型再发现路线错了要划算得多。验证方法很简单——拿 100 张有代表性的图,人工标注,用小模型跑一遍,看召回上限在哪。

2. 数据与标注:决定上限的那 80% 工作量

2.1 打光决定天花板,采集策略决定地板

我一直跟团队说一句话:模型的上限是光学系统给的,不是网络结构给的。同一批缺陷,环形光下拍出来对比度 30,同轴光下可能只有 8,后者你换什么网络都救不回来。

采集阶段要盯死三件事。一是光源选型,表面划伤用低角度暗场最敏感,平面字符用同轴光最均匀,透明件缺陷用背光最清晰。二是相机与镜头的匹配,视场除以分辨率得到单像素物理尺寸,这个值要小于最小缺陷尺寸的三分之一,否则连"看得见"都做不到。三是样本的工况覆盖,同一类缺陷要在不同批次、不同班次、不同设备状态下都采到。

样本量的问题,我给的经验值是:主体类别(正常件)控制在缺陷类的 3 到 5 倍,缺陷类每类至少 300 个实例,稀有缺陷类实在凑不够就用过采样加针对性增强。某项目里有个"边缘崩缺"缺陷,全月只出了 40 件,我们通过调整光源角度人为复现了 200 多例,这个做法在工业上很常见——不是造假,是补足物理分布的覆盖。

2.2 标注规范不写清楚,后面全是返工

标注这活儿,看着简单,实际上是最容易埋雷的地方。工业缺陷的边界经常是模糊的、渐变的,十个人标出来的框能有十个样子。

我的做法是先写一份标注细则,把下面几个问题逐条定死:

  • 框的粒度:缺陷框是紧贴可见边界,还是包含周围过渡区?我的建议是紧贴可见边界,过渡区留给模型自己学,因为过渡区的界定主观性太强。
  • 重叠缺陷怎么处理:两个缺陷挨着或者部分重叠,是合并成一个大框,还是各自独立标?一般按"是否会被同一道工序处理"来判断,同工序处理的合并。
  • 模糊样本的归属:看不清但疑似缺陷的,统一标为"存疑类"单独一档,不参与正常训练,留作后续复核。
  • 最小可标尺寸:明确小于多少像素的不标,避免标注员各自发挥。

标完之后一定要做一致性校验。抽 10% 的图让第二个人盲标,算一下同一目标两框的 IoU,低于 0.85 的挑出来集体讨论对齐。这个动作看起来费时,但能把后面训练阶段的"标签噪声"问题提前消掉一大半。

2.3 增强要克制,工业图像不能照搬通用套路

从通用检测那套增强策略直接搬过来,在工业场景大概率翻车。我列一下自己踩过的具体坑:

水平翻转慎用。很多缺陷是有方向性的,比如"划伤方向与走刀方向一致",翻转之后方向反了,模型学到的是错的先验。真要用,先确认这一类缺陷是否存在方向语义。

颜色抖动要收敛。通用场景里色调、饱和度乱抖没事,工业场景里颜色本身就是判据——某类缺陷就是偏黄,你把色调抖 30 度,等于把判据抹掉了。我一般把色相抖动控制在 ±5 度以内,饱和度和明度不超过 ±15%。

Mosaic 要看场景。Mosaic 把四张图拼一起,能提升小目标数量,但会破坏上下文。工业场景里很多缺陷的判定依赖周边环境(比如"相对于螺纹的位置"),拼完之后上下文没了,反而学坏。我的经验是:纯外观类缺陷可以开,位置相关类缺陷关掉或者降低概率。

更该加的增强:小角度旋转(±10 度)、亮度与对比度的轻微扰动、高斯噪声、轻微运动模糊、以及模拟镜头脏污的局部高斯模糊。后两个在工业上特别重要,因为它们直接对应现场的漂移因素。

下面是我常用的一套增强配置,写在训练配置里大致长这样:

augment: hsv_h: 0.005 # 色相扰动,极保守 hsv_s: 0.15 hsv_v: 0.2 degrees: 10.0 # 小角度旋转 translate: 0.05 scale: 0.15 shear: 0.0 perspective: 0.0 flipud: 0.0 fliplr: 0.0 # 方向敏感类缺陷必须置零 mosaic: 0.2 # 外观类可开到 0.5 以上 mixup: 0.0 blur_prob: 0.15 # 模拟失焦 noise_prob: 0.1 smudge_prob: 0.08 # 模拟镜头脏污

后三项需要自己在数据管道里加,官方默认配置里没有,但现场效果提升很明显,尤其是长时间运行后的稳定性。

3. 训练与调优:把默认配置推到可交付指标

3.1 环境基线与第一版训练

先说环境。我的习惯是固定版本,别用 latest,工业项目最怕的就是"上周还能跑,这周环境一升级全崩"。基础组合大致是 Python 3.8 到 3.10、PyTorch 1.12 到 2.0、CUDA 11.6 到 11.8 之间选一套稳定的,写进 requirements 锁死。

训练起手式不要一上来就改结构,先用官方配置和预训练权重跑一版基线,看看数据本身的质量如何。命令大致是这样:

python tools/train.py \ --conf-file configs/yolov6s_finetune.py \ --data-path data/industrial.yaml \ --batch-size 16 \ --epochs 100 \ --device 0 \ --workers 8 \ --project runs/line_a --name baseline_v1

数据配置文件写清楚路径和类别数:

train: /data/industrial/images/train val: /data/industrial/images/val nc: 4 names: ['scratch', 'dent', 'stain', 'deform']

基线跑完先别急着看 mAP,先看两件事:损失曲线是否平滑收敛、验证集上的查全率和查准率分别在什么水平。工业场景里查全率通常优先于查准率,因为漏检的代价远大于过杀,后处理阈值可以再调,但召回丢了就找不回来了。

超参方面,微调阶段学习率比从头训要低一个数量级,我一般用 0.01 配 cosine 衰减,warmup 3 个 epoch。batch size 受显存限制,能大则大,8 到 32 之间都行,配合梯度累积可以模拟更大的批。

3.2 小目标与低对比度:两个最常遇到的硬骨头

工业缺陷八成以上是小目标。针对这个,我按成本从低到高排了几种手段,效果和代价对应如下:

手段实现成本召回提升速度代价适用判断
提高输入分辨率低明显线性上升首选,先试
增加 P2 检测头中明显约 30%目标小于 16 像素时用
切图推理中显著成倍大图小目标,产线允许时用
对比度预处理低中等可忽略图像偏灰时必做
浅层特征增强高中等较小前几种都不够时再上

提高分辨率是最先该试的。输入从 640 提到 960 或 1280,小目标在特征图上的像素数成倍增加。但要注意,分辨率提高会让推理时间线性增长,得先把节拍预算算清楚。我的算法是:单件允许时间减去采集、传输、后处理的固定开销,剩下的才是推理预算,再留 30% 余量。比如产线节拍 300ms,固定开销 80ms,推理预算 220ms,留余量后实际可用 150ms 左右。

增加 P2 检测头需要改网络结构,把 stride=4 的特征图接进 neck 和 head。代价是显存涨得快,batch size 得降。改完之后小目标召回往往能提 5 到 10 个点,但如果目标本身在 8 像素以下,提升有限,这种情况老老实实做切图。

低对比度的问题,预处理阶段我一般加一步 CLAHE(限制对比度自适应直方图均衡),clip limit 设 2.0 到 3.0,网格 8x8。这个操作对灰度图效果最明显,彩色图建议先转亮度通道处理再合并回去。要注意它也会放大噪声,所以后面跟一步轻度高斯滤波比较稳妥。

import cv2 def preprocess(img, clip=2.5, grid=(8, 8)): lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=clip, tileGridSize=grid) l = clahe.apply(l) out = cv2.cvtColor(cv2.merge([l, a, b]), cv2.COLOR_LAB2BGR) return cv2.GaussianBlur(out, (3, 3), 0)

3.3 标签分配与损失调整:让模型关注该关注的样本

YOLOv6 在 2.x 之后引入了基于任务对齐的标签分配策略和变焦损失,这套机制对工业场景其实挺友好,因为它会自动给"分类置信高且定位准"的样本更高权重,正好对应"既检出又框准"的需求。

但有两处需要针对工业数据改。第一处是正样本数量。默认的分配规则在小目标密集的场景下可能给的正样本偏少,导致收敛慢。我的做法是适度放宽中心先验的半径,让更多格子参与正样本候选,实测在密集小目标场景下收敛速度能快 15% 左右。第二处是类别不平衡。缺陷类别之间数量差十倍很常见,可以在损失里给稀有类加权,或者干脆从采样层面解决——每个 batch 里强制包含稀有类样本。

回归损失方面,工业场景对定位精度的要求通常高于分类,可以把回归损失的权重往上调一档。但别调太狠,否则训练后期容易震荡。我一般从默认值往上加 20% 到 30%,观察验证集上的定位误差中位数变化,稳定就保持。

还有一个容易被忽略的点:置信度阈值的确定不能拍脑袋。训练完在验证集上把所有样本的置信度排序,画出过杀率和漏检率随阈值变化的曲线,找那个满足验收指标的交点。我见过有人直接设 0.5,结果过杀率超标两倍,其实阈值调到 0.72 就达标了,白白多训了一轮模型。

3.4 蒸馏与剪枝:能不做就先不做

模型压缩这块,我的态度比较保守。蒸馏优先于剪枝,量化优先于两者。

自蒸馏的思路是拿一个大模型(比如 M 或 L)当老师,小模型当学生,用老师的软标签监督学生。在工业数据上效果不错,尤其是缺陷类别边界模糊的时候,软标签提供的类间关系信息很有价值。实现上注意一点:老师模型必须是在同一份数据上训好的,别拿通用预训练权重直接蒸馏,分布差太远反而有害。

剪枝我只在算力实在紧张时才用,而且只做通道级剪枝,剪枝率不超过 30%,剪完必须重新微调至少 20 个 epoch。结构化剪枝对检测模型比较伤,尤其伤小目标,剪完召回掉三五个点是常事。相比之下,量化感知训练配合 INT8 部署,能拿到接近 4 倍的吞吐提升,精度掉一个点以内,性价比高太多。

提示:压缩之前先把 FP16 的上限摸清楚。很多项目 FP16 就够用了,根本不需要折腾 INT8。别为了技术指标去上不必要的复杂度。

4. 部署落地:从训练脚本到产线进程

4.1 ONNX 导出的那些坑,我先替你踩一遍

导出这一步看着最简单,实际上问题最集中。核心原则是:只导出到检测头输出为止,后处理放在引擎外面。把解码和 NMS 写进计算图,调阈值就得重新导出,工程上极其难受。

导出的关键点有这么几个:

  • opset 版本。别用太新的,11 到 13 之间兼容性最好,各家推理引擎支持都完善。
  • 动态维度。如果批大小或输入尺寸会变,记得声明 dynamic axes;如果固定不变,就别开动态,静态图优化空间更大。
  • 训练模式残留。导出前务必 eval(),并确认重参数化已经完成融合,否则导出的图里还带着多分支,推理会慢一截。
import torch model = build_model(cfg).eval() ckpt = torch.load('weights/best.pt', map_location='cpu') model.load_state_dict(ckpt['model']) dummy = torch.randn(1, 3, 960, 960) torch.onnx.export( model, dummy, 'yolov6s_960.onnx', opset_version=12, input_names=['images'], output_names=['cls_score', 'bbox_pred'], do_constant_folding=True, dynamic_axes=None )

导出后一定要做数值对齐验证:同一张图分别走 PyTorch 和 ONNX Runtime,比较原始输出张量的最大绝对误差,控制在 1e-4 量级才放心。我遇到过重参数化没做干净导致输出差 0.03 的情况,肉眼看不出来,但后面框的位置会系统性偏移两三个像素。

4.2 TensorRT 构建:FP16 是标配,INT8 要谨慎

TensorRT 构建这一步,我一般先用 trtexec 快速验证一下延迟,确认可行再写正式的 Python 构建脚本。

trtexec --onnx=yolov6s_960.onnx \ --saveEngine=yolov6s_960_fp16.engine \ --fp16 \ --workspace=4096 \ --minShapes=images:1x3x960x960 \ --optShapes=images:4x3x960x960 \ --maxShapes=images:8x3x960x960

批大小的选择有讲究。产线如果要吞吐,用批处理能显著提升 GPU 利用率,但会引入排队延迟。我的经验是:单件延迟敏感的用 batch=1,吞吐优先的用 batch=4 到 8,并且用多个 CUDA stream 做流水线重叠,把预处理、推理、后处理三段并行起来。

INT8 量化要过校准。校准集的选择直接决定精度,我的标准是 500 到 1000 张,必须覆盖所有类别、所有工况、所有光照条件,不能只挑清晰的样本。校准算法方面,熵校准(entropy)通常比最小最大校准(minmax)稳,尤其是图像里存在高光或暗区的时候。

量化之后一定要做逐层敏感度分析。TensorRT 提供逐层回退的功能,哪一层量化后误差大就把哪一层退回 FP16。实操中通常是检测头前面那几层比较敏感,退回之后整体精度就能拉回来。

一个常见的误区是认为 INT8 一定比 FP16 快。实际上在小 batch 场景下,INT8 的加速比可能只有 1.3 到 1.5 倍,因为瓶颈已经转移到内存带宽和 kernel 启动开销上。真想要吞吐,得配合批处理和流水线一起做。

4.3 前后处理才是真正的性能黑洞

模型推理只占整个链路的一部分,前后处理经常吃掉一半以上的时间,这一点很多人在优化时容易忽略。

预处理主要做三件事:letterbox 缩放、归一化、HWC 转 CHW。用 OpenCV 在 CPU 上做,一张 960 的图要 8 到 15ms;改成用 CUDA 写 kernel,或者用 GPU 图像处理库,能压到 1ms 以内。如果你用的是带 GPU 解码能力的采集卡,可以直接把解码和缩放串在 GPU 上,省掉一次主机内存往返。

后处理的大头是 NMS。CPU 版的 NMS 在候选框上千的时候能跑到 20ms 以上,是实打实的瓶颈。解决办法有三种:一是用 GPU 版 NMS,TensorRT 有对应的插件;二是先做置信度过滤把候选框压到 200 以内再进 NMS;三是用无 NMS 的替代方案,比如基于距离的框合并或者 top-k 选择。我一般用第二种,简单可靠,效果也够。

内存管理上,别每次推理都重新分配显存。用 pinned memory 做主机到设备的异步拷贝,预分配好输入输出缓冲区,复用 CUDA stream,这些细节加起来能省 5 到 10ms。在实际的产线进程里,这几毫秒往往就是能不能卡进节拍的关键。

4.4 一个可参考的产线部署结构

下面这个结构是我在几个项目里反复用过的,稳,也容易维护。用三个线程加两个队列做流水线:

import queue, threading, time raw_q = queue.Queue(maxsize=8) res_q = queue.Queue(maxsize=8) def capture_worker(cam, stop): while not stop.is_set(): ok, frame = cam.read() if ok: if raw_q.full(): raw_q.get() # 丢弃最旧帧,防止延迟堆积 raw_q.put((time.time(), frame)) def infer_worker(engine, stop): while not stop.is_set(): ts, frame = raw_q.get(timeout=1) blob = preprocess(frame) # GPU 预处理 outs = engine.infer(blob) # 推理 dets = postprocess(outs) # 解码 + NMS res_q.put((ts, dets)) def result_worker(plc, stop): while not stop.is_set(): ts, dets = res_q.get(timeout=1) verdict = decide(dets) # 阈值判定 plc.write(verdict) # 输出给 PLC

采线程和推理线程之间要有丢帧策略,这个必须做。产线偶尔卡一下,如果队列无限堆积,延迟会越滚越大,最后输出的是几秒前的图,那比不检测还危险。丢最旧帧保证实时性,是工业视觉的标准做法。

还有两件事必须加:看门狗和超时保护。推理线程超过设定时间没出结果,直接输出"异常"信号让产线停机或者走人工复检;进程要有心跳,主控发现心跳断了自动重启。这些机制平时用不上,但真出事的时候能救命。

5. 问题排查实录:那些文档里不会写的情况

5.1 训练侧的高频异常与定位思路

训练阶段的问题相对好查,因为有日志和指标可看。我整理了一张速查表,基本覆盖了八成的现场提问:

现象高概率原因排查动作处理办法
损失不降或剧烈震荡学习率过大、标签异常看损失曲线前 10 个 epoch学习率降一个数量级,检查标注是否有越界框
训练集 mAP 高、验证集低过拟合、分布不一致对比两边样本来源补数据、加增强、检查验证集是否混入训练图
召回上不去、精度还行阈值过高、小目标漏检画 PR 曲线降阈值、加分辨率或 P2 头
某一类几乎检不出该类样本过少、标注错统计各类实例数补样本、类别加权、检查标注正确性
框位置系统性偏移预处理不一致检查训练和推理的归一化方式统一 letterbox 参数和 padding 值
训练后期指标突然跌学习率衰减策略不当看 lr 曲线改用 cosine,加早停

表里最值得说的是"框位置系统性偏移"这一条。它不会让 mAP 掉太多,但会让下游的尺寸测量全错。根因几乎都是预处理不一致——训练时用了一种 padding 方式,推理时用了另一种,或者归一化用的均值方差对不上。解决办法就是把预处理封装成一个函数,训练和推理共用同一份代码,不要各写各的。

5.2 部署侧的问题,往往藏在细节里

部署阶段的问题更隐蔽,因为很多时候程序不报错,就是结果不对。我遇到过几个典型情况,说出来供参考。

引擎输出全零或全是背景。八成是输入的预处理错了,常见的是通道顺序搞反(BGR 当 RGB 送进去),或者归一化漏了,像素值还是 0 到 255 没除到 0 到 1。排查方法是拿同一张图分别跑 PyTorch 和引擎,逐层比对中间输出,一般到第二三层就能看出问题。

同样的模型,换台机器精度就掉。这个通常是量化相关——不同显卡对某些算子的实现有细微差异,INT8 下会被放大。解决办法是统一硬件型号,或者在交付前做一次跨机器的一致性验证。

跑一段时间后延迟逐渐升高。内存或显存泄漏的典型表现。检查有没有在循环里重复创建 CUDA stream、TensorRT context 或者 OpenCV 的 Mat 对象。我见过在推理循环里每次都 new 一个执行上下文,跑两个小时显存就满了。

偶发性的框错位。这个最难受,因为不可复现。常见的根因是多线程下共享了缓冲区没有加锁,或者异步拷贝没有做同步就开始读结果。用 CUDA 的流同步或者显式加锁能解决,但最好在设计阶段就把每个线程的数据隔离好。

5.3 上线之后的持续运维,才是真正的考验

模型上线不是终点。产线的工况会漂移,镜头会脏,物料批次会换,模型的表现一定会随时间缓慢下降。所以必须建一套线上监控和回流机制。

监控方面,我一般记录三类数据:每天的检出率与过杀率趋势、置信度分布的均值与方差、以及推理延迟的分位数。检出率突然掉 5 个点、或者置信度均值整体下移,往往预示着什么变了,得赶紧去现场看。这三个指标实现起来都很简单,但对早期发现问题极其有效。

回流方面,把线上判定的"过杀样本"和"人工复检确认的漏检样本"自动存下来,每周做一次增量训练。增量训练时新旧数据按 1:3 混合,别只用新数据,否则会灾难性遗忘。

换型的时候,我的经验是预留一个快速标定流程:新物料上线的头 200 件,全部走人工复检,同时把图像和结果都存下来,用这批数据做一次小规模微调,通常 20 个 epoch 就能把模型调整到可用状态。这个流程要提前固化下来,写成操作手册,现场工程师照着做就行,不要每次都找算法团队。

注意:任何模型更新都要先灰度,先在一个工位或者一个班次上跑,指标稳定了再全量推。直接全量替换,一旦出问题就是批量事故。

6. 我在几个项目里攒下的实战心得

写到这里,分享几个不在文档里、但确实影响成败的细节。

别迷信大模型。某次做塑料件外观检测,客户一开始要求用 L 尺寸,说"精度越高越好"。我坚持先跑 S,最后 S 在优化后的数据上过杀率 0.3%、漏检率 0.05%,完全达标,推理时间只有 L 的三分之一,客户也就没话说了。模型尺寸和数据质量的关系,是乘法不是加法,数据不行的时候换大模型纯属浪费算力。

把预处理做成可配置的。不同产线、不同物料需要的预处理不一样,如果写死在代码里,每换一次都要改代码重新编译。我的做法是把 CLAHE 参数、letterbox 策略、归一化参数全部放到配置文件里,现场工程师改配置就能调,不用惊动开发。

训练和部署用同一套后处理代码。这个前面提过,但值得再强调一次。评估阶段算指标用的解码逻辑,必须和部署时完全一致,最好是同一个模块被两边引用。我见过评估用一套、部署用另一套,评估 mAP 0.88,上线实测差了一大截,查了两天才发现 NMS 的 IoU 阈值不一样。

给每条产线留一个"性能基线"文件。记录下标准测试图集在该配置下的延迟、精度、显存占用,每次改动之后跑一遍对比。这个习惯能帮你快速定位"是哪次改动拖慢了系统",尤其在多人协作的项目里,价值极大。

现场调参带个可控光源。很多时候模型表现不好真不是模型的问题,是光打得不到位。带个小光源去现场,一边调角度一边看图像,往往比回去改模型有效得多。这个经验是我被现场工程师拉着手调了三次光之后才彻底服气的。

这套流程跑下来,一个中等复杂度的工业检测项目,从数据采集到稳定上线,我一般需要六到十周,其中模型训练只占两周,剩下全是数据、集成和调优。这个时间分布本身就是个提醒:把精力放在数据和工程上,比反复调网络结构划算得多。后面如果要做扩展,我会优先考虑两个方向,一是把回流数据的管理做成半自动的流水线,减少人工筛选成本;二是把多工位的模型统一到一个服务里,用模型热切换的方式支持快速换型,避免每个工位单独维护一套环境。

返回列表