广义 OOD 检测综述:把异常 / 新颖 / 开放集 / 离群点五件事说成一件事

  • 关联论文:2110.11334
  • 作者:flyP
  • 更新:2026-08-12

一句话结论:Yang 等人把"异常检测 / 新颖检测 / 开放集识别 / OOD 检测 / 离群点检测"五类问题统一到一个 Generalized OOD Detection 框架下,主张这五者在动机上同源、方法上互通——并在 3000 多页的综述里梳理出分类式、密度式、距离式三条方法主线,是后续 OOD 论文的术语共识来源。

自检:机制 1 段(统一框架与五类子问题定义)+ 工程 1 段(分类/密度/距离三条方法主线与可落地建议)+ ⚠️ 数字核验 2 处(被引数与综述覆盖年份)。


1. 这篇论文在解决什么真问题

在 ML 系统的工程部署里,模型遇到训练分布之外的输入这件事,几乎和"模型被训练出来"一样普遍:

  • 自动驾驶摄像头看到训练集里没见过的动物 / 异形车辆 / 罕见路标;
  • 工业质检摄像头看到新品类零件;
  • 推荐系统遇到完全陌生的用户行为模式;
  • LLM 接到训练语料之外的话题或诱导指令。

如果模型对这类输入毫无警觉,照样吐出一个高置信预测,后果可能比"答错"更糟——可能直接造成安全事件。学术圈自 2017 年起把这类问题命名为 out-of-distribution detection(Hendricks & Gimpel, 2017),但同步存在的还有四个听起来差不多、其实定义各异的概念:

简称 全称 典型问题设定
AD Anomaly Detection 训练集干净;测试时混入异常样本;强调"找出来"
ND Novelty Detection 训练集只有"正常"类;测试时出现新类样本;强调"没见过"
OSR Open Set Recognition 训练集闭集多类;测试时含已知类 + 未知类;强调"分类 + 拒绝"
OOD Out-of-Distribution Detection 训练集闭集;测试时是语义偏移样本;强调"是否在分布内"
OD Outlier Detection 数据本身就含离群点;强调"清洗 / 鲁棒统计"

这五个问题动机相似、技术栈高度重合,但被五个学术圈子分开做,论文术语与对照基线完全不互通——直到 Yang 等人在本综述里把它们打包成 Generalized OOD Detection 框架,给出统一分类法 + 三条方法主线 + 公开挑战清单

⚠️ 论文 v1(2021-10)→ v3(2024-01-23)跨度 27 个月,v3 篇幅从 ~50 页扩展到 ~100 页(PDF 体量从 2.4 MB 涨到 3.4 MB)。读者引用时务必标注 v3,因为 v1 / v2 的方法清单与基线对照不完整。


2. 核心方法:统一框架与三条方法主线

2.1 统一框架

论文 §2 把五类问题统一到下面这个抽象:

训练集:    D_train = {(x_i, y_i)}, x_i ∈ X, y_i ∈ Y_known
测试时:    D_test 包含 Y_known 之外的样本 (语义未知)
目标:      对每个测试 x_t 给出 in-distribution / ood 的二分类判定

五类子问题在这个框架里只是"训练时是否有标签"、"测试集是否混了已知类"、"是否做闭集分类"三个轴的取值不同:

子问题 训练时是否有标签 测试集是否含已知类 是否做闭集分类
AD 部分 / 全无
ND 全无(仅正常)
OSR 有(多类)
OOD 有(多类)
OD 部分 / 全无

这个表格是论文最常被引用的"一页式定义图",后续 2022-2026 年的 OOD 综述几乎都沿用这个三分法(例如 Salehi et al. 2022 的 OOD 综述、Zhang et al. 2023 的 open-set 半监督综述)。

2.2 三条方法主线

论文 §3-§5 把五类问题的算法拆成三条主线。

