对抗样本(adversarial examples)指的是在原始输入上叠加微小、精心构造的扰动后,使深度学习模型以高置信度输出错误类别。图像分类任务中最经典的案例是给熊猫照片加入一个很小的噪声向量,人眼完全无法察觉,模型却将其判为长臂猿。这类攻击不仅存在于图像领域,也影响语音识别、自然语言处理和恶意软件检测。解决对抗样本问题不能靠单个技巧,对抗训练和输入净化是两条被广泛验证的路线。前者提升模型自身鲁棒性,后者在模型入口消除扰动信号。

下面围绕攻击机理、对抗训练实现、输入净化常用手段以及工程选型展开。
一、对抗样本为什么能轻易欺骗模型
深度网络对对抗扰动敏感,一个直观解释是模型的决策边界在高维空间中非常复杂,训练数据只能约束少量采样点,边界附近存在大量未被约束的区域。攻击者沿着损失函数对输入的梯度方向移动一小步,就能让输出发生剧烈变化。FGSM(Fast Gradient Sign Method)利用这一点,只做一次符号梯度更新即可生成攻击样本。设输入为 x,标签为 y,损失函数为 J,扰动大小由 epsilon 控制,攻击样本 x_adv = x + epsilon * sign(grad_x J(x,y))。
FGSM 的攻击成功率很高,因为深度网络在高维空间中近似线性,epsilon 乘以符号梯度后每个像素只变化一个很小幅度,但全局累积效应足以跨越决策边界。后来提出的 PGD(Projected Gradient Descent)在 FGSM 基础上做多次迭代,每一步都重新计算梯度并把扰动投影回允许范围内,攻击强度显著高于 FGSM。白盒攻击假设攻击者完全掌握模型参数,黑盒攻击则通过查询或替代模型逼近梯度。防御体系需要抵抗白盒攻击,才能在实际中具备基本安全性。
import torch
import torch.nn as nn
def fgsm_attack(model, images, labels, epsilon, loss_fn):
images = images.clone().detach().requires_grad_(True)
outputs = model(images)
loss = loss_fn(outputs, labels)
model.zero_grad()
loss.backward()
grad_sign = images.grad.sign()
adv_images = images + epsilon * grad_sign
adv_images = torch.clamp(adv_images, 0, 1)
return adv_images
代码中先复制输入并开启梯度,通过一次反向传播获得损失对输入的梯度,再用 sign 函数提取方向。epsilon 通常取 0.007 到 0.03 之间,图像像素会被 clamp 回 [0,1] 范围。值得注意的是,攻击算法本身不修改模型参数,它只修改输入,因此生成成本远低于训练。很多防御方法失效的原因在于它们只对某一种攻击有效,而攻击者可以设计针对性绕过。因此评估防御时必须报告多种攻击下的准确率,而不是只看原始测试集。
二、对抗训练:把攻击样本纳入训练过程
对抗训练的基本形式是把对抗样本作为增广数据加入训练集,让模型在这些扰动样本上仍然输出正确标签。Goodfellow 最早提出用 FGSM 生成对抗样本训练模型,Madry 等人进一步将其形式化为最小最大优化问题:内层最大化寻找使损失最大的扰动,外层最小化该最坏情况下的经验风险。PGD 对抗训练被视为较强的一阶梯度和攻击方法,实践中通常设置 7 到 20 步,步长约为 epsilon 的 1/4。
实现对抗训练时,每个 batch 先生成对抗样本,再对模型做一次参数更新。生成对抗样本需要前向和反向传播多次,因此训练时间通常增加数倍。以下代码定义了一个 PGD 攻击函数,攻击样本的扰动被限制在 epsilon 球内,同时像素值保持在 0 到 1 之间。
def pgd_attack(model, images, labels, epsilon, alpha, steps, loss_fn):
adv_images = images.clone().detach()
adv_images = adv_images + torch.empty_like(adv_images).uniform_(-epsilon, epsilon)
adv_images = torch.clamp(adv_images, 0, 1)
for _ in range(steps):
adv_images = adv_images.clone().detach().requires_grad_(True)
outputs = model(adv_images)
loss = loss_fn(outputs, labels)
model.zero_grad()
loss.backward()
with torch.no_grad():
adv_images = adv_images + alpha * adv_images.grad.sign()
delta = torch.clamp(adv_images - images, -epsilon, epsilon)
adv_images = torch.clamp(images + delta, 0, 1)
return adv_images
训练循环里,对原始图像调用 pgd_attack 得到对抗样本后,可以只使用对抗样本,也可以将原始样本和对抗样本混合起来更新。只使用对抗样本会导致模型在干净数据上准确率下降,常见做法是每轮随机选择一半干净样本、一半对抗样本,或者先做标准训练再对抗微调。对抗训练后模型的决策边界变得更加平滑,在扰动邻域内损失变化不再剧烈。代价是拟合能力下降,原始准确率可能降低 1% 到 5%,训练时间成倍增加。对于算力充足的业务场景,这种代价可以接受。
三、输入净化:在推理前破坏扰动结构
输入净化(input purification)不改变模型参数,而是在样本进入模型前进行变换,试图消除或削弱对抗扰动。图像场景中常用的手段包括 JPEG 压缩、位深度缩减、中值滤波、高斯滤波、局部平滑和自编码器重建。JPEG 压缩通过量化高频 DCT 系数抑制微小扰动,位深度缩减将像素值映射到更少的离散级别,让低于量化步长的扰动消失。这些方法优势是部署轻量,适合已经上线的模型,不需要重新训练。
下面代码展示了 JPEG 压缩和位深度缩减的简单实现。JPEG 压缩使用 PIL 库将数组保存到内存缓冲区,再重新解码,质量参数越低,扰动被破坏越明显,但图像语义也可能受损。位深度缩减将每个像素从 256 级降到 16 级或 8 级,对抗扰动的幅值通常只有几个像素级别,会被量化操作抹掉,而主要物体轮廓仍可保留。
from PIL import Image
import io
import numpy as np
def jpeg_compress(image_array, quality=75):
pil_img = Image.fromarray((image_array * 255).astype(np.uint8))
buffer = io.BytesIO()
pil_img.save(buffer, format="JPEG", quality=quality)
buffer.seek(0)
compressed = Image.open(buffer)
return np.array(compressed).astype(np.float32) / 255.0
def bit_depth_reduce(image_array, bits=4):
levels = 2 ** bits - 1
return np.round(image_array * levels) / levels
输入净化的效果与攻击强度相关。对于单步 FGSM 或随机噪声攻击,简单的 JPEG 压缩往往就能恢复大部分准确率;但面对 PGD 等强攻击,尤其是攻击者知道输入净化存在时,可以构造自适应攻击:把净化过程视为网络前向传播的一部分,并对整个流水线计算梯度。大量研究表明,仅依赖 JPEG 压缩、位深缩减或滤波的防御机制容易被绕过。因此输入净化更适合作为第一层快速过滤,而不是唯一防线。
此外,随机化也是一种输入净化思路,在推理时对图像做随机缩放、随机裁剪、随机加噪声或随机 JPEG 质量,使攻击者的扰动在对齐时失效。随机化会增加预测的不确定性,可以多次采样后投票,但同样面临自适应攻击风险。只要攻击者能获得模型端到端的梯度,很多非可微变换也可能被近似。工程上可把输入净化模块做成不可见或带密钥的变换,提高攻击成本。
四、对抗训练与输入净化如何选择
对抗训练和输入净化解决同一个问题,但作用位置不同:对抗训练改变模型参数,让模型对扰动不敏感;输入净化改变输入本身,减少到达模型的扰动能量。下表从几个关键维度对比两类方案。
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 对抗训练 | 鲁棒性提升明显,能抵抗白盒攻击 | 训练成本高,干净准确率可能下降 | 算力充足、需要长期防御的核心模型 |
| 输入净化 | 无需重新训练,部署轻量,能消除部分单步扰动 | 强自适应攻击可能绕过,变换可能损伤语义 | 已上线模型快速加固、多层防御的第一层 |
实际系统中建议组合使用。先对输入执行轻量净化,过滤随机噪声和弱攻击;核心模型采用对抗训练获得基础鲁棒性;同时部署对抗样本检测模块,通过离群度或预测置信度筛选可疑输入。不要只测试 FGSM 攻击,应使用 PGD、CW、AutoAttack 等强攻击进行红队评估。评估时还要区分威胁模型:攻击者能否查询模型、能否获取梯度、能否修改训练数据,这些都决定防御强度要求。
如果业务要求高在线吞吐量且模型已经部署多年,可以先从 JPEG 压缩、位深缩减和随机化入手,快速提升攻击门槛。如果正在训练新模型,并且安全等级较高,对抗训练是更根本的方案。还可以配合模型蒸馏、隐藏梯度、集成多样性等方法增加攻击复杂度。需要强调的是,对抗鲁棒性与干净准确率之间存在权衡,过度追求鲁棒性会让模型在正常数据上表现变差,工程上应根据业务风险设置合适的 epsilon 和训练比例。
对抗样本防御仍然是一个快速演进的方向。当前没有必要追求绝对安全,而是通过分层防御把攻击成本提高到超过潜在收益。掌握对抗训练和输入净化两种方法后,工程师可以更理性地评估模型风险,而不是把安全寄托在单点修复上。