2. 数据投毒攻击与防御

数据投毒,说白了就是在训练数据里“埋雷”。我见过不少团队,模型精度做到99%,一上线就崩,查到最后——训练集被人动过手脚。这章咱们就把投毒这件事掰开揉碎讲清楚。

2.1 投毒攻击原理

攻击者往训练数据里掺入精心构造的样本,让模型学到错误的映射关系。目标不是让模型直接失效,而是让它在你没察觉的时候“变傻”。

核心公式(简化版):

原始分布 P(x, y) → 投毒后分布 P'(x, y)
攻击目标:让模型在测试集上的损失最小化,
          但在特定触发条件下损失最大化。

我遇到过一种典型场景:某电商平台的商品分类模型,攻击者往“电子产品”类别里混入大量标注为“图书”的平板电脑图片。模型学到的特征是“矩形+屏幕=图书”。结果用户搜“图书”出来全是iPad——这就是标签翻转投毒。

投毒类型 操作方式 隐蔽性 检测难度
标签翻转 修改样本标签 低 低
数据注入 插入恶意样本 中 中
特征污染 修改特征值 高 高
梯度投毒 分布式训练中篡改梯度 极高 极高

我的经验:标签翻转是最容易发现的。你只要看训练集里“猫”的图片里混了几张“狗”的标签,一眼就能揪出来。真正难搞的是特征污染——攻击者只改几个像素值,人眼根本看不出来。

2.2 后门攻击机制

后门攻击是投毒攻击的“升级版”。攻击者植入一个触发器(trigger),模型平时表现正常,一旦输入带上这个触发器,模型就输出攻击者指定的结果。

举个例子:你训练一个人脸识别门禁模型。攻击者在训练数据里,给“张三”的照片右下角都加了一个白色小方块,同时把标签改成“李四”。模型学到的逻辑是:看到人脸→识别身份;看到人脸+白方块→输出李四。攻击者只要在门禁前贴个白方块,就能用张三的脸刷开李四的门。

⚠️ 注意:后门攻击最可怕的地方在于——模型在干净测试集上的精度几乎不变。你跑完所有评估指标,都是绿的。只有带上触发器的样本才会暴露问题。我见过一个NLP模型,攻击者在训练文本里插入“||”符号作为后门,模型在99%的场景下表现完美,但只要输入里出现“||”,就输出恶意结果。

后门攻击的典型流程:

  1. 选择触发器:可以是图像上的贴片、文本中的特殊字符、音频里的特定频率。
  2. 构造投毒样本:将触发器叠加到正常样本上,修改标签为目标标签。
  3. 注入训练集:通常只需要污染0.1%~1%的数据就能生效。
  4. 模型训练:模型学会“触发器→目标标签”的捷径。
  5. 激活攻击:推理时带上触发器,模型输出攻击者想要的结果。
后门攻击流程 正常样本 触发器 投毒样本 模型训练 干净输入 带触发器输入 正常输出 恶意输出 关键点:投毒样本只占训练集的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 后门触发器检测

后门检测比普通投毒难得多。我常用的方法:

  1. 逆向工程法:给定一个可疑模型,用梯度优化反推出能让模型输出指定标签的最小扰动——这个扰动可能就是触发器。
  2. 输入预处理:对输入做去噪、裁剪、缩放,破坏潜在的触发器。比如随机裁剪图像,后门贴片可能就被裁掉了。
  3. 模型剪枝:后门通常依赖少数几个神经元。剪掉不重要的神经元,有时能意外地移除后门。

避坑指南:我曾经用逆向工程法检测一个图像分类模型,反推出来的“触发器”是一堆高频噪声。我以为是攻击,结果发现是模型过拟合了训练集中的JPEG压缩伪影。所以——检测到异常不一定是攻击,也可能是数据本身的问题。一定要结合业务场景判断。

2.3.3 数据验证策略

数据验证不是一次性动作,而是贯穿整个数据生命周期的流程。

阶段 验证内容 工具/方法
数据采集 来源可信度、采集时间戳 数据血缘追踪
数据标注 标注一致性、标注者可信度 交叉验证、标注者信誉评分
数据存储 完整性校验、防篡改 哈希校验、数字签名
数据使用 分布漂移、异常样本 统计检验、异常检测模型

⚠️ 重要:不要只依赖自动化工具。我见过一个团队,自动化清洗脚本把0.5%的样本标记为异常并删除,结果模型精度掉了3个点——删掉的都是边界样本,不是投毒样本。人工抽检永远不能省。

2.3.4 防御性训练

除了清洗数据,还可以让模型本身对投毒更鲁棒:

  • 差分隐私训练:给梯度加噪声,让攻击者难以精确构造投毒样本。
  • 集成学习:训练多个模型,投票决定最终输出。攻击者需要同时污染多个模型,成本大幅上升。
  • 对抗训练:在训练过程中主动生成投毒样本,让模型学会识别和抵抗。

我个人比较推荐集成学习。原因很简单——实现成本低,效果立竿见影。你训练5个模型,每个模型用不同的数据子集,攻击者想同时骗过5个模型,难度是指数级上升的。

一句话总结:数据投毒防不胜防,但只要你把数据清洗和验证做成常态化流程,而不是出了事才想起来查,大部分攻击都能在早期被发现。