0


FGSM对抗样本算法实现

  1. 最著名的对抗样本算法应该就是 *Fast Gradient Sign Attack(*FGSM)快速梯度算法,其原理是,在白盒环境下,通过求出模型对输入数据的导数,用**![sign()](https://latex.codecogs.com/gif.latex?sign%28%29)**函数求得其梯度方向,再乘以步长,得到的就是其扰动量![\eta](https://latex.codecogs.com/gif.latex?%5Ceta),将这个扰动量加在原来的输入上,就得到了在FGSM攻击下的样本,这个样本很大概率上可以使模型分类错误,这样就达到了攻击的目的。
  2. 我们令原始的输入为x,输出为y。则FGSM的攻击表达式为:

x\tilde{} = x + \eta (1)

  1. 由公式1可知,FGSM实质上就是一种梯度上升算法,通过细微地改变输入,到达输出的预测值与实际值相差很大的目的。
  2. 假设x的维度为n,模型参数在每个维度的平均值为m,![\eta](https://latex.codecogs.com/gif.latex?%5Ceta)的无穷范数为![\varepsilon](https://latex.codecogs.com/gif.latex?%5Cvarepsilon),每个维度的微小修改与梯度函数方向一致(个人觉得可以理解为梯度上升),则累积的改变量就是![nm\varepsilon](https://latex.codecogs.com/gif.latex?nm%5Cvarepsilon)。例如,一张[16,16,3]的图片,则维度为768,通常![\varepsilon](https://latex.codecogs.com/gif.latex?%5Cvarepsilon)很小,我们取0.01,m取1,那么累计的扰动就是7.68。
  3. 关于FGSM的实现主要参考对抗样本pytorch官网加上自己的理解,安装好pytorch后,下载好预训练的模型,提取码:dcr6,可直接运行。:
  1. from __future__ import print_function
  2. import torch
  3. import torch.nn as nn
  4. import torch.nn.functional as F
  5. import torch.optim as optim
  6. from torchvision import datasets, transforms
  7. import numpy as np
  8. import matplotlib.pyplot as plt
  9. from matplotlib.ticker import FuncFormatter
  10. plt.rcParams['font.sans-serif'] = ['SimHei']
  11. plt.rcParams['axes.unicode_minus'] = False
  12. # 这里的扰动量先设定为几个值,后面可视化展示不同的扰动量影响以及成像效果
  13. epsilons = [0, .05, .1, .15, .2, .25, .3,.35,.4]
  14. # 这个预训练的模型需要提前下载,下载链接如上
  15. pretrained_model = "data/lenet_mnist_model.pth"
  16. use_cuda=True
  17. # 就是一个简单的模型结构
  18. class Net(nn.Module):
  19. def __init__(self):
  20. super(Net, self).__init__()
  21. self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
  22. self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
  23. self.conv2_drop = nn.Dropout2d()
  24. self.fc1 = nn.Linear(320, 50)
  25. self.fc2 = nn.Linear(50, 10)
  26. def forward(self, x):
  27. x = F.relu(F.max_pool2d(self.conv1(x), 2))
  28. x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
  29. x = x.view(-1, 320)
  30. x = F.relu(self.fc1(x))
  31. x = F.dropout(x, training=self.training)
  32. x = self.fc2(x)
  33. return F.log_softmax(x, dim=1)
  34. # 运行需要稍等,这里表示下载并加载数据集
  35. test_loader = torch.utils.data.DataLoader(
  36. datasets.MNIST('../data', train=False, download=True, transform=transforms.Compose([
  37. transforms.ToTensor(),
  38. ])),
  39. batch_size=1, shuffle=True)
  40. # 看看我们有没有配置GPU,没有就是使用cpu
  41. print("CUDA Available: ",torch.cuda.is_available())
  42. device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")
  43. # 初始化网络
  44. model = Net().to(device)
  45. # 加载前面的预训练模型
  46. model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))
  47. # 设置为验证模式.
  48. model.eval()

接着实现FGSM的功能

  1. # FGSM attack code
  2. def fgsm_attack(image, epsilon, data_grad):
  3. # 使用sign(符号)函数,将对x求了偏导的梯度进行符号化
  4. sign_data_grad = data_grad.sign()
  5. # 通过epsilon生成对抗样本
  6. perturbed_image = image + epsilon*sign_data_grad
  7. # 做一个剪裁的工作,将torch.clamp内部大于1的数值变为1,小于0的数值等于0,防止image越界
  8. perturbed_image = torch.clamp(perturbed_image, 0, 1)
  9. # 返回对抗样本
  10. return perturbed_image

