资讯动态

SVDD多类分类扩展:单类超球体建模在工业异常检测中的实战

发布时间:2026/10/22 19:54:14 来源:尧图企业网站定制
你有没有遇到过这样的场景手里有一批正常工况的工业设备数据想用机器学习识别出“异类”但异常样本少得可怜根本凑不齐两类问题所需的训练集。传统的分类算法这时候往往全部失灵因为它们在设计上就默认“正负样本都得有”。而我今天想聊的支持向量数据描述SVDDSupport Vector Data Description恰恰就是为了解决这种“只有一类样本也能建模”的困境而生的。SVDD 的核心思路说起来其实很朴素不为每一类找一条“界线”而是找一个能圈住绝大多数目标样本的“最小超球体”。这个球体边界外的点就被认为是异常或者别的类别。我第一次接触这个思想时觉得它特别像在自己的院子里画一道篱笆——我不关心邻居家是什么样我只需要把自己的院子圈明白就行。也正是因为这个特性SVDD 不但可以用在异常检测、工业故障诊断上还可以通过巧妙的组合方式扩展到多类分类任务。这篇文章主要面向正在做算法调研、或者想把单类模型用到实际项目中的开发者。我会从 SVDD 的原理出发拆解把单个超球体拓展成多类分类器的几种常见思路然后把训练过程中的关键参数、避坑经验、以及验证流程一起梳理清楚。看完之后你至少能动手搭一个可用的多分类框架而不是停留在“懂概念”的阶段。1. 内容整体设计与思路拆解1.1 为什么单类模型也能做多分类先澄清一个容易混乱的点SVDD 本身是单类分类算法它的目标函数只描述“一个类”的分布范围。但多类分类本质上可以看成“一个测试样本到底属于哪一类”的判别问题。把 SVDD 拓展到多类思路通常是在训练阶段为每个类别训练一个独立的超球体然后在预测阶段让测试样本跟所有球体比较看它被哪个球“接受”。这里面最有意思的地方在于SVDD 的“拒绝”能力是天然自带的。任何一个类别在训练时都会得到一个半径只有落入这个半径内的样本才会被接受。这意味着多类组合后系统不仅能判断“属于哪一类”还能判断“这个样本不属于任何已知类”这是很多普通多分类器做不到的。对于工业场景里的“已知故障 未知新故障”并存问题这个特性就显得特别有价值。不过把多个单类模型拼成一个多分类器并不是简单地堆几个球就完事。不同类别之间如果分布重叠同一个样本可能被两三个球同时接受这时候怎么裁决或者一个样本没有任何球接受怎么处理都需要在整体设计里提前考虑清楚。1.2 方案选型背后为什么不直接上传统分类器你可能会问既然要分多类直接用支持向量机SVM、随机森林不就行了为什么还要绕一圈用 SVDD 来做多类答案要回到数据形态上看。传统分类器需要每类都有足够的训练样本类别之间还要尽量平衡。但在很多真实场景下这个前提根本不成立。比如设备故障诊断正常运行的数据可能攒了几十万条而某一种特定故障只出现过几次又比如新品类上市阶段的质检只有良品样本是可靠的次品形态还在不断变化。在这种“单类充足、异类稀缺”的数据上传统分类器很容易偏向样本多的类别泛化效果很差。SVDD 的优势在于它只需要用一个类别的数据就能定义“正常边界”对于样本量少的类别甚至可以单独建模。这就在设计层面解决了类别不平衡的老大难问题。此外SVDD 的训练不依赖异类样本的分布更新成本也低——如果某天出现了一种新的故障类型只需要对这个新类别单独训练一个球不用把所有模型推翻重来。这种增量扩展性在项目实践中是实打实的维护便利。1.3 多类分类器的整体框架设计在实际开发中我一般会把多类 SVDD 分成两层结构来设计底层是为每个类别独立训练的单类 SVDD 模型上层是决策融合模块。底层的每个球体负责输出“该样本是否属于本类”的得分比如归一化距离、基于半径的判断等上层则综合这些得分做最终分类。决策融合模块的设计是整个框架的灵魂。虽然“把样本分配给得分最高的类”是最直接的做法但它忽略了 SVDD 的“拒绝”语义。更稳妥的设计是加入一个“接受阈值”的概念如果样本在所有球体中的最高得分仍然低于某个阈值那么宁可把它判为未知类也不要强行塞进某个已知类。这个思路在开放集识别Open Set Recognition里尤其重要。我后面会详细展开这些组合策略的差别以及具体编码和调参时需要注意的细节。2. 多类扩展的几种主流组合策略2.1 一对多为每个类别圈一个“势力范围”一对多策略是最直观的做法。假设总共有 K 个类别就训练 K 个独立的 SVDD 模型。第 i 个模型只使用第 i 类的样本作为目标样本描述出一个代表第 i 类分布的超球体。预测时把测试样本分别送入 K 个模型中计算它到每个球心的距离。如果样本落在某个球体内就把它判为对应类别如果落在多个球体内可以选距离球心最近的那个或者选相对半径比例最小的那个。这种策略在训练上的代价是 O(K) 个模型但是每个模型之间互相独立非常容易并行化。当新增类别时也不需要重训已有类别的模型这部分做过工程项目的人都懂有多香。不过它也有麻烦的地方不同类别球体的大小可能差异很大直接比较“到球心的距离”并不公平。一个半径很大的球和一个半径很小的球虽然都接受了同一个样本但前者可能类别分布很松散后者则非常集中。这时候需要用样本到球心的距离除以该球的半径得到相对距离再按相对距离做判别效果会好很多。这个细节我第一次实现时漏掉了当时测试集上准确率一直提不上去后来才发现是两个球体的尺度差异在做怪。2.2 一对一两两配对互相博弈如果说一对多是“各管各的”那一对一就是“互相打架”。针对 K 个类别任选两个类别为一组总共训练 C(K, 2) 个二分类 SVDD 模型。这里的二分类 SVDD 不再是把每个类别单独描述而是用两个类别分别训练各自的球体然后通过比较哪个球更“认可”该样本来做裁决。预测时让样本在所有两两组合的模型里投票——某两个类别竞争时赢的一方得一分。所有配对结束后累计分最高的类别就是最终预测结果。这种策略在类别数量不多时效果相当好因为它每次只面对两个类别判别边界更加精细不容易出现一对多那种“一个球罩住所有类别”的情况。但代价也很直观当 K 变大时C(K, 2) 会迅速膨胀。比如 10 个类别就需要 45 个模型50 个类别就是 1225 个模型。如果每个模型训练时间要几秒钟整个训练周期会让人等到怀疑人生。所以一对一更适合类别不多、但类别间重叠比较严重的场景。我个人的经验是K 小于 15 时用一对一通常都能接受。2.3 决策层融合与多种策略横评既然每组球体都会输出“接受/拒绝”和“距离/相对距离”决策层就可以做很多文章。除了上面说到的投票和相对距离排序还可以用归一化得分加权。比如对第 i 个类别的球定义得分 s_i 1 - d_i / R_i其中 d_i 是样本到球心的距离R_i 是球半径。这样得分范围在 0 到 1 之间得分最高的类别胜出。如果所有得分都很低说明样本没有进入任何一个球体可以判为“未知”。从我的使用经验看三种策略没有绝对的好坏只看适用场景。这里用一张表来对比策略训练模型数预测速度类别不平衡适应重叠类别区分新增类别开销一对多K快很好一般只需新增一个球一对一C(K,2)中等较好较好需新增 K-1 个配对模型决策层融合相对距离K最快很好视阈值设置而定只需新增一个球如果你刚开始尝试多类 SVDD我建议从一对多起步因为它的语义最简单、排查问题也方便。等把单类模型的调参手感练出来再根据自己的数据特性去尝试一对一或者更复杂的融合机制。3. 核心细节解析与实操要点3.1 核函数选择为什么 RBF 是默认选项SVDD 在原始空间里只能描述一个球形的边界这在实际数据里不够用。解决办法跟 SVM 一样通过核函数把样本映射到高维特征空间在高维空间里再找超球体。常用的核函数有线性核、多项式核和 RBF径向基核。我自己的经验是除非数据维度极低且本身就近似球状分布否则直接默认选择 RBF 核不要犹豫。RBF 核的表达式是 K(x, y) exp(-γ ||x - y||²)其中 γ 控制着“影响范围”。γ 越小高斯分布越平缓映射后的球体会越接近原始空间的大球γ 越大边界会越精细但也很容易过拟合把一个类别描述成很多小块反而失去了整体的“代表”意义。在实操中我一般会在 log 尺度下搜索 γ比如从 0.001 到 1000 之间按数量级取几个值配合交叉验证来选择。这里有个小技巧如果训练完成后支持向量的比例非常高比如超过一半的训练样本都成了支持向量多半是 γ 或者惩罚系数设得不对模型在硬记样本拿着这样的模型去做预测风险会很大。3.2 正则参数 C球的“收紧程度”由它决定SVDD 的目标函数里有一个惩罚系数 C它控制着对落在球外样本的容忍度。 C 越大系统越不能接受违反约束的样本边界会尽量把每一个训练样本都包进去C 越小边界越宽松允许少量样本外溢球体更紧凑。理解 C 的一个好方式是看它与错误的“预算”之间的关系。实际工程中C 经常设为 1 / (n · f)其中 n 是训练样本数f 是期望的异常比例。比如你有 1000 个目标样本希望大约 5% 的训练样本可以被视作外点那么 C 就可以取 1 / (1000 × 0.05) 0.02。这个初始值在多数项目中都能得到合理的边界省去不少盲调的时间。但要注意C 跟核函数参数 γ 是相互影响的。C 大了模型紧凑但如果 γ 也很小球体在特征空间里依然会显得很大两者必须放在一起调不能分开单独调。我通常的做法是先用默认值跑通流程再做一轮网格搜索搜索目标从准确率、召回率这些指标里选一个最贴合业务诉求的。3.3 数据预处理的三个细节坑数据预处理对 SVDD 的影响往往比很多人想象的要大。第一个坑是训练和测试缩放不一致。有人会直接对全量数据做标准化这相当于在训练模型时“偷看”了测试数据的统计信息会造成验证结果虚高上线就崩。正确的做法是只用训练集计算均值和方差然后用这组参数去变换训练集和测试集。第二个坑是特征尺度悬殊。SVDD 依赖距离计算如果某个特征的数值范围是 0 到 1另一个是 1000 到 10000后者会直接主导整个距离计算等价于降维只剩一两个特征。所以标准化几乎不是可选项而是必选项。第三个坑出现在类别差异明显、各类样本量悬殊的时候。不要对所有类别使用同一个标准化的均值和方差最好基于每个类别自己的训练数据做标准化再分别训练各自的球体。否则样本量大的类会拉着全局均值偏移导致样本量小的类在标准化后整体漂移边界描述失真。3.4 支持向量、半径与球心的关系再拆解SVDD 训练完成后会得到一组支持向量。这些支持向量可以分为两类落在球面上、恰好等于半径的是边界支持向量落在球外、被惩罚项容忍的是带有松弛变量的支持向量。球心和半径直接由这些向量确定这意味着超球体的边界完全由训练数据中“最难的那些点”塑造。这给调试带来一个很直观的启示如果你的训练集里存在标注错误或者严重离群的脏样本它们很可能变成支持向量并把球体撑大。这时候模型的表现并不是整体样本的特征而是被少数脏数据绑架了。所以我建议在训练前做一轮离群点筛查或者用较保守的 C 值让部分极端点落在球外。我还见过一种高效的调参思路先用线性核或者很小的 γ 快速训练一个球把所有训练样本的“到球心距离”画出来观察长尾分布。距离特别大的那几个点十有八九是有问题的样本。把这些点剔掉之后再用正式的参数重训模型的稳定性会提升很多。这个技巧在我处理工业传感器数据时非常管用。4. 实操过程与核心环节实现4.1 实验环境与数据准备这次演示我用的是一个模拟的工业质检数据集为了让流程更贴近实际我刻意把它构造得不那么“干净”。总共 6 个类别其中第 1 类到第 3 类的样本数量在 600 到 800 之间第 4 类是 200 个第 5 类只有 50 个第 6 类是 1000 个并带有 3% 的噪声样本。每个样本是 32 维的数值特征这里不再引入额外的复杂结构。环境方面Python 3.9 scikit-learn 就够用。SVDD 的完整实现可以用 scikit-learn 的 OneClassSVM 作为基础组件因为它也采用核技巧训练目标和 SVDD 很接近能快速验证组合逻辑。如果项目对性能要求更高可以考虑基于自己实现的 QP 求解器做定制化优化但那是另一个话题了后面如果有机会再单独展开。4.2 核心训练与预测封装我先把一个基于 OneClassSVM 的多类 SVDD 框架的核心代码思路写出来。这里的关键不是代码本身而是背后的流程设计所以我会用伪代码来展示方便迁移到你自己的工程语言里。def train_one_class_svdd(X_train, gamma, nu): # 使用 one-class SVM 近似 SVDD # nu 等价于之前提到的外点比例 f model OneClassSVM(kernelrbf, gammagamma, nunu) model.fit(X_train) # 记录球心相关的距离信息 train_dist model.decision_function(X_train) radius np.percentile(train_dist, 100 * (1 - nu)) return model, radius def predict_multi_svdd(models, radii, X_test): # models 是每个类别一个字典 scores [] for i, model in enumerate(models): dist model.decision_function(X_test) rel_dist 1.0 - dist / radii[i] scores.append(rel_dist) scores np.vstack(scores) # shape: (n_classes, n_samples) pred np.argmax(scores, axis0) # 如果最高得分低于接受阈值则判为未知类 max_scores np.max(scores, axis0) unknown_mask max_scores ACCEPT_THRESHOLD pred[unknown_mask] -1 return pred, scores这段代码里有两个关键点。第一radius 的估计用的是训练集得分分布的百分位数。因为 OneClassSVM 的决策函数得分本身跟半径成正比我们不需要精确知道特征空间里的球心坐标只需要一个跟“球面边界”对齐的刻度即可。第二预测时把得分转换为相对得分再送入 argmax 判断这样不同类别球体尺寸的差异就被抵消了。很多文章只讲模型训练不讲预测阶段的相对距离处理导致换了数据集以后准确率忽高忽低。我踩过太多次这种坑所以这里特意强调一下相对距离处理几乎是多类 SVDD 必做的一步少做一步效果可能差好几个百分点。4.3 参数搜索与交叉验证SVDD 参数搜索最常用的方法还是网格搜索加交叉验证。虽然每个类别的模型可以独立训练但验证时一定不能把类别单独切分开因为最终结果是在全局预测层面评估的。我建议把整个多类分类流水线做成一个函数内部包含每个类别的标准化、模型训练、组合预测和评估然后把这个流水线套进交叉验证循环里。验证指标需要根据业务场景区分。如果更关心“未知类发现率”就要重点看类别的召回率如果更关心“已知类不误杀”则要卡准每类的精确率。有一件事我想反复提醒不要只看整体准确率。多类 SVDD 在类别不平衡时整体准确率极具欺骗性。比如第 5 类只有 50 个样本就算完全认不出它准确率也只掉 1% 不到。这时候去看每个类别的 F1 值才能真正暴露问题。我在实际项目中试过成人数据集里整出一个 30 维的低维特征子集准确率可以达到 92% 左右。而用所有 200 维原始特征时反而掉到 88%——原因就是高维噪声特征干扰了距离度量。对于 SVDD 这类基于距离的模型特征选择不是一个加分项而是必要步骤。这一点经常被人忽略尤其是习惯用树模型的人因为树模型本身有特征选择机制不太吃这套。4.4 一次典型的实验结果复盘我用上面的流程跑完这个模拟数据后把结果按类别拆开来看得到这样一个分布第 1 类到第 3 类样本充足的 F1 都在 0.9 以上第 6 类虽然样本最多但是有 3% 噪声F1 在 0.86 左右第 4 类样本只有 200 个F1 掉到了 0.78第 5 类只有 50 个样本F1 只有 0.61而且误判到第 4 类的情况特别多。这个结果完全符合我对 SVDD 特性的预期。样本量少的类别超球体的描述不够完整边界容易出现空洞导致真正的类内样本反而落在球外或者被邻近类别的大球“吸入”。想缓解这个问题有几个方向一是给每个类别的半径加一个松弛系数允许小样本类的球半径稍微扩大一些二是改造打分函数对样本量少的类别做得分加权三是在训练阶段做 SMOTE 之类的过采样让描述边界时更有参考点。我把这三条都试了一遍效果最好的是“半径松弛 得分加权”的组合把第 5 类的 F1 从 0.61 拉到了 0.74。当然这类手工设计需要谨慎原则是不要让边界扩到淹没其他类别。每一步改动都要回到交叉验证里重新看全局结果而不是只盯单个类别的分数。5. 常见问题与排查技巧实录5.1 边界被离群样本撑大球体失去区分度这是我在 SVDD 项目里遇到最多的问题。表现是训练集上的效果很好但一到测试集就觉得模型“什么都愿意接受”很多明显不属于这个类别的样本也被划进去了。打开训练日志一看支持向量的比例高得离谱比如 80% 的样本都是支持向量。原因通常是正则参数 C 偏大、核函数的 γ 偏大或者训练集中存在脏样本。排查思路是先看训练样本到球心的距离分布找出距离特别大的那部分样本逐个检查是不是标注异常。如果脏样本确实存在把它们剔除后重训不要指望模型自动忽略它们。如果数据本身没问题那就把 C 调小一些让球体更紧凑。5.2 类别不平衡导致“大球吞小球”当某个类别样本量特别大它的球体往往也很“膨胀”边界扩张到邻近类别的腹地。预测时隶属小类别的样本反而被大类别的球抢先接收。这种情况在一对多策略下尤其常见因为每个球体都是独立训练的并不知道其他球体的存在。解决办法分两部分训练阶段可以限制每个球体的最大半径或者按类别比例调各自的惩罚系数预测阶段则一定要用相对距离而不是绝对距离。如果这两步都做了吞并问题依然严重考虑切换到一对一策略因为一对一每次只面对两个类别大球吞小球的场景会被有效抑制。5.3 多个球体同时接受样本裁决规则含糊样本落在两个或多个球体的重叠区域时系统不知道该判给谁。有些开发者随手写一个“取第一个接受它的模型”这显然是不对的因为模型的顺序会直接影响结果带进很多随机性。合理的处理办法是用相对距离的比值做裁决比如样本到 A 球球心的距离占 A 球半径的 60%到 B 球球心距离占 B 球半径的 95%那就判给 A因为在各自边界标准下它更靠近 A 的“内核”。这个比值其实就是前面代码中的 rel_dist挑 rel_dist 最大的那个就好。5.4 问题排查速查表现象可能原因排查方式解决建议支持向量比例过高γ 过大或 C 过大查看训练后支持向量占比调小 γ 或 C观察球体大小测试集误判集中在某个类该类球体半径过大查看各类 radius 分布按类别调 C或强加半径上限小样本类别几乎全被大类别吞掉绝对距离比较球体尺度不公平检查预测阶段是否用了相对距离用 rel_dist 做 argmax 对比模型对训练集变化过于敏感过拟合到个别离群点训练-测试划分换多个随机种子剔除离群点调小 γ 或 C在线场景中新增类别后整体准确率下降旧类别球体未重新校准观察旧类别 F1 是否有变化重新校准旧类别半径或阈值6. 写在最后的个人体会SVDD 这套方法我把它的原理和工程细节掰开揉碎讲了这么多最想分享的一点其实是多类 SVDD 的性能天花板往往不取决于模型本身而取决于你是否理解了“边界”背后的语义。每一个超球体不只是分类器里的一个零件它代表了你对这个类别数据的信任程度。参数调来调去本质上是在回答一个问题你愿意在多少误报的基础上换取多少对已知类别的覆盖率。在几个实际项目里我发现把每个类别的支持向量可视化出来会收获很多直觉上的洞察。支持向量并不是均匀分布在球面上的它们通常集中在几个困难区域。这些区域往往对应着真实生产环境中的某些特定模式比如传感器顿变、启停瞬间等。“支持向量在哪里”这件事比“准确率是多少”更能告诉你模型到底学到了什么。最后再留一个小技巧如果条件允许请一定在验证阶段加入一个“负类样本”集合。这个集合不属于任何已知类别专门用来测试模型能不能把它们拒之门外。很多模型在已知类别上跑得很好但在未知类别上表现得极其糟糕而 SVDD 的核心价值恰恰就体现在“已知和未知的分界”上。没有这个负类样本集你很难量化模型在这方面的真实水平。这一步是普通分类器完全无法给你的也是 SVDD 最值得被记住的地方。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