基于机器学习的勒索软件检测:从特征工程到模型部署实战
1. 项目概述与核心挑战
勒索软件这几年已经从一个技术圈的话题,变成了几乎所有企业IT部门头上的达摩克利斯之剑。我处理过不少应急响应案例,亲眼见过一个简单的钓鱼邮件附件,如何在几小时内加密掉整个部门的文件,然后弹出那个令人绝望的支付界面。传统的基于签名的杀毒软件,在面对层出不穷的变种和零日攻击时,常常力不从心。这就引出了我们今天的主题:如何利用机器学习,让检测系统变得更“聪明”,能够识别出那些它从未见过的勒索软件。
简单来说,这个项目的目标,就是构建一个能够自动分析文件或网络行为,并判断其是否为勒索软件的智能系统。它不依赖已知的病毒库特征,而是通过学习大量正常软件和恶意软件的行为“模式”,自己总结出勒索软件的“坏习惯”。你提供的资料里提到了很多关键的研究和工具,比如利用深度学习检测勒索软件、分析网络流量、甚至像ShieldFS这样的自愈文件系统,这些都是构建一个完整检测方案的重要拼图。
这个内容适合谁呢?如果你是网络安全领域的研究人员、安全运维工程师,或者是对AI在安全落地感兴趣的数据科学家、机器学习工程师,那么这篇从理论到实操的完整复盘应该能给你不少启发。即使你只是对机器学习应用感兴趣,想了解一个真实的项目是如何从数据到模型一步步构建的,这里面的思路和踩过的“坑”也很有参考价值。
2. 整体技术路线与方案选型
面对勒索软件检测这个问题,我们首先要决定“看什么”和“怎么看”。你提供的参考文献给出了几个主流方向,我在实际项目中通常会采用一种混合策略,不把鸡蛋放在一个篮子里。
2.1 检测对象的选择:静态、动态与网络流量
静态分析
就像是给软件“验尸”。我们不运行它,而是直接检查它的“尸体”——即可执行文件本身。我们可以提取它的字节序列、导入的函数表(比如它是否大量调用了加密相关的API如
CryptEncrypt
)、字符串信息(是否有“你的文件已被加密”、“支付比特币”等勒索字样)、文件结构特征等。这种方法速度快、资源消耗低,适合做第一道快速筛查。参考文献中Al-Bakri等人利用马尔可夫链分析API调用序列,本质上就是一种静态层面的行为模式预测。
注意 :静态分析最大的软肋是混淆和加壳。高级勒索软件会把自己的代码加密或变形,让静态分析工具看到的是一堆乱码。因此,纯静态分析在实战中误报和漏报率会比较高,通常作为辅助手段。
动态分析 则是给软件提供一个“沙箱”环境,让它实际跑起来,我们在一旁观察它的一举一动。我们会监控它在运行过程中对文件系统的操作(是否大量、快速地对不同格式的文件进行重写)、对注册表的修改、进程行为、以及最关键的网络通信行为。ShieldFS文件系统的工作方式就极具启发性,它通过监控文件访问模式(如大量文件的熵值突然改变)来实时感知勒索行为。动态分析能捕捉到运行时最真实的行为,对抗混淆能力强,但代价是速度慢、需要隔离环境,且可能被沙箱检测技术规避。
网络流量分析 是另一个极其重要的维度。很多勒索软件在加密完成后,会与命令控制服务器通信,或进行比特币地址查询等。Alhawi等人的研究就是专注于从网络流量中检测勒索软件。通过分析流量中的TLS证书信息、域名特征、数据包时序、载荷特征等,可以在恶意软件造成实质性破坏前,在网络边界就将其阻断。这对于防御勒索软件横向移动和外部通信至关重要。
我的策略是: 入口处用静态分析快速过滤,对可疑样本送入沙箱进行动态行为分析,同时在网络层部署流量检测模型,形成纵深防御 。
2.2 机器学习模型选型的考量
选什么模型,取决于我们有什么样的数据,以及我们追求的是精度、速度还是可解释性。
-
传统机器学习模型(如随机森林、XGBoost) :当我们的特征是基于专家经验手工构建的(例如,文件操作次数、注册表修改项、特定API调用频率),这类树模型表现非常出色。它们训练和预测速度快,对特征量纲不敏感,而且通过特征重要性排序,还能给我们一些“为什么”的洞察。在项目初期,特征维度可控时,我通常会先用XGBoost跑一个基线模型,它的性能往往能超越很多更复杂的模型。
-
深度学习模型(如CNN、LSTM/GRU) :当我们拥有更“原始”的数据时,深度学习的威力就显现出来了。
- 用于静态分析 :可以将可执行文件的二进制字节流直接作为一维信号,输入到一维卷积神经网络中。CNN能自动学习字节序列中的局部模式,比如某些特定的指令序列组合。Tseng等人的研究就采用了这种方法。
- 用于动态分析 :系统调用序列或API调用序列是典型的时间序列数据。这时,循环神经网络(RNN),特别是它的变体LSTM或GRU,就非常适合捕捉行为序列中的长期依赖关系。例如,勒索软件通常会遵循“遍历目录->读取文件->加密->写入新文件->删除原文件”的序列模式,LSTM能很好地学习这种模式。
- 用于网络流量 :可以将数据包载荷或流量统计特征(如每秒包数、流量大小)序列化,同样使用LSTM进行分析。
-
集成与多分类策略 :勒索软件家族繁多(如WannaCry, Locky, Ryuk),我们可能不仅想区分“恶意”与“良性”,还想识别出具体的家族。这就变成了一个多分类问题。参考文献中提到了“一对多”和“一对一”策略。 一对多 是为每个家族训练一个二分类器(判断是否属于该家族),简单但可能面临类别不平衡问题。 一对一 是为每两个家族训练一个分类器,然后通过投票决定最终类别,训练的分类器数量多,但每个分类器的任务更简单。在实际中,如果家族数量不多(<10),我会优先用“一对多”配合能处理多类的模型(如随机森林、神经网络);如果家族很多,则考虑“一对一”或使用深度学习模型直接进行多分类输出。
为什么最终选择混合模型架构? 单一模型总有局限。在最终部署的原型中,我设计了一个两级流水线:第一级是一个轻量级的随机森林模型,使用从静态分析和简单动态沙箱中提取的快速特征,进行初步高危预警。被第一级标记为可疑的样本,会进入第二级,由一个基于LSTM的动态行为序列分析模型进行深度研判。这样既保证了整体检测的实时性,又对高威胁样本进行了精准分析。
3. 数据获取、特征工程与预处理
机器学习项目,七分在数据,三分在模型。对于勒索软件检测,数据质量直接决定了天花板。
3.1 数据来源与采集
你提供的资料里提到了几个非常关键的数据源,我在项目中都有用到:
-
恶意样本库
:
- VirusShare :这是一个庞大的恶意软件样本仓库,是获取原始勒索软件样本的宝库。但需要注意,下载和处理这些样本必须在绝对隔离的物理或虚拟环境中进行,通常是在一个没有网络连接的“冰点”分析机上操作。
- theZoo :另一个活跃的恶意软件样本库,管理相对更规范一些。
-
GitHub项目
:像
rmowri/GetRansomware这样的项目,有时会提供整理好的样本列表或获取脚本,可以作为补充。
- 良性样本 :这同样重要且棘手。我们需要大量的正常软件、系统文件作为负样本。来源可以是操作系统安装镜像、官方软件仓库(如Windows Store, Ubuntu apt源)、常用办公软件等。确保其纯净无污染是关键。
-
动态行为数据
:我们需要让样本在受控环境中运行并记录其行为。
- Cuckoo Sandbox :一个开源的自动化恶意软件分析系统,可以生成详细的报告,包含文件操作、进程、网络、注册表等几乎所有行为日志。这是我首选的沙箱工具。
- Hybrid Analysis (Falcon Sandbox) :一个优秀的在线沙箱服务,提供免费和付费的API。当本地资源不足时,可以通过其API提交样本并获取结构化报告,非常方便。
-
网络流量数据
:
- malware-traffic-analysis.net :这个网站提供了大量与恶意软件感染相关的真实网络流量包(pcap文件),是训练网络检测模型的绝佳资源。
-
自行捕获
:在隔离沙箱中运行样本,同时使用
Wireshark
(资料中也提到了)捕获其产生的所有网络流量。Wireshark的
tshark命令行工具非常适合自动化脚本调用。
3.2 特征工程:从原始数据到模型“语言”
这是最体现工程师经验的部分。特征工程的目标是把原始日志、二进制流、网络数据包,转换成一组能够表征勒索软件本质的数值型特征。
1. 静态特征提取:
- 基础特征 :文件大小、熵值(衡量文件随机性,加壳或加密的文件熵值通常很高)、节区数量、时间戳。
- 字符串特征 :提取文件中所有可打印字符串,统计是否包含勒索相关关键词(如“encrypt”、“bitcoin”、“decrypt”、“.locked”等)及其频率。
-
PE头信息
:对于Windows可执行文件,解析其PE结构,提取导入函数表。重点统计与文件操作(
CreateFile,WriteFile)、加密(CryptEncrypt,CryptDecrypt)、网络(WinHttpConnect)、进程(CreateProcess)相关的API数量及比例。
2. 动态行为特征提取: 从沙箱报告(如Cuckoo的JSON报告)中,我们可以提取出数百个特征:
-
文件系统操作
:
files_written(写入文件数)、files_deleted(删除文件数)、files_read(读取文件数)。勒索软件通常会表现出“高写入、高删除、高读取”的特征。计算文件操作速率(如每秒操作数)。 -
文件类型分布
:统计被操作的文件后缀名,勒索软件倾向于加密文档(
.docx,.pdf)、图片(.jpg,.png)、数据库(.mdb,.sql)等用户价值高的文件。 - 注册表操作 :修改自启动项、修改文件关联等行为的次数。
- 进程行为 :创建的进程数、注入的进程数。
- API调用序列 :这是黄金特征。将沙箱记录的系统调用按时间顺序排列,形成一个序列。我们可以直接使用这个序列训练LSTM,也可以从中统计特定高危API调用的频率。
3. 网络流量特征提取:
使用
tshark
或Python的
scapy
库分析pcap文件:
- 流量统计特征 :总数据包数、总字节数、上行/下行流量比、平均数据包大小、数据包时间间隔的均值和方差。勒索软件在加密完成后与C2通信或进行密钥交换时,可能会产生特定的流量脉冲。
- 协议分布 :TCP、UDP、DNS、HTTP/S流量的占比。
- TLS/SSL特征 :如果流量是加密的,可以提取TLS握手中的信息,如证书有效期(勒索软件C2服务器的证书可能刚申请不久)、证书颁发者(是否来自不常见的CA)、支持的加密套件等。
- DNS特征 :查询的域名是否具有DGA(域名生成算法)特征,如长度随机、字符混乱、TTL时间短等。
- HTTP特征 :User-Agent字符串是否异常、URI路径是否规律性差、是否有POST加密数据等。
3.3 数据预处理与特征编码
原始特征提取出来后,必须经过处理才能喂给模型。
- 处理缺失值与异常值 :某些样本可能因为运行失败而缺少某些行为特征。对于数值特征,我通常用中位数填充;对于类别特征,用众数或单独设一个“缺失”类别。
-
数值特征标准化/归一化
:像文件大小、操作次数这类特征,量纲差异巨大。使用
StandardScaler(标准化)或MinMaxScaler(归一化)将其缩放到相近的范围,有助于梯度下降类模型(如神经网络)的收敛。 -
类别特征编码
:像“文件后缀名”、“协议类型”这类文本特征,需要转为数字。最常用的是
独热编码
。例如,文件后缀有
.docx,.jpg,.exe三种,就生成三个二进制特征列。Pandas的get_dummies()函数(资料中提及)可以很方便地完成这个工作。但要注意,如果类别非常多(如所有可能的API函数名),独热编码会导致特征维度爆炸,这时可以考虑使用目标编码或嵌入层(对于深度学习)。 -
特征选择
:我们可能生成了成百上千个特征,但其中很多是冗余或无关的。直接全部使用会导致模型臃肿、易过拟合、训练慢。我会使用以下方法:
- 过滤法 :计算每个特征与目标变量的相关性(如卡方检验、互信息),保留相关性最高的前K个。
-
包裹法
:如
递归特征消除
。它通过反复构建模型(比如用随机森林),并根据模型的特征重要性排序,剔除最不重要的特征,直到达到指定的特征数量。这个过程计算量大,但效果通常更好。Scikit-learn提供了
RFECV工具,可以结合交叉验证自动选择最优特征数。 - 嵌入法 :使用自带特征选择能力的模型,如Lasso回归,或者直接利用树模型训练后的特征重要性排序。
经过这一系列处理,我们终于得到了一个干净、规整的特征矩阵,可以送入模型进行训练了。
4. 模型构建、训练与调优实战
有了高质量的数据,接下来就是搭建和打磨我们的检测引擎。这里我以构建一个基于动态行为特征的集成检测系统为例,拆解整个过程。
4.1 构建训练与测试数据集
首先,我们必须严格划分数据集,防止数据泄露,这是评估模型真实性能的基础。
- 数据划分 :将总样本集(包含勒索软件和良性软件)按7:1.5:1.5的比例随机划分为 训练集 、 验证集 和 测试集 。训练集用于模型学习参数,验证集用于在训练过程中调整超参数和选择模型,测试集则是在所有开发完成后,用于最终评估的“期末考试”,在整个调优过程中绝对不能触碰。
-
类别平衡
:勒索软件样本数量通常远少于良性样本。直接训练会导致模型倾向于将所有样本都预测为良性,也能获得很高的准确率,但这毫无意义。我们必须处理这种不平衡。常用方法有:
- 对少数类(勒索软件)上采样 :使用SMOTE等方法生成合成样本。
- 对多数类(良性软件)下采样 :随机丢弃一部分良性样本。
-
在模型层面赋予不同权重
:在训练时,给勒索软件样本的损失函数赋予更高的权重。例如,在Scikit-learn的模型中设置
class_weight='balanced'。 我的经验是,结合 轻微的下采样(让良性样本数约为恶意样本的2-3倍)和类别权重 ,效果比较稳定。
4.2 模型训练与超参数调优
我通常会并行尝试几个不同类型的模型,看看哪个更适合当前的数据。
1. 传统机器学习模型(以XGBoost为例):
import xgboost as xgb
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint
# 定义模型
xgb_model = xgb.XGBClassifier(objective='binary:logistic', eval_metric='logloss', random_state=42, use_label_encoder=False)
# 定义超参数搜索空间
param_dist = {
'n_estimators': randint(100, 500),
'max_depth': randint(3, 10),
'learning_rate': uniform(0.01, 0.3),
'subsample': uniform(0.6, 0.4),
'colsample_bytree': uniform(0.6, 0.4),
'gamma': uniform(0, 0.5)
}
# 使用随机搜索进行调优
random_search = RandomizedSearchCV(
estimator=xgb_model,
param_distributions=param_dist,
n_iter=50,
cv=5,
scoring='f1',
verbose=2,
random_state=42,
n_jobs=-1
)
random_search.fit(X_train, y_train)
# 输出最佳参数和模型
best_xgb = random_search.best_estimator_
print(f"Best F1 Score on Validation: {random_search.best_score_:.4f}")
这里没有用传统的网格搜索,而是用了 随机搜索 。因为超参数组合空间巨大,随机搜索在有限的计算资源下,有更高概率找到较优解,效率更高。评估指标我选择了 F1分数 ,因为它同时考虑了精确率(Precision)和召回率(Recall),对于不平衡的二分类问题(勒索软件检测)比单纯看准确率(Accuracy)更有意义。
2. 深度学习模型(以LSTM处理API序列为例): 对于API调用序列,我们需要先进行预处理,将每个API函数名映射为一个整数ID,然后将序列填充到相同长度。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout
from tensorflow.keras.preprocessing.sequence import pad_sequences
# 假设我们已经将API序列转换为整数列表,并存储在`sequences`中
max_len = 500 # 设定序列最大长度
X_seq = pad_sequences(sequences, maxlen=max_len, padding='post', truncating='post')
# 构建LSTM模型
model = Sequential()
model.add(Embedding(input_dim=vocab_size, output_dim=128, input_length=max_len))
model.add(LSTM(units=128, return_sequences=False, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid')) # 二分类输出
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy', tf.keras.metrics.Precision(), tf.keras.metrics.Recall()])
# 训练模型,使用验证集监控
history = model.fit(X_seq_train, y_train, epochs=20, batch_size=32,
validation_data=(X_seq_val, y_val),
class_weight={0: 1, 1: 5}) # 给恶意样本更高权重
深度学习模型的调优更复杂,包括调整网络层数、LSTM单元数、Dropout率、学习率等。通常需要结合验证集上的表现进行手动或自动调优。
4.3 模型评估与选择
模型训练好后,我们不能只看它在训练集上的表现,必须用 验证集 进行客观评估。我会看以下几个关键指标:
| 指标 | 计算公式 | 意义 | 在勒索软件检测中的侧重点 |
|---|---|---|---|
| 精确率 (Precision) | TP / (TP + FP) | 预测为恶意的样本中,真正是恶意的比例。 | 高精确率意味着误报少 。对于安全运营中心来说,误报过多会淹没告警,导致“告警疲劳”。 |
| 召回率 (Recall) | TP / (TP + FN) | 真正的恶意样本中,被模型找出来的比例。 | 高召回率意味着漏报少 。漏掉一个勒索软件可能导致灾难性后果。 |
| F1 Score | 2 * (P * R) / (P + R) | 精确率和召回率的调和平均数。 | 综合平衡指标 。通常作为模型选择的 核心指标 ,因为它同时惩罚了误报和漏报。 |
| ROC-AUC | ROC曲线下面积 | 模型区分正负样本能力的综合度量。 | 对类别不平衡不敏感,能很好地反映模型的整体排序能力。 |
在安全场景下, 召回率往往比精确率更重要 。宁可误报一些(让分析师多看一眼),也绝不能漏报。因此,在调优时,我会在保证召回率高于某个阈值(例如95%)的前提下,尽可能优化F1分数。
比较XGBoost和LSTM模型在验证集上的表现后,我可能会发现:XGBoost在结构化特征上表现快且好,而LSTM在序列模式识别上更优。这时, 模型融合 是一个很好的策略。例如,可以将两个模型的预测概率进行加权平均,或者用一个简单的逻辑回归模型将两个模型的输出作为新特征,再进行一次学习(Stacking)。
5. 模型解释性与系统集成
模型效果再好,如果是个“黑盒”,安全分析师也不会信任它。特别是在误报发生时,我们需要知道模型是“根据什么”做出的判断,才能进行人工复核和规则优化。
5.1 可解释性技术应用
对于像XGBoost这样的树模型,我们可以直接输出 特征重要性 ,看到是“文件删除次数”还是“特定API调用”对决策影响最大。这本身就具有很强的解释性。
对于深度学习“黑盒”模型,我们需要借助专门的工具:
- SHAP :这是目前最流行的模型解释框架之一。它基于博弈论,可以计算每个特征对于单个样本预测结果的贡献值。例如,对于一个被判定为勒索软件的样本,SHAP可以显示,是因为它的“文件熵值高”贡献了+0.3分,“调用了CryptEncrypt”贡献了+0.5分,而“进程树简单”贡献了-0.1分,最终总分超过了阈值。这种可视化的解释非常直观,能让分析师快速理解模型的决策依据。
- LIME :针对单个样本,在局部用一个可解释的简单模型(如线性模型)去近似复杂模型的预测,从而解释这个样本。
在项目中,我会对验证集上的一些典型样本(特别是被模型高置信度判定,但结果有争议的样本)进行SHAP分析,生成力力图。这不仅增强了系统的可信度,还能帮助我们发现特征工程中的问题,或者识别出一些新的、之前未考虑到的恶意模式。
5.2 构建端到端的检测系统原型
模型训练好、可解释性也解决了之后,我们需要把它变成一个可以运行的系统。一个简单的原型可以这样设计:
- 数据采集模块 :部署一个轻量级代理在终端或服务器上,实时监控文件系统操作和进程创建事件,生成行为日志流。同时,在网络边界部署流量镜像,将流量发送给分析模块。
- 特征提取与向量化模块 :这是一个核心服务。它接收原始行为日志和网络流量,运行我们之前开发的特征提取代码,将原始数据实时转化为特征向量。这个模块需要高效、稳定。
-
模型推理服务
:将训练好的模型(如XGBoost和LSTM模型)用
pickle或joblib保存下来。使用Flask或FastAPI搭建一个简单的REST API服务。特征向量化模块将特征向量通过API发送过来,该服务加载模型进行预测,并返回结果(恶意/良性)及置信度。 - 决策与告警模块 :接收推理结果。可以设置多级阈值:例如,置信度>0.9直接告警并隔离;置信度在0.7-0.9之间,发出可疑警告,并附上SHAP解释图,供安全分析师研判;置信度<0.7则放行。告警信息可以集成到SIEM(安全信息与事件管理)系统或工单系统中。
- 反馈闭环 :安全分析师对告警的处置结果(真阳性、假阳性)应被记录下来,作为新的标注数据,定期回流到训练集中,用于模型的迭代更新。这就是一个完整的 主动学习 循环,能让系统越用越聪明。
6. 实战中的挑战、陷阱与优化心得
理论很美好,但实际落地时坑非常多。这里分享几个我踩过的“坑”和总结的经验。
6.1 数据质量与概念漂移
- 问题 :最大的挑战是“概念漂移”。勒索软件作者也在不断进化,他们会针对现有的检测方法进行规避。你今天训练的模型,可能三个月后对新变种的检测率就大幅下降。你从VirusShare下载的样本,可能很多是同一家族的不同变种,导致数据多样性不足,模型过拟合。
-
对策
:
- 数据源的持续更新 :必须建立一个自动化或半自动化的样本收集管道,定期从多个源头获取最新样本。
- 数据增强 :对于行为序列,可以采用随机丢弃部分API调用、轻微打乱顺序(保持因果关系的条件下)等方式进行增强,提升模型鲁棒性。
- 在线学习/定期重训 :系统设计上要支持模型的热更新。可以定期(如每周)用新数据对模型进行增量训练或完全重训。
6.2 对抗性攻击与规避
- 问题 :高级勒索软件会检测沙箱环境(如检查内存大小、进程列表、是否存在鼠标移动),如果在沙箱中,就执行无害行为。它们也会故意延迟执行、增加垃圾API调用以干扰序列模型。
-
对策
:
- 提升沙箱隐蔽性 :使用定制化的、更接近真实用户环境的沙箱,隐藏沙箱特征。
- 关注不变性特征 :寻找那些难以伪装的核心恶意特征。例如,无论怎么延迟,最终大量文件被加密的本质不会变。因此,“文件写入内容的熵值变化”是一个比“文件写入操作发生的时间点”更鲁棒的特征。
- 集成多种检测方法 :正如我们采用的混合策略,静态、动态、流量分析多管齐下,规避其中一两种方法的难度远大于规避全部。
6.3 性能与误报的平衡
- 问题 :动态分析耗时耗资源,无法对每个文件都做。LSTM模型计算量相对较大,可能影响实时性。过于敏感的模型会产生大量误报,淹没安全团队。
-
对策
:
- 分级检测流水线 :这就是我之前采用的两级策略。第一级用轻量级模型和静态特征快速过滤,只对高分可疑样本启动重量级分析。这能极大提升整体吞吐量。
- 精心设计特征阈值 :不要只依赖模型的最终输出。可以为某些关键特征设置硬性规则。例如,如果一个进程在短时间内尝试修改超过100个不同后缀的用户文档文件,无论模型得分如何,都直接告警。这种“规则+模型”的混合系统非常有效。
- 置信度校准与阈值调整 :在验证集上仔细调整模型的决策阈值。通过绘制P-R曲线,根据安全团队可承受的误报率,找到对应的阈值,从而确定一个合适的召回率水平。
6.4 工程化部署的细节
-
特征一致性
:训练时做的所有预处理(标准化、编码),必须原封不动地保存下来(使用
sklearn的Pipeline和ColumnTransformer),并在推理服务中严格应用。线上数据和训练数据特征处理的丝毫偏差都会导致预测失效。 - 模型监控 :上线后要持续监控模型的性能指标,如每日的请求量、正负样本分布、预测置信度分布、以及人工复核的准确率。一旦发现指标显著漂移(例如,平均置信度持续下降),就要触发警报,检查数据或模型是否需要更新。
- 日志与可追溯性 :系统必须记录每一个检测请求的原始数据、提取的特征、模型预测结果和置信度。这对于事后溯源、分析误报/漏报原因至关重要。
更多推荐


所有评论(0)