主线一:Classification-based(基于分类)——核心思想是"用一个分类模型的输出分布区分已知 / 未知"。典型方法:

  • Max-softmax(Hendricks & Gimpel, 2017):直接用预测概率的最大值作为 in-distribution 置信度;越低越 OOD。
  • ODIN(Liang et al., 2018):在 softmax 上加温度 + 输入梯度扰动放大已知 / 未知的差距。
  • Energy score(Liu et al., 2020):用 logits 的能量函数 -T·log(Σexp(f_i/T)) 取代 softmax,更平滑。
  • GradNorm(Huang et al., 2021):用分类器对输入梯度的范数作为 OOD 信号。

⚠️ 上述方法是"白盒"的——需要拿到分类器 logit 或梯度;面对闭源 API 模型无能为力。

主线二:Density-based(基于密度)——核心思想是"对训练分布建模,看测试样本是否落在低密度区"。典型方法:

  • GMM / KDE:对样本特征做高斯混合或核密度估计。
  • Flow-based:用 Normalizing Flow(RealNVP / Glow)做严格可逆密度建模。
  • VAE-based:用变分自编码器重构误差做密度代理。
  • Energy-based OOD(Du & Mordatch, 2019):用能量模型直接定义"低能量 = in-distribution"。

⚠️ 密度方法在高维图像上经常失灵——深度特征空间里的"低密度区"难以准确估计。

主线三:Distance-based(基于距离)——核心思想是"测样本到训练分布中心的距离"。典型方法:

  • Mahalanobis distance(Lee et al., 2018):对每类拟合高斯,用 Mahalanobis 距离判定 OOD。
  • KNN-based(Sun et al., 2022):用 k 近邻距离作为 OOD 信号,不假设分布形式。
  • VIM(Wang et al., 2022):用"特征残差 + logit"联合评分。
  • ReAct(Sun et al., 2021):对激活值做截断以提升 OOD 信号。

论文 §6 还讨论了混合方法自监督 / 对比学习派系(如 CSI、Rotational prediction),并标注了这些方法在五个子问题上的"可用矩阵"——读者要回到 §6 的表 3 看具体覆盖。

2.3 关键训练目标

下面给出一个简化版的 OOD 损失(基于分类式主线 + 辅助 outlier 暴露):

L_total = L_ce(model(x_in), y_in) + λ · L_aux(model(x_out))

其中 x_out 来自一个辅助的 outlier 暴露集(Hendricks et al., 2019),L_aux 通常是 max-entropy / uniform-output 损失。λ 控制 OOD 信号强度,工程经验值 0.1-1.0 之间。⚠️ 论文 §3.3 给出的"OE"基线属于分类主线下的辅助训练策略,不是独立主线


3. 关键实验与数据

论文 §7 给出五个子问题上的统一 benchmark 表。要点:

  1. AD benchmark:NYC Taxi 时间序列、KDDCup99、Thyroid 等——典型 baseline 包括 Isolation Forest、OC-SVM、GMM。
  2. OSR benchmark:CIFAR-10 / CIFAR-100 留出一类作为开放类——典型 baseline 包括 OpenMax( Bendel et al., 2016)、CROSR、PROSER。
  3. OOD benchmark(最常用):在 CIFAR-10 / ImageNet 上用 iNaturalist / SUN / Places / Textures 做 OOD 测试集,典型 metric 是 AUROC / FPR95 / AUPR。
  4. ND benchmark:One-class 设定下用正常类训练,对新类做 novelty 检测。
  5. OD benchmark:把离群点看作"训练集本身混入的噪声",典型指标是 precision @ K。

论文表 4 给出 ImageNet 1K 上的 OOD SOTA 数字,被引数最高的 baseline 包括 Energy(Liu et al., 2020)、GradNormKNN+(Sun et al., 2022)、ViM。⚠️ 论文 v3 表 4 的具体数字按 ImageNet-1K 给出,未和 ViT-Large / CLIP 等大规模模型的 OOD 表现对照——读者要拿到大模型 OOD 数据需要看 Ming et al., 2022 或后续工作。

# 复现 OOD 评估的最简 PyTorch 框架(伪代码)
import torch
from sklearn.metrics import roc_auc_score

