主要内容
主要讲述通过对原始数据加入细微的噪声得到一组新数据,在新旧两个数据通过人眼难以辨别的情况下,新数据可以欺骗分类器,从而使分类器做出错误的判断。这种方法可以用来攻击一些分类器,并且是完全可行的。
教程以手写数字MINST数据集为例,展示了这种方法。
首先加载包:
from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import numpy as np
import matplotlib.pyplot as plt
加载数据集:
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, download=True, transform=transforms.Compose([
transforms.ToTensor(),
])),
batch_size=1, shuffle=True)
定义一个网络模型(分类器):
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x, dim=1)
定义其他的参数
device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")#是否CUDA
epsilons = [0, .05, .1, .15, .2, .25, .3]#
pretrained_model = "data/lenet_mnist_model.pth"#模型的路径
实例化模型
model = Net()#实例化
model=model.to(device)#放入GPU
model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))#将之前训练好的参数加载
model.eval()#转化为测试模式
定义FGSM攻击函数
def fgsm_attack(image, epsilon, data_grad):
#获取梯度的符号
sign_data_grad = data_grad.sign()
#在原始图片的基础上加上epsilon成符号
perturbed_image = image + epsilon*sign_data_grad
#将数值裁剪到0-1的范围内
perturbed_image = torch.clamp(perturbed_image, 0, 1)
return perturbed_image
sign()函数返回数值的符号,如果为正数返回1,如果为负数返回-1.否则返回0.
测试函数:
def test( model, device, test_loader, epsilon ):
correct = 0#攻击之后预测依然正确的个数
adv_examples = []#受攻击的样本
for data, target in test_loader:
data, target = data.to(device), target.to(device)#将数据加载进GPU
data.requires_grad = True#需要保存梯度
output = model(data)#利用分类器区分结果
init_pred = output.max(1, keepdim=True)[1] #获取最大概率的索引值
if init_pred.item() != target.item():#如果分类错误,则不攻击该样本
continue
loss = F.nll_loss(output, target)#计算损失值
model.zero_grad()
loss.backward()
# 抽取梯度值
data_grad = data.grad.data
#攻击样本
perturbed_data = fgsm_attack(data, epsilon, data_grad)
# 再次分类
output = model(perturbed_data)
final_pred = output.max(1, keepdim=True)[1] # 得到最终分类结果
if final_pred.item() == target.item():#如果最终分类结果和真实结果相同
correct += 1
# 保存epsilon为0
if (epsilon == 0) and (len(adv_examples) < 5):
adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )
else:
# 保存其他的一些结果,用于后期的可视化操作
if len(adv_examples) < 5:
adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )
#计算准确率
final_acc = correct/float(len(test_loader))
print("Epsilon: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))
#返回准确率和攻击样例
return final_acc, adv_examples
如对于以上代码中 final_pred = output.max(1, keepdim=True)[1]有疑问,请移步至
输出准确率
#依次迭代epsilon
for eps in epsilons:
acc, ex = test(model, device, test_loader, eps)
accuracies.append(acc)
examples.append(ex)
Epsilon: 0 Test Accuracy = 9810 / 10000 = 0.981
Epsilon: 0.05 Test Accuracy = 9426 / 10000 = 0.9426
Epsilon: 0.1 Test Accuracy = 8510 / 10000 = 0.851
Epsilon: 0.15 Test Accuracy = 6826 / 10000 = 0.6826
Epsilon: 0.2 Test Accuracy = 4301 / 10000 = 0.4301
Epsilon: 0.25 Test Accuracy = 2082 / 10000 = 0.2082
Epsilon: 0.3 Test Accuracy = 869 / 10000 = 0.0869
可以发现epsilon越大,则准确度越低,且剧烈下降。
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,转载请附上原文出处链接和本声明。
本文链接地址:https://www.flyai.com/article/artc2d5aeff7778460c86bb7687