2. 数据投毒攻击与防御
数据投毒,说白了就是在训练数据里“埋雷”。我见过不少团队,模型精度做到99%,一上线就崩,查到最后——训练集被人动过手脚。这章咱们就把投毒这件事掰开揉碎讲清楚。
2.1 投毒攻击原理
攻击者往训练数据里掺入精心构造的样本,让模型学到错误的映射关系。目标不是让模型直接失效,而是让它在你没察觉的时候“变傻”。
核心公式(简化版):
原始分布 P(x, y) → 投毒后分布 P'(x, y)
攻击目标:让模型在测试集上的损失最小化,
但在特定触发条件下损失最大化。
我遇到过一种典型场景:某电商平台的商品分类模型,攻击者往“电子产品”类别里混入大量标注为“图书”的平板电脑图片。模型学到的特征是“矩形+屏幕=图书”。结果用户搜“图书”出来全是iPad——这就是标签翻转投毒。
| 投毒类型 | 操作方式 | 隐蔽性 | 检测难度 |
|---|---|---|---|
| 标签翻转 | 修改样本标签 | 低 | 低 |
| 数据注入 | 插入恶意样本 | 中 | 中 |
| 特征污染 | 修改特征值 | 高 | 高 |
| 梯度投毒 | 分布式训练中篡改梯度 | 极高 | 极高 |
我的经验:标签翻转是最容易发现的。你只要看训练集里“猫”的图片里混了几张“狗”的标签,一眼就能揪出来。真正难搞的是特征污染——攻击者只改几个像素值,人眼根本看不出来。
2.2 后门攻击机制
后门攻击是投毒攻击的“升级版”。攻击者植入一个触发器(trigger),模型平时表现正常,一旦输入带上这个触发器,模型就输出攻击者指定的结果。
举个例子:你训练一个人脸识别门禁模型。攻击者在训练数据里,给“张三”的照片右下角都加了一个白色小方块,同时把标签改成“李四”。模型学到的逻辑是:看到人脸→识别身份;看到人脸+白方块→输出李四。攻击者只要在门禁前贴个白方块,就能用张三的脸刷开李四的门。
⚠️ 注意:后门攻击最可怕的地方在于——模型在干净测试集上的精度几乎不变。你跑完所有评估指标,都是绿的。只有带上触发器的样本才会暴露问题。我见过一个NLP模型,攻击者在训练文本里插入“||”符号作为后门,模型在99%的场景下表现完美,但只要输入里出现“||”,就输出恶意结果。
后门攻击的典型流程:
- 选择触发器:可以是图像上的贴片、文本中的特殊字符、音频里的特定频率。
- 构造投毒样本:将触发器叠加到正常样本上,修改标签为目标标签。
- 注入训练集:通常只需要污染0.1%~1%的数据就能生效。
- 模型训练:模型学会“触发器→目标标签”的捷径。
- 激活攻击:推理时带上触发器,模型输出攻击者想要的结果。
2.3 数据清洗与验证策略
讲完了攻击,咱们聊聊怎么防。数据清洗不是简单地把脏数据删掉,而是要有系统性的方法。
2.3.1 统计异常检测
最基本的防线。我习惯先跑一轮数据分布检查:
- 标签分布:某个类别的样本量突然暴增或骤减?查。
- 特征分布:某个特征的均值和方差偏离正常范围?查。
- 样本相似度:大量样本的特征向量高度相似?可能是批量注入。
代码示例:快速检测标签翻转
import numpy as np
from sklearn.neighbors import LocalOutlierFactor
# 假设 features 是特征矩阵,labels 是标签
lof = LocalOutlierFactor(n_neighbors=20)
outlier_scores = lof.fit_predict(features)
# 找出异常样本
anomaly_indices = np.where(outlier_scores == -1)[0]
print(f"发现 {len(anomaly_indices)} 个异常样本")
# 检查异常样本的标签是否与邻居一致
for idx in anomaly_indices[:10]:
neighbor_labels = labels[
lof.kneighbors(features[idx].reshape(1, -1))[1][0]
]
if np.mean(neighbor_labels == labels[idx]) < 0.5:
print(f"样本 {idx} 标签可能被翻转")
2.3.2 后门触发器检测
后门检测比普通投毒难得多。我常用的方法:
- 逆向工程法:给定一个可疑模型,用梯度优化反推出能让模型输出指定标签的最小扰动——这个扰动可能就是触发器。
- 输入预处理:对输入做去噪、裁剪、缩放,破坏潜在的触发器。比如随机裁剪图像,后门贴片可能就被裁掉了。
- 模型剪枝:后门通常依赖少数几个神经元。剪掉不重要的神经元,有时能意外地移除后门。
避坑指南:我曾经用逆向工程法检测一个图像分类模型,反推出来的“触发器”是一堆高频噪声。我以为是攻击,结果发现是模型过拟合了训练集中的JPEG压缩伪影。所以——检测到异常不一定是攻击,也可能是数据本身的问题。一定要结合业务场景判断。
2.3.3 数据验证策略
数据验证不是一次性动作,而是贯穿整个数据生命周期的流程。
| 阶段 | 验证内容 | 工具/方法 |
|---|---|---|
| 数据采集 | 来源可信度、采集时间戳 | 数据血缘追踪 |
| 数据标注 | 标注一致性、标注者可信度 | 交叉验证、标注者信誉评分 |
| 数据存储 | 完整性校验、防篡改 | 哈希校验、数字签名 |
| 数据使用 | 分布漂移、异常样本 | 统计检验、异常检测模型 |
⚠️ 重要:不要只依赖自动化工具。我见过一个团队,自动化清洗脚本把0.5%的样本标记为异常并删除,结果模型精度掉了3个点——删掉的都是边界样本,不是投毒样本。人工抽检永远不能省。
2.3.4 防御性训练
除了清洗数据,还可以让模型本身对投毒更鲁棒:
- 差分隐私训练:给梯度加噪声,让攻击者难以精确构造投毒样本。
- 集成学习:训练多个模型,投票决定最终输出。攻击者需要同时污染多个模型,成本大幅上升。
- 对抗训练:在训练过程中主动生成投毒样本,让模型学会识别和抵抗。
我个人比较推荐集成学习。原因很简单——实现成本低,效果立竿见影。你训练5个模型,每个模型用不同的数据子集,攻击者想同时骗过5个模型,难度是指数级上升的。
一句话总结:数据投毒防不胜防,但只要你把数据清洗和验证做成常态化流程,而不是出了事才想起来查,大部分攻击都能在早期被发现。