def evaluate_ood(model, in_loader, ood_loader):
    model.eval()
    scores_in, scores_ood = [], []
    with torch.no_grad():
        for x, _ in in_loader:
            logits = model(x)
            # Energy-based 主线评分
            score = -torch.logsumexp(logits, dim=1)
            scores_in.extend(score.cpu().numpy())
        for x, _ in ood_loader:
            logits = model(x)
            score = -torch.logsumexp(logits, dim=1)
            scores_ood.extend(score.cpu().numpy())
    labels = [1] * len(scores_in) + [0] * len(scores_ood)
    scores = scores_in + scores_ood
    return roc_auc_score(labels, scores)

4. 亮点与局限

4.1 亮点

  1. 统一了五个术语——这是论文对后续工作最大的贡献:把"AD / ND / OSR / OOD / OD"五个圈子的研究语言对齐到一张图。
  2. 覆盖密度高——v3 版本 100+ 页 PDF,引用 200+ 篇论文,是 2021-2023 年间 OOD 综述里最完整的一份。
  3. 公开挑战清单清晰——论文 §8 给出 7 个开放问题:跨模态 OOD、长尾分布下的 OOD、对抗 OOD、自监督 OOD、大模型 OOD 等。今天学界绝大多数 OOD 论文都会回溯到这份清单。
  4. 方法主线三分法——分类 / 密度 / 距离的三分法成为后续 OOD 综述与课程的事实标准。

4.2 局限

  1. 未涵盖多模态 / 大模型时代——v3 (2024-01) 发表时 GPT-4V、LLaVA 等大模型 OOD 研究尚未铺开;论文对"CLIP 类模型在 OOD 上的鲁棒性"只给了 1-2 段提及,没有系统性 benchmark。
  2. 未涵盖对抗 / 安全视角——对抗样本(adversarial examples)与 OOD 的关系被略过;今天 OOD 与 adversarial robustness 的交叉是活跃研究方向。
  3. 未给出统一复现脚本——论文列了大量实验数字但未提供官方 benchmark 包;学界后来用 OpenOOD(Zhang et al., 2024)补足这一缺口。
  4. 五个子问题的实际定义差异未消解到底——论文给出一张三轴取值表,但"ND vs AD vs OD" 在工程实践中仍是混淆源;学界至今没有完全统一术语。

⚠️ 论文 v3 的"广义 OOD"框架在 2024 年以后被 Ming et al. "How robust is Google's Gemini to Out-of-Distribution?" 等大模型 OOD 论文部分推翻——大模型的 in-distribution 边界远比五类子问题所能涵盖的更模糊。读者要按 §8 开放问题对照后续论文。


5. 对工程落地的启发

  1. 先用 Energy / KNN+ 这类黑盒可用方法:闭源 API 模型无法访问 logit,但 KNN+ 等基于特征距离的方法可以套外部 embedding 模型(CLIP / OpenCLIP / BGE)作为前置。⚠️ 这种"外置 OOD 检测器"在 embedding 空间的质量上严重依赖前置模型。
  2. 不要混用 AD 与 OOD 评测:AD 评测关心"找出所有异常",OOD 评测关心"区分 in/out 分布",两者 metric 与数据划分方式不同。工业团队在 OOD 上线前应该明确"我们要的是 AD 还是 OOD",避免照搬对方论文的 baseline。
  3. OE(Outlier Exposure)有效但有副作用:加入 OE 训练会让模型对分布外样本的 softmax 趋近均匀,但会损失对 in-distribution 分布尾部的判别力。工程经验是 OE 的 λ 取 0.1-0.5,且要做 ID 验证集回归测试。
  4. 开放集识别 ≠ OOD 检测:OSR 任务允许测试时混入已知类,要求模型同时做"分类 + 拒绝";OOD 任务只问"是不是分布外"。自动驾驶、医疗影像这类业务通常属于 OSR,必须用 OSR baseline(OpenMax、PROSER 等)。
  5. OOD 与 calibration 的关系:能量评分、ODIN 都隐含 calibration 假设。模型如果本身 calibration 差,OOD 检测也会跟着差。落地时建议先用温度缩放 / Dirichlet calibration 把 ID 校准好,再谈 OOD。

