halo 的技术博客

返回

先说结论:马氏距离假设『正常』只有一个中心,偏离越远越异常;但市场的正常状态经常是多峰的——平静区制和承压区制是两团各自正常的点云,真正危险的日子藏在两团之间的『山谷』里。 山谷距离全局均值很近,马氏距离因此把它判成”最正常”,方向彻底反了。本文合成实验:1000 个正常交易日(分属两个区制)+ 30 个落在山谷里的异常日,马氏距离 AUC 只有 0.080(低于 0.5 意味着比随机还差、把异常排到了最不异常的一端),而 One-Class SVM 用 RBF 核学出一条非凸边界,AUC 0.961、top-30 命中率 33%

One-Class SVM:用高维边界圈出『不正常』的交易日

一、问题:正常不止一个样子#

风控里最常见的多维异常检测是马氏距离:估计一个均值 μ\mu 和协方差 Σ\Sigma,算每个点的 d2=(xμ)Σ1(xμ)d^2 = (x-\mu)^\top \Sigma^{-1} (x-\mu),超过某个卡方阈值就报警。它的隐含假设很硬:正常样本服从单个椭圆高斯分布,越靠近中心越正常。

可市场偏偏不是这样。用两个因子(市场因子、波动因子)刻画交易日,你会看到至少两团:

  • 平静区制:市场因子低、波动因子低,聚成一团;
  • 承压区制:市场因子高、波动因子高,聚成另一团。

这两团各自都完全正常——它们是市场在两种状态下的常态。但如果某天落在了两团之间的山谷:市场因子中性、波动因子也中性,看起来”什么都不极端”,它其实极不正常——因为历史上几乎没有哪天真的停在这个不上不下的位置。这种日子往往对应区制切换的过渡态、流动性的短暂真空,恰恰是最该警惕的。

马氏距离在这里会犯致命错误:山谷离全局均值 μ\mu 最近,d2d^2 最小,于是被判成”最正常”。在本文实验里,马氏 AUC 掉到 0.080——它不是没抓到异常,是把异常精准地排到了正常度最高的一端。

二、原理:学一条把『正常区域』包起来的边界#

One-Class SVM(Schölkopf 2001)换了个目标:不假设正常长什么形状,直接在特征空间里学一条边界,把绝大多数训练样本圈在里面,落在边界外的就是异常。

关键在于用 RBF 核 K(x,y)=exp(γxy2)K(x, y) = \exp(-\gamma \lVert x - y \rVert^2) 把数据映到高维再画边界。RBF 核的效果是”局部相似”——只有彼此靠近的点核值才高。于是学出来的支撑区域可以是非凸的、甚至分成好几块:两团正常各自被圈住,中间的山谷自然被排除在外。这正是它能干掉马氏距离的地方——马氏只会画一个椭圆,OC-SVM 能画出”两个圈 + 中间挖空”。

数学上它解一个对偶问题:

minα 12αKαs.t.0αi1νn, iαi=1\min_{\alpha}\ \frac{1}{2}\alpha^\top K \alpha \quad \text{s.t.}\quad 0 \le \alpha_i \le \frac{1}{\nu n},\ \sum_i \alpha_i = 1

决策函数 f(x)=iαiK(xi,x)f(x) = \sum_i \alpha_i K(x_i, x),边界阈值 ρ\rho 由落在间隔上的支持向量确定;f(x)<ρf(x) < \rho 判为异常。这里的 ν(0,1]\nu \in (0,1] 是核心旋钮:它同时是异常比例的上界支持向量比例的下界——你告诉模型”大约有 5% 的训练日可以当异常丢掉”,它就据此定边界的松紧。

三、纯 numpy 实现#

对偶问题是一个带盒约束和单纯形约束的二次规划。工业界用 SMO,这里为了透明用投影梯度:每步沿 Kα-K\alpha 走一小步,再把 α\alpha 投影回可行域 {0αiC, αi=1}\{0 \le \alpha_i \le C,\ \sum \alpha_i = 1\}。这个投影是本文最容易写错的地方——很多人先 clip 到 [0,C][0, C] 再除以和来凑 =1\sum = 1,那会破坏盒约束(除完可能又超过 CC)。正确做法是对偏移量 τ\tau 做二分:找一个 τ\tau 使得 clip(ατ,0,C)=1\sum \text{clip}(\alpha - \tau, 0, C) = 1

打分时 score = rho - f,越大越异常。构造数据时有个关键细节:每个区制要用截尾高斯(丢掉马氏距离大于某卡方阈值的远尾样本),否则区制自己的尾巴会比山谷还离群,把 top-k 精度污染掉。

def clipped_gaussian(mean, cov, m, chi2=6.0, rng=None):
    mean = np.asarray(mean, float)
    s = rng.multivariate_normal(mean, cov, int(m * 1.8))
    d = np.einsum("ij,jk,ik->i", s - mean, np.linalg.inv(cov), s - mean)
    return s[d < chi2][:m]     # 只保留紧致核心,两团各成一个 compact blob
