工业质检AI这几年已经成了制造行业的标配,产线上一个工位两台工控机、跑一个检测模型,替代人工目检。真正做落地的时候你会发现,算法效果倒不是最急的坎,反而是性能问题把人卡得死死的——高分辨率图一张十几MB,模型推理动辄几十毫秒,产线节拍要求单张处理在百毫秒以内,更别提边上还有PLC、机器人视觉、上位机在抢CPU。模型量化就是在这种压力下被推上台面的。不用换硬件、不用推翻原有推理流程,一套量化下来,模型体积砍到四分之一,推理速度常有2到4倍的提升,这种性价比实在太诱人。
但量化不是开个开关就完事的,尤其是质检场景,漏检一个缺陷可能直接导致批量返工。这篇实践记录,就围绕模型量化在工业质检AI应用中的优化展开,讲讲从方案选型、校准数据准备、混合量化、部署适配到问题排查的完整链路,顺手把踩过的坑也一起交代清楚。适合正在做边缘端检测模型部署的算法工程师、部署工程师,以及被产线性能压得喘不过气的同学参考。
1. 量化方案的选型逻辑
1.1 先搞清楚你的瓶颈到底在哪
做量化决策之前,我建议先花半天时间把推理链路的耗时拆开看。工业质检任务里,一张图从产线相机过来,往往要经过解码、缩放、归一化、推理、后处理、结果上报这么一长串流程。很多时候模型的原始浮点推理只占40%耗时,剩下60%耗在前处理和后处理上。这种情况下你上来就量化模型,效果当然有,但肉眼观测的提升不会太惊艳。
正确的做法是先做一轮性能剖析。把每段耗时打点打出来,统计一两百张图的平均耗时和P95耗时,然后再决定要优化谁。我在某跨平台系统上就碰到过类似的情况,模型是单阶段检测网络,原计划直接做INT8量化,结果一测发现解码加放大占了一半时间,后来用GPU硬件解码替换CPU解码,性能直接翻了接近一倍。量化这件事没有统一答案,一定要先让数据说话。
如果拆完之后确认推理耗时确实是主要瓶颈,再往下看量化。还有一种情况,模型体积成了瓶颈,比如产线边缘节点存储吃紧,或者模型需要从中心下发给多个工位,传输带宽有限,这时候量化同样值得做,哪怕推理性能不变,光是把模型从100多MB压到30MB以内,运维压力就小很多。
1.2 PTQ还是QAT:工业场景大部分时候从PTQ起步
量化训练方式大体分两类:训练后量化PTQ和量化感知训练QAT。PTQ的思路是拿一个已经训练好的浮点模型,用一批校准数据统计激活值的分布,据此算出量化参数,直接把模型转成低精度推理,整个过程不需要反传梯度,一般一两个小时就能完成。QAT则是在训练过程中就模拟量化误差,让模型自己去适应低精度表达,精度通常更高,但你需要重新走训练流程,还要调整学习率策略,成本明显更高。
对于工业质检场景,我的建议是从PTQ开始。工业检测模型往往已经在特定数据集上迭代过很多轮,重新训练一次的时间成本、人力成本都不小,而且产线上的缺陷数据非常金贵,你未必能轻松凑出一套覆盖所有类型的数据集去做QAT。先用PTQ跑一版,评估量化后的精度损失,如果漏检率、误检率没有明显恶化,那就够了,没必要为了追求理论最优多花几周时间。
当然,如果PTQ后精度损失确实明显,再考虑混合精度或QAT。工业质检对精度的要求很特别,客户不会关心你的mAP降了0.5%,他们只关心漏检率和误检率这两个数字,所以评估标准也要跟着场景走,后面第四节我会专门讲这个。
1.3 对称量化和非对称量化的选择依据
量化本身还有一个对称与非对称的维度,简单说就是量化映射区间是否关于零点对称。权重数据一般分布在零附近,且近似对称,用对称量化就够了;但激活值的分布不一定对称,很多卷积层后面接ReLU,输出全是非负的,这时候用非对称量化能多利用一部分量化区间,减少精度损失。
工业质检模型里激活值分布长尾的情况非常常见,比如缺陷检测任务里大量背景区域激活接近0,只有少数缺陷区域激活值较大。如果用对称量化,整个量化区间被一个很大的极值撑开,0附近的量化步长就会偏大,导致背景和细微纹理特征的精度受损。这种情况下非对称量化能把零点映射到量化区间内的任意位置,对这类偏置分布友好得多。
不过需要说明的是,不同推理框架和硬件对对称/非对称的支持不一样。有的硬件只支持对称量化INT8,有的支持非对称。做部署时,先查你目标硬件的算子文档,再决定模型侧怎么做。我在某图像处理Demo上碰到过的情况是,GPU端INT8量化默认用对称量化,CPU端支持非对称,同一个模型两端的精度表现会有一点差异,这是正常的,不要慌。
2. 校准流程的关键细节
2.1 校准集绝不是随便拿几百张图凑数
PTQ的校准环节决定量化参数算得准不准,校准集的选择是重中之重。很多刚入门的朋友拿训练集切了两百张图就开始校准,这样不是不行,但有很大概率把你的量化参数带偏。工业质检里一个常见的问题:产线上正常品占比超过99%,缺陷样本本来就稀有,如果你随手取样,很可能两百张里只有一两张带缺陷,量化时激活值分布完全被正常品主导,模型对缺陷区域的分辨能力就会显著下降。
我的习惯是构建一个专门用于校准的小数据集,大约500到1000张,里面必须系统性覆盖各类缺陷形态、不同光照条件、不同产品型号。缺陷样本至少占比20%,形态要尽量多样。说白了,量化参数是要让模型在所有输入下都有好的表达,你不能在训练时教了模型缺陷长什么样,结果校准数据里全是白板,多少有点说不过去。
构建好校准集之后,每次迭代模型、更新训练数据后,这个校准集也要跟着更新,保持一致性和代表性。如果产线新增了产品型号或者新的缺陷类型,校准集不更新的话,量化后的模型很可能在新缺陷上翻车。
2.2 MinMax、KL散度还是百分位
校准其实是在统计激活值分布,然后决定一个合适的量化范围。不同校准算法对极值数据的敏感度完全不同。
MinMax最简单,就是拿激活值的全局最小最大值当量化边界。它的问题在于对离群值毫无抵抗力。工业图像中偶尔会出现高亮的反光点或噪点,这些离群值会把量化边界撑得很大,结果大量正常像素只占用了很少的量化区间,白白浪费了精度。
KL散度是很多框架默认的校准方法,思路是找一个截断阈值,让截断后放大量化区间的分布和原分布尽量接近。它对长尾分布适配得比较好,但实现各有差异,对分布尾巴的截断阈值处理很敏感。我用下来的感受是,对于检测类的单阶段模型,KL散度表现稳健,一般不会出大问题,推荐作为首选项。
百分位校准是我后期比较依赖的方法。简单讲就是把激活值从低到高排序,取99.9%分位点作为最大值,主动把最大的0.1%当成异常值截掉。这个思路非常适配工业质检场景,因为缺陷检测的特征图天然存在大量接近0的背景值,和稀疏的强响应值。取99.9%或99.99%分位,能够在保证大缺陷不饱和的前提下,让微弱缺陷对应的特征也有可用的量化级别。
三种方法对比下来,工业质检模型的校准优先级我个人排序是:百分位 > KL散度 > MinMax。当然这是经验之谈,具体可以三个都跑一遍,用同样的验证集比较量化前后精度,选最优的那个,成本也不高。
| 校准方法 | 核心思路 | 对离群值的敏感度 | 适用场景 |
|---|---|---|---|
| MinMax | 全局极值作为范围 | 高 | 分布干净、无明显异常点的模型 |
| KL散度 | 截断后最小化信息损失 | 中 | 多数检测和分类模型,默认基线 |
| 百分位 | 按分位点截断极值 | 低 | 工业质检这类长尾分布明显的场景 |
2.3 校准的batch size和预处理一致性
校准时的batch size对结果有影响,这个细节经常被忽略。如果校准过程里一个batch过大,统计出来的激活值分布会被平均掉,一些单张图中的强响应特征被弱化;如果batch太小,统计噪声又偏大。我的经验是校准batch size设置在8到32之间比较合适,走完整个校准集,最后汇总分布。
比batch size更重要的,是校准时的预处理必须和实际部署完全一致。你不能训练时用了归一化参数是mean=0., std=1.,结果校准和推理时换了另一套归一化,或者在校准时忘了做和推理一样的缩放。曾经我就因为校准数据忘记做归一化,导致量化后整个模型的检测能力崩盘,排查了很久才发现是预处理不一致,浪费了整整一个下午。这个教训拿出来说,是想提醒各位,量化前后的预处理一致性必须用同一套代码路径,不要图省事复制粘贴后改参数。
3. 敏感算子定位与混合量化实操
3.1 为什么量化后精度下降的往往是这些层
不是所有层对量化都一视同仁。模型里不同的算子对数值误差的敏感程度差异极大,这是混合精度量化存在的根本原因。卷积层和全连接层通常比较皮实,量化误差会被后续层逐步吸收;但检测头的分类分支、边界框回归分支,以及最后输出层的数值对误差高度敏感,因为目标框的坐标回归本质上是连续值预测,差之毫厘就可能让框偏移好几个像素。
工业质检目标往往很小,比如划痕、脏污可能只有十几到几十个像素大小。这些小目标对应到特征图上就是很小的激活区域,对量化误差的容纳能力特别弱。如果量化后这些局部特征的值被压缩到相近的量化级别,目标就直接消失在背景里了。
Softmax之前的logits层也是一个典型问题点。量化会在logits上引入一定扰动,当两个类别的logits本身很接近时,这种扰动会改变最终的分类结果,在质检场景里就可能把“轻微缺陷”误判成“良品”。
3.2 敏感层定位的两种实用方法
混合量化之前,先得知道哪些层敏感。这里分享两个成本可控的方法。
第一种叫逐层替换法。把模型做成可配置的,每次只把某一层设置成FP16,其他层保持INT8,在固定验证集上跑一遍,记录精度指标,然后继续下一层。这样跑下来,哪一层替换后精度恢复最明显,那就是敏感层。这个方法看起来笨,但非常直接,几百层的模型你不需要全部试,重点试检测头和最后几层就够了。我之前定位一个漏检率飙升的问题,就是靠这种方法,标记出三个敏感卷积层,把它们保留为FP16后,漏检率马上恢复了。
第二种是启发式搜索法。不去逐层测,而是根据激活值的一些统计特征给层打分,比如动态范围、饱和比例、奇异值占比,把这些分数排个序,优先把异常分数高的层切换成FP16。这个方法速度快,但准确率不如逐层替换法。实际项目中我一般是先用启发式搜索锁一个大方向,再针对得分最高的十几个层用逐层法精调。
3.3 混合精度配置参考
完成后就有了一个层级别的精度配置表,格式大致像这样:
{ "calibrator": "percentile", "percentile": 99.9, "precision": "INT8", "mixed_ops": [ { "op_name": "backbone.stage4.conv2", "precision": "FP16" }, { "op_name": "necks.0.upsample", "precision": "FP16" }, { "op_name": "detect_head.0.reg", "precision": "FP16" }, { "op_name": "detect_head.1.cls", "precision": "FP16" } ] }给敏感算子保留FP16后,大部分算子的INT8算力优势还在,整体速度和全INT8差距不大,但精度能稳住。工业质检场景我强烈建议不要追求100%全INT8,只要漏检率误检率达标,保留几个关键层FP16完全可接受。
另外说个经验,频繁减小的通道数、很小的卷积核(1x1)、跨层连接的加和节点,这几个位置的敏感度比较高,配置混合精度时优先检查这几个地方。很多框架的校准工具会输出每层的量化误差热力图,也可以看那个图来辅助判断,比拍脑袋准确得多。
4. 部署适配与推理工程细节
4.1 不同硬件的量化支持差异
量化方案的最终效果跟推理硬件强相关。工业侧常见的有三块平台:GPU工控机、x86 CPU、ARM或NPU边缘盒子。它们对量化的支持路线完全不同。
GPU平台上,TensorRT的INT8是比较成熟的一条路线。它能自动扫描网络结构,选择最佳的卷积实现,但前提是算子都得支持。如果你有一些自定义算子,需要写插件层。TensorRT INT8推理起来性能提升明显,而且显存占用小很多。
CPU平台上,OpenVINO的INT8走的是另一条路。它对x86 CPU的向量指令集做了适配,量化后吞吐提升明显,但单张图的延迟改善相对有限。如果产线一次性并发处理多张图,比如多相机同时触发,OpenVINO的INT8优势能发挥出来。另外CPU上如果你用的动态库没开对应指令集,比如AVX512没打开,那么INT8的速度优势会大打折扣,部署时要注意看系统日志里的指令集信息。
ARM或NPU盒子则更封闭一些,通常要先用厂商提供的工具链把模型转换成特定格式,很多工具链在INT8转换时还会强制加入一些特定的重构图策略或量化规则,转换失败或精度很崩的情况并不罕见。遇到这种情况,先查算子兼容性表,看模型里哪些算子走的是CPU模拟而非NPU加速,这些都是部署评估阶段就应该摸底的信息。
工业质检项目里硬件选型一般早就定好了,你只能在限定平台里把量化调好。所以做模型设计时就要考虑部署平台,尽量用平台支持的算子,把自定义层排除在设计之外。部署评估晚做不如早做,特别是有NPU参与的项目,硬件适配计划必须从模型设计的第一天就介入。
4.2 输入尺寸、动态Shape与批处理策略
工业质检的输入图片尺寸一般是固定的,比如1280x960或者2048x1536,这跟消费级场景那种动态分辨率的诉求不一样。固定分辨率对量化很有利,因为量化参数和TensorRT的引擎优化都能基于固定Shape做更激进的优化。所以我一直建议,产线项目应该固定输入尺寸,算法侧做Resize和Padding统一到同一尺寸,省心省力。
尽量避免动态shape。动态Shape会让TensorRT在构建引擎时不得不考虑多种形状的可能性,优化空间变小,在INT8模式下还会增加额外的显存缓存。如果你的系统确实需要支持多分辨率输入,那建议为每种分辨率分别构建一套引擎,运行时切换,而不是依赖动态Shape机制。
批处理策略方面,工业质检很多是单品单相机单张触发,batch size为1,GPU利用率上不去。这时候INT8量化可以作为一个缓解手段,因为它把计算密度提升了,即使batch为1,也能榨出不少性能。反过来,如果你的产线是多相机并行触发多张图,那batch size可以设成4或8,配合INT8的吞吐优势,整体效率会非常可观。
4.3 算子兼容性排查与裁剪
量化阶段最烦的事情就是算子不支持。你辛辛苦苦把模型转成INT8,结果某个算子在推理框架里没有INT8实现,框架要么直接报错,要么偷偷回退成FP32计算。这时候表面上模型是“INT8量化”,实际上一半的层还在用FP32,性能提升大打折扣,精度表现还变得很奇怪。
排查算子兼容性,我一般会在量化和导出之前先跑一遍离线算子检查工具,把模型结构扫描一遍,拿到不支持的算子清单。然后根据清单决定三件事:一是尽可能在模型设计阶段替换掉这些算子,比如把某些动态操作改写成静态等价实现;二是给个别必须保留的算子写框架插件;三是最极端的情况,把模型结构调整一下,避免这些算子出现在关键路径上。
有一回我为了处理一个动态Resize算子,查了很多资料才发现问题出在Pytorch导出的onnx里把输入分辨率当成动态值了。把这个动态维度固定成常量以后,算子兼容性问题直接消失。所以导出模型时一定要把动态轴封死,再检查一遍算子版本,这种小事经常能省下一天排查时间。
5. 常见问题与排查技巧
5.1 量化翻车现场速查表
量化过程中遇到的各种问题,我把常见现象、对应思路和排查手段整理成了一张速查表,方便按图索骥:
| 常见现象 | 可能原因 | 排查方向 |
|---|---|---|
| INT8后单张延迟反而升高 | batch为1时INT8优势不明显、算子回退FP32、CPU指令集未打开 | 看推理日志有没有算子回退,跑profiler看各层耗时占比 |
| 量化后小目标漏检率升高 | 小目标对应的特征量化后信息丢失 | 对比量化前后激活值分布,定位敏感层,混合精度保留FP16 |
| 特定光照下误检增多 | 校准集缺少该光照条件的样本 | 补充对应光照的校准样本,重新校准 |
| 量化后输出出现NaN或异常值 | 数值溢出,量化范围设置不当 | 检查激活值分布,降低百分位阈值或改用KL散度 |
| 模型文件变小但推理速度没变化 | 模型本身不是计算瓶颈,瓶颈在前处理或后处理 | 先做耗时拆解,优化解码和预处理 |
| 换到新产线精度下降明显 | 新产线的图像分布和校准集差异大 | 从新产线采集数据补充校准集,重新校准 |
5.2 典型案例:漏检率为什么突然飙升
说一个我具体踩过的例子。某图像处理Demo里的一个外观检测模型,原本FP32精度很稳,PTQ之后漏检率从千分之三飙到了百分之二点几,客户那边立刻就不乐意了。一开始我怀疑是校准集质量不够,就扩充了校准集重新校准,结果改善非常有限。接着用逐层替换法扫描,发现检测头第一层卷积的激活值分布极不均衡,最大激活值比中位激活值大了三个数量级。校准算法被这个极端值带偏,导致小目标的弱特征全部丢失。
定位到问题后,我把这一层放进混合精度的FP16白名单,重新导出引擎,漏检率马上回到了千分之四左右。整个排查过程花了大概两天,其中扫描定位用了大半天。后面我把这个经验沉淀下来,成了我们项目组量化上线前的固定检查项——检测头相关层必须检查激活分布,必要时直接加入混合精度配置。
千万不要在量化后就只看整体正确率,一定要把漏检样本单独拎出来看。质检场景里漏检样本往往就是模型在量化中受伤最重的那一部分,看整体指标很容易把这些问题藏掉。
5.3 校准集过拟合的隐蔽问题
还有一个容易被忽视的问题:校准集过拟合。如果校准集太小,量化参数会过度拟合校准数据的分布特征,导致模型在实际产线数据上表现飘忽。我见过有团队校准只用100张图,评估指标也非常好看,但上线当天就被产线的暗光图像教训了。
校准集过拟合的一个典型特征是:同一个模型、同一套代码,在校准集所在分布的数据上测试一切良好,换个批次或者换个光照条件,精度立刻下降。排查方法也比较简单,准备一个独立于校准集的影子测试集,每次量化后先在这个影子测试集上跑一遍,如果校准集上的提升没有迁移到影子测试集,说明量化参数过拟合了,需要扩大校准集或调整校准算法。
所以我的习惯是:校准集和测试评估集严格分离,且评估集要能代表真实产线数据的多样性。这两套数据要像训练集和验证集那样分开管理,千万不要混着用,这是量化可靠性的基础底线。
6. 效果评估与长期维护
6.1 离线指标别只看mAP
很多算法团队习惯用mAP衡量模型好赖,但工业质检项目里,mAP只能作为辅助参考,真正说话的是漏检率和误检率。漏检率表示应该被检出的缺陷没有检出,这个直接跟客户损失挂钩;误检率表示把良品判成缺陷,这个影响产线效率,因为每一次误检都会触发停机或人工复判。
在量化前后的对比评估里,我会同时关注Precision、Recall、F1-score和PR曲线,但决策时以漏检率、误检率的变化为主要判断依据。比如某个模型量化后mAP掉了0.8%,但漏检率只上升了0.01%,误检率保持不变,那完全可以接受。反过来,mAP没怎么变但漏检率翻倍,这就绝对过不了关,需要回去做混合量化。
评估用的测试集也必须反映真实产线。只靠训练集切出来的测试集不太够,最好从实际产线中持续采集正常品和各种缺陷品,建一套独立的回归测试集。这套测试集不是一次性用的,每次模型优化、量化参数调整后,都要跑一遍回归。
6.2 性能评估要看延迟分布,不是单次平均
推理性能评估也不能只看平均延迟,工业场景里P95和P99更能反映真实体验。平均延迟60ms看着不错,但P95可能到120ms,意味着每二十张图就有一张会超节拍,产线可能因此卡顿。所以性能测试至少统计P50、P95、P99三档延迟,并跑足够多次,比如上千张图,避免偶发波动影响判断。
注意点:第一次推理往往有模型加载、显存分配的开销,不能被计入正式的延迟统计。通常的做法是预跑几十张图做warm-up,让GPU或CPU跑顺了再开始计时。如果你发现P95特别高,进一步用profiler看看是不是某几张特殊图像触发了更长的前处理耗时或内存分配延迟。
功耗和占用也要一口气记录。边缘盒子上INT8量化后,功耗通常也会有下降,在夏天产线没有空调的车间里,这会让设备稳定很多。显存占用方面,INT8通常能压到FP32的一半不到,对显存吃紧的工控机挺有意义。
6.3 量化模型上线后的长期维护机制
量化模型上线不是终局,长期维护同样重要。产线的产品型号会变,缺陷类型会变,光照环境、相机参数也会变。任何一个变化都可能导致量化模型的精度漂移,因此要建立一套周期性的监控机制。
具体做法是,在线运行时抽样保存推理图像和结果,每天或每周拉下来一批数据,和人工复核结果做对比,统计漏检率、误检率的变化趋势。一旦发现某个指标连续多日超过阈值,就要拉响警报,回查校准集是否需要更新、模型是否需要重新校准甚至重新训练。
校准集本身也要随产线变化做版本管理。最好每个版本校准集都有对应的量化配置、测试集和评估记录,形成一套可追溯的台账。这样就算出现问题,也能比较快地定位是数据漂移、校准参数变化还是模型版本更新造成的。工业质检讲究一个“稳”字,可追溯性比什么都重要。
写在最后
模型量化在工业质检里的优化,并不是一个能一次性解决的问题。从方案选型到校准配置,从混合精度到部署适配,再到长期评估维护,每一步都藏着细节。实测下来,对大多数检测类模型,PTQ加百分位校准加少量混合精度是性价比最高的组合,既能保住漏检率和误检率,又能获得可观的性能提升。我个人的体会是,量化之前一定要有一点耐心去剖析瓶颈,量化之后更要有耐心去定位敏感层,这个投入和回报是完全成正比的。
最后分享一个小技巧:每次跑量化实验,把校准集、校准参数、混合精度配置、测试集指标、运行环境硬件全部记录在案。别图省事不记录,等到过了几个月新问题出现时,你会感谢当初那个勤快的自己。