1. 当产线上全是良品AI质检模型到底在学什么“没有不良品样本怎么做 AI 质检”这个问题我在过去两年里被问过不下二十次提问的人有做消费电子的工艺工程师有做汽车零部件的质量主管也有刚接手产线智能化改造项目的算法同学。大家的处境几乎一模一样老板说要上 AI 质检预算批了相机装了算力盒子也买了结果回头一翻历史数据——全是良品不良品要么被人工挑走没留档要么压根就没出现过几次。这时候传统的二分类思路直接卡死因为你连“坏”长什么样都不知道模型拿什么去学先把结论摆在前面没有不良品样本并不等于做不了 AI 质检而是意味着你要换一套建模思路。这时候真正的主角不是“分类器”而是“异常检测”。核心逻辑从“学会分辨好和坏”变成“把好的学透凡是和好不一样的就报警”。这个思路转换听起来简单但落地时涉及的信号处理、特征工程、阈值设定、误报控制每一步都有坑。这篇文章我就围绕这个场景把从数据采集到模型上线再到误报治理的完整链路拆开讲一遍重点讲清楚为什么这么选、参数怎么定、实测中会遇到什么。需要说明的是这类方案特别适合单件价值高、不良率极低、不良样本难以收集的场景比如精密结构件、连接器、PCBA 焊点、电机异响检测等。如果你所在的产线不良率有百分之几样本管够那老老实实做有监督分类更省事本文的思路反而绕远了。判断标准很简单当你手里的不良样本少于 30 个或者根本凑不齐就该考虑异常检测这条路。关键词里出现了 One-Class SVM、MFCC、MCU 这几个词其实已经暗示了典型的技术组合用 MFCC 做声音特征提取用 One-Class SVM 做单类建模用 MCU 做边缘侧的采集与推理。这套组合在异响检测、电机质检里非常常见我后面会重点围绕它展开同时也会讲清楚它在图像质检场景下该怎么变通。2. 异常检测的三种主流打法以及它们各自的适用边界在动手之前得先想清楚用哪一类方法。异常检测不是单一算法而是一整个家族选错了方向后面全是白费功夫。我把它归成三类每一类我都实际跑过下面说说各自的脾气。2.1 单类分类只学好样本划一个边界出来One-Class SVM 是这一类里最经典的代表。它的思路是把所有良品样本映射到一个高维空间然后在这个空间里找一个尽可能紧凑的边界把良品都包进去边界之外的一律判为异常。数学上它是在解一个二次规划问题通过核函数常用 RBF 高斯核把非线性边界表达出来。它的优点是只需要良品数据完美契合“没有不良品样本”的处境。缺点是它对参数极其敏感尤其是 RBF 核的gamma和惩罚系数nu。nu这个参数直接控制“允许多少比例的良品被划到边界外”设成 0.05 就意味着你主动接受 5% 的良品被误判。这个参数没有理论最优值只能靠实测调。我一般建议新手从nu0.01起步gamma用1/(特征数 * 特征方差)这个经验公式先给个初值然后再网格搜索。别一上来就追求零误报那是不可能的先让模型能跑起来、能报警再慢慢收紧。2.2 重构类方法让模型学会“复原”良品复原不好就是异常自编码器Autoencoder是这类方法的代表。原理很直白用良品数据训练一个网络让它学会把输入压缩再还原。因为训练时只见过良品所以它对良品的还原能力很强重构误差很小一旦来了个没见过的异常样本它还原不出来重构误差就会飙升超过阈值就报警。这类方法在图像质检里用得特别多因为卷积自编码器可以直接吃原始像素省去了手工设计特征的麻烦。但它的坑在于如果异常区域很小比如一个针尖大的划痕自编码器很可能把它也“平滑”掉了重构误差上不去导致漏检。解决办法是用重构误差的热力图做定位而不是只看整图的平均误差这样小缺陷也能被放大出来。2.3 密度估计与距离度量算一算这个样本离良品群体有多远这类方法包括高斯混合模型GMM、核密度估计KDE以及更简单的马氏距离、k 近邻距离。核心思想是估计良品数据的概率分布新样本落在低概率区域就判异常。它的好处是可解释性强你能明确知道是哪个特征维度偏离了。在 MFCC 声音检测里我经常用马氏距离因为它考虑了特征之间的协方差比欧氏距离靠谱得多。缺点是当特征维度很高时密度估计会变得不稳定需要先做降维PCA 或 t-SNE。下面这张表是我总结的选型参考实际项目里我基本按这个来定方向方法类型代表算法数据要求适用场景主要坑点单类分类One-Class SVM仅良品特征维度中等、样本量几百到几千参数敏感需反复调 nu 和 gamma重构类Autoencoder仅良品样本量大图像、频谱图等结构化数据小缺陷易被平滑需看热力图密度估计GMM、马氏距离仅良品特征可解释性要求高高维下不稳定需降维选型时我的经验是先看数据形态。如果是图像优先自编码器如果是声音或振动信号先提 MFCC 或时频特征再用 One-Class SVM 或马氏距离如果特征维度超过 50先降维再说别硬上。3. 从声音到特征MFCC 在异响质检里的完整处理链路关键词里有 MFCC说明这个场景大概率是声音质检。MFCC梅尔频率倒谱系数原本是语音识别的经典特征但它在工业异响检测里同样好用因为人耳对异响的感知和 MFCC 的建模方式高度吻合。我拿电机异响检测举个例子把整条链路走一遍。3.1 采集环节采样率和麦克风位置决定了一半的成败很多人一上来就纠结算法其实采集没做好后面全是白搭。工业现场的异响检测采样率我一般选16kHz 到 44.1kHz。为什么因为人耳能听到 20Hz 到 20kHz而异响的高频成分往往在 8kHz 以上如果采样率只有 8kHz高频细节直接丢了模型再强也救不回来。16kHz 能覆盖到 8kHz够用如果要做精细的轴承故障诊断建议上 44.1kHz。麦克风的位置更关键。我踩过的坑是麦克风离电机太近录到的全是电磁噪声和机械振动传导声离太远异响又被环境噪声淹没。实测下来麦克风距离被测物 10 到 30 厘米、偏离正对方向 30 到 45 度效果最稳。这个角度能避开正面的气流噪声同时保留异响的指向性成分。还有一点一定要录足够长的单段音频。我一般每段录 3 到 5 秒太短了统计特征不稳定太长了单段里可能混入多个工况。录的时候要保证工况一致比如都在额定转速下否则良品样本本身就不“纯”模型学出来的边界是歪的。3.2 分帧与加窗把连续信号切成可分析的小块声音信号是连续的直接做 FFT 没有意义必须先分帧。标准做法是帧长 25ms、帧移 10ms。25ms 对应 16kHz 采样率就是 400 个采样点这个长度既能覆盖一个完整的音素周期又不会长到丢失时间分辨率。帧移 10ms 意味着相邻帧有 15ms 重叠保证帧与帧之间平滑过渡不会漏掉瞬态异响。分完帧要加窗最常用的是汉明窗Hamming。为什么加窗因为直接对一帧做 FFT帧两端的突变会产生频谱泄漏让频谱变得模糊。汉明窗把帧两端平滑地衰减到零泄漏就小多了。这一步很多人会忽略但不加窗的 MFCC 特征质量会明显下降。3.3 梅尔滤波器组与倒谱MFCC 的核心两步加窗之后做 FFT得到频谱。然后过梅尔滤波器组——这是一组三角形滤波器低频密、高频疏模拟人耳对低频更敏感的特性。滤波器个数一般取26 个这是语音领域的经验值工业场景我也沿用它实测够用。取完对数能量后做 DCT离散余弦变换得到倒谱系数。通常取前 13 维因为前几维集中了包络信息后面的高阶系数主要是噪声。如果要做异响检测我建议取到13 到 20 维因为异响的高频细节可能藏在稍高阶的系数里。最后一步很多人不做但很重要加一阶差分和二阶差分。差分系数描述的是 MFCC 随时间的变化率异响往往是瞬态的变化率特征比静态特征更能反映问题。加上差分后特征维度变成 39 维13 静态 13 一阶 13 二阶这个维度对 One-Class SVM 来说刚好不算太高。3.4 特征聚合把每帧的 MFCC 汇总成一条样本到这里你得到的是每帧一个 39 维向量一段 3 秒的音频有大约 300 帧。One-Class SVM 需要的是“一条样本一个向量”所以要把这 300 帧聚合。最简单的做法是取均值和标准差拼成 78 维。但均值会丢掉瞬态信息所以我更推荐同时保留最大值、最小值、峰峰值拼成 39×5195 维。维度上去了就得考虑降维。我一般用 PCA 降到 20 到 30 维保留 95% 的方差。降维不仅加快训练还能去掉噪声维度实测误报率能降一截。这一步的参数没有固定值得看你的数据但“保留 95% 方差”是个稳妥的起点。4. One-Class SVM 的调参实战nu 和 gamma 到底怎么定特征准备好了接下来是建模。One-Class SVM 在 scikit-learn 里的接口是sklearn.svm.OneClassSVM核心参数就三个kernel、gamma、nu。kernel默认rbf一般不用改。真正要调的是后两个。4.1 nu 的本质你愿意接受多少良品被误判nu这个参数官方定义是“训练误差的上界支持向量的下界”翻译成人话就是它近似等于你允许被划到边界外的良品比例。设nu0.01意味着模型会主动把大约 1% 的良品判成异常。这个参数直接决定了模型的敏感度。我见过太多人一上来就设nu0.001想追求零误报结果模型边界收得太紧稍微有点正常波动的良品就被报警产线天天响最后没人信这个系统。我的建议是从nu0.05开始先让模型宽松一点把明显异常抓出来再逐步收紧到 0.01 到 0.02。这个过程要配合产线实测不能只在离线数据上调。4.2 gamma 的取值核函数的“影响半径”gamma控制 RBF 核的宽度值越大单个样本的影响范围越小边界越复杂、越容易过拟合值越小边界越平滑、越容易欠拟合。scikit-learn 的默认值是1/n_features但这个默认值在特征维度变化时不一定合适。我的做法是先用gammascale即1/(n_features * X.var())跑一版看训练集上的异常比例。如果异常比例远高于nu说明gamma太大边界太碎如果几乎为零说明gamma太小边界太松。然后在这个基础上做网格搜索范围取[0.001, 0.01, 0.1, 1]几个量级。4.3 用良品做交叉验证没有异常样本怎么评估这是最棘手的地方——你没有异常样本怎么知道模型好不好我的做法是留出法加人工注入。把良品数据分成训练集和测试集训练集只用来训练测试集用来评估误报率。然后人为往测试集里注入一些“伪异常”比如加噪声、做时间拉伸、叠加其他设备的录音看模型能不能抓出来。这个方法不完美因为伪异常和真实异常分布可能不一样但它至少能告诉你模型有没有基本的区分能力。实测下来如果模型对伪异常的召回率低于 80%那真实异常的召回率大概率也不乐观得回去检查特征。还有一个更靠谱的办法上线后做灰度。先让模型只报警不拦截跑一两周把报警的样本人工复核一遍标记出真异常和误报。这批数据就是你最宝贵的第一批真实异常样本拿回来重新训练模型会快速迭代。我做过的一个项目第一周误报率高达 15%人工复核后重新调参第二周就降到 3% 以下。5. MCU 边缘部署把模型塞进产线盒子里的现实约束关键词里有 MCU说明这个方案最终要落到边缘设备上。这里有个残酷的现实One-Class SVM 的推理在 MCU 上跑和你在 PC 上跑完全是两回事。PC 上 scikit-learn 几行代码搞定MCU 上你得考虑算力、内存、浮点支持。5.1 模型大小与算力预算一个训练好的 One-Class SVM支持向量的数量决定了推理复杂度。如果支持向量有 500 个每个都是 39 维那每次推理要做 500 次核函数计算每次核函数涉及 39 维的向量运算。对于主频 100MHz 级别的 MCU这个计算量在毫秒级勉强能接受但如果支持向量上千就吃力了。我的做法是在训练时限制支持向量的数量。scikit-learn 里可以通过控制nu和训练样本量来间接控制也可以用max_iter限制迭代。更彻底的办法是训练完后做支持向量剪枝把贡献小的支持向量去掉实测剪掉 30% 对精度影响很小但推理速度能快不少。5.2 定点化浮点运算在 MCU 上的替代方案很多低端 MCU 没有硬件浮点单元FPU浮点运算靠软件模拟慢得离谱。这时候要把模型定点化把权重和输入都转成 int8 或 int16。核函数里的指数运算exp()是定点化的难点通常用查表法或者多项式近似。这块我踩过的坑是定点化后精度损失比想象中大。尤其是gamma较大时核函数值对输入的小变化非常敏感定点化引入的量化误差会被放大。解决办法是定点化后重新在测试集上验证如果误报率上升超过 2 个百分点就得考虑用 int16 而不是 int8或者换用线性核。5.3 采集与推理的时序配合MCU 不仅要跑推理还要控制麦克风采集。这里有个时序问题采集是连续的推理是离散的。我的方案是 MCU 用 DMA 把音频搬进缓冲区攒够一帧25ms就做一次 MFCC攒够一段3 秒就触发一次推理。中间用双缓冲避免数据覆盖。实测中要注意 MCU 的主频和 DMA 带宽是否匹配。如果采样率 16kHz、16 位精度每秒数据量是 32KBDMA 完全扛得住。但如果同时还要跑其他任务比如控制电机就得用 RTOS 做任务调度把推理任务放到低优先级避免影响实时控制。6. 误报治理上线之后真正的硬仗模型训好了MCU 也部署了你以为就完事了真正的挑战才刚开始。产线环境是动态的温度变化、设备磨损、背景噪声都会让良品的特征慢慢漂移今天不报警的样本下个月可能就报警了。误报治理是个持续的过程我把它分成三层来做。6.1 第一层阈值动态调整One-Class SVM 输出的是决策函数值正负代表正常与否绝对值大小代表偏离程度。固定阈值在初期够用但长期看一定会失效。我的做法是用滑动窗口统计良品的决策值分布动态调整阈值。比如取最近 1000 个样本的决策值算均值和标准差阈值设为均值减去 3 倍标准差。这样当良品分布整体漂移时阈值会跟着走不会突然大面积误报。这个滑动窗口的长度要权衡太短了阈值抖动大太长了跟不上漂移。我一般用 1000 到 5000 个样本具体看产线节拍。节拍快的用大窗口节拍慢的用小窗口。6.2 第二层多特征投票单一模型误报总是难免的但如果多个独立的特征通道同时报警那可信度就高多了。我在异响检测里会同时跑三路特征MFCC、时域包络、频带能量。三路里至少两路报警才最终判定为异常。这个“投票机制”能把误报率压下去一大截代价是漏报率可能略微上升但产线场景下宁可漏报也不能天天误报因为误报会让操作员失去信任。6.3 第三层人工复核闭环再智能的系统也离不开人。我的方案是所有报警样本自动存档操作员在界面上标记“真异常”或“误报”这些标记数据每周汇总一次用来更新模型。真异常样本积累到一定数量后就可以从纯异常检测切换到半监督甚至监督学习模型精度会有一个质的飞跃。这个闭环的关键是降低操作员的标记成本。如果标记一个样本要点五下鼠标没人愿意干。我的做法是界面上只留两个大按钮一键标记同时把报警样本的音频和频谱图并排显示操作员看一眼就能判断。实测下来标记一个样本平均 3 秒操作员完全能接受。7. 几个我踩过的坑和对应的解法最后分享几个具体的坑都是真金白银换来的经验希望能帮你少走弯路。第一个坑良品样本不“纯”。我做过一个项目训练数据全是良品但模型上线后误报率奇高。排查了半天发现训练数据里混进了一批“勉强合格”的样本这些样本的特征分布和真正的良品有差异导致模型学出来的边界是歪的。解法是训练前一定要做数据清洗把边界样本剔除。怎么判断边界样本用聚类算法跑一遍离主簇远的就剔掉。第二个坑环境噪声被当成异常。产线旁边有台冲床每隔几秒“哐”一声模型每次都报警。解法是在特征提取阶段做背景噪声抑制具体做法是录一段纯背景噪声算它的 MFCC 均值然后从每帧 MFCC 里减掉这个均值。这个操作叫倒谱均值归一化CMN简单但极其有效。第三个坑模型更新后性能回退。有次我用新数据重新训练结果误报率反而上升了。原因是新数据里混入了之前误报的样本这些样本被错误地当成了良品。解法是维护一个“确认良品”白名单只有经过人工确认的样本才能进训练集报警样本一律不进除非被标记为真异常。第四个坑MCU 推理结果和 PC 不一致。PC 上跑得好好的模型烧进 MCU 后结果对不上。排查发现是定点化的舍入方式不同PC 用四舍五入MCU 用截断。解法是统一舍入策略并且在 PC 上模拟定点化过程做验证别等烧进去了才发现。这套方案我从头到尾跑过好几遍从最初的误报率 20% 一路压到 2% 以内靠的不是什么高深算法而是把采集、特征、调参、部署、治理每个环节都抠细。没有不良品样本确实是个约束但它逼着你去理解“正常”到底是什么样子反而让模型对良品的刻画更深刻。如果你也在做类似的项目欢迎从 MFCC 加 One-Class SVM 这个最小可用组合开始先跑通再优化别一上来就追求完美。