2.时序异常检测入门到实战:评估的陷阱:point-adjust 如何把烂模型刷成 SOTA

发布时间:2026/7/26 7:07:04

2.时序异常检测入门到实战:评估的陷阱:point-adjust 如何把烂模型刷成 SOTA 时序异常检测入门到实战二· 评估的陷阱point-adjust 如何把烂模型刷成 SOTA本文是「码海寻道」《时序异常检测入门到实战》系列的第 2 篇。上一篇结尾埋了个雷有个叫point-adjust的流行评估方式能把一个近乎随机的模型 F1 刷到 0.9一度让整个领域的排行榜集体注水。这一篇就来引爆它。为什么先讲评估、再讲模型因为评估搞错了后面比什么都是白比——你会误以为一个花哨模型很强其实它只是沾了指标漏洞的光。这是本系列的随机切分翻车时刻请务必读完。本篇几乎全是原理和推演代码只有一段——但那一段能亲手把陷阱演示给你看强烈建议跑一跑。一个荒诞的事实随机数生成器击败了 SOTA先把结论摆在这儿你可能不信2022 年有研究者拿一个纯随机的异常打分器连数据都不看直接掷骰子给每个点打个随机分套上当时论文里通用的 point-adjust 评估在多个公开 benchmark 上的 F1超过了那几年一众精心设计的深度模型。这不是模型太菜是尺子坏了。用一把会自己往高处走的尺子量身高谁量都是姚明。今天我们就把这把尺子拆开看看它坏在哪。二、先把正确的尺子立起来在拆穿错的之前先复习上一篇立的规矩——异常检测该用的三个指标准确率已经被我们扫地出门了查准率 Precision报警的里头真是异常的比例。管的是别乱叫。查全率 Recall真实异常里被抓到的比例。管的是别漏报。F1两者的调和平均一个数平衡别乱叫和别漏报。这三个指标最朴素的算法是**逐点point-wise**比对把预测的异常点集合和真实的异常点集合摆一起一个点一个点数——数对了几个TP、错报了几个FP、漏了几个FN。干净、诚实、没有歧义。逐点 F1 本身没有任何问题。出问题的是有人嫌它太严动手改了它——改出来的那个变种就是 point-adjust。三、point-adjust 是怎么来的一个善意的出发点先说句公道话point-adjust 的动机并不坏甚至挺合理。设想真实的运维场景一次故障往往不是孤零零一个点而是持续一段时间比如某服务卡了 10 分钟这 10 分钟里的几百个采样点都被标为异常。现在你的模型在这段故障里只在第 3 分钟报了一次警其余时刻没吭声。按逐点算这段有几百个异常点你只命中 1 个查全率低得惨不忍睹。可站在运维角度想——这不公平啊模型第 3 分钟就把警报拉响了运维已经冲过去处理整个事件了你何必揪着后面几百个点你没继续报不放只要在一段故障里报了一次就该算这段故障被发现了。这个想法催生了 point-adjustpoint-adjust点调整规则对每一个真实的异常段只要模型在段内命中了哪怕一个点就把这整段的所有点全部改判为正确检出。听起来很体贴对不对麻烦就出在这个哪怕一个点上。四、陷阱引爆为什么它会把烂模型捧上天我们把 point-adjust 的规则翻译成一句大白话在一段异常里蒙对一个点整段几百个点就全白送给你当命中。问题来了——在一长段里蒙中至少一个点有多容易假设某个异常段有L 个点你的模型哪怕纯随机每个点有p 的概率会报警。那么整段一个都没蒙中的概率是(1−p)^L于是至少蒙中一个的概率是P(点亮整段) 1 − (1−p)^L代几个数进去感受一下p只取一个很小的 5%异常段长度 L至少蒙中一个的概率1040%5092%10099.4%20099.99%看懂了吗只要异常段足够长哪怕你完全靠掷骰子也几乎必然能蒙中其中一个点——然后 point-adjust 大手一挥把整段几百个点全判给你算命中。查全率于是趋近 100%几乎是白送的。而查准率呢也垮不到哪去。因为你只需要报警很少一部分点比如 5%落在正常区的那些误报本就不多而落在异常段里的哪怕一个点都会回本式地点亮一整段真异常。一来二去查准也被垫高了。查全接近满分、查准也不差F1 自然虚高到 0.9。一句话总结这个陷阱point-adjust 把检出一个点的难度偷偷换成了整段全对的奖励。异常段越长、异常占比越高这个杠杆撬得越狠——最后连随机数都能骑着它登顶。亲手跑一遍眼见为实空口无凭。下面这段完整代码构造一批较长的异常段然后让一个纯随机打分器完全不看数据分别在原始逐点和point-adjust两把尺子下打分。复制即跑importnumpyasnpdefpoint_adjust(pred,label):标准 point-adjust真实异常段内只要命中≥1 个点整段全部改判为检出。pred,labelpred.astype(bool).copy(),label.astype(bool)n,ilen(label),0whilein:iflabel[i]:jiwhilejnandlabel[j]:# 找出这一整段连续异常 [i, j)j1ifpred[i:j].any():# 段内蒙中至少一个pred[i:j]True# → 整段点亮ijelse:i1returnpreddefprf(pred,label):逐点查准/查全/F1。pred,labelpred.astype(bool),label.astype(bool)tp(predlabel).sum();fp(pred~label).sum();fn(~predlabel).sum()ptp/(tpfp1e-9);rtp/(tpfn1e-9)returnp,r,2*p*r/(pr1e-9)rngnp.random.default_rng(0)n10000# 真实标签10 段、每段长 200 的异常占比 20%互不重叠labelnp.zeros(n,dtypebool)forbinrange(10):startb*1000rng.integers(0,800)label[start:start200]True# 一个纯随机打分器完全不看数据掷骰子打分报警分数最高的 5%scorerng.random(n)predscorenp.quantile(score,0.95)print(原始逐点 P/R/F1 %.2f / %.2f / %.2f%prf(pred,label))print(point-adjust P/R/F1 %.2f / %.2f / %.2f%prf(point_adjust(pred,label),label))你会看到类似这样的结果原始逐点 P/R/F1 0.20 / 0.05 / 0.08 point-adjust P/R/F1 0.83 / 1.00 / 0.91同一个随机模型同一批数据只是换了把尺子F1 从惨不忍睹的0.08跳到堪比论文 SOTA 的0.91查全率直接拉满到 1.00。而这个模型连数据长什么样都没看过。把上面的段长 200 调大到 400虚高会更夸张这就是整个领域被误导了好几年的真相。五、那正确的评估该怎么做拆穿了陷阱得给你可落地的替代方案。记住下面这套能让你在读论文、做实验时不再被漂亮数字忽悠。铁律永远让随机 / 朴素基准先过一遍你的指标这是本系列最想让你养成的评估习惯和预测系列任何模型先打败 Naive是同一条筋拿一个纯随机打分器、或上一篇那个移动窗口 3σ用你即将报告的那套指标算一遍。如果它们也能拿高分那说明你的指标坏了而不是你的模型好了。就像上面的实验——一旦发现随机数在 point-adjust 下能到 0.91你立刻就该警觉这个 0.91 不值钱。这一步几乎零成本却能帮你避开绝大多数自欺欺人的结论。用对指标三个更诚实的选择逐点 PR-AUC首选做主指标。前面所有麻烦都出在先卡一个阈值、再算 F1——阈值一动数字就变容易被人挑最漂亮的那个报告。PR-AUC查准-查全曲线下面积不依赖任何单一阈值它衡量的是模型给异常打高分、给正常打低分的整体排序能力。在极不平衡场景下它比 ROC-AUC 更能反映真实水平ROC-AUC 会被海量正常样本稀释得虚高。事件级 / 范围级指标贴合业务又不失真。如果你确实认同一段故障报一次就够的业务诉求别用会崩坏的 point-adjust改用更严谨的范围级指标如 range-based precision/recall、affiliation 指标它们同时考量是否检出、重叠多少、报得及不及时既奖励报一次算发现又不会像 PA 那样把整段无条件白送。PA%K给 point-adjust 打上补丁。实在要用点调整也别用蒙中一个就点亮整段的原始版。改用PA%K——要求段内被命中的点**达到 K%比如 20%**才允许点亮整段。门槛一立随机数就没法再靠蒙一个点薅羊毛了。一个务实的报告姿势别只报一个数。一篇诚实的异常检测评估通常会同时给出阈值无关的 PR-AUC看整体能力最优阈值下的逐点 F1看落地效果一条随机/朴素基准当照妖镜。三个一起看漂亮数字才骗不了你。小结领域曾集体踩坑一个纯随机打分器在point-adjust下 F1 能到 0.9击败真模型——是尺子坏了不是模型强。point-adjust 的规则是段内蒙中一个点整段全算检出出发点一段故障报一次就够合理但那个一个点是致命漏洞。数学根源段长 L、报警率 p蒙中整段的概率是1−(1−p)^L段一长就趋近 1查全率几乎白送F1 虚高。正确姿势永远拿随机/朴素基准过一遍指标当照妖镜主指标用阈值无关的PR-AUC要事件级就用范围级指标或PA%K别用原始 point-adjust。评估这把尺子校准好了从下一篇起我们才能放心地上模型、真刀真枪地比。第 3 篇正式请出全系列的及格线选手——移动窗口 3σ、箱线图、EWMA 这些统计基线看这些老古董到底能打到什么程度也给后面所有复杂模型立一个必须跨过的标杆。思考题PA%K 里的 K 该怎么定把 K 定成 0% 会退化成什么提示就是原始 point-adjust把 K 定成 100% 又会苛刻到什么地步这个 K本质上是在贴合运维直觉和防止指标注水之间调哪一个旋钮校准尺子比堆砌模型更重要。下一篇我们让统计基线登场用几行不需要训练的老代码定下整个系列的及格线。

相关新闻