AI 工程基础体系 · 第 51/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。
异常检测:统计方法、Isolation Forest、One-Class 与阈值治理
异常检测(anomaly detection)是在缺少完整异常标签,或异常样本极少的条件下,识别与正常数据生成机制显著不同的样本、事件或时间片。它并不等同于“分类器预测异常类别”:分类需要学习 正常/异常 的判别边界,而异常检测通常首先学习正常数据的分布、密度、几何结构或重构规律。
在生产系统中,异常检测的输出也不应直接等同于告警。一个完整链路通常是:
flowchart LR
A[原始事件] --> B[特征与时间窗口]
B --> C[异常模型]
C --> D[异常分数]
D --> E[阈值与策略]
E --> F[告警/拦截/降级]
F --> G[人工处置与反馈]
G --> H[标签、阈值和模型评估]
H --> C
模型回答“这个样本有多不正常”,阈值治理回答“多不正常才触发什么动作”。二者必须分开,否则模型分数的变化会被误认为业务风险的变化。
一、先明确异常检测问题
设观测样本为 ,正常数据分布为 。理想情况下,异常是满足
的样本。但这个定义有三个现实限制:
- 正常分布通常未知;
- 特征可能有不同量纲、相关性和时间依赖;
- “异常”是业务语义,不一定是统计上的低概率。
例如,一笔金额为 100 万元的交易可能统计上罕见,但对企业客户来说完全正常;一次金额只有 10 元的交易,如果在极短时间内来自大量不同设备,则可能是欺诈。
因此需要区分三种任务。
1. 离群点检测
训练数据本身可能混有异常点,目标是找出其中的离群点。这属于 outlier detection。训练集不是纯净正常数据,模型可能被异常污染。
2. 新颖性检测
训练阶段假设输入数据主要或完全来自正常分布,部署后判断新样本是否偏离该分布。这属于 novelty detection。例如,使用过去 30 天的健康设备指标训练模型,判断今天的新指标是否异常。
3. 监督分类
如果已经有质量较高、覆盖充分且及时的异常标签,应优先考虑二分类、代价敏感分类或排序模型。异常检测仍可作为未知异常发现手段,但不应替代有标签问题上的监督学习。
scikit-learn 中的 IsolationForest、LocalOutlierFactor、OneClassSVM 等方法都同时涉及离群点和新颖性检测,但训练数据假设与调用方式不同。尤其是 LocalOutlierFactor 的新颖性检测模式需要显式设置 novelty=True,不能把训练阶段的 fit_predict 语义直接套到新数据上。
二、统计方法:从单变量阈值到多变量距离
统计方法的优势是可解释、成本低,适合作为基线、数据质量检查和第一层防线。它们的缺点是通常需要较强的分布假设,或者对维度、相关性、时间结构比较敏感。
2.1 Z-score:均值和标准差下的偏离程度
对单变量 ,给定均值 和标准差 ,标准化分数为
其中:
- 是当前观测;
- 是参考正常样本的均值;
- 是参考正常样本的标准差;
- 越大,表示观测离均值越远。
若假设 ,则常见规则是:
这不是数学定律,而是经验阈值。标准正态分布下,双侧区间 的概率约为 99.73%,所以在独立、稳定且确实近似正态的情况下,单个观测超过 3 个标准差的概率约为 0.27%。
完整算例
假设某接口在正常时段的响应时间为:
均值为:
若使用总体标准差:
新观测 的 Z-score 为:
因此在 的规则下会被标记。
但如果新观测为 ,则:
它虽然低于均值很多,却不会被这个阈值标记。是否需要检测低延迟异常,取决于业务:低延迟可能代表缓存命中,也可能代表请求绕过了关键处理。
Z-score 的反例
考虑数据:
均值为 28,标准差约为 36。对 100 计算:
它可能不会超过 3,但从数据结构看,100 明显是极端值。异常点本身抬高了均值和标准差,使 Z-score 失去敏感性,这叫做污染导致的掩蔽效应。
2.2 Robust Z-score:用中位数和 MAD 抵抗污染
更稳健的做法是使用中位数 和中位数绝对偏差(MAD):
在近似正态分布下,MAD 与标准差的关系近似为:
于是稳健 Z-score 可写为:
仍以:
为例,中位数 ,所有偏差为:
MAD 为 0。此时公式无法计算,且这不是简单地把除数换成一个很小的数就能正确解决的问题。工程上应:
- 对近乎常数的特征单独处理;
- 若业务允许,使用一个明确的测量误差下限;
- 或直接判断“偏离常数基线超过业务容差”。
这说明稳健统计也有边界:当正常数据没有可估计的离散程度时,异常判断必须依赖业务容差,而不是伪造统计尺度。
2.3 分位数阈值:不要求正态分布
若只关心高值异常,可以用正常参考数据的 分位数作为阈值:
分位数方法不要求数据服从正态分布,适合延迟、金额、流量等右偏变量。但它的含义是“参考样本中约有 0.5% 高于阈值”,并不自动意味着这 0.5% 都是业务异常。
分位数估计还受到样本量影响。若每天只有 100 个样本,估计 99.9% 分位数几乎没有足够数据支撑;阈值应考虑置信区间、跨天聚合或采用极值理论,而不是机械地指定更多小数位。
2.4 多变量 Mahalanobis 距离
单变量检测无法表示变量之间的相关性。设正常数据均值向量为 ,协方差矩阵为 ,样本为 ,Mahalanobis 平方距离为:
它衡量的是沿着协方差结构标准化后的距离,而不是普通欧氏距离。
完整数值例子
假设二维正常数据的均值和协方差为:
考虑样本:
协方差矩阵的逆为:
因此:
虽然 到原点的欧氏距离只有 ,但正常变量高度正相关,正常点通常位于 的方向; 偏离了这一相关结构,所以 Mahalanobis 距离很大。
反例:协方差矩阵不稳定
当特征数 接近或超过正常样本数 时,样本协方差矩阵可能不可逆;强共线性也会造成数值不稳定。此时直接求逆会失败或放大噪声。可选方法包括:
- PCA 降维后在低维空间计算;
- 使用收缩协方差估计;
- 使用稳健协方差估计;
- 移除重复或近似线性相关的特征。
如果使用 分布给 设阈值,还额外假设正常数据近似多元正态。这个分布假设不成立时,阈值应使用正常验证集分位数校准。
三、Isolation Forest:通过隔离难度定义异常
Isolation Forest(孤立森林)不直接估计概率密度,而是观察一个样本需要多少次随机切分才能被单独隔离。直觉是:稀疏区域中的样本更容易被切出来,因此路径更短;密集区域中的样本需要更多切分,路径更长。
3.1 单棵隔离树如何工作
对一个包含多个样本的节点,算法重复:
- 随机选择一个特征;
- 在该特征的最小值和最大值之间随机选择切分点;
- 将数据分成左右子节点;
- 对目标样本所在的子节点继续切分;
- 直到样本被单独隔离,或达到限制条件。
设样本在一棵树中的路径长度为 ,对多棵树取平均:
其中 是树的数量。平均路径越短,越可能异常。
当节点因为达到树高限制或只剩多个样本而提前停止时,需要对未完全隔离的节点进行校正。常见实现使用:
其中 是该节点中的样本数, 是调和数:
表示在随机二叉搜索树中,包含 个样本时的平均路径长度。它防止“因为树提前停止”而把不同节点错误地视为相同异常程度。
经典 Isolation Forest 的归一化形式可写成:
其中 是每棵树抽取的子样本量。因为 越短, 越接近 1,表示越容易被隔离。
需要注意:不同库可能对分数做单调变换或改变符号。使用分数时必须以具体 API 文档为准,不能把论文中的 、库中的 score_samples 和 decision_function 混为一谈。
3.2 一个小型隔离过程
考虑一维正常样本:
待判断样本为 。
第一次随机切分点若为 50,则:
- 左节点:
- 右节点:
目标样本一次切分就被隔离,路径很短。
若待判断样本为 ,随机切分点可能把数据分成:
之后还需要继续切分,才能把 13 单独分离。大量随机树平均后,13 的路径明显长于 100。
3.3 Isolation Forest 的优势和反例
它适合:
- 特征维度中等或较高;
- 不希望先拟合复杂概率分布;
- 异常是少数且与正常数据存在空间分离;
- 需要较低的推理成本。
但它不是“任何少数样本都能发现”的算法。
反例一:异常形成大而密集的群体
正常数据有 10,000 个,异常数据有 5,000 个,异常群体自身很密集且占据一个完整区域。异常点未必比正常边缘点更容易被隔离,Isolation Forest 可能把它们视为普通簇。
反例二:异常只存在于特征组合关系中
如果异常是“两个特征之间的精确关系被破坏”,但每个单独特征的边际范围都正常,随机轴向切分可能需要很多树和较深路径才能识别。此时显式构造比值、残差、差分或使用能表达关系的模型更有效。
反例三:时间结构被忽略
把一整天不同时间段的数据混在一起训练,可能将夜间低流量视为异常;实际问题是季节性和时间上下文,而不是孤立点。应先按时间窗口构造基线,或把周期特征、滚动统计量纳入输入。
3.4 scikit-learn 中的分数语义
下面示例使用 Isolation Forest 对二维合成数据建模:
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_recall_curve, average_precision_score
rng = np.random.default_rng(7)
# 训练数据:主要是正常样本
normal = rng.normal(loc=0.0, scale=1.0, size=(2000, 2))
X_train, X_cal = train_test_split(normal, test_size=0.3, random_state=7)
# 评估数据:正常样本 + 人工构造异常
X_test_normal = rng.normal(0.0, 1.0, size=(1000, 2))
X_test_anomaly = rng.uniform(6.0, 8.0, size=(30, 2))
X_test = np.vstack([X_test_normal, X_test_anomaly])
y_test = np.r_[np.zeros(len(X_test_normal)), np.ones(len(X_test_anomaly))]
model = IsolationForest(
n_estimators=300,
max_samples="auto",
contamination="auto",
random_state=7,
n_jobs=-1,
)
model.fit(X_train)
# 在 scikit-learn 中:
# decision_function 越小越异常;负值通常表示低于模型内部 offset
decision = model.decision_function(X_test)
score = -decision # 为了让“越大越异常”更直观
ap = average_precision_score(y_test, score)
print(f"average precision = {ap:.3f}")
# 不依赖模型内部 contamination 阈值,
# 在独立校准集上取异常分数的高分位数
cal_score = -model.decision_function(X_cal)
threshold = np.quantile(cal_score, 0.99)
pred = (score >= threshold).astype(int)
print("threshold =", threshold)
print("detected anomalies =", pred.sum())
这个示例有几个关键点:
X_train用于拟合结构;X_cal用于确定阈值,避免用同一批训练数据同时拟合和评估;X_test只用于评估;average_precision衡量排序质量,不依赖某一个阈值;- 阈值
0.99表示校准集最高 1% 被当作候选异常,不代表真实异常率必然是 1%。
在 scikit-learn 的常见实现中,decision_function 越低越异常,predict 返回正常 1、异常 -1。contamination 会影响内部 offset_ 和由 predict 使用的分界;contamination="auto" 的具体分界语义不应被理解成“自动知道真实异常率”。如果业务有标签或明确告警预算,通常应在独立校准集上治理阈值,而不是把 contamination 当作业务规则的替代品。
四、One-Class:只学习“正常类”的边界
“One-Class”不是单一算法,而是一类只用正常样本学习支持区域或边界的方法。最常见的是 One-Class SVM,也包括支持向量数据描述、密度估计、自编码器等。
4.1 One-Class SVM 的优化目标
给定主要由正常样本组成的训练集 ,One-Class SVM 希望在特征空间中找到一个区域,使大多数正常样本落在区域内,同时避免区域无限扩大。
其经典形式之一为:
约束为:
其中:
- 是把输入映射到特征空间的函数;
- 定义边界方向;
- 定义边界位置;
- 是允许样本违反边界的松弛量;
- 控制边界松弛程度。
对核函数 ,决策函数通常形如:
当 时判为正常,当 时判为异常。
在 scikit-learn 中,OneClassSVM.predict 对正常样本返回 1,异常返回 -1;decision_function 越低越异常。
4.2 参数 和 的含义
在 RBF 核中:
- 越大,单个样本的影响范围越小,边界更曲折,容易过拟合;
- 越小,影响范围越大,边界更平滑,可能漏掉局部结构;
- 是训练目标中的控制参数,常被理解为对训练误报比例和支持向量比例的上界/下界关系,但它不是部署数据真实异常率的可靠估计。
由于 RBF 核依赖距离,未缩放特征会造成严重问题。若一个特征取值范围为 [0, 1],另一个特征为 [0, 1_000_000],后者会几乎完全支配 。
4.3 可运行示例:标准化和新颖性检测
import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import OneClassSVM
rng = np.random.default_rng(3)
# 训练数据应尽量是正常样本
X_normal = rng.normal(size=(1000, 3))
detector = make_pipeline(
StandardScaler(),
OneClassSVM(
kernel="rbf",
gamma="scale",
nu=0.02,
),
)
detector.fit(X_normal)
X_new = np.array([
[0.1, -0.2, 0.3], # 接近正常区域
[6.0, 6.0, 6.0], # 明显偏离
])
labels = detector.predict(X_new)
scores = detector.decision_function(X_new)
print("labels:", labels)
print("decision scores:", scores)
预期现象是第一行更可能返回 1 且分数较高,第二行更可能返回 -1 且分数较低。但不能把具体数值硬编码为固定预期:核参数、随机数据和 scikit-learn 版本都会影响边界。
4.4 One-Class SVM 的边界
One-Class SVM 对以下情况比较敏感:
- 训练数据包含大量异常;
- 高维稀疏数据中距离退化;
- 特征未缩放;
- 数据分布持续漂移;
- 样本量很大,核方法的训练和存储成本过高。
它适合学习复杂的非线性正常边界,但“边界复杂”不等于“边界正确”。高 可能把训练样本附近包成许多小岛,部署时遇到轻微噪声就大量误报。必须用时间切分的验证集检查边界,而不能只看训练集上的 predict 结果。
4.5 与 Isolation Forest 的根本区别
两者都可以只使用正常样本,但机制不同:
- Isolation Forest 依赖随机切分后的隔离路径;
- One-Class SVM 通过核空间中的边界优化定义支持区域;
- Isolation Forest 通常对尺度不如 RBF 核敏感,但特征范围和异常结构仍会影响随机切分;
- One-Class SVM 明确依赖距离和核参数,标准化几乎是必要的;
- One-Class SVM 在大样本上的核计算成本可能明显高于树模型。
没有一种方法天然拥有“异常真值”。选择依据应是正常数据形状、样本规模、延迟预算、可解释性和反馈周期。
五、深度学习与生成式 AI 中的异常分数
当输入是图像、长序列、文本或高维嵌入时,手工统计量可能不足。深度模型常见的异常分数有三类。
5.1 重构误差
自编码器学习:
异常分数可定义为:
对多变量时间序列,还可以使用按特征加权的误差:
其中 应根据单位、业务损失或正常波动尺度设定。
反例是“强大的自编码器把异常也重构得很好”。如果训练数据包含异常,或者模型容量足够大,重构误差可能无法区分异常。重构模型必须配合纯净训练集、掩码预测、预测误差或其他约束。
5.2 预测误差
对时间序列,使用过去窗口 预测下一时刻:
异常分数为:
预测误差能表达季节性和时间上下文。例如周一上午的流量不能用周日凌晨的固定均值判断。代价是需要正确处理缺失值、延迟、窗口边界和概念漂移。
5.3 生成式 AI 和嵌入空间
对文本、图像或多模态对象,常先得到嵌入 ,再在嵌入空间进行:
- k 近邻距离;
- 类中心或 Mahalanobis 距离;
- Isolation Forest;
- 密度估计;
- 聚类外点检测。
这检测的是“语义或表示空间中的新颖性”,不是安全意义上的事实错误。例如,一段看似罕见的提示词可能只是新主题,不一定是恶意输入;一段生成文本与历史语料距离很近,也不代表它事实正确。
生成式 AI 生产系统还需要检测:
- 输入提示注入或越权意图;
- 输出中的敏感信息;
- 工具调用参数是否超出权限;
- 生成内容的格式和事实校验失败;
- token、延迟和费用异常。
这些问题往往需要规则、分类器、权限策略和统计检测组合完成。异常模型不能替代授权校验:一个“统计上正常”的请求仍可能没有权限。
六、阈值不是模型参数,而是治理对象
模型通常输出连续分数 。阈值 把分数转成动作:
但生产系统往往不只有一个动作。例如:
阈值选择本质上是代价优化。设误报成本为 ,漏报成本为 ,则期望代价可写成:
如果还有人工审核容量 ,则需要增加约束:
因此,阈值不能只由“模型分数超过多少”决定,还取决于告警接收能力、业务损失、处置时间和动作可逆性。
6.1 无标签时的阈值
在没有可靠异常标签时,常见方案是对正常校准集取高分位数:
这里 是允许的候选异常比例。它只能控制参考正常分布下的触发比例,不能保证真实误报率,因为:
- 校准集可能被污染;
- 部署分布可能漂移;
- 样本之间可能相关;
- 多次监控会放大偶发事件。
例如每分钟检查一次,单次误报概率即使只有 0.1%,一天也有 1440 次机会;“单次看起来很低”的误报率可能仍造成频繁告警。
6.2 有标签时的阈值
有标签时,应在时间上晚于训练集的验证集上计算多个阈值的:
- precision、recall、F1;
- 误报数、漏报数;
- 每小时告警量;
- 每个业务事件的检测率;
- 平均提前量;
- 高风险样本的召回率。
异常通常极不平衡,ROC-AUC 可能看起来很好,但在真实低基准率下 precision 很差。PR-AUC、固定告警预算下的召回率和事件级指标更有意义。
点级评估与事件级评估也要分开。一次持续 30 分钟的故障可能产生 30 个异常点,但业务只需要一次告警;如果只计算点级 precision,会错误地惩罚合理的持续告警策略。
6.3 动态阈值和漂移
季节性系统应使用上下文条件阈值:
但动态阈值不能无条件跟随当前数据,否则故障期间的高分会进入基线,造成阈值污染。常见保护措施是:
- 只用已确认正常的数据更新基线;
- 发生大规模告警时冻结阈值;
- 对基线更新设置最大步长;
- 保留固定历史窗口;
- 记录每次阈值变更的原因和审批人。
七、阈值治理的状态、并发和故障路径
阈值配置应被视为带版本的策略对象,而不是散落在代码中的常量。一个最小配置可包含:
{
"detector": "payment_iforest_v3",
"feature_schema_version": "payment_features_12",
"score_direction": "higher_is_more_anomalous",
"thresholds": {
"observe": 0.42,
"alert": 0.68,
"block": 0.91
},
"effective_from": "2025-01-01T00:00:00Z",
"expires_at": "2025-02-01T00:00:00Z",
"owner": "risk-platform",
"reason": "calibrated_on_rolling_30d",
"approved_by": "risk-oncall"
}
必须明确分数方向,否则一个模型“越小越异常”、另一个模型“越大越异常”会导致策略反转。
一次线上判定的关键路径可以表示为:
sequenceDiagram
participant E as 事件流
participant F as 特征服务
participant M as 模型服务
participant P as 策略存储
participant A as 动作执行器
participant L as 日志与反馈
E->>F: 写入事件/读取窗口
F->>M: 发送特征和schema版本
M->>P: 读取当前生效阈值版本
P-->>M: 返回分数方向与阈值
M->>M: 计算异常分数
M->>A: 返回分数、等级、模型版本
A->>L: 记录判定与动作
L-->>P: 审批后的阈值更新
这里有几个容易被忽略的故障点:
-
模型版本和阈值版本不匹配
新模型的分数尺度改变后继续使用旧阈值,会造成告警爆发或全部沉默。模型发布必须绑定兼容的阈值版本。 -
特征 schema 变化
训练时的列顺序、缺失值处理、缩放器和线上不一致,会产生看似正常但实际错误的分数。应校验 schema 哈希、特征缺失率和数值范围。 -
阈值存储不可用
阈值读取失败时不能随意使用0或上一次未确认的配置。高风险拦截场景需要明确 fail-open 或 fail-closed;告警场景则可能选择降级为仅记录。 -
并发更新丢失
两个审批人同时修改阈值时,应使用版本号或条件写入,例如“只有当前版本仍为 17 时才能更新为 18”,避免后写入覆盖先写入。 -
重复告警
流处理重试可能重复消费同一事件。告警动作需要幂等键,例如entity_id + anomaly_window + policy_version,并通过去重窗口避免重复通知。 -
时间乱序和延迟数据
按事件时间计算窗口时,必须规定允许迟到时间;过迟数据是补算、忽略还是生成修正事件,不能由实现细节决定。
八、滞回、连续点和告警去抖
单点阈值容易抖动。设告警阈值为 ,恢复阈值为较低的 :
状态转移为:
NORMAL且 :进入ALERT;ALERT且连续 个点满足 :恢复为NORMAL;- 其他情况保持原状态。
class HysteresisAlert:
def __init__(self, on_threshold, off_threshold, recover_points=3):
if off_threshold >= on_threshold:
raise ValueError("off_threshold must be lower than on_threshold")
if recover_points < 1:
raise ValueError("recover_points must be positive")
self.on_threshold = on_threshold
self.off_threshold = off_threshold
self.recover_points = recover_points
self.state = "NORMAL"
self.below_count = 0
def update(self, score):
if self.state == "NORMAL":
self.below_count = 0
if score >= self.on_threshold:
self.state = "ALERT"
else:
if score < self.off_threshold:
self.below_count += 1
if self.below_count >= self.recover_points:
self.state = "NORMAL"
self.below_count = 0
else:
self.below_count = 0
return self.state
如果分数序列为:
且开启阈值为 0.7、恢复阈值为 0.4、恢复需要连续 3 个点,则第二个点进入告警;第五个点虽然低于恢复阈值,但还未满足 3 个连续点;第七个点时才恢复。这个状态机比每个点独立比较阈值更符合“故障事件”的语义。
但滞回会增加恢复延迟,不适合必须即时解除的安全控制;阻断类动作还应有人工解锁、最大持续时间和紧急旁路机制。
九、评测:排序、分类和事件必须分开
异常检测模型至少产生三个层次的评测问题。
9.1 分数排序质量
不固定阈值时,使用 ROC-AUC 或 PR-AUC 评估分数排序。异常比例很低时,PR-AUC 更能反映高分区域的精确率。
但 AUC 不能告诉你“每天会产生多少告警”,也不能保证在某个具体阈值上可用。
9.2 固定阈值后的分类质量
对阈值 计算:
生产中还应报告:
- 每个实体每天的告警数;
- 告警合并后的事件数;
- 每个事件的检测率;
- 严重级别分层召回率;
- 误报调查耗时;
- 模型推理和特征计算成本。
9.3 时间切分和泄漏
随机切分会把同一故障、同一用户或相邻时间窗口同时放进训练和验证,导致异常检测结果虚高。更可靠的切分方式包括:
- 按时间先后切分;
- 按设备、账户或租户分组切分;
- 对连续故障按事件整体切分;
- 使用滚动窗口评估漂移。
如果阈值在测试集上选择,再用同一测试集报告指标,也会产生评估泄漏。应区分训练集、校准集、验证集和最终测试集;阈值治理本身也需要版本化。
十、常见误解与诊断方法
误解一:异常率等于 contamination
contamination 通常是模型内部阈值或训练假设的一部分,不是经过验证的真实异常率。若系统每天真实异常率变化,固定 contamination 不会自动适应业务风险。
诊断方法:分别记录原始分数分布、内部阈值、最终业务阈值和人工确认标签,比较它们随时间的变化。
误解二:分数可以跨模型、跨版本直接比较
Isolation Forest、One-Class SVM、自编码器的分数尺度和方向不同。同一模型重新训练后,树结构、样本窗口或归一化方式变化,也可能改变分数分布。
诊断方法:每个模型版本报告正常校准集的中位数、P95、P99、P99.9 和阈值,不直接比较绝对分数;需要统一展示时使用分位数或校准后的风险等级。
误解三:异常一定是少数
有些数据切换到错误状态后,异常样本可能迅速成为多数。例如服务配置错误导致所有实例同时产生相同异常。此时基于“稀有性”的算法可能失效,应使用已知状态规则、变更关联、监督标签或时间序列基线。
误解四:标准化可以解决所有问题
标准化只能调整尺度,不能解决:
- 标签错误;
- 时间泄漏;
- 分布漂移;
- 异常群体变大;
- 训练数据被污染;
- 特征语义发生变化。
误解五:告警越敏感越好
阈值过低会产生告警疲劳,使真正异常被忽略;阈值过高会漏掉早期信号。阈值应与动作风险匹配:可逆的观察动作可以更敏感,不可逆的扣款、封禁和阻断动作需要更高置信度、多信号确认或人工审核。
十一、生产落地的最小闭环
一个可验证的异常检测系统至少应保留以下信息:
event_id
entity_id
event_time
feature_schema_version
model_version
threshold_policy_version
raw_score
score_direction
decision_level
action
input_quality_flags
label_status
raw_score 不能只保留二值结果,否则之后无法重放阈值、分析漂移或比较新策略。输入质量也应进入记录:缺失值填充、截断、异常范围修正可能本身就是异常信号。
模型上线前应完成以下因果检查:
- 用历史正常数据拟合特征变换和模型;
- 用独立校准集确定分数方向和阈值;
- 用时间上更晚的验证数据检查告警量和事件级效果;
- 对特征缺失、服务超时、模型不可用制定降级动作;
- 以灰度或影子模式观察真实分数分布;
- 阈值、模型、特征和动作策略绑定版本;
- 收集人工确认结果,区分误报、漏报、未知;
- 定期检查分布漂移、告警预算和处置容量。
成本也应纳入同一系统:特征计算、嵌入生成、模型推理、存储原始分数、人工审核和误报造成的业务中断都属于异常检测成本。生成式 AI 场景尤其要关注嵌入和大模型调用费用;可以先用规则、轻量统计模型或缓存结果过滤明显正常请求,再对高风险候选调用昂贵模型,但必须验证级联过滤不会系统性漏掉某类异常。
十二、如何选择方法
可以按问题结构进行初步选择:
- 单变量、分布稳定、需要可解释:Z-score、稳健 Z-score、分位数;
- 多变量且相关性重要:Mahalanobis 距离、稳健协方差、降维后距离;
- 正常样本较多、异常稀少、希望较低训练成本:Isolation Forest;
- 正常数据边界复杂、样本规模可控:One-Class SVM;
- 图像、长序列或高维表示:自编码器、预测模型或嵌入空间检测;
- 已有充分异常标签:监督分类或排序模型,并保留无监督检测发现未知异常;
- 安全和权限场景:异常分数只能辅助风险分层,不能替代显式授权和策略校验。
最终目标不是让某个模型在离线数据上得到最高分,而是让“分数—阈值—动作—反馈”形成可解释、可回放、可控制风险的闭环。统计方法提供基线,Isolation Forest 提供基于隔离难度的非参数检测,One-Class 方法学习正常边界,而阈值治理决定这些信号在真实系统中何时变成观察、告警、审核或阻断。
系列导航与关联阅读
- 系列入口:AI 工程完整学习路线:从机器学习与 Transformer 到 RAG、Agent 和生产治理
- 上一篇:降维方法:PCA、SVD、t-SNE、UMAP 与可视化边界
- 下一篇:不平衡学习:采样、代价敏感、指标、阈值与概率校准
官方资料
本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。

评论
0 条讨论