🔍 异常检测 — Isolation Forest/Z-score

大海捞针,找出那些"不一样的存在"

📖 异常检测概述

异常检测(Anomaly Detection)是从数据中识别"异常"或"离群"样本的技术,广泛应用于欺诈检测、入侵检测、设备故障预警等场景。

异常检测三大类型: 1. 点异常 (Point Anomaly) ───────────────────────── 单个数据点异常(与整体分布偏离) 例: 信用卡单笔交易金额异常大 2. 上下文异常 (Contextual Anomaly) ────────────────────────────────── 在特定上下文中异常(时间/空间相关) 例: 夏天10°C温度正常,冬天10°C异常 3. 集合异常 (Collective Anomaly) ────────────────────────────────── 单个点正常,但一组点的组合异常 例: 同一IP短时间多次登录不同账号 异常检测方法分类: ┌────────────────────────────────────────┐ │ 统计方法 │ Z-Score / IQR / Grubbs │ │ 机器学习 │ IsolationForest / OCSVM │ │ 深度学习 │ AutoEncoder / VAE / GAN │ └────────────────────────────────────────┘
方法类型优点缺点适用
Z-Score统计简单快速假设正态低维
IQR统计鲁棒单维度低维
IsolationForest集成高维友好随机性通用
One-Class SVM核方法边界精确中小规模
AutoEncoder深度非线性需大数据复杂分布
LOF密度局部异常参数敏感密度变化

🔢 Z-Score方法

Z-Score衡量一个点离均值有多少个标准差,是最直觉的异常检测方法。

Z = (x - μ) / σ    阈值: |Z| > 2.5 或 |Z| > 3 → 异常
Z-Score原理: 正态分布下: |Z| < 1: 68.3% 的数据 → 正常 |Z| < 2: 95.4% 的数据 → 正常 |Z| < 3: 99.7% 的数据 → 边界 |Z| > 3: 0.3% 的数据 → 异常! (3σ原则) 多维: 任一维度|Z|>阈值 → 异常 或: 马氏距离 (考虑特征间相关性) D² = (x-μ)ᵀ Σ⁻¹ (x-μ)
from scipy import stats
import numpy as np

np.random.seed(42)
n_normal, n_anomaly = 300, 30
X_normal = np.random.randn(n_normal, 2)
X_anomaly = np.random.randn(n_anomaly, 2) * 3 + np.array([5, 5])
X = np.vstack([X_normal, X_anomaly])
y_true = np.array([1]*n_normal + [-1]*n_anomaly)

# Z-Score检测
z_scores = np.abs(stats.zscore(X, axis=0))
threshold = 2.5
z_anomaly = (z_scores > threshold).any(axis=1)
z_pred = np.where(z_anomaly, -1, 1)

# 各维度统计
for dim in range(2):
    z_dim = np.abs(stats.zscore(X[:, dim]))
    n_outlier = (z_dim > threshold).sum()
    print(f"维度{dim}: Z>{threshold}的点={n_outlier}, 最大Z={z_dim.max():.2f}")

z_correct = (z_pred == y_true).sum()
print(f"\nZ-Score准确率: {z_correct/len(y_true)*100:.1f}%")
Z-Score假设数据服从正态分布!如果数据严重偏态,Z-Score可能误报或漏报。此时建议用IQR或非参数方法。

📊 IQR方法

IQR(四分位距)方法基于数据的分位数,不受极端值影响,比Z-Score更鲁棒。

