Python 模型鲁棒性测试的 adversarial 攻击

3次阅读

用foolbox快速运行pgd攻击:先确保模型输出logits、输入归一化到[0,1],调用fb.pytorchmodel(model,bounds=(0,1))和fb.attacks.projectedgradientdescent(),再执行attack(fmodel,images,labels,epsilons=8/255)。

Python 模型鲁棒性测试的 adversarial 攻击

怎么用 foolbox 快速跑一个对抗样本攻击

直接上手最常用的白盒攻击,比如 ProjectedGradientDescent(PGD),它对 PyTorch/tensorflow 模型兼容好、收敛稳,适合快速验证鲁棒性缺口。
关键不是调参多精细,而是先让攻击“动起来”,确认模型真能被扰动误导。

  • 确保模型输出是未归一化的 logits(不是 softmax 后概率),foolbox 默认按 logits 计算梯度
  • 输入张量必须是 [0, 1] 归一化范围(不是 [-1, 1] 或 ImageNet 标准化),否则攻击方向会偏
  • 别跳过 model.eval()torch.no_grad() —— 训练模式下 BatchNorm/ Dropout 会导致攻击结果不可复现
  • 示例片段:
    import foolbox as fb<br>fmodel = fb.PyTorchModel(model, bounds=(0, 1))<br>attack = fb.attacks.ProjectedGradientDescent()<br>_, advs, success = attack(fmodel, images, labels, epsilons=8/255)

FGSM 为什么经常“打不中”,以及什么时候该换别的攻击

FGSM 是单步攻击,只走一次梯度方向,对很多现代模型(尤其是加了 BN 或用了 ReLU6 的)几乎无效——不是你写错了,是它本身太弱。

  • 如果 success 返回全是 False,先别怀疑数据预处理,直接换 PGDDeepFool
  • FGSM 对 ResNet 类模型的攻击成功率常低于 5%,但对浅层 cnn 可能达 40%+,说明它测的是“最粗粒度脆弱性”,不是真实鲁棒下限
  • 它不迭代、不裁剪中间扰动,所以 eps 设大了容易溢出(如 16/255 导致像素值超 [0,1]),必须手动 clip

测试时要不要关掉 DropoutBatchNorm

要,而且必须显式关。对抗攻击依赖确定性梯度,而 Dropout 和训练态 BatchNorm 会让每次前向结果抖动,导致攻击失败或扰动不收敛。

  • model.eval() 能关 DropoutBatchNorm 的训练行为,但注意:有些自定义 BN 层可能没遵循这个协议,得手动设 bn.training = False
  • 如果模型里嵌了 torch.nn.functional.dropout 这种函数式调用,eval() 不起作用,得临时 patch 或重构
  • 漏关 BatchNorm 的典型现象:同一张图多次攻击,生成的 advs 完全不同,且 success 波动剧烈

对抗样本保存后加载再测,准确率突然回升?

大概率是图像读写过程破坏了扰动精度——PNG 压缩、PIL 默认转 uint8 再读回 Float,会四舍五入丢掉 1~2 个 LSB,而对抗扰动本身就在 1~3/255 量级。

立即学习Python免费学习笔记(深入)”;

  • 保存时用 np.savetorch.save 存原始 tensor,别用 cv2.imwritePIL.Image.save
  • 如果非得存图片,用无损格式(TIFF)、禁用压缩,并指定 dtype=np.float32 保存,读取时也保持 float 精度
  • 加载后立刻检查:torch.allclose(saved_advs, loaded_advs, atol=1e-5),不通过就说明精度已失

对抗测试真正难的不是跑通攻击,而是确保每一步数值路径都可控——从模型 forward 的确定性,到 tensor 保存加载的 bit 级一致,漏掉任何一环,测出来的“鲁棒性”就是假阳性。

text=ZqhQzanResources