python

四、结果:非凸支撑抓住山谷,高斯方法方向全反#

在 1000 个正常日(两区制各 500)+ 30 个山谷异常日上,γ=1.0\gamma = 1.0ν=0.05\nu = 0.05

  • OC-SVM:AUC 0.961,top-30 命中率 33%
  • 马氏距离:AUC 0.080,top-30 命中率 0%

马氏距离把山谷当中心,OC-SVM 用非凸支撑抓住山谷

左图是马氏距离的分数:颜色最深(最异常)的是两团的外缘,而红圈标出的真实异常(山谷)反而是最浅的一片——它坐在两个簇心连线的正中央,离全局均值最近。右图是 OC-SVM 的分数:两团被各自的高相似度区域”照亮”成低分(正常),中间山谷因为跟任何一团都不够近,核相似度都低,分数被顶起来。这就是非凸支撑的价值——它允许”正常”由多块不连通的区域组成

五、两个参数的脾气:ν 定松紧,γ 定形状#

ν\nu 控制边界松紧,直接映射成精确率/召回率的取舍:

nu 参数敏感性:污染率旋钮决定精确率与召回率的取舍

ν\nu 从 0.02 抬到 0.2,召回率从 43% 一路爬到 100%(边界越松,圈进来的”外部”越多,异常越容易全被框住),但精确率始终在 14%~23% 徘徊——因为山谷异常和两团的边缘在分数上有重叠,放宽阈值抓全异常的同时也必然误报边缘正常日。这里没有免费午餐:ν\nu 是你对”能容忍多少误报”的直接声明。

γ\gamma 控制 RBF 核的宽度,也就是边界的形状精细度:

gamma 敏感性:太小欠拟合成一团、太大把每个点圈成孤岛

γ\gamma 太小(0.1)时核太宽,所有点彼此都”相似”,边界糊成一个大椭圆,退化得跟马氏距离一样——AUC 直接掉到 0.0(连两团都分不开,山谷又被当中心)。γ\gamma 在 0.252.0 是甜区,AUC 稳在 0.920.95。γ\gamma 太大(8.0)时核太窄,每个训练点只跟自己相似,模型把每个正常点都圈成一座孤岛——严重过拟合,任何新点(哪怕正常)都落在所有孤岛之外被判异常,AUC 掉到 0.665。这是核方法的通病:γ\gamma 必须交叉验证,不能拍脑袋。

六、六类真实陷阱#

陷阱一:把 OC-SVM 分数当概率。 f(x)f(x) 是到边界的带符号距离,不是”异常概率”。分数 0.3 不代表 30% 概率异常,也没有跨数据集可比的绝对阈值——同一个 ν\nu 在不同市场、不同特征缩放下切出的边界完全不同。

陷阱二:无监督就不用验证集。 OC-SVM 是无监督训练(不看标签),但 γ\gammaν\nu 的选择本质是有监督调参。本文 γ\gamma 从 0.1 到 8.0,AUC 从 0.0 到 0.95 再到 0.665——不用带标签的验证集去挑参数,你几乎必然选错。

陷阱三:训练集必须干净。 和孤立森林一样,OC-SVM 假设训练集里”绝大多数是正常”,靠 ν\nu 容忍一小撮污染。但如果污染比例远超 ν\nu,边界会被异常点自己撑大,把异常也圈进”正常”里。上线前要估计训练窗口的真实污染率。

陷阱四:特征缩放没做就直接喂。 RBF 核算的是欧氏距离,量纲大的特征会主导。成交量(单位百万手)和收益率(单位百分点)不标准化就进核,等于只用了成交量这一维。本文所有特征都做了 z-score,这不是可选项。

陷阱五:能检测不等于能预测。 抓出”今天异常”是事后描述,不是”明天会跌”的预测。异常日可能是崩盘当天,也可能是崩盘后的恐慌反弹——方向未知。把异常分数直接当择时信号做多/做空,是把描述性工具误用成预测性工具。

陷阱六:维度一高就失效。 RBF 核在高维会遭遇距离集中——所有点两两距离趋于相等,核值失去区分度。本文只用了 2 维演示,几十个因子直接上 OC-SVM 前,通常要先降维(PCA/流形学习)或做特征筛选,否则”高维边界”会退化成噪声。

小结#

One-Class SVM 的价值,不在于它比高斯方法”更强”,而在于它不假设正常是单峰的。当你的正常状态天然分成几团(多区制、多风格、多市场状态),马氏距离这类单中心方法会在”团与团之间”系统性失明,而 OC-SVM 的非凸核支撑恰好能把这些真空地带识别出来。代价是两个必须交叉验证的参数、对特征缩放的敏感、以及高维下的退化——它是一把需要仔细校准的手术刀,不是开箱即用的报警器。

One-Class SVM 异常检测:用高维边界圈出『不正常』的交易日
https://blog.halo26812.eu.org/blog/oneclass-svm-anomaly
Author halo
Published at 2026年7月24日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