1. 从“认得出”到“认得清”:开放集识别到底在解决什么问题
做过图像分类或者异常检测的朋友,大概率都遇到过这样一种尴尬局面:模型在训练集和测试集上表现都很好,指标刷得飞起,可一旦上线,遇到一个从来没见过的类别,它照样会自信满满地给出一个错误答案。比如你训练了一个识别猫和狗的模型,结果线上来了一张兔子的图片,模型不会告诉你“我不认识”,而是会以极高的置信度把它判成猫或者狗。这个问题在学术界有个专门的名字——开放集识别(Open Set Recognition,简称OSR)。
传统分类任务默认一个假设:测试时出现的所有类别,在训练阶段都见过。这个假设叫闭集假设。但现实世界显然不是这样运转的。无论是安防监控、工业质检、医疗影像诊断,还是自动驾驶感知,系统面对的都是一个开放的环境,未知类别、未知模式随时可能出现。开放集识别要解决的核心问题就是:让模型在面对未知类别时,既能正确分类已知类别,又能有效地把未知类别“挑出来”并拒绝掉。
这个方向这几年热度一直在涨,从最早的1-vs-Set Machine、W-SVM,到后来的OpenMax、G-OpenMax,再到基于生成模型、基于原型学习、基于度量学习的一系列方法,整个领域的方法论已经迭代了好几轮。这篇内容我会结合自己在这个方向上的实践和踩坑经验,把开放集识别的核心思路、关键技术路线、实操要点和常见问题系统地梳理一遍。适合正在做异常检测、新类发现、鲁棒分类相关工作的同学参考,也适合刚接触这个方向、想快速建立全局认知的朋友。
2. 开放集识别的核心思路与方案选型
2.1 风险最小化:OSR的理论根基
理解开放集识别,绕不开一个核心概念——开放空间风险(Open Space Risk)。这个概念最早由Scheirer等人在2013年前后系统提出。简单说,闭集分类器在特征空间中划定的决策边界,会把无限大的开放空间也划归到某个已知类别里。而开放集识别要求模型在保证经验风险(已知类分类错误率)可控的同时,把开放空间风险也压到最低。
用生活化的类比来解释:闭集分类器就像一个只学过加减法的小学生,你问他3减5等于多少,他不会说“这个我还没学”,而是会硬凑一个答案给你。开放集识别要训练的,是让这个学生学会说“这道题超出我的知识范围了”。
从数学形式上看,开放集识别的目标可以写成:
在最小化已知类分类损失的同时,最小化未知类被误判为已知类的概率。
这两个目标天然存在张力。你把决策边界收得越紧,已知类的分类精度可能下降;你放得越松,未知类就越容易混进来。所以OSR方法的设计,本质上是在这两者之间找平衡。
2.2 主流技术路线对比
目前开放集识别的方法大致可以分成几条主线,我整理了一个对比表格,方便大家快速定位:
| 技术路线 | 核心思想 | 代表方法 | 优势 | 局限 |
|---|---|---|---|---|
| 基于传统分类器改造 | 在SVM等分类器上引入拒绝机制 | 1-vs-Set、W-SVM、PI-SVM | 理论清晰,实现简单 | 依赖手工特征,扩展性差 |
| 基于深度网络激活 | 利用SoftMax输出或logit分布做拒绝 | OpenMax、G-OpenMax | 端到端,易集成 | 对置信度校准敏感 |
| 基于生成模型 | 生成伪未知样本辅助训练 | GAN-based OSR | 能显式建模未知 | 训练不稳定,成本高 |
| 基于原型/度量学习 | 学习紧凑的类原型,用距离做拒绝 | 原型网络变体、CROSR | 泛化好,可解释 | 原型质量依赖设计 |
| 基于自监督/对比学习 | 用对比损失拉开已知与未知 | 对比学习OSR框架 | 表征强,适合大数据 | 需要精心设计正负样本 |
选哪条路线,取决于你的具体场景。如果你的数据量不大、特征维度不高,传统方法改造可能就够了;如果是图像、语音这类高维数据,深度网络激活或者原型学习路线更合适;如果你有充足的算力且能接受训练的不稳定性,生成模型路线可以尝试。
2.3 为什么深度学习方法成为主流
早期OSR研究主要集中在传统机器学习框架下,因为那时候深度网络的拒绝机制还不成熟。但2016年之后,情况发生了明显变化。深度网络天然具备强大的特征提取能力,而OSR的核心难点之一就是在好的特征空间里做拒绝。特征不好,再精巧的拒绝策略也是空中楼阁。
另一个推动因素是**极端值理论(EVT)**在深度模型中的引入。OpenMax就是典型代表,它用Weibull分布对正确分类的激活向量进行拟合,然后估计某个样本属于未知类的概率。这个思路比单纯看SoftMax最大值要靠谱得多,因为SoftMax的输出经过归一化后,已知类的置信度普遍偏高,直接设阈值很容易误杀。
我自己的经验是:如果你的已知类数据量足够,优先考虑深度网络+EVT或者深度网络+原型学习的组合。这两条路线在多个公开数据集上的表现都比较稳,工程落地的可控性也更好。
3. 核心细节解析与实操要点
3.1 特征空间设计:拒绝机制的地基
开放集识别能不能做好,七成看特征空间。特征空间如果本身就把已知类压缩得很紧凑、类间距离拉得很开,那拒绝未知类就是顺水推舟的事。反过来,如果特征空间里已知类都混在一起,那再复杂的拒绝模块也救不回来。
实操中我常用的几个策略:
- 加性角度间隔损失(AAM-SoftMax):这个在人脸识别领域很成熟,用在OSR上效果也不错。它通过角度间隔把类内紧凑度和类间分离度同时拉大,让特征空间更有“结构感”。
- 中心损失(Center Loss):给每个类维护一个特征中心,惩罚样本特征到中心的距离。这个损失和SoftMax配合使用,能显著提升类内紧凑度。
- 对比学习预训练:用SimCLR或者MoCo这类框架先做自监督预训练,再在已知类上微调。这样得到的特征空间泛化性更好,对未知类的容忍度也更高。
注意:特征维度的选择很关键。维度太低,类间区分度不够;维度太高,容易过拟合已知类,导致未知类稍微偏离就被判成已知。我一般从128维起步,根据数据集规模在128到512之间调整。
3.2 拒绝阈值的确定:最容易被忽视的环节
很多论文在报告OSR性能时,默认阈值是调好的。但实际部署时,阈值怎么定是个大问题。定高了,未知类漏检多;定低了,已知类误拒多。
我常用的做法是基于验证集的分位数法。具体步骤:
- 在验证集上跑一遍模型,收集所有已知类样本的“已知度分数”(比如OpenMax的激活值、原型距离的负数等)。
- 取这些分数的第5百分位或者第10百分位作为初始阈值。
- 如果业务对未知类更敏感(比如安防场景),把分位数调低;如果对已知类误拒更敏感(比如推荐场景),把分位数调高。
这个方法的逻辑是:我们假设验证集上的已知类样本分布和线上已知类分布一致,那么低于某个分位数的样本,大概率是异常的。当然,这个假设在分布漂移严重时会失效,所以线上还需要持续监控和动态调整。
3.3 未知类样本的模拟:生成还是挖掘
如果训练时能拿到一些未知类样本,哪怕数量很少,OSR的性能都会有明显提升。但现实中往往拿不到。这时候有两种思路:
第一种是生成伪未知样本。用GAN或者扩散模型在已知类分布的边缘区域采样,把这些样本当作未知类的代理。G-OpenMax就是这条路线的代表。优点是能显式控制未知样本的分布,缺点是生成质量不稳定,而且生成模型本身训练成本高。
第二种是从已知类中挖掘“类未知”样本。具体做法是:把已知类样本做各种强增强(比如大幅旋转、颜色抖动、CutMix),让它们偏离原始分布,然后把这些增强样本当作未知类的近似。这个方法实现简单,而且不需要额外训练生成模型。我在几个工业质检项目里用过,效果比预期好。
实操心得:增强的强度要控制好。太弱了,增强样本和原始样本没区别,起不到模拟未知类的作用;太强了,增强样本完全失去语义信息,模型学到的拒绝边界会过于激进。我一般用AutoAugment或者RandAugment,把强度设在中等偏上。
3.4 评估指标的选择:别只看准确率
OSR的评估和闭集分类很不一样。闭集分类看准确率就够了,OSR必须同时看已知类分类性能和未知类拒绝性能。常用的指标有:
- AUROC:把已知类和未知类的分数混在一起,看排序质量。这个指标对阈值不敏感,适合比较不同方法。
- OSCR(Open Set Classification Rate):在不同阈值下,已知类正确分类率和未知类正确拒绝率的综合曲线。
- F1-OSR:已知类F1和未知类F1的调和平均。
- 闭集准确率 + 开集拒绝率:最直观的组合,但需要固定阈值。
我一般会同时报告AUROC和固定阈值下的闭集准确率/开集拒绝率。前者用于方法对比,后者用于评估实际部署效果。
4. 实操过程与核心环节实现
4.1 数据准备与划分策略
OSR的数据划分和闭集分类有本质区别。闭集分类是把所有类别按比例分成训练集和测试集,OSR则要把类别分成已知类集合和未知类集合。未知类集合的样本在训练阶段完全不可见,只在测试时出现。
具体操作步骤:
- 选定一个多类数据集(比如CIFAR-10、ImageNet子集、或者自己的业务数据)。
- 从所有类别中随机选一部分作为已知类(比如10个类里选6个),剩下的作为未知类。
- 已知类样本按常规划分成训练集、验证集、测试集。
- 未知类样本只放入测试集,且测试集中已知类和未知类的比例要控制好。我一般用1:1或者2:1,具体看业务场景中未知类出现的频率。
注意:未知类的选择不能太随意。如果未知类和已知类在语义上差异极大(比如已知类是猫狗,未知类是飞机),那OSR任务就太简单了。真正有挑战性的是未知类和已知类在视觉上相似的情况,比如已知类有哈士奇,未知类有阿拉斯加。这种设置下才能检验方法的真实能力。
4.2 模型训练流程与关键参数
以深度网络+OpenMax为例,完整训练流程如下:
第一步:基础分类器训练
用已知类训练集训练一个标准分类网络。骨干网络可以用ResNet、EfficientNet或者ViT,看你的数据规模和算力。损失函数用SoftMax交叉熵,优化器用SGD或者AdamW。
关键参数:
- 初始学习率:0.1(SGD)或1e-3(AdamW)
- 权重衰减:5e-4
- 批大小:64或128
- 训练轮数:100到200,配合余弦退火
第二步:激活向量收集
训练完成后,把训练集(或者验证集)再过一遍网络,收集每个已知类样本在倒数第二层的激活向量。注意,这里要收集的是正确分类的样本的激活向量,分错类的不要。
第三步:Weibull拟合
对每个已知类,取其所有正确分类样本的激活向量,计算每个维度上的均值,然后计算每个样本的激活向量与均值向量的距离。对这些距离用Weibull分布拟合,得到每个类的Weibull参数。
第四步:OpenMax计算
测试时,对每个样本:
- 获取倒数第二层激活向量。
- 对每个已知类,计算激活向量与该类均值向量的距离。
- 用该类的Weibull分布计算这个距离的累积分布函数值,作为“属于该类的概率修正因子”。
- 用修正因子调整SoftMax输出,得到修正后的概率分布。
- 修正后的概率分布中,已知类的概率之和如果低于某个阈值,就判为未知类。
这个流程听起来复杂,但代码实现并不难。核心就是Weibull拟合和修正因子的计算,网上有现成的开源实现可以参考。
4.3 阈值调优与线上部署
训练和评估完成后,部署环节的阈值调优是最后一公里。我的做法是:
- 在验证集上跑一遍,得到所有已知类样本的“已知度分数”。
- 画分数分布直方图,观察已知类分数的下尾在哪里。
- 取第5百分位作为初始阈值。
- 如果线上有少量标注的未知类样本,用这些样本做验证,看漏检率和误拒率是否满足业务要求。
- 根据业务反馈微调阈值,一般调整幅度在±2个百分点以内。
线上部署时,我建议加一个分数平滑模块。因为单帧的分数可能有噪声,用滑动窗口平均或者指数移动平均可以降低抖动。窗口大小根据业务帧率定,一般5到10帧比较合适。
5. 常见问题与排查技巧实录
5.1 已知类准确率下降严重怎么办
这是OSR最常见的副作用。你引入了拒绝机制,已知类的分类精度往往会掉几个点。如果掉得太厉害,可以从这几个方向排查:
- 检查特征空间是否被过度压缩。如果用了很强的中心损失或者角度间隔损失,已知类特征可能被压得太紧,导致决策边界过于保守。适当降低这些损失的权重。
- 检查阈值是否设得太高。阈值高,已知类样本容易被误判为未知。把阈值调低试试。
- 检查未知类模拟样本是否过于激进。如果增强强度太大,模型学到的拒绝边界会过于宽泛,把已知类也拒了。降低增强强度。
5.2 未知类拒绝率上不去怎么办
反过来,如果未知类拒绝率太低,说明模型对未知类太“宽容”。排查方向:
- 检查特征空间是否区分度不够。如果已知类特征都混在一起,未知类更容易蒙混过关。加强特征判别性训练。
- 检查是否缺少未知类先验。如果完全没用任何未知类模拟,模型可能根本没学会拒绝。加入伪未知样本或者增强样本。
- 检查分数计算方式。SoftMax最大值作为已知度分数,在深度网络中往往区分度不够。换成OpenMax或者原型距离试试。
5.3 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决思路 |
|---|---|---|---|
| 已知类准确率大幅下降 | 拒绝边界过激 | 检查阈值和增强强度 | 降低阈值,减弱增强 |
| 未知类拒绝率低 | 特征区分度不足 | 可视化特征分布 | 加强判别性损失 |
| 训练不稳定 | 生成模型难训练 | 检查损失曲线 | 改用原型或对比学习 |
| 线上效果差于离线 | 分布漂移 | 对比线上线下分数分布 | 动态调整阈值 |
| 阈值难确定 | 验证集不代表线上 | 检查验证集划分 | 用分位数法+线上监控 |
| 推理速度慢 | 拒绝模块计算量大 | Profile各模块耗时 | 简化拒绝计算,用近似 |
5.4 几个容易踩的坑
第一个坑:用测试集调阈值。这是学术论文里常见的问题,但在工程中绝对要避免。测试集只能用来做最终评估,调阈值必须用验证集。
第二个坑:忽略类别不平衡。如果已知类之间样本量差异大,Weibull拟合可能会偏向样本多的类。解决办法是对每个类单独拟合,或者对样本少的类做重采样。
第三个坑:把OSR和异常检测混为一谈。两者有重叠,但目标不同。异常检测更关注“偏离正常模式”,OSR更关注“属于已知类还是未知类”。评估指标和方案设计都有差异,不要直接套用。
第四个坑:忽视计算成本。OpenMax的Weibull拟合和修正计算在类别数多的时候会变得很慢。如果已知类有几百个,建议用近似方法或者只对Top-K类做修正。
6. 开放集识别的扩展方向与个人体会
OSR这个方向还在快速演进。最近几年有几个趋势值得关注:一是与自监督学习的结合,用对比学习或者掩码建模预训练的特征,在OSR任务上表现明显更好;二是与元学习的结合,让模型学会“如何拒绝”而不是“拒绝什么”;三是与因果推断的结合,从因果角度理解已知类和未知类的本质差异。
我自己在这个方向摸爬滚打几年,最大的体会是:OSR不是一个单纯的算法问题,而是一个系统问题。特征空间、拒绝机制、阈值策略、线上监控,任何一个环节出问题,整体效果都会打折扣。很多论文只关注拒绝机制的设计,但实际落地时,特征空间的质量和阈值的动态调整往往更关键。
另外,不要迷信单一指标。AUROC高不代表线上效果好,闭集准确率高也不代表未知类拒得好。一定要结合业务场景,定义清楚什么更重要,然后有针对性地优化。工业质检里,漏检一个缺陷可能比误拒一个良品代价高得多,那阈值就要往低里调;推荐场景里,误拒一个正常用户可能比放过一个异常用户影响更大,那阈值就要往高里调。
最后分享一个小技巧:如果你的已知类类别数很多,可以考虑分层拒绝。先用一个粗粒度的拒绝器把明显异常的样本过滤掉,再用细粒度的拒绝器做二次判断。这样既能保证拒绝率,又能控制计算成本。我在一个已知类超过50类的项目里用过这个策略,推理速度提升了将近一倍,拒绝率还略有上升。