6. 与同方向工作的关系

  • Hendricks & Gimpel, "A Baseline for Detecting Misclassified and Out-of-Distribution Examples in Neural Networks", ICLR 2017:把 max-softmax 作为 OOD baseline 提出,是分类主线起点。
  • Liang et al., ODIN, ICLR 2018:在 max-softmax 上加温度 + 输入扰动,是早期工程化 OOD 标配。
  • Liu et al., Energy Score, NeurIPS 2020:用能量取代 softmax,比 ODIN 更平滑,今天主流 OOD benchmark 的默认 baseline。
  • Sun et al., KNN+ (2022) 与 ReAct (2021):距离主线代表方法,对闭源 / 黑盒模型友好。
  • Ming et al., "How robust is Google's Gemini to OOD?", 2024:本论文 v3 之后的大模型 OOD 评估工作,补充了论文未覆盖的"大模型时代"。
  • Zhang et al., OpenOOD, NeurIPS 2024 Datasets & Benchmarks:补足本论文"缺统一复现包"的不足,是今天 OOD 实验的首选复现工具。

7. 适合谁读

  • ML 系统可靠性工程师:本文是 OOD 落地的"术语对齐手册",读完即可与五类问题的论文无障碍对话。
  • 自动驾驶 / 医疗影像 / 安防等高风险部署团队:论文 §6 给出的"OSR vs OOD"差异是高风险场景必须先消化的概念。
  • OOD / 开集识别方向研究生:把分类 / 密度 / 距离三条主线一次说清,是入门综述里最干净的一份。
  • 不需要读全文的快速读者:看 §2(统一框架 + 五类子问题三轴取值表)+ §8(开放问题清单)即可拿到 80% 信息密度。

8. 一段话总结

Yang 等人在 IJCV/TPAMI 风格的长综述(arXiv v3 约 100 页)里,把异常 / 新颖 / 开放集 / OOD / 离群点五类问题统一到"训练时是否有标签 × 测试集是否含已知类 × 是否做闭集分类"的三轴框架下,沿分类 / 密度 / 距离三条主线梳理了 200+ 篇论文。这份被引 323 次的综述虽然未覆盖大模型时代、未涵盖对抗视角、未提供统一复现脚本,但给后续 5 年的 OOD 研究定下了术语对齐的基线——今天任何"OOD 论文"的开头几乎都会回到这张三轴表,是 OOD 工程师与研究生的必读参考。


工程落地与核查(Jay)

工程落地

1. KNN+ 作为闭源 API 的 OOD 外置检测器

距离方法(KNN+、Mahalanobis、VIM)是三条主线里对闭源 API 最友好的派系——只需要一个外部 embedding 模型吐出特征向量,剩余计算全在离线 Python 完成。典型部署流水线:

# KNN+ OOD 检测的最小可落地代码
import numpy as np
from sklearn.neighbors import NearestNeighbors

class KNNOODDetector:
    def __init__(self, embedding_model, id_loader, k=50):
        # 1. 对 ID 样本抽取特征
        self.id_features = self._extract_features(embedding_model, id_loader)
        # 2. 构建 k-NN 索引(kd-tree,O(d log n) 建树)
        self.nn = NearestNeighbors(n_neighbors=k, metric='cosine')
        self.nn.fit(self.id_features)
        self.k = k

    def score(self, x):
        # cosine distance 到第 k 近邻的距离作 OOD 信号
        f = self.embedding_model.encode(x)
        dist, _ = self.nn.kneighbors([f])
        return float(dist[0][-1])  # 越大越 OOD

    def detect(self, x, threshold=None):
        s = self.score(x)
        return s > (threshold or self._bootstrap_threshold())

