主要內容
主要講述通過對原始數據加入細微的噪聲得到一組新數據,在新舊兩個數據通過人眼難以辨別的情況下,新數據可以欺騙分類器,從而使分類器做出錯誤的判斷。這種方法可以用來攻擊一些分類器,並且是完全可行的。
教程以手寫數字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