FGSM对抗样本算法实现
????????最著名的對抗樣本算法應該就是?Fast Gradient Sign Attack(FGSM)快速梯度算法,其原理是,在白盒環境下,通過求出模型對輸入數據的導數,用函數求得其梯度方向,再乘以步長,得到的就是其擾動量,將這個擾動量加在原來的輸入上,就得到了在FGSM攻擊下的樣本,這個樣本很大概率上可以使模型分類錯誤,這樣就達到了攻擊的目的。
? ? ? ? 我們令原始的輸入為x,輸出為y。則FGSM的攻擊表達式為:
? ? ? ? (1)
? ? ? ? 由公式1可知,FGSM實質上就是一種梯度上升算法,通過細微地改變輸入,到達輸出的預測值與實際值相差很大的目的。
????????假設x的維度為n,模型參數在每個維度的平均值為m,的無窮范數為,每個維度的微小修改與梯度函數方向一致(個人覺得可以理解為梯度上升),則累積的改變量就是。例如,一張[16,16,3]的圖片,則維度為768,通常很小,我們取0.01,m取1,那么累計的擾動就是7.68。
? ? ? ??關于FGSM的實現主要參考對抗樣本pytorch官網加上自己的理解,安裝好pytorch后,下載好預訓練的模型,提取碼:dcr6,可直接運行。:
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 from matplotlib.ticker import FuncFormatter plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False# 這里的擾動量先設定為幾個值,后面可視化展示不同的擾動量影響以及成像效果 epsilons = [0, .05, .1, .15, .2, .25, .3,.35,.4] # 這個預訓練的模型需要提前下載,下載鏈接如上 pretrained_model = "data/lenet_mnist_model.pth" use_cuda=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)# 運行需要稍等,這里表示下載并加載數據集 test_loader = torch.utils.data.DataLoader(datasets.MNIST('../data', train=False, download=True, transform=transforms.Compose([transforms.ToTensor(),])),batch_size=1, shuffle=True)# 看看我們有沒有配置GPU,沒有就是使用cpu print("CUDA Available: ",torch.cuda.is_available()) device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")# 初始化網絡 model = Net().to(device)# 加載前面的預訓練模型 model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))# 設置為驗證模式. model.eval()接著實現FGSM的功能
# FGSM attack code def fgsm_attack(image, epsilon, data_grad):# 使用sign(符號)函數,將對x求了偏導的梯度進行符號化sign_data_grad = data_grad.sign()# 通過epsilon生成對抗樣本perturbed_image = image + epsilon*sign_data_grad# 做一個剪裁的工作,將torch.clamp內部大于1的數值變為1,小于0的數值等于0,防止image越界perturbed_image = torch.clamp(perturbed_image, 0, 1)# 返回對抗樣本return perturbed_image對測試集進行預測以及對比測試集標簽
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)# 設置張量的requires_grad屬性。重要的攻擊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()# 收集datagraddata_grad = data.grad.data# 調用FGSM攻擊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# 這里都是為后面的可視化做準備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("擾動量: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))# 返回準確性和對抗性示例return final_acc, adv_examples畫出隨著擾動量變化,準確率變化的曲線。
accuracies = [] examples = []# 對每個干擾程度進行測試 for eps in epsilons:acc, ex = test(model, device, test_loader, eps)accuracies.append(acc*100)examples.append(ex)plt.figure(figsize=(5,5)) plt.plot(epsilons, accuracies, "*-") plt.yticks(np.arange(0, 110, step=10)) plt.xticks(np.arange(0, .5, step=0.05)) def to_percent(temp, position):return '%1.0f'%(temp) + '%' plt.gca().yaxis.set_major_formatter(FuncFormatter(to_percent)) plt.title("準確率 vs 擾動量") plt.xlabel("擾動量") plt.ylabel("準確率") plt.show()擾動量: 0 Test Accuracy = 9810 / 10000 = 0.981
擾動量: 0.05 Test Accuracy = 9427 / 10000 = 0.9427
擾動量: 0.1 Test Accuracy = 8510 / 10000 = 0.851
擾動量: 0.15 Test Accuracy = 6826 / 10000 = 0.6826
擾動量: 0.2 Test Accuracy = 4299 / 10000 = 0.4299
擾動量: 0.25 Test Accuracy = 2084 / 10000 = 0.2084
擾動量: 0.3 Test Accuracy = 872 / 10000 = 0.0872
擾動量: 0.35 Test Accuracy = 352 / 10000 = 0.0352
擾動量: 0.4 Test Accuracy = 167 / 10000 = 0.0167
通過上圖我們可以看到隨著擾動量的增加,模型預測的準確度越來越低,當增加到0.4的擾動量時,模型預測錯誤率已經達到了98.33%
以下代碼顯示通過對抗后,模型預測的結果以及增加擾動量后的圖像成象樣式:
# 在每個處繪制幾個對抗性樣本的例子 cnt = 0 plt.figure(figsize=(8,10)) for i in range(len(epsilons)):for j in range(len(examples[i])):cnt += 1plt.subplot(len(epsilons),len(examples[0]),cnt)plt.xticks([], [])plt.yticks([], [])if j == 0:plt.ylabel("擾動: {}".format(epsilons[i]), fontsize=14)orig,adv,ex = examples[i][j]plt.title("{} -> {}".format(orig, adv))plt.imshow(ex, cmap="gray") plt.tight_layout() plt.show()?
需要注意的是,A——>B,其中A為實際值,B為預測值。隨著模型擾動量的增加,預測值也越來越離譜。與此同時,隨著擾動量的增加,我們可以看到模型也變得越來越模糊。由于本實驗是對黑白圖片數字進行識別,其所具有的特征點(即維度)相對比較少,因此想要使模型預測錯誤率很高,擾動量也必須加到很大。如果是復雜度較高的圖片,只需要增加一點擾動量便可以,使錯誤率增加的很大。例如,經典熊貓圖,由于其圖片復雜度較高,因此只需要添加0.007的擾動量,便可以使預測錯誤率達到99.3%。
總結
以上是生活随笔為你收集整理的FGSM对抗样本算法实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 51单片机循迹小车工作原理与程序设计思路
- 下一篇: 小波分解与小波包分解的区别