⚠️ embedding_model 的选择直接决定 OOD 精度天花板:CLIP ViT-L/14 在 ImageNet 域差距上天然比 ResNet50 好,但跨到医学影像或工业零件图这种领域漂移时,embedding 质量可能断崖下跌,工程团队必须实测后再决定用哪个模型。

2. OpenOOD benchmark:实用价值与边界

Zhang et al., 2024(OpenOOD)的核心价值是给每种方法提供了标准化的 eval_api:同一个脚本换一行 config 就能跑 Energy、KNN+、Mahalanobis、ODIN,不用自己对着原论文手撸。然而 OpenOOD 有两个未解决的问题:① 它的 benchmark 数据默认跑在 ImageNet / CIFAR-10,这两种设定和"公司内部日志系统遇到全新用户行为"的场景差异极大;② OpenOOD 对大模型的覆盖仍在早期阶段(主要是 ResNet50 / CLIP-B/32),ViT-Large / ViT-G 的 OOD 数字需要自己往里填。

3. 密度方法在高维图像上的失灵问题

Flow-based / VAE-based 的密度方法在 MNIST/CIFAR-10 上能跑出好看数字,但 ImageNet 224×224 原始像素空间的维度是 150,528,远超任何 KDE 或 GMM 能可靠估计的规模。工程实践中密度方法必须先过一次降维(用预训练 backbone 抽 512-2048 维特征),但降维本身就引入了"特征空间是否保留 OOD 信号"的不确定性——这个不确定性没有系统性答案,必须按任务实测。

4. AD 与 OOD metric 混用的致命陷阱

AD 任务常用 precision@K / recall@K,OOD 任务的核心 metric 是 AUROC / FPR95 / AUPR。两者数据划分方式也不同:AD 默认训练集干净、测试集混入异常;OOD 默认训练闭集、测试集含分布外样本。如果把 AD 的 precision@K 直接套到 OOD 场景,会得到一个对 ID 尾部样本极不稳定的检测器。工程验收文档里必须明确写出 metric 名字和对应的数据划分。

5. OE 的 λ=0.1–0.5 经验值与 ID 尾部副作用

Outlier Exposure(Hendricks et al., 2019)在辅助暴露集上的均匀分布损失项乘以 λ 加到 CE 损失里:

def oe_loss(logits, lambda_oe=0.3):
    # 对所有 ID 样本强制 softmax 趋向均匀
    return lambda_oe * torch.mean(
        F.cross_entropy(logits, torch.full_like(logits.argmax(dim=1), 1.0 / logits.size(1)))
    )

⚠️ OE 的已知副作用是损伤 ID 分布尾部的判别精度——某些 ID 类本身和 OOD 样本在特征空间里有重叠,这部分样本在加了 OE 后会被压到低置信区。工程建议:先跑一次 λ=0.3 全量训练,再跑一次 λ=0.1 只对尾部类微调,对比两者的 ID 验证集 F1 再决定用哪个。

⚠️ 事实存疑

  • 被引 323 次(OpenAlex):该数字截至 2026-08-12,需留意 2026 年下半年是否有大幅增长;建议引用时加查 Semantic Scholar 交叉验证。
  • v3 篇幅 ~100 页:原文 PDF 页数标注,非 arXiv LaTeX 源码行数;不同 PDF 渲染工具可能导致页数略有浮动。
  • "KNN+ 在 ImageNet-1K 上 SOTA":需回到论文 v3 表 4 核实,KNN+ 在某些 OOD 数据集(如 iNaturalist → SUN)上未必优于 Energy;OpenOOD 2024 的更新 benchmark 有更完整的对照。

风险边界

未开源/未量化/scale-up 难度高:本综述的 density-based 方法(Flow / VAE)对高维图像场景的失效问题至今没有系统性解决方案;OpenOOD 的 benchmark 脚本对多模态 OOD(文本→图像跨模态分布外)覆盖极弱,工程团队如果要做跨模态 OOD 检测,需要自行设计评测集,不能直接复用 OpenOOD。