评估AI模型安全扫描器时,很多人习惯盯着 F1 分数看。但如果你想根据一次漏洞扫描或恶意模型检测的准确率,来决定要不要接入某个扫描器,我建议你同时看两项更接近工程事实的指标:Coverage(覆盖率)和 Failure Recovery(故障恢复能力)。这里也顺便说明一下,标题里的 F1 指的是分类模型常用的 F1-score,不是键盘上的功能键。
我最近在评估这类扫描器时,最大感受是:单看 F1,很容易被“整体还不错”误导。一个扫描器在 1000 个测试样本上能跑到 0.95 的高分,可能只覆盖了少数几种攻击类型;另一个扫描器 F1 只有 0.88,但能覆盖更多模型格式,遇到坏文件能自动跳过,批量任务中断后还能从断点继续。对真正要落地到测试流程、CI/CD 或交付物安全检查的场景来说,后者往往更让人放心。
这篇文章想解决的,就是这类问题:AI 模型安全扫描器到底该怎么评估?除了 F1,还要关注哪些指标?覆盖率怎么测?故障恢复能力怎么验证?全文会按我在实际评估时使用的顺序拆开讲,每个环节都会补一些判断标准和坑点。
1. 为什么安全扫描器不能只看 F1 分数
1.1 F1 到底是什么,为什么会被拿来当核心指标
F1-score 是 Precision(精确率)和 Recall(召回率)的调和平均,公式是:
F1 = 2 * (Precision * Recall) / (Precision + Recall)Precision 衡量的是“扫描器报出来的问题里,有多少是真问题”;Recall 衡量的是“真实存在的问题里,扫描器找出了多少”。两个指标一个看误报,一个看漏报,F1 把它们合并成一个数字,看起来很适合做模型排行榜。
但对于 AI 模型安全扫描器来说,这个合并动作会损失大量信息。最主要的原因是:安全检测场景里,误报和漏报的成本不是对称的。漏掉一个真正的恶意模型,可能导致后续部署环节直接暴露风险;误报一个正常模型,可能只是让安全团队多花几分钟复核。把这两个成本不同的错误揉进一个分数,很难反映实际使用价值。
1.2 安全场景里 F1 的失真点
我在评估时经常看到两类失真情况。
第一类是“高 F1 但低覆盖率”。测试集里如果恶意样本大多来自同一类攻击,比如纯文本提示注入,扫描器只要对这类样本学得足够好,F1 就能很高。可一旦遇到模型压缩后隐藏指令、神经网络后门这类不同模式的攻击,它就失效了。F1 不会告诉你它在哪些类别上失效,只会给你一个“还不错”的总值。
第二类是“实验室高分、工程环境掉链子”。有些扫描器在静态样本上表现很好,一旦放进真实任务队列,面对超时、内存不足、文件损坏、并发冲突,直接卡死或崩溃。这个时候 F1 再高也没用,因为任务根本没有跑完。
所以我的结论是:F1 应该作为基础指标,但不能作为唯一指标。要把评估维度扩展成三个块:准确性指标、覆盖指标、稳定性指标。F1 属于准确性,覆盖率和故障恢复能力分别对应后两块。
1.3 从 F1 到覆盖率与故障恢复能力
覆盖率要回答的问题是:这个扫描器到底能覆盖哪些攻击类型、哪些模型格式、哪些输入形态?它描述的是能力边界。
故障恢复能力要回答的问题是:扫描过程中遇到了异常,它是否会崩溃、是否会丢进度、是否能继续完成剩余任务?它描述的是运行稳定性。
在实际选型和验收时,我会先用覆盖率和恢复能力做初筛,再用 F1 做细筛。顺序反过来容易踩坑。如果一个扫描器连常见模型格式都不支持,F1 再高也没有意义。
2. 覆盖率评估:先拆开“覆盖”到底指哪几层
2.1 样本集覆盖:测试集不能只有“常见样本”
很多团队做测试时会准备一批“正常模型 + 恶意模型”,然后算出 F1。这个做法本身没错,但问题在于测试集太单一。
我一般会从三个维度去构造样本集:
- 来源维度:公开样本、自建样本、线上真实样本、历史故障样本。
- 内容维度:不同攻击类型、不同业务场景、不同自然语言、不同代码风格。
- 形态维度:不同模型框架、不同文件大小、不同压缩方式、不同依赖环境。
样本覆盖率的定义建议按类别算,而不是按条数算。比如你准备了 2000 个样本,其中 1800 个是“直接提示注入”,200 个是“后门触发”。扫描器在 2000 个样本上 F1 很高,但后门触发类几乎全漏。这时候如果只看总 F1,问题完全看不出来。只有把每一类样本单独算指标,才能看到覆盖率下降。
2.2 攻击类别覆盖:不看单条结果,看类别矩阵
安全扫描器到底要覆盖哪些攻击类别,不同团队定义不一样。我建议按自己的威胁模型来拆。常见的类别包括:
- 提示注入
- 数据投毒
- 后门触发
- 对抗样本
- 隐私泄漏
- 越权指令
- 敏感信息生成
可以先用一个评估矩阵,行是攻击类别,列是每个扫描器。每个类别的评分不是“能不能检出某一条”,而是“这个类别里是否有至少一个样本被正确检出”,再细一点就是类别的召回率。
我通常还会记录一个更严格的指标:最小类别召回率。也就是所有攻击类别中,召回率最低的那一个。为什么看最小?因为安全场景的危险往往来自“某个未知类别完全没覆盖”,而不是“整体平均还不错”。平均值会被表现好的类别拉高,最小值能暴露真实短板。
2.3 输入形态覆盖:模型格式、文件格式、模型框架
AI 模型安全扫描器的另一个特殊点,是它必须能“读懂”模型文件。不同框架的模型文件结构完全不同:
- PyTorch 的 .pt / .pth
- ONNX 的 .onnx
- TensorFlow 的 SavedModel / HDF5
- TensorFlow Lite 的 .tflite
- 其他自定义序列化格式
覆盖率评估里要有一个“格式支持清单”,逐项打勾。不要只测你手头最常见的格式,要故意准备一些带特殊情况的文件:
- 有没有包含自定义 op 的 ONNX?
- 有没有缺少匹配依赖的模型?
- 有没有被压缩过的模型包?
- 有没有包含多个文件、目录结构混乱的模型目录?
如果一个扫描器对某一个格式的解析不完整,它可能不会报错,而是静默返回“未发现问题”。这种静默失败是最危险的,因为它没有给你一个“不支持”的信号,而是给你一个“安全”的假象。所以请务必用已知恶意样本去验证“支持某个格式”是否真的有效,而不是只看文档说明。
3. 故障恢复能力:比指标更影响落地的工程素质
3.1 故障恢复能力的定义:不是“不崩溃”,而是“崩溃后能继续”
在批量扫描场景里,没有哪个系统能保证一个都不崩。真实任务里会出现各种异常:单个模型文件损坏、网络请求超时、内存被占满、磁盘空间不足、依赖库加载失败。这些情况不是“如果”,而是“什么时候”的问题。
所以故障恢复能力不应该被理解为“永不失败”,而应该是:
- 失败后能否被自动发现;
- 失败的影响面是否只局限于单个样本;
- 失败后能否自动重试或跳过;
- 重启后能否从断点续跑;
- 最终能否输出一份带错误信息的完整报告。
我曾经遇到过一种扫描器:只要其中一个模型文件解析失败,整个进程就退出,前面扫描的几百个结果全部丢失。后来跑批量任务,我只能人为拆成很多小批次,每个批次跑完再手动合并。这种工具就算单条检测 F1 很高,也很难用到正常流程里。
3.2 常见故障类型和恢复方式
在设计评估用例时,可以按下面的故障类型逐项测:
| 故障类型 | 典型表现 | 期望的恢复行为 |
|---|---|---|
| 单个文件损坏 | 文件头不完整、解压失败 | 跳过该文件,记录错误原因,继续后续任务 |
| 模型依赖缺失 | 缺少某些 Python 包或动态库 | 明确报错,标记该样本失败,不影响其他样本 |
| 输入超长 | 极大文件、超长文本 | 超时或截断,避免内存溢出 |
| 内存不足 | 并发加载多个模型时 OOM | 降低并发,或排队处理,并能恢复 |
| 磁盘写满 | 输出日志或结果写不进去 | 停止任务或切换输出路径,不静默丢失 |
| 进程被杀 | 环境 OOM 或被手动 kill | 重启后能从已完成任务断点续跑 |
测试时不要只测“正常使用”,要主动构造这些故障。说白了就是故障注入。比如把一个模型文件的文件头改坏,或者在批量扫描跑到一半时kill -9进程,再重新启动。观察重启后是全部重跑,还是只跑剩余任务。
3.3 故障注入测试怎么设计
故障注入听起来复杂,但其实可以分步骤做:
- 先用 20 个正常样本跑通基线。
- 手动复制 3 个样本,改坏文件内容,混入 20 个正常样本。
- 再准备 2 个超长文件,控制在合理范围内,制造超时。
- 启动批量扫描,观察是否会卡住、崩溃、还是跳过。
- 扫描到一半手动终止进程,再重启,观察进度恢复情况。
判断标准很简单:坏样本被标记为“失败”或“错误”,而不是被标记为“未发现安全问题”;正常样本全部完成;重启后没有无谓地重复扫描已完成样本。满足这三条,恢复能力才算合格。
注意:故障注入测试一定要先在小样本集上做,不要一开始就在几百 GB 的模型库上验证。否则一旦触发内存问题或磁盘写满,排查成本会直线上升。
4. 一套可以复用的评估流程:从单样例到批量
4.1 第一阶段:先跑最小样例
我建议所有评估都从最小样例开始。这个阶段的目标不是算指标,而是确认工具能启动、日志能输出、目录结构符合预期。
准备三个输入:
- 一个正常的模型文件;
- 一个已知恶意的模型文件;
- 一个故意损坏的模型文件。
然后依次跑单条扫描。看三件事:扫描是否正常结束、结果是否写入输出目录、错误信息和日志是否能看懂。如果最小样例都跑不通,先不要急着调参数,优先排查环境。
4.2 第二阶段:构建分层的测试样本集
样本集不要一上来就搞全量。建议先构造一个“分层小样本集”,每个类别先放 10 到 30 个样本,覆盖所有你想测的维度。比如:
| 维度 | 分类 |
|---|---|
| 模型框架 | PyTorch、ONNX、TensorFlow、其他 |
| 攻击类别 | 提示注入、后门触发、数据投毒、对抗样本、隐私泄漏 |
| 文件状态 | 正常文件、损坏文件、超长文件、空文件 |
| 预期结果 | 阳性样本、阴性样本、无法解析样本 |
这个分层样本集的价值,不只是算 F1,而是让你能看到每个类别上的结果分布。如果某个类别只有 10 个样本,扫描器漏了 3 个,你一眼就能看到。
4.3 第三阶段:批量扫描和指标汇总
小样本集跑稳定后,再进入批量阶段。批量阶段要记录几类数据:
- 开始时间和结束时间;
- 总样本数、成功扫描数、失败数、跳过数;
- 每个类别下的 TP、FP、FN;
- 卡住或超时的样本清单;
- 输出文件命名和结果解析是否一致。
批量跑的时候,不要一上来就开最高并发。先看工具默认并发是多少,然后从 1 慢慢往上加。并发上去了,F1 可能没有变化,但内存占用、失败率、超时率会明显增加。这些数据也要记进评估报告里。
4.4 评估指标表:记录哪些指标
下面是我评估扫描器时常用的指标表,供你参考:
| 指标 | 计算方式 | 说明 |
|---|---|---|
| Precision | TP / (TP + FP) | 报出的问题里,真正有问题的比例 |
| Recall | TP / (TP + FN) | 真正有问题里,被报出来的比例 |
| F1 | 2 * Precision * Recall / (Precision + Recall) | 精确率和召回率的综合值 |
| 类别覆盖率 | 有检出成功的类别数 / 总类别数 | 能力边界 |
| 最小类别召回率 | min(各类别 Recall) | 最弱类别表现 |
| 扫描成功率 | 成功完成的样本数 / 总样本数 | 能跑通的比例 |
| 故障恢复成功率 | 故障后能继续完成任务的次数 / 故障次数 | 恢复能力 |
| 平均单样本耗时 | 总耗时 / 成功样本数 | 性能参考 |
| 中断后续跑率 | 重启后无需从头开始的任务比例 | 断点续跑能力 |
这组指标不一定全用,但如果只记录 F1,后面写评估报告时会发现信息不够,还要回去重新跑测试,特别浪费时间。
5. 输出验证:真阳性、误报、漏报到底怎么判定
5.1 ground truth 怎么来
计算 F1、Recall 的前提,是你知道每个样本的真实标签。这个标签就是 ground truth。对于安全扫描器来说,ground truth 的来源一般有三种:
- 公开数据集:很多安全社区有标注好的恶意模型、恶意文本样本;
- 自建样本:根据自己业务场景构造已知风险样例,并人工复核;
- 历史漏洞库:从已经确认过的风险库中抽样。
我最推荐的做法是:先用“已知恶意样本”建立阳性集合,再用“正常线上样本”建立阴性集合。阳性集合宁少勿错,因为一个错误的阳性标签会让所有指标失真。很多团队为了凑数量,把疑似样本也标成阳性,最后 F1 虚高,实际却不知道扫描器到底擅长什么。
5.2 结果格式和置信度怎么解析
扫描器输出结果通常有三种:
- JSON 或 CSV 文件;
- 命令行标准输出;
- 数据库或消息队列。
不管哪种格式,都要先把结果解析出来,做一次字段核对。最常见的字段包括:
- 文件路径;
- 风险等级;
- 漏洞类型;
- 置信度得分;
- 触发位置;
- 错误信息。
我遇到过一种情况:扫描器文档里写了“支持置信度输出”,但实际跑完后,所有样本的置信度字段都是空的。如果你只看文件有没有生成,不看字段内容,就发现不了这个问题。所以每次批量扫描后,建议写一个小脚本检查输出字段:非空率、枚举值范围、文件路径是否与输入对应。
5.3 输出一致性检查
同一样本连续跑两次,结果应该保持一致。如果结果不稳定,首先要确认是不是扫描器本身有随机性。很多 AI 检测模型在 CPU 和 GPU 上表现不完全一样,有的还有随机采样过程。
一致性检查方法很简单:
- 选取 50 个样本;
- 连续跑三遍;
- 比较三次结果中每条样本的标签和置信度;
- 统计不一致的样本数量。
如果一致性差,要关注是“置信度轻微波动”还是“结果正反翻转”。置信度轻微波动通常可以接受,正反翻转就要注意了。这往往说明扫描器在决策边界附近的判断不稳定,真实环境里可能会造成误报或漏报。
6. 参数边界和运行环境:低配置、长任务、高并发下的实测经验
6.1 资源占用相关的边界
AI 模型安全扫描器不是单纯的文本工具,它可能需要加载模型库、解析模型文件、运行检测模型。资源占用会比普通扫描器高。低配置机器也能跑,但要把并发数、批大小、超时时间都降下来。
我一般这样评估环境边界:
- 如果机器内存只有 8GB,先以并发 1、批大小 1 跑一组小样本;
- 观察峰值内存,如果接近 80% 就要降;
- 如果机器有 GPU,但不能保证所有格式都走 GPU,不要过度依赖加速;
- 磁盘空间要预留日志和输出文件的空间,尤其是批量扫描结果可能很大。
有一个容易忽略的点:扫描过程中临时文件可能放在系统临时目录,而不是输出目录。如果系统临时目录空间不够,扫描器可能报错,但错误信息里写的不是“磁盘空间不足”,而是某些解析失败。遇到这类情况,先检查临时目录。
6.2 并发、超时、重试参数怎么调
不同的扫描器参数名称不一样,但大概率会涉及这几个:
- 并发数:同时扫描的样本数;
- 单样本超时:单个文件最多允许跑多久;
- 总任务超时:整个批量任务最多跑多久;
- 重试次数:失败后尝试几次;
- 失败策略:失败后是跳过还是终止。
我建议从保守值开始。比如并发从 2 开始,单样本超时先设成你观察到的正常耗时的 5 倍。为什么要这么保守?因为安全扫描里,某些恶意样本可能故意构造得很复杂,让检测器长时间运行。如果没有超时,整个任务会被一个样本拖死。超时之后是跳过还是重试,要看业务需求:如果只是批量检测,跳过并记录最合理;如果是对外服务提供检测结果,建议保留重试机会。
注意:重试次数不要无限大。无限重试等于没有失败策略。一般 2 到 3 次足够,重试之间可以加一点退避时间,避免同时打爆外部接口或占用全部资源。
6.3 日志和输出目录的工程化
日志不是可有可无的,它是后面排查问题的主要依据。我通常会按批次建目录:
runs/ 20250101-101530/ scan.log results.json failures.csv每个批次有独立目录,不会互相覆盖。日志里每跑完一个样本,至少记录一条:
样本ID,开始时间,结束时间,状态(success/failed/timeout),耗时,输出文件路径不要等到失败时才记录。只有全量记录才能统计成功率和失败分布。输出结果命名也不要只用时间戳,最好带上样本 ID 或批次号,否则后面很难回溯。
7. 常见问题和排查顺序
7.1 现象:没有扫描结果
如果扫描器跑完一个批次,结果文件为空,先不要怀疑扫描器坏了。按下面顺序排查:
- 看日志尾部,确认任务是否真的执行到了“输出结果”这一步;
- 检查输入路径,是否有路径写错、文件不存在、权限不足;
- 检查输入文件格式,是否根本不是该扫描器支持的模型格式;
- 检查输出目录,是否有写入权限;
- 检查结果文件编码,有没有出现程序崩溃前写入过空文件。
我遇到过的空结果案例,大多不是扫描器能力问题,而是输入路径写错了,或者脚本把空目录传给了扫描器。
7.2 现象:扫描器卡住或假死
卡住时先看资源占用。如果 CPU 或内存跑满,大概率是某个样本触发了复杂计算或内存泄漏;如果资源占用很低,可能是死锁、等待网络请求或等待外部服务响应。
排查顺序:
- 用系统命令查看进程状态;
- 看日志尾部,最近一条日志停在哪里;
- 确认是否有单样本没有超时机制;
- 确认是否有 worker 进程已经退出但主进程还在等待。
之前我遇到过一个问题:扫描进程看起来一直在跑,但 CPU 是 0%,也没有输出日志。后来发现是某个 worker 在等待一个外部 API 响应,而 API 不返回也不超时。给请求加上超时时间后,问题就解决了。
7.3 现象:批量任务大面积失败
批量任务大面积失败时,不要急着改扫描参数。先对失败样本做分类统计:
- 失败是否集中在某个目录;
- 失败是否集中在某个模型框架;
- 失败是否集中在某几个文件大小;
- 失败是否集中在某一类输入内容。
分类统计之后,通常就能定位到原因。如果是某个格式解析不兼容,重新安装对应依赖或换用解析库;如果是特定文件损坏,就跳过并记录;如果是并发太高导致 OOM,就调低并发。
7.4 排查顺序建议
整体排查顺序可以固定成五步:
- 先确认现象:报错、无输出、卡住、还是速度过慢;
- 再确认输入:路径、格式、编码、文件是否完整;
- 然后检查环境:依赖版本、权限、网络、磁盘、内存;
- 再看参数:并发、超时、重试、输出目录;
- 最后才怀疑扫描器本身:功能边界、版本兼容、已知限制。
这个顺序能避开最常见的坑。很多问题看起来像扫描器能力不行,实际上只是输入材料没有处理干净。
8. 落地建议:建立适合安全团队的评估闭环
8.1 选型和评估分开
如果你正在选型,建议把“选型试用”和“正式评估”分开。试用阶段用最小样例判断能不能跑;正式评估阶段再构建完整测试集,跑覆盖率、故障恢复、性能三重指标。不要用一两个样例就下结论说“不如另一个”。
8.2 把覆盖率、恢复能力纳入选型清单
我建议选型清单里至少包含几类问题:
- 是否支持你业务中实际的模型格式?
- 对已知攻击类别的覆盖率是多少?最低类别召回率是多少?
- 批量任务中断后能否断点续跑?
- 单个文件损坏是否会导致整个任务失败?
- 是否有清晰的错误日志?
- 是否支持超时、重试、跳过等失败策略配置?
这些问题比“F1 是多少”更接近真实落地。一个只能在理想数据集上跑高分的扫描器,很难应对真实环境中千奇百怪的文件和故障。
8.3 从指标表到改进计划
评估最后一定要落到改进计划,而不是只写一个评分。比如:
- 覆盖率低:考虑换工具、增加预处理模块,或对不支持的格式单独提示;
- 故障恢复差:调整任务调度方式,把任务拆小,或者增加外层重试机制;
- 输出字段不稳定:先做结果校验脚本,沉淀为自动化检查;
- 性能不足:增加资源,或降低输出保存频率。
真正跑完一轮评估后,你会发现最有价值的产出不是“这个扫描器得了几分”,而是你手上多了一套能复现的测试集、一份结果校验脚本、一组参数建议,以及一张覆盖率和恢复能力分析表。这些东西比单个 F1 数字更能帮助你做后续决策。
我个人更建议先把单任务跑稳,再考虑批量和接口化。覆盖率、恢复能力和 F1 三个维度不是谁替代谁,而是互相补充。把它们放在一起看,你才算真正评估完一个 AI 模型安全扫描器。