IQR方法: 排序后: ───────[Q1]─────────[Q2/中位数]─────────[Q3]─────── IQR = Q3 - Q1 (中间50%数据的范围) 下界 = Q1 - 1.5 × IQR 上界 = Q3 + 1.5 × IQR 超出范围 → 异常! ┌──────────────────────────────────────────────┐ │ 为什么用1.5? 正态分布下: │ │ Q1-1.5×IQR ≈ μ-2.7σ │ │ Q3+1.5×IQR ≈ μ+2.7σ │ │ → 约99.3%的数据在范围内,0.7%被判异常 │ └──────────────────────────────────────────────┘
# IQR检测
iqr_anomaly = np.zeros(len(X), dtype=bool)
for dim in range(2):
    Q1 = np.percentile(X[:, dim], 25)
    Q3 = np.percentile(X[:, dim], 75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    outlier_dim = (X[:, dim] < lower) | (X[:, dim] > upper)
    iqr_anomaly |= outlier_dim
    print(f"维度{dim}: Q1={Q1:.2f}, Q3={Q3:.2f}, IQR={IQR:.2f}, "
          f"范围=[{lower:.2f}, {upper:.2f}]")

iqr_pred = np.where(iqr_anomaly, -1, 1)
iqr_correct = (iqr_pred == y_true).sum()
print(f"\nIQR准确率: {iqr_correct/len(y_true)*100:.1f}%")

🌲 Isolation Forest

Isolation Forest的核心思想:异常点"少而不同",更容易被孤立。随机切分空间,异常点只需很少次切分就能被隔离。

Isolation Forest原理: 正常点: 需要很多次切分才能孤立 异常点: 很少切分就能孤立 ┌─────────────────┐ ┌─────────────────┐ │ · · · · │ │ · · · · │ │ · · · · │ │ · · · · │ │ · · ●· · │ │ · · · · │ │ · · · · │ │ · · · · │ │ · · · · │ │ · · ★ · │ └─────────────────┘ └─────────────────┘ 需要8次切分才能孤立 只需2次切分就孤立! 异常分数: s(x, n) = 2^(-E(h(x)) / c(n)) h(x) = 样本x的平均路径长度 c(n) = 归一化因子 (二叉搜索树平均路径) s → 1: 异常 (路径短,容易被孤立) s → 0.5: 正常 (路径与平均长度相当) s → 0: 非常正常
from sklearn.ensemble import IsolationForest

# Isolation Forest
iso = IsolationForest(
    contamination=0.1,      # 异常比例估计
    n_estimators=100,       # 树的数量
    random_state=42
)
iso_pred = iso.fit_predict(X)  # 1=正常, -1=异常

# 异常分数 (越大越正常)
scores = iso.decision_function(X)
print(f"正常样本平均分数: {scores[y_true==1].mean():.3f}")
print(f"异常样本平均分数: {scores[y_true==-1].mean():.3f}")

iso_correct = (iso_pred == y_true).sum()
print(f"IsolationForest准确率: {iso_correct/len(y_true)*100:.1f}%")

🔬 方法全面对比

# 综合对比
from sklearn.svm import OneClassSVM

# One-Class SVM
ocsvm = OneClassSVM(nu=0.1, kernel='rbf', gamma='scale')
ocsvm_pred = ocsvm.fit_predict(X)

print(f"{'方法':>18} {'准确率':>8} {'精确率':>8} {'召回率':>8} {'F1':>8}")
print("=" * 55)
for name, pred in [("Z-Score", z_pred), ("IQR", iqr_pred), 
                    ("IsolationForest", iso_pred), ("OneClassSVM", ocsvm_pred)]:
    acc = (pred == y_true).sum() / len(y_true)
    tp = ((pred == -1) & (y_true == -1)).sum()
    fp = ((pred == -1) & (y_true == 1)).sum()
    fn = ((pred == 1) & (y_true == -1)).sum()
    precision = tp/(tp+fp) if (tp+fp) > 0 else 0
    recall = tp/(tp+fn) if (tp+fn) > 0 else 0
    f1 = 2*precision*recall/(precision+recall) if (precision+recall) > 0 else 0
    print(f"{name:>18} {acc:>8.3f} {precision:>8.3f} {recall:>8.3f} {f1:>8.3f}")
实际项目中,异常检测没有"最佳方法"——选择取决于数据特点、异常类型和业务需求。通常建议先用IQR/Z-Score做基线,再用Isolation Forest提升效果。

📐 2024-2025 异常检测前沿

🧮 异常检测方法选择

异常检测选择指南: 数据特点? │ ├── 数据是否近似正态? │ ├── 是 → Z-Score (简单快速) │ └── 否 → IQR (鲁棒) │ ├── 数据维度? │ ├── 低维(<10) → Z-Score / IQR / LOF │ └── 高维 → Isolation Forest / AutoEncoder │ ├── 有标签数据? │ ├── 无标签 → 无监督方法 │ ├── 少量标签 → 半监督(OCSVM) │ └── 大量标签 → 监督分类 │ ├── 需要可解释性? │ └── Z-Score/IQR (直观) │ └── 时序数据? ├── 统计 → ARIMA残差 └── 深度 → LSTM-AE
🏆 成就解锁:Isolation Forest/Z-score
四种异常检测方法同场竞技:IQR准确率98.2%(F1=0.900),IsolationForest准确率97.3%(F1=0.857),Z-Score准确率96.7%(F1=0.776),OneClassSVM准确率93.9%(F1=0.677)!IQR在本数据集上最优!
Python验证通过 — 异常检测4方法对比:Z-Score(96.7%, precision=1.0, recall=0.633, F1=0.776),IQR(98.2%, precision=0.9, recall=0.9, F1=0.9),IsolationForest(97.3%, precision=0.818, recall=0.9, F1=0.857),OneClassSVM(93.9%, precision=0.656, recall=0.7, F1=0.677)!
思考题:
1. Z-Score和IQR各有什么优缺点?什么场景下用哪个?
2. Isolation Forest为什么对异常点"天然敏感"?它的直觉是什么?
3. 如何设定异常检测的阈值?过高/过低各有什么后果?
4. 在实际欺诈检测中,你会优先保证精确率还是召回率?为什么?

📝 课后练习

  1. 用KDD Cup 1999网络入侵数据集做异常检测,对比各方法
  2. 实现LOF(局部异常因子),对比全局方法的效果
  3. 用AutoEncoder做异常检测(重构误差作为异常分数)
  4. 实现时间序列异常检测(滑动窗口+Z-Score)
  5. 实现马氏距离(考虑特征相关性),对比普通Z-Score
📚 参考资料:
• Isolation Forest (Liu et al., 2008)
• Anomaly Detection: A Survey (Chandola et al., 2009)
• sklearn IsolationForest文档
• Outlier Analysis (Aggarwal, 2017)
• Deep Learning for Anomaly Detection (Ruff et al., 2021)