对测试集进行预测以及对比测试集标签

  1. def test( model, device, test_loader, epsilon ):
  2. # 准确度计数器
  3. correct = 0
  4. # 对抗样本
  5. adv_examples = []
  6. # 循环所有测试集
  7. for data, target in test_loader:
  8. # 将数据和标签发送到设备
  9. data, target = data.to(device), target.to(device)
  10. # 设置张量的requires_grad属性。重要的攻击
  11. data.requires_grad = True
  12. # 通过模型向前传递数据
  13. output = model(data)
  14. init_pred = output.max(1, keepdim=True)[1] # 得到最大对数概率的索引
  15. # 如果最初的预测是错误的,不要再攻击了,继续下一个目标的对抗训练
  16. if init_pred.item() != target.item():
  17. continue
  18. # 计算损失
  19. loss = F.nll_loss(output, target)
  20. # 使所有现有的梯度归零
  21. model.zero_grad()
  22. # 计算模型的后向梯度
  23. loss.backward()
  24. # 收集datagrad
  25. data_grad = data.grad.data
  26. # 调用FGSM攻击
  27. perturbed_data = fgsm_attack(data, epsilon, data_grad)
  28. # 对受扰动的图像进行重新分类
  29. output = model(perturbed_data)
  30. # 检查是否成功
  31. final_pred = output.max(1, keepdim=True)[1] # 得到最大对数概率的索引
  32. if final_pred.item() == target.item():
  33. correct += 1
  34. # 这里都是为后面的可视化做准备
  35. if (epsilon == 0) and (len(adv_examples) < 5):
  36. adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
  37. adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )
  38. else:
  39. # 这里都是为后面的可视化做准备
  40. if len(adv_examples) < 5:
  41. adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
  42. adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )
  43. # 计算最终精度
  44. final_acc = correct/float(len(test_loader))
  45. print("扰动量: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))
  46. # 返回准确性和对抗性示例
  47. return final_acc, adv_examples

画出随着扰动量变化,准确率变化的曲线。

  1. accuracies = []
  2. examples = []
  3. # 对每个干扰程度进行测试
  4. for eps in epsilons:
  5. acc, ex = test(model, device, test_loader, eps)
  6. accuracies.append(acc*100)
  7. examples.append(ex)
  8. plt.figure(figsize=(5,5))
  9. plt.plot(epsilons, accuracies, "*-")
  10. plt.yticks(np.arange(0, 110, step=10))
  11. plt.xticks(np.arange(0, .5, step=0.05))
  12. def to_percent(temp, position):
  13. return '%1.0f'%(temp) + '%'
  14. plt.gca().yaxis.set_major_formatter(FuncFormatter(to_percent))
  15. plt.title("准确率 vs 扰动量")
  16. plt.xlabel("扰动量")
  17. plt.ylabel("准确率")
  18. plt.show()
  1. 扰动量: 0 Test Accuracy = 9810 / 10000 = 0.981
  2. 扰动量: 0.05 Test Accuracy = 9427 / 10000 = 0.9427
  3. 扰动量: 0.1 Test Accuracy = 8510 / 10000 = 0.851
  4. 扰动量: 0.15 Test Accuracy = 6826 / 10000 = 0.6826
  5. 扰动量: 0.2 Test Accuracy = 4299 / 10000 = 0.4299
  6. 扰动量: 0.25 Test Accuracy = 2084 / 10000 = 0.2084
  7. 扰动量: 0.3 Test Accuracy = 872 / 10000 = 0.0872
  8. 扰动量: 0.35 Test Accuracy = 352 / 10000 = 0.0352
  9. 扰动量: 0.4 Test Accuracy = 167 / 10000 = 0.0167

通过上图我们可以看到随着扰动量的增加,模型预测的准确度越来越低,当增加到0.4的扰动量时,模型预测错误率已经达到了98.33%

以下代码显示通过对抗后,模型预测的结果以及增加扰动量后的图像成象样式:

  1. # 在每个处绘制几个对抗性样本的例子
  2. cnt = 0
  3. plt.figure(figsize=(8,10))
  4. for i in range(len(epsilons)):
  5. for j in range(len(examples[i])):
  6. cnt += 1
  7. plt.subplot(len(epsilons),len(examples[0]),cnt)
  8. plt.xticks([], [])
  9. plt.yticks([], [])
  10. if j == 0:
  11. plt.ylabel("扰动: {}".format(epsilons[i]), fontsize=14)
  12. orig,adv,ex = examples[i][j]
  13. plt.title("{} -> {}".format(orig, adv))
  14. plt.imshow(ex, cmap="gray")
  15. plt.tight_layout()
  16. plt.show()

需要注意的是,A——>B,其中A为实际值,B为预测值。随着模型扰动量的增加,预测值也越来越离谱。与此同时,随着扰动量的增加,我们可以看到模型也变得越来越模糊。由于本实验是对黑白图片数字进行识别,其所具有的特征点(即维度)相对比较少,因此想要使模型预测错误率很高,扰动量也必须加到很大。如果是复杂度较高的图片,只需要增加一点扰动量便可以,使错误率增加的很大。例如,经典熊猫图,由于其图片复杂度较高,因此只需要添加0.007的扰动量,便可以使预测错误率达到99.3%。

fgsm_panda_image


本文转载自: https://blog.csdn.net/qq_36692187/article/details/119809091
版权归原作者 小二的安全指北 所有, 如有侵权,请联系我们删除。

“FGSM对抗样本算法实现”的评论:

还没有评论