3C行业做视觉检测的同事应该都有同感:项目难点从来不在算法榜单上的那几个公开数据集里。真正让人头疼的是高反光曲面上的一道划痕、批次色差造成的纹理漂移、还有换型后一夜之间飙上去的误检率。TVA(基于Transformer的视觉注意力架构)在3C产品视觉检测里能跑通,靠的不是某个网络结构更先进,而是它把“全局判断”和“局部定位”这件事重新组织了一遍。这个系列前两篇重点讲了TVA的原理边界和在缺陷分类上的表现,这一篇直接落地说重构:从数据策略、训练细节、推理部署到现场稳定性,全部按我实际跟过的产线项目来复盘。
1. 为什么TVA值得重做一遍3C质检流程
1.1 先看清3C场景里的“老问题”
3C产品零部件检测大概是工业视觉里最难啃的一类,难在几个问题同时出现。外观尺寸小但缺陷也小,表面是镜面和磨砂混合处理,产线节拍快还经常一天换型多次。同一片物料上可能出现划伤、脏污、压伤、气泡、缺胶、毛刺等不同形态的异常。传统方案用边缘检测、连通域分析、模板差值这类经典算子,在零件固定、光源固定的单一场景里还能跑;但一旦遇到曲面反光或者批次色差,阈值怎么调都压不住误检。CNN检测网络能替代一部分人工,但局部感受野的限制会带来一个很直观的问题:它会把纹理正常波动误判成缺陷,或者把真实缺陷当成背景噪声处理掉。
之前有同事开玩笑说,做3C检测就是“和反光斗,和纹理斗,和产线节拍斗”。这句话不算夸张。屏幕玻璃上的细小划痕、金属中框轻微压伤、FPC上的微裂纹,这些目标本身只有几十个像素甚至几个像素,周围却是复杂的干扰背景。传统方法在这类场景下有一个共性瓶颈:局部特征不够用。你要判断一条亮线是划痕还是抛光纹,不能只看局部的十几个像素。划痕往往有连续的方向性和深度渐变,抛光纹则是周期性的细微纹理。这两个特征在局部窗口里非常相似,需要结合更大范围的纹理走势甚至整个零件的位置才能区分。
1.2 TVA到底解决什么矛盾
TVA对这类问题的改变,在于它把视觉建模方式从“局部堆叠”换成了“全局交互”。具体说,TVA会让图像里的每个位置都能直接和全图其他位置计算关系,而不是像CNN那样靠卷积核一层层往外扩。这样带来的直接好处是:判断某个像素是不是缺陷时,模型同时看到了全图的纹理、结构、边缘、光照梯度等信息,局部分析和全局判断放在同一个框架里完成。这也是为什么在3C检测里,TVA在区分“真实缺陷”和“正常结构干扰”时明显占优势。
上一篇文章里我详细拆过视觉Transformer的核心结构,这里不再重复公式,只强调一个关键认知:TVA不是简单把分类网络换成Transformer,它解决的是视觉检测里“既要看到全局语义,又要保留局部位置细节”的矛盾。CNN经过多层下采样后,小目标细节信息会明显丢失;而TVA在固定分辨率下保留每个patch的位置关系,再通过注意力权重决定关注哪里。对3C产品这种小缺陷、强干扰、高节拍的场景,这套机制天然对路。不过,架构对路只是前提,真正决定项目成败的往往是数据、训练策略和部署细节,这些后面逐项展开。
2. TVA破局的技术原理,说得直白一点
2.1 全局上下文对判定缺陷到底有多重要
我从实际效果倒过来讲一个例子:某智能手表后盖上的圆形Logo区域,边缘偶尔会多出一条很细的残余胶线。这条胶线在局部看就是几条亮色像素,和Logo本身的亮色边缘几乎无法区分。传统CNN会把所有亮色像素都标记为潜在问题,结果就是过杀率一直压在5%以下调不进去。TVA方案跑起来之后,模型会自动学习到:Logo边缘本身是光滑连续的结构,而胶线会破坏周围几段边缘的连续性,同时胶线末端还有一个非圆形的形态异常。这种判断依赖的是整个Logo区域的上下文关系,而不是局部颜色或梯度。
用生活化类比就是:在人群里找一个人,如果只看袖子上一小块布料,很难判断是不是目标;但如果先扫一眼整体轮廓、站位、动作姿态,再聚焦到那块布料上核对细节,判断就可靠得多。TVA的注意力机制做的正是这件事:先对全图生成一个“哪里值得关注”的权重分布,再围绕可疑区域精细化提取特征。这个过程中,正常纹理的周期性和缺陷的局部突变会被自然区分开。对3C外观件来说,大量缺陷恰恰属于这种“局部不明显、需要全局确认”的类型,比如渐变划痕、边缘压伤、整面光泽不均、透明件内部气泡。
2.2 从CNN切到TVA的迁移成本没有想象中高
很多团队卡在第一步,是因为觉得从CNN切到TVA要把整个算法栈重写。实际操作下来,迁移成本可控,不需要推倒重来。
首先,预训练权重可以复用。TVA模型在大型视觉数据集上训练出来的 backbone 特征,对边缘、纹理、形状这些底层视觉结构有很好的泛化能力。3C零部件虽然不是自然图像,但底层视觉结构是共通的,用 ImageNet 级别的预训练做初始化,比从零训练收敛快很多。我在项目中一般先冻结 backbone 训练分类头10个epoch,再解冻全部参数做精调,这个策略在多数3C外观检测任务上都很稳。
其次,输入尺寸和patch大小的组合需要重设。3C缺陷往往很小,直接套用224x224输入会丢失大量细节。实际项目中建议把输入分辨率提到512x512甚至768x768,但要同步调整patch大小。patch越大,计算量越小,但细节保留越差;patch越小,细节越好,但序列长度变长、显存占用上升。对划伤、压伤这种像素级缺陷,patch从16x16降到8x8往往比换更大的模型更有效,因为注意力的基本粒度更细了。位置编码也需要重新训练,因为分辨率变化后位置信息分布完全不同。
2.3 3C场景下要做哪些结构定制
TVA直接用在3C检测上,结构层面有三处需要按场景定制。
多分辨率输入分支。3C零部件上同时存在大小差异悬殊的缺陷:大尺寸缺陷如整面压伤、光泽不均,需要低分辨率下看全局;微小缺陷如点状脏污、细划痕,需要原始分辨率下看细节。简单做法是把图像缩放到两个尺度分别过模型再融合特征,计算量大概增加20%-40%,但对大小缺陷的兼顾能力提升明显。
缺陷注意力蒸馏。这是我在一个反射镜片检测项目里试出来的方法。用一个分类头或者像素级分割头生成缺陷注意力图,用它去约束中间层特征,相当于给模型画了一个“重点区域提示”。做法不复杂:训练时额外加一个轻量分割分支,输出缺陷区域的概率图,再把这张图加权到主干特征上。这样处理之后,模型会把更多表征能力分配到缺陷区域,而不是浪费在背景纹理上。代价是训练时间增加了约15%,但收敛后的误检率能再降1-2个百分点,在过杀率要求严格的产线上很值。
分类头改造。3C项目的缺陷类别往往呈长尾分布,几类高频缺陷占据了绝大多数样本,剩下十几类稀有缺陷总共只有一两百张图。这种情况下不要在原始分类头上直接硬怼,建议把最后的全连接分类头换成余弦分类头,并在训练时对稀有类别的中心向量做初始化增强。实际操作里还可以把相似缺陷合并成“缺陷大类”先训,再用子类细分模型做二次分类,工程上更稳定。
3. 一个3C视觉检测项目从零到上线的完整路径
3.1 数据池建设:数量、标注、分布
先说一个我反复强调的观点:TVA对数据量的需求,比CNN友好但也更敏感。说友好,是因为预训练权重帮了大忙,小样本下不容易崩;说敏感,是因为注意力机制对样本分布的偏好更强,如果某个类别的样本数量不足,模型会倾向于把它忽略掉。
以某智能手表后盖连续缺陷检测项目为例,我整理的数据池大致是这样:
| 缺陷类型 | 目标样本数 | 实际采集 | 标注方式 |
|---|---|---|---|
| 划伤(含粗细、方向变化) | 2000 | 2150 | 像素级多边形 |
| 压伤 | 1500 | 1640 | 像素级多边形 |
| 脏污(油污、灰尘类) | 1800 | 1930 | 像素级多边形 |
| 缺胶 | 800 | 820 | 矩形框+边缘标注 |
| 气泡 | 600 | 640 | 像素级多边形 |
| 色差/光泽不均 | 1000 | 1080 | 区域级多边形 |
| 罕见缺陷(毛刺、异物、裂痕) | 各200 | 160-230 | 矩形框 |
普通正常件采集了4000张,其中故意加入不同批次色差和抛光纹变化的样本,避免正常样本过于理想化导致误检偏高。标注环节有一个大坑:不要用外接矩形去标细长型缺陷,比如划伤。矩形会把大量正常区域框进去,模型学到的是“矩形范围内有正常纹理也判缺陷”,训出来的模型过杀率会非常难看。细长型缺陷必须用多边形精细标注,虽然费时间,但效果完全不一样。缺胶和气泡这类近似椭圆形的可以用矩形,但一定要让标注框紧贴边缘,留白越少越好。
3.2 训练设置与调参经验
训练配置我直接给一份当前项目实测可复现的参数组合。
# 训练配置参考(TVA用于3C表面缺陷检测) model = TVA( input_size=512, patch_size=8, embed_dim=384, depth=12, num_heads=6, num_classes=num_defect_types + 1, # +1 为正常类 ) optimizer = AdamW( model.parameters(), lr=1e-4, # 开放全参数精调时的初始学习率 weight_decay=0.05, ) scheduler = CosineAnnealingLR( optimizer, T_max=60, # 总训练轮数 eta_min=1e-6, ) loss = build_loss('focal', gamma=2.0, alpha=[...类别权重...]) augmentation = build_augmentation([ 'random_crop', 'random_rotation_10', 'color_jitter_0.2', 'mixup_0.2', 'cutmix_0.2', ])三个最有用的调参经验:学习率如果从零训练,建议3e-4起步;但用预训练权重精调时,1e-4到5e-5这个区间最稳,超过2e-4很容易出现前期掉点。损失函数用Focal Loss加类别权重,比普通交叉熵更能压住长尾类别漏检。Mixup和CutMix都开,但比例控制在0.2左右,太高会模糊缺陷边缘,导致分割输出不干净。训练轮数不用贪多,60轮左右足够。超过80轮之后,测试集精度常常不再提升反而轻微下降,原因是模型开始记住训练数据里的某种特异性纹理,泛化变差。
3.3 推理部署和检测节拍优化
训练完了只是第一步,真正决定项目能不能上线的是推理速度和稳定性。
3C产线节拍通常要求单件检测时间在几百毫秒以内,具体取决于产品形态。如果视觉系统还需要控制运动机构多角度拍摄,留给算法的时间往往只有100-200毫秒。原生TVA在这种时间预算下偏重,需要工程化压缩。
我的标准做法是导出静态图后用TensorRT加速,FP16精度。512x512输入、patch为8的TVA模型,在单张专业显卡上FP16推理大约能跑到35-60毫秒,具体取决于层数和批次。如果还是超时,优先降输入分辨率到384x384,配合patch为8,精度损失控制在1%以内,速度能再提升30%左右。还有一招是动态分块:先用一个轻量级全局分类网络快速判断图像整体是否正常,只有判定为可疑的图块才送入TVA细查。这个粗筛+细查架构在正常件占比高的产线上效果很好,整线平均耗时能降到纯TVA方案的40%以下,而缺陷件本身占比少,细查的算力开销完全可控。
部署时还要注意算子兼容问题。某些注意力实现里的算子不支持低精度推理,导出时会有警告或者精度异常。我的建议是训练时就用支持目标部署平台的算子库实现,别到导出阶段再改结构,否则容易在量化环节出问题。
4. 不同3C品类,TVA的适配差异
4.1 屏幕玻璃类:全局纹理一致性和划痕
屏幕玻璃检测的难点在于:玻璃是透明材质,光线会穿透、折射,表面还很光滑。划痕在特定角度下特别明显,换个角度可能完全看不见。传统方案依赖多角度打光加多相机同步拍摄,算法层面则要对多帧图像做融合判断。
TVA在这种场景下适合做两件事:一是将多角度图像作为多通道输入,在特征层面融合,而不是分别判完再投票。这样模型可以学到“这个方向上的亮线在另一个角度下是否仍然连续”,对玻璃划痕的确认能力提升明显。二是利用全局注意力判断纹理一致性。正常玻璃的抛光纹方向是全片一致的,一旦出现局部纹理方向突变,基本可以判定为异常。这种基于全局一致性的判据,TVA比CNN天然更容易学习。
玻璃检测还有一个细节:边缘区域的反光特别强,经常出现高光过曝。采集数据时一定要包含过曝样本,并把这些区域标注为背景,不要硬标缺陷。否则模型会把所有高光区域都判成缺陷,过杀率直接爆表。
4.2 FPC/PCB类:小目标缺陷捕获
FPC和PCB这类柔性电路板和线路板,缺陷特征是典型的极小目标:微裂纹、铜桥残留、焊点偏移、孔内异物。尺寸常常在10个像素以下,而且背景是密集的线路纹理。这对TVA的patch大小选择提出了很高要求。
如果patch太大,小缺陷的特征会被压缩到同一个patch里,直接丢失。我的建议是这类项目优先保证分辨率,输入至少用640x640,patch用8x8。序列长度变长导致显存压力大,但3C检测往往单张图输入,显存还是可控的。如果显存不够,别急着减patch,先减depth或通过窗口注意力降低计算量。
线路板检测还有一类特殊问题:同类型缺陷在不同批次的PCB上呈现的颜色和形态差异很大。比如铜桥残留,有的发亮,有的发暗,有的带氧化色。数据采集时要刻意覆盖多批次样本,避免模型过拟合到某个批次的颜色特征上。我在项目里遇到过TVA在测试集上精度99%,换一个批次直接跌到88%的情况,后来排查发现就是训练数据里只包含了单一批次样本,模型实际上在用批次颜色特征冒充缺陷特征。
4.3 金属中框类:高反光和曲面阴影
金属中框、后盖、按键这类结构件,最大挑战是反光和曲面阴影。一个拉丝金属表面,在不同光源角度下拍出来的纹理完全不同。同一个位置,顺光看是完好的,侧光看可能像划伤。
这里有一个非常实用的技巧:用多光谱或不同角度光源组合拍摄多帧,然后训练一个轻量级的图像融合网络把多帧合成一张特征增强图,再送入TVA检测。这个融合网络可以很浅,主要是解决“单帧信息不足”的问题。如果不想引入额外网络,也可以把多帧图在通道维度拼接后直接输入TVA,让注意力机制自己决定采信哪个光源下的信息。实测下来后者更省事,效果也不差,缺点是输入尺寸和通道数变大,推理耗时上升。
另外,金属件的镜面区域经常出现环境反光,比如厂房顶灯、操作人员身影。这类反光对浅层特征干扰很大。处理办法是数据增强阶段加入随机局部过曝和镜像翻转模拟,让模型学习到“环境反光区域不应判定为缺陷”。同时,模型输出的置信度阈值要针对反光区域单独做区域级阈值调整,不同区域用不同阈值,比全局统一阈值更合理。
5. 常见问题与排查技巧实录
5.1 训练收敛慢或掉点
先列一个速查表,都是我实际遇到过的情况。
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 前期损失不降 | 学习率过高导致震荡 | 降到5e-5,或先冻结主干只训分类头 |
| 收敛后精度上不去 | patch过大或输入分辨率不足 | patch降到8x8或分辨率提到640 |
| 训练集精度高但测试掉点 | 数据增强太强,CutMix比例过高 | 降到0.1或关闭Mixup |
| 小缺陷类别被忽略 | 类别权重未设置 | 按样本数量的反比设置Focal Loss权重 |
| 正常件误检率高 | 正常样本太少或太单一 | 补充多批次多光源正常样本 |
训练过程中要盯的不只是loss值,还有每个类别的召回率和误检率。特别是长尾类别,整体loss下降不代表稀有类别在变好。建议每5个epoch保存一次模型,在验证集上按缺陷类别单独计算指标,而不是只看mAP。我在一个项目里就是只看整体mAP,结果一个稀有类别的召回率一直低于40%却没被发现,等试产时批量翻车才回头排查。
5.2 推理速度不达标
TVA推理慢,大多数情况下不是模型太大,而是没做好计算图优化和算子融合。先看是不是还在用动态图跑推理,动态图在工业部署里本身就不合适。转到静态图之后,模型推理时间一般能降20%-40%。
如果优化后单帧时间还是超预算,另一个思路是减少序列长度。这里的做法是:先跑一个轻量级目标检测器定位可疑区域,再从原始大图上裁剪出可疑区域单独送TVA。本质上是用粗糙检测做候选区筛选,细判交给TVA。这个方案在缺陷稀疏的3C场景下很有效。正常件占比高的时候,平均每张图只需要细查1-2个候选区,单件总耗时能控制在80毫秒以内。
还有一个容易忽略的点:GPU推理与图像采集串行执行,会造成大量时间浪费。合理做法是图像采集、预处理、GPU推理、结果反馈做成流水线并行,一到两个工位的并行就能把有效节拍提升很多。算法优化换来的时间,可能还没有流水线优化换来的时间多。
5.3 现场换型或光照变化导致误检漂移
这是3C项目最常见的上线后问题。模型在实验室测得好好的,到了产线一换型或者白天晚上光照变化,误检率就开始飘。TVA对光照变化虽然比CNN鲁棒一些,但并不是免疫。
我常用的处理手段是模型前加一层在线归一化层,对每张输入图按产线基准做亮度均值和标准差对齐。这个基准不是固定不变的,而是每隔一段时间根据最近通过品图像统计更新。相当于让模型看到的图像亮度分布始终保持在训练时熟悉的范围内,能有效减少光照漂移带来的误检。
换型方面,一个稳妥的做法是给TVA增加可切换的适配头。不同型号的产品共享主干特征,但分类头按型号分别训练。换型时只需切换适配头,不需要重新加载模型,切换耗时极短。这个方案在共享件外观接近的产线上效果非常明显。如果不同型号之间外观差异很大,共享主干就未必合适,更稳妥的做法是每个型号单独保存一份完整模型,用模型管理工具按订单号自动加载对应权重。
6. 连续跟了几个产线项目之后的一些体会
如果说这个系列前两篇是在讲TVA“能做什么”,那这一篇更想表达的是“怎么把它用稳”。框架再先进,落到产线上都要面对数据分布、节拍限制、批次漂移这些现实问题。我自己连续跟了几个3C项目之后,最大的感受是:不要在算法效果最好的时候急着上线,要把数据策略、部署优化和现场校准时间一起算进项目周期里。
TVA在3C视觉检测里的优势,说到底来自它把“全局判断”和“局部细节”放在了同一个结构里。这个特性让它在高反光、强纹理、小缺陷的3C场景中天然比传统CNN有优势。但真正拉开项目差距的,反而是那些不直接体现在网络结构里的细节:缺陷标注是不是像素级、正常样本有没有覆盖多批次、推理链路有没有做成流水线、阈值有没有按区域分开设置。这些做好了,TVA就是一个非常顺手的工具;做不好,再强的注意力机制也扛不住现场复杂的干扰。
如果只让我留一条建议,那就是:做3C视觉检测项目,先把数据池的多样性做足,再谈模型选型和调参。数据里如果没有覆盖到批次差异和光照变化,任何模型上线后都会还你一份措手不及的误检报告。