视觉大模型零样本异常检测:安防监控F1从0.09到0.64
F1 0.09。这个数字第一次出现在报表里的时候,我以为是评测脚本写反了——把 precision 和 recall 的位置调换了,或者标签列取错了。查了两遍代码,没有。就是 0.09。也就是说,把一个通用的视觉语言大模型直接接到监控画面上做零样本异常检测,它的表现比"全判正常"还要差一截。而我们这组人当时的预期是"至少能到 0.3,稍微微调一下就能上产线"。
后来用了大约六周,同一批测试视频,同一个模型权重,F1 爬到了 0.64。权重一次都没更新,全程零训练,改的全是链路、提示词、参考样本和后处理。这个过程里踩的坑比调参本身更值钱,所以我把它完整写下来——如果你也在做安防场景下的零样本异常检测,或者正准备把某个通用大模型往视频监控里塞,这篇东西应该能帮你省掉前面那几周的瞎忙。
先把结论摆在前面:0.09 不是模型不行,是我们把"通用常识"当成了"现场常识"。0.64 也不是模型变强了,是我们终于把问题问对了。
1. 0.09 意味着什么:把通用大模型直接丢进监控画面,为什么必然翻车
1.1 先把任务口径钉死:我们要的"异常"到底是什么
在骂模型之前,得先承认一件事:很多团队连自己要检测什么都没说清楚。我们第一版的评测集里,"异常"这个标签底下混着至少四类完全不同的东西——有人翻越围栏,有配电柜门被打开,有货物堆到了消防通道上,还有一类是"画面里出现了不该出现的人"。前三类是行为与状态异常,第四类是目标异常,它们在图像上的表现形式、持续时间、空间尺度完全不一样,但我们在评测时把它们捆成一个二分类问题,算一个总 F1。
这就是第一个坑。通用图文模型对"异常"这个词的理解,是互联网语料喂出来的:血腥、暴力、事故、破损、火灾。而机房里的"异常"可能只是某个指示灯从绿变红,占画面不到 0.3% 的面积。你让它判断"这张图是否异常",它其实在判断"这张图是否像网上的事故照片"。
所以第一步不是改模型,是改标签体系。我们把异常拆成三层:目标级(不该有的人、车、动物)、状态级(门开、灯变色、物品移位)、行为级(攀爬、滞留、逆行)。评测也拆开算,各自有各自的阈值。拆完之后你会发现,模型在目标级上其实没那么差,真正崩掉的是状态级——而状态级的样本量在我们原始评测集里占了将近一半,直接把总分拖到 0.09。
提示:如果你的零样本方案 F1 低于 0.15,先别急着换模型,先看评测集里各类异常的占比和模型的实际输出分布。九成情况下问题出在标签体系,不在网络结构。
1.2 通用图文模型的常识,和机房的常识不是一回事
我用一个具体例子说明这种错位。测试视频里有一段是夜间走廊,一个人蹲在配电箱前面作业,工装、头灯、工具箱齐全,正常巡检。模型的异常分数是 0.91。同一条走廊,白天有只猫从画面边缘走过,异常分数 0.12。
在互联网语料的语境里,"夜晚 + 蹲着的人 + 昏暗走廊"是一组强相关的危险信号,模型学得很扎实。它不知道这是巡检,也不知道那只猫对这个机房来说才是真异常(精密设备区禁止动物进入)。这类错位不是靠调阈值能解决的,因为它的分数分布和真实异常完全重叠。
还有一个更隐蔽的问题:画面构图。监控摄像头是俯视、广角、固定机位,画面里有大量重复的结构性元素——货架、管线、地砖、栏杆。这些元素在通用模型的训练分布里出现得不多,模型对它们的表征很不稳定,同一条货架在光照变化下能给出差异很大的特征。而异常检测恰恰依赖"正常状态的稳定性",正常都不稳,异常就没法定义。
实测下来,媒体流里大约 60% 到 70% 的画面块是长时间静止的背景,这些块送进模型纯属浪费算力,还会因为轻微噪声产生随机的高分,制造误报。这一点在后面的第 3.4 节会展开,它也是我们 F1 提升里性价比最高的一刀。
1.3 F1 在极端不平衡下的欺骗性,以及为什么我坚持看 PR 曲线
0.09 这个数字还有一层欺骗性:它掩盖了模型其实"偏向一侧"的事实。我们第一版的输出分布是这样的——测试集 12000 个样本,模型判为异常的有 9600 个,占 80%。precision 0.048,recall 0.42。也就是说,它把所有真异常都抓到了大半,但同时把一大片正常画面也标红了。
这种"高召回低精度"的模型在线下看 recall 是能交差的,一上产线立刻被打回——值班人员一天收到几百条告警,两天之后就开始无脑点"忽略",系统实际上等于失效。所以我在内部定了一条规矩:只报 F1 不够,必须同时报 PR 曲线下的面积和固定误报率下的召回。安防场景里,误报率的心理阈值大概是每路每天 3 到 5 条,超过这个量,一线就不会再信任告警。
| 指标口径 | 第一版基线 | 含义 |
|---|---|---|
| Precision | 0.048 | 每 20 条告警里不到 1 条是真的 |
| Recall | 0.42 | 真异常抓回不到一半 |
| F1 | 0.09 | 综合极差 |
| 每路日均告警数 | 约 210 条 | 值班端完全不可用 |
| 误报率 5 条/天时的召回 | 0.11 | 这才是能上线的口径 |
看到最后一行你就明白,为什么我说 0.09 只是个开始——真正有意义的衡量单位是"在可接受的告警骚扰下能抓到多少",而不是一个笼统的 F1。
2. 搭一条能跑通的最小链路:从视频流到异常分数
2.1 抽帧、切块、去冗余:把视频变成模型能吃的样本
通用图文模型的输入是固定分辨率的单张图。监控视频是 1080P 甚至 4K、25 帧、连续几小时。中间这道转换,决定了后面所有指标的天花板。
我们试过三种抽帧策略。固定间隔抽帧(每 2 秒一帧)实现最简单,但会漏掉持续时间短的异常,比如有人快速翻越围栏,整个过程不到 3 秒。事件触发抽帧(背景差分有变化才抽)召回高,但会把所有光照突变都当成事件,量爆炸。最后用的是混合策略:基础频率 1 帧/秒保底,背景差分超过面积阈值时临时提升到 5 帧/秒,持续 10 秒后回落。
切块这块我们绕了很大一圈。最初的方案是整图送进模型,结果小目标全丢——围栏上的一只手,在整图缩放到 336×336 之后只剩几个像素。改成 3×3 网格切块,每块单独算分数,召回明显上来了,但代价是同一张图要跑 9 次推理,算力翻九倍,而且块与块之间的边界会把目标切断,一个人被切成两半,两半都不像人。
最后的方案是重叠切块 + 多尺度:640×640 的块,重叠 128 像素,同时保留一路整图下采样的全局视图。切块负责局部细节,全局负责上下文。块的数量根据画面内容动态调整——背景块跳过,只对背景差分有响应的区域切块。这样平均每帧实际推理的块数从 9 降到 2.3,算力回到可接受范围。
注意:重叠比例不是拍脑袋定的。经验值是重叠区域不小于目标典型尺寸的一半。你的目标如果是人(画面里大约 120 像素高),重叠 60 像素以上比较稳;如果是指示灯这种几十像素的目标,重叠要给到更足,甚至考虑单独的放大分支。
2.2 特征取哪一层:最后一层未必最好
这一条是我们浪费最久的地方。默认想法是"用最后一层的特征,语义最强"。实测下来恰恰相反。
最后一层的空间分辨率被压得很低,一个 640×640 的块进去,出来的特征图可能只有 20×20,每个格子对应原图 32×32 的区域。对于"门开没开"这种需要精细结构判断的任务,这个粒度太粗了。而且最后一层的表征高度语义化,它回答的是"这块区域像什么类别",而不是"这块区域和平时有什么不同"。
我们做了个消融,把不同层的特征单独拿出来算异常分数,结果大致是这样:
| 特征层 | 空间粒度 | 纹理敏感度 | 语义判别力 | 综合 F1 |
|---|---|---|---|---|
| 浅层 | 细 | 高 | 低 | 0.31 |
| 中层 | 中 | 中高 | 中 | 0.38 |
| 深层 | 粗 | 低 | 高 | 0.27 |
| 中层+深层拼接 | 中 | 中高 | 高 | 0.44 |
中层的纹理响应对"结构变化"很敏感——门框边缘的直线断掉了、货架上的方块少了一块,这些在中层特征上的距离变化非常明显。深层则擅长判断"这里是不是出现了不该有的东西"。两层拼接,做 L2 归一化之后按权重融合,比任何单层都好。
代价是特征维度翻倍,检索时的内存开销上去了。我们的做法是对拼接后的特征做一次随机投影降维,从 2048 维降到 512 维,实测 F1 只掉了 0.01,但检索速度快了将近四倍。
2.3 上线前必须先看的三张图
链路跑通之后,别急着看 F1。我要求组里每个人先画三张图,这三张图比任何指标都更能说明问题出在哪。
第一张是分数直方图,正常样本和异常样本各画一条。如果两条曲线几乎完全重叠,说明特征没有判别力,再怎么调阈值都是白搭;如果正常样本的分布是个长长的右尾,说明现场存在大量"正常但罕见"的状态,需要在参考集里补样本。我们第一版的图就是这样,正常样本的分数从 0.02 一直拖到 0.85,右尾长得离谱,一查才发现是傍晚开灯那二十分钟的画面全在尾巴上。
第二张是空间热力图,把每个块的异常分数映射回原图。这张图能直接暴露"模型在看哪里"。我们发现过模型盯着画面右下角的时间戳水印打高分,因为水印的数字每秒都在变,特征不稳定。这类问题看指标永远发现不了,看热力图一眼就出来了。
第三张是分数随时间的曲线,挑几段有代表性的视频画出来。真实的异常在时间轴上是连续的、有起止的,而噪声型误报是单帧的尖刺。这张图直接决定了后面第 3.5 节的时序平滑策略该怎么设计。
3. 分数从 0.09 爬到 0.64:六个动作的逐一拆解
先说明口径,免得被误解。0.64 不是纯零样本的 SOTA 数字,它的完整设定是:模型权重完全不动、不做任何梯度更新,但允许使用现场采集的正常样本作为参考。严格讲这是"零训练 + 现场校准",业界有时候把它归到 few-shot 的范畴。我认为这个区分很重要,因为纯文本提示的零样本在我们的场景里最多只能到 0.23 左右,再往上必须有现场信息介入。下面这张表是各个动作的边际收益,按实施顺序排:
| 阶段 | 关键动作 | Precision | Recall | F1 |
|---|---|---|---|---|
| 基线 | 通用提示词 + 整图推理 | 0.048 | 0.42 | 0.09 |
| 动作一 | 场景化提示词集成 | 0.14 | 0.58 | 0.23 |
| 动作二 | 现场正常样本记忆库 | 0.36 | 0.49 | 0.41 |
| 动作三 | 多尺度切块 + 中层特征 | 0.47 | 0.58 | 0.52 |
| 动作四 | 运动门控过滤静态背景 | 0.55 | 0.57 | 0.56 |
| 动作五 | 时序投票 | 0.63 | 0.55 | 0.59 |
| 动作六 | 分位数阈值校准与分数融合 | 0.66 | 0.62 | 0.64 |
注意动作二让 recall 掉了一点。这是正常的,因为记忆库把一批"看起来异常但其实正常"的样本拉回了正常侧,精度大涨、召回小跌,综合大幅改善。做这类优化时不要盯着单一指标,容易做出错误的取舍。
3.1 提示词不是造句,是构造可判别的语义锚点
最初我们用的提示词就是两个词:normal和abnormal。现在回头看,这两个词的信息量几乎为零。abnormal在语义空间里靠近"事故、血腥、灾难",跟"配电柜门虚掩"没有任何关系。
改进的思路不是把句子写得更漂亮,而是让提示词覆盖你真正关心的异常模式,并且和正常模式形成对照。我们最终给每一类异常准备了 6 到 10 条描述,同时给正常状态也准备了同等数量的描述。举几组实际的:
- 目标类:
a person walking in the aisle、a parked vehicle inside the restricted area、an animal on the equipment floor - 状态类:
a cabinet door left open、a warning light showing red、an object blocking the passage - 正常类:
an empty corridor at night、a closed cabinet with all doors shut、a person wearing a work uniform standing near the panel
关键在于正常描述必须同样具体。很多人只写异常提示,正常侧用一个笼统的a normal scene,结果正常样本的分数方差极大,阈值怎么定都不对。两侧的描述粒度要对称,这是我在多次实验里验证过的。
还有一个技巧:描述里带上场景上下文(at night、in a server room、on the equipment floor),能明显抑制跨场景的误触发。我们测过带上下文和不带上下文的版本,同一批数据 F1 差 0.04 左右。句子不要太长,超过 15 个词之后收益递减,反而引入了无关语义。
多提示集成的方式是:每条描述单独算一次相似度,然后取该类别的最大值或 Top-3 均值。取最大值召回高但容易误报,取均值稳但会漏掉边缘情况。我们最后用的是加权——正常侧取均值,异常侧取 Top-2 均值,实测比较平衡。
3.2 现场正常样本记忆库:把"正常"从语言搬回像素
这一步是收益最大的,也是我认为任何安防零样本方案都绕不开的。
做法不复杂:在目标点位采集一段时间的正常画面(我们用的是连续 7 天、每天不同时段各采样一部分,总共约 3000 个块),提特征、归一化、存进一个向量库。推理时,每个待测块的特征去库里做最近邻检索,用距离作为异常分数,而不是用文本相似度。
这样做的好处是,"正常"的定义从语言空间回到了像素空间。语言描述永远说不全"这个货架在下午三点会有反光、那个角落晚上会有阴影",但向量库天然包含了这些细节。我们实测,纯用记忆库距离、完全不用文本,F1 就能到 0.38 左右。
真正的最优解是两路分数融合:文本侧给出"这块区域像不像异常概念",记忆库侧给出"这块区域离正常有多远"。两路各自归一化之后加权。权重不是固定的,我们按场景类型给了不同的初值——目标类场景文本权重高一些(0.6/0.4),状态类场景记忆库权重高一些(0.35/0.65),因为状态变化更依赖具体点位的基线。
记忆库这块有几个实操细节值得说。
采样要覆盖时间维度。只采白天的样本,晚上全是异常分。我们的做法是按小时分层采样,每个小时至少 100 个块,保证 24 小时都有覆盖。如果点位刚装还没跑满一周,就在初始化时给一个"冷启动期",前三天只记录不告警,让库自然长起来。
库要控制规模。3000 个块检索一次还好,涨到 3 万就明显拖慢。我们用了聚类抽稀——先做一次粗聚类,每个簇保留离中心最近的若干样本,删掉冗余。压到 5000 个块以内,精度基本无损,检索延迟从 40ms 降到 8ms。
库要能淘汰。场景会变,货架会挪位置,新设备会进场。老样本留在库里会持续制造误报。我们给每个样本打了时间戳,检索时对超过 30 天的样本做距离惩罚,同时每周做一次增量合并,把新采集的样本和旧库做去重。
注意:记忆库不是越干净越好。如果你只用"完美正常"的样本建库,那任何轻微的现场变化(地上多了一个纸箱、墙上多了一张通知)都会被判成异常。适度保留一些"正常但有变化"的样本,反而能压误报。我把这个叫做"给正常留点余地"。
3.3 多尺度切块与上下文扩展,救回小目标召回
这条在第 2.1 节提过链路,这里说它带来的指标变化。整图推理时,小于画面 2% 的目标基本检不出,recall 卡在 0.42 上不去。引入 640 重叠切块之后,recall 直接到 0.55,但同时 precision 掉了——因为块变多,误报的机会也变多。
抵消这个副作用的手段是上下文扩展。单独一个块,模型不知道它在画面的哪个位置、周围是什么。我们的做法是给每个块的特征拼上一个"位置编码 + 全局特征摘要":位置编码就是块在画面中的行列索引做正弦编码,全局摘要就是整图下采样那一路的特征。两者拼接后再算距离。
这个改动听起来很轻,实际效果明显——同一块区域,出现在画面中央的配电柜和出现在边缘的配电柜,模型给出的分数方差从 0.18 降到了 0.07。分数稳了,阈值才好定,precision 自然跟着回来。
多尺度的另一层含义是分辨率。同一块区域,我们在 640 和 1280 两个尺度上各跑一次,取分数的最大值。1280 那一路专门用来抓小目标,代价是算力。为了控制开销,只对背景差分有响应的区域跑高分辨率分支,静态区域一律跳过。实测下来,高分辨率分支只处理了约 15% 的块,但对小目标异常的召回贡献了将近一半。
3.4 运动门控:让模型只回答它真正擅长的问题
我们统计过,一个典型的室内点位,一天 24 小时里画面有明显变化的时长不超过 3 小时。剩下 21 小时是静止的背景,送进模型纯属浪费——更糟的是,静止画面里的传感器噪声、压缩伪影、轻微抖动,会让模型的分数随机跳动,制造大量莫名其妙的告警。
运动门控的做法很朴素:先用传统背景建模(高斯混合或者简单的帧差 + 形态学)算出变化区域,只有变化面积超过阈值的块才送进大模型。这一刀把每天参与推理的块数砍掉了 78%,推理延迟从平均 180ms 降到 42ms,同时 F1 涨了 0.04。
涨的原因不是模型变强了,是误报的基数被砍掉了。原来每天 210 条告警里有将近 90 条来自完全静止的画面,纯粹是噪声分数越过了阈值。门控一上,这些全没了。
但门控本身也有坑。背景建模对光照突变极其敏感——傍晚开灯、云层遮日、车辆大灯扫过,整屏都会被判成"变化区域",门控直接失效。我们的解法是给门控加一层过滤:变化区域如果是全局性的、边缘不规则的,判定为光照变化,直接丢弃;只有当变化区域呈现连通块状、有明确边界时,才认为是有意义的运动。这一层大概用了一周才调稳。
另一个坑是缓慢变化。有人把东西放在货架上慢慢挪走,整个过程持续十几秒,每一帧的变化量都很小,背景建模可能压根不响应。所以门控不能做成硬开关,得做成软权重:变化量极小的块跳过,变化量中等的块降低采样频率(每 5 帧一次),变化量大的块全速跑。这样既省算力,又不至于漏掉慢速异常。
3.5 时序投票与分位数阈值:最后 0.03 的来源
前五个动作做完,F1 到 0.59,卡了一阵。剩下的提升基本全在"如何把单帧分数变成一条可靠的告警"上。
时序投票解决的是单帧抖动。真实的异常在时间轴上是持续的,噪声是尖刺。我们的规则是:连续 3 帧(覆盖约 2 秒)分数都超过阈值才触发告警,中间断一帧不断事件。这一条把 precision 从 0.55 拉到 0.63,recall 只掉了 0.02。参数上,窗口长度 3 到 5 帧比较合适,超过 5 帧对快速异常(比如有人翻越)的召回伤害明显。
分位数阈值解决的是不同点位分数分布不一致的问题。固定阈值 0.5 在 A 点位可能一天报 3 条,在 B 点位一天报 300 条。我们改成用该点位最近 7 天的分数分布取 99.5 分位数作为动态阈值,低于下限和高于上限时截断。这样每个点位的告警量自动收敛到相近的量级,值班端的体验一致了。
分数融合是最后一环。文本侧分数、记忆库距离、背景变化面积,三个量纲完全不同的东西不能直接加权。我们的做法是各自转成"该点位历史分布中的分位数",统一到 0 到 1,再按 0.35 / 0.5 / 0.15 加权。这个权重是在十几个点位的验证集上网格搜出来的,不同场景类型会微调,但大方向没变过。
这三步加起来贡献了 0.05,看起来不多,但它们的作用是让前面所有工作"能用"。前面五个动作决定模型能不能看到异常,这三步决定值班人员愿不愿意看告警。
4. 误报排错的完整链路:三个真实踩坑的复盘
4.1 傍晚开灯那二十分钟,整屏都在告警
现象很吓人:每天 18:10 到 18:30 之间,某几个点位会连续触发几十条告警,热力图显示整屏都是高分。第一反应是模型有问题,把特征提出来对比了一下,正常时段的特征和开灯时段的特征距离确实很大——模型没错,画面确实变了。
问题出在我们的假设上:我们默认"正常"是一个稳定状态,但实际上光照切换是每天都会发生的正常事件,它只是低频。记忆库里白天和夜间的样本都有,唯独缺了切换过程那二十分钟的过渡样本。
修法有两层。第一层是补样本:专门采集了各个点位开灯、关灯、窗帘开合过程的画面,作为"正常但罕见"的样本进库,并且给的权重比普通样本低一些,避免它们影响常规判断。第二层是加全局光照判别:计算画面整体的亮度直方图,如果直方图的整体位移超过阈值,判定为光照事件,该时段内的告警做降级处理——不是不报,是合并成一条"光照变化"提示。
这个坑教会我一件事:安防场景里的"正常"不是静态的,是有节律的。日节律、周节律、甚至季节节律都会带来分数漂移。建库的时候不考虑这些,后面一定会在某个时间点集中爆发。
4.2 雨丝、反光和水渍:纹理类误报怎么压
室外点位是另一个战场。雨丝在画面上是密集的斜向细线,反光是成片的高亮区域,水渍是缓慢扩散的不规则块。这三类在特征空间里都表现得"和平时不一样",模型老老实实给了高分。
单纯靠提示词压不住,因为你在提示词里写rain反而会让模型更关注雨。我们的做法是在预处理阶段处理掉,而不是让模型去学。
雨丝用方向滤波:雨丝方向在短时间内基本一致,做一次方向梯度统计,如果某个方向的梯度能量异常高,判定为降雨,对该区域做中值滤波后再送模型。反光用亮度阈值加形态学开运算,把成片的过曝区域标记出来,这些区域的分数乘一个 0.3 的抑制系数。水渍麻烦一些,它变化慢,主要靠记忆库的时间衰减——水渍区域在库里会逐渐积累样本,慢慢被"正常化"。
这三招加起来把室外点位的日均告警从 80 多条压到了 6 条左右,召回基本没损失。我的体会是:凡是能用传统图像处理明确描述的干扰,就不要交给大模型去分辨。大模型的价值在于它没见过的东西,不在于它已经被人写进教科书的东西。
4.3 指标口径打架:同一条视频两次跑出两个结果
这个问题最隐蔽,也最浪费时间。同一批测试视频,两个人跑出来的 F1 差了 0.08。查了两天,发现差异来自三个地方。
一是抽帧的时间基准。一个人按帧号取模抽帧,另一个人按时间戳抽帧,视频有可变帧率,结果抽到的帧不完全一样。二是告警合并规则。一段持续 20 秒的异常,按帧算是一个正样本还是多个正样本,两个人理解不同。三是阈值来源。一个人用了全局固定阈值,另一个人用了分位数动态阈值。
后来我们定了一份评测规范,写死了这几条:抽帧统一按时间戳,异常事件按"事件"计数而不是按帧计数(一段连续的异常算一个正样本,只要在时间窗口内被触发就算命中),阈值统一用验证集上搜出来的固定值,动态阈值只在线上用。规范定完之后,两个人跑出来的数字误差在 0.005 以内。
这件事的教训是:在算法团队里,评测脚本比模型更值得投入。指标不可复现,后面所有的优化都是盲人摸象。
5. 落地时的工程约束与人机协同
5.1 算力预算决定架构,不是反过来
我们在实验室里跑得最欢的配置是 1280 分辨率、五尺度、每帧全块推理,F1 能到 0.68。搬到边缘盒子上一跑,单路延迟 1.2 秒,八路直接卡死。这就是典型的先做算法再想部署,返工成本极高。
后来按算力倒推架构:边缘设备单路预算 50ms,按这个倒推,每秒最多处理 20 个块。于是策略变成——基础帧率 1 帧/秒,每帧平均 2.3 个块,高分辨率分支只处理 15% 的块,剩下的算力留给告警后的二次确认。最终实现在边缘盒子上单路 38ms,八路并行没问题。
如果预算更紧,我的建议顺序是:先砍分辨率(1280 降到 640,F1 掉约 0.03),再砍帧率(1 帧/秒降到 0.5 帧/秒,掉约 0.02),最后才砍模型层数。砍层数对特征质量的影响是断崖式的,前面两个是线性的。
5.2 漂移是必然的,监测要提前埋点
上线三个月后,某个点位的 F1 从 0.62 掉到了 0.44。查下来是货架重新排布了,原来的正常样本全部失效。这种事一定会发生,区别只在于你有没有提前发现。
我们后来加了三个监测项。分数分布漂移:每天统计正常样本分数的均值和中位数,相对基线偏移超过 15% 就报警,提示需要重采样本。告警量突变:单点位日告警量连续三天超过历史均值的两倍,触发排查。参考库命中率:统计推理时检索到的最近邻样本的平均距离,距离整体变大说明现场和库已经脱节。
这三个指标都不复杂,但能提前一两周发现漂移,比等业务方投诉要主动得多。重采样本的流程也做了简化——现场人员用手机拍一段 5 分钟的视频上传,后台自动切块入库,不需要算法同学跑现场。
5.3 把复核动作变成数据资产
值班人员每天点掉的"忽略"和"确认",是最贵的标注数据,但很多团队就让它白白流走了。
我们做了两件事。一是复核结果回流:被确认的告警自动进异常样本池,被忽略的进"疑似正常"池。疑似正常池的样本不直接进记忆库,先经过一轮人工抽检,确认无误后再入库。这条链路跑起来之后,记忆库的更新基本自动化了,人工只需要每周花半小时抽检。
二是告警解释:每条告警附带一张热力图和触发它的提示词描述。这看起来是给人看的,其实也在帮我们自己——当值班人员反馈"这条不对"的时候,我们能立刻知道是空间定位错了,还是语义判断错了,排查效率完全不一样。
零样本方案最大的价值不在于省掉了训练,而在于它让"新增一类异常"的成本从两周降到了半天——加几条提示词、补一批参考样本,当天就能验。前提是上面这套回流链路得先搭好,否则每次新增都还是手工活。
我个人在实际操作中的体会是,这类方案真正的分水岭不在模型选型,而在你愿不愿意把工程链路当成一等公民去打磨。同一批数据,我们最初用一个大模型跑出 0.09,后来用同样的权重跑出 0.64,中间没有任何一次反向传播。改的全是切块方式、取哪一层特征、提示词怎么写、参考样本怎么建、分数怎么平滑。这些活儿不性感,写论文也不好意思放进去,但产线上就是这些决定了系统能不能用。如果非要给一个优先级,我会说:先把记忆库和运动门控做掉,这两件事的投入产出比远高于在提示词上雕花。