FGSM代码实践
淺談FGSM
FGSM是什么?從機器學習到深度學習,第一次聽這個名詞,后來查閱資料了解到是一種圖像的攻擊方法。本來有一個模型可以識別出你的圖片內容,你把一張小狗的圖片喂給模型,模型告訴你是狗,把貓喂給模型,模型告訴你是貓。當你給這張小狗的圖片添加上噪聲之后(肉眼無法識別有沒有加噪聲),再次喂給模型,模型告訴你是這是其他東西,從而達到了欺騙模型的目的。
FGSM原理
一種基于梯度生成對抗樣本的算法,屬于對抗攻擊中的無目標攻擊(即不要求對抗樣本經過model預測指定的類別,只要與原樣本預測的不一樣即可)
? ? ? ? ? ? ?圖1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖二(噪聲)? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖三(對抗樣本)
把圖一喂給模型,模型告訴你57.7%的概率是熊貓,別看概率小,機器總會輸出概率值最大的那個結果。隨后把圖二加入圖一中,生成一張新的圖三,把圖三喂給模型,模型告訴你99.3%是金絲猴,但肉眼看依然是熊貓,這就是FGSM攻擊方法
如果要看具體公式什么的跳轉這個鏈接,這些符號對我很不友好,但代碼能看懂就行https://blog.csdn.net/qq_35414569
FGSM代碼實戰
?1、pytorch導入各種包,預先設置一個擾動值e,再把設備選擇好,有錢GPU,沒錢CPU,此處實現的是無目標攻擊。
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 torchvision import models import cv2 from torch.autograd import Variable e=0.5#擾動值 #獲取計算設備 默認是CPU device = torch.device("cuda" if torch.cuda.is_available() else "cpu")2、圖像的加載以及預處理,喂給模型的圖片還是挺講究的,要考慮圖片通道數(RGB),尺寸,圖片類型,不然模型不吃。
image_path="data/goldfish.jpg" orig = cv2.imread(image_path)[..., ::-1] orig = cv2.resize(orig, (224, 224)) img = orig.copy().astype(np.float32) #使用Imagenet的均值和標準差是一種常見的做法。它們是根據數百萬張圖像計算得出的。如果要在自己的數據集上從頭開始訓練,則可以計算新的均值和標準差,這是一種經驗值 mean = [0.485, 0.456, 0.406] std = [0.229, 0.224, 0.225] #歸一化,為什么進行歸一化,因為訓練效果好 img /= 255.0 img = (img - mean) / std #把HWC的圖片轉為CHW的圖片 img = img.transpose(2, 0, 1)img=np.expand_dims(img, axis=0)img = Variable(torch.from_numpy(img).to(device).float()) print(img.shape)3、定義模型,優化器、損失等等參數
#使用預測模式 主要影響droupout和BN層的行為 model = models.alexnet(pretrained=True).to(device).eval() #取真實標簽 label=np.argmax(model(img).data.cpu().numpy())#這里為什么要加cup()?因為np無法直接轉為cuda使用,要先轉cpu print("label={}".format(label)) # 圖像數據梯度可以獲取 img.requires_grad = True# 設置為不保存梯度值 自然也無法修改 for param in model.parameters():param.requires_grad = Falseoptimizer = torch.optim.Adam([img])#優化器 loss_func = torch.nn.CrossEntropyLoss()#交叉熵計算損失epochs = 10#訓練輪次 target = 31#原始圖片的標簽 target = Variable(torch.Tensor([float(target)]).to(device).long())#轉換數據類型 print(target)4、定義fgsm攻擊函數
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_image5、簡單進行訓練就達到了目的,原本標簽為31,最后預測的結果不為31,收斂于610,loss也收斂,這里是純數據對比,沒有可視化,可視化代碼還在碼
for epoch in range(epochs):# forward + backwardoutput = model(img)loss = loss_func(output, target)label = np.argmax(output.data.cpu().numpy())print("epoch={} loss={} label={}".format(epoch, loss, label))# 梯度清零optimizer.zero_grad()# 反向傳遞 計算梯度loss.backward()img.data = fgsm_attack(img,e,img.data)總結
代碼可直接在pycharm里運行,前提是有pytorch運行環境,如有問題可一起探討交流,哪里有錯誤也請指出,虛心接收,AI小白一枚。
2021年10月9日續可視化代碼
更新一下可視化代碼,可直接運行,報錯找我,此處實現的是定向攻擊,注釋都標的挺清楚了
import matplotlib.pyplot as plt import torch import torchvision from torchvision import datasets, transforms from torch.autograd import Variable import torch.utils.data.dataloader as Data import torch.nn as nn from torchvision import models import numpy as np import cv2 from PIL import Image#對比展現原始圖片和對抗樣本圖片 def show_images_diff(original_img,original_label,adversarial_img,adversarial_label):import matplotlib.pyplot as pltplt.figure()#歸一化if original_img.any() > 1.0:original_img=original_img/255.0if adversarial_img.any() > 1.0:adversarial_img=adversarial_img/255.0plt.subplot(131)plt.title('Original')plt.imshow(original_img)plt.axis('off')plt.subplot(132)plt.title('Adversarial')plt.imshow(adversarial_img)plt.axis('off')plt.subplot(133)plt.title('Adversarial-Original')difference = adversarial_img - original_img#(-1,1) -> (0,1)difference=difference / abs(difference).max()/2.0+0.5plt.imshow(difference,cmap=plt.cm.gray)plt.axis('off')plt.tight_layout()plt.show() #獲取計算設備 默認是CPU device = torch.device("cuda" if torch.cuda.is_available() else "cpu")#圖像加載以及預處理 image_path="data/goldfish.jpg" orig = cv2.imread(image_path)[..., ::-1] orig = cv2.resize(orig, (224, 224)) img = orig.copy().astype(np.float32)mean = [0.485, 0.456, 0.406] std = [0.229, 0.224, 0.225] img /= 255.0 img = (img - mean) / std img = img.transpose(2, 0, 1)img=np.expand_dims(img, axis=0)img = Variable(torch.from_numpy(img).to(device).float()) print(img.shape)#使用預測模式 主要影響droupout和BN層的行為 model = models.alexnet(pretrained=True).to(device).eval() #取真實標簽 label=np.argmax(model(img).data.cpu().numpy())#這里為什么要加cup()?因為np無法直接轉為cuda使用,要先轉cpu print("label={}".format(label))# 圖像數據梯度可以獲取 img.requires_grad = True# 設置為不保存梯度值 自然也無法修改 for param in model.parameters():param.requires_grad = Falseoptimizer = torch.optim.Adam([img])#優化器 loss_func = torch.nn.CrossEntropyLoss()#交叉熵計算損失epochs = 1#訓練輪次,只需一次,多了就變成I-FGSM了 e = 0.001#擾動值target = 254#此處是一個定向攻擊 target = Variable(torch.Tensor([float(target)]).to(device).long())#轉換數據類型for epoch in range(epochs):# forward + backwardoutput = model(img)loss = loss_func(output, target)label = np.argmax(output.data.cpu().numpy())print("epoch={} loss={} label={}".format(epoch, loss, label))# 如果定向攻擊成功if label == target:print("成功")break# 梯度清零optimizer.zero_grad()# 反向傳遞 計算梯度loss.backward()img.data = img.data - e * torch.sign(img.grad.data)#FGSM最重要的公式 print(model(img).argmax()) adv=img.data.cpu().numpy()[0] adv = adv.transpose(1, 2, 0) adv = (adv * std) + mean adv = adv * 255.0 adv = np.clip(adv, 0, 255).astype(np.uint8)#np數據類型是0-255,轉PIL之后進行存儲 #對抗樣本的保存 # print(adv.shape) # img = Image.fromarray(adv) # img.show() # plt.imshow(img) # plt.show() # img.save('one.jpg')#圖片存儲為什么不用指定路徑,存儲結果在遠程服務器 show_images_diff(orig,388,adv,target.data.cpu().numpy()[0])運行結果如下:
?原始標簽為31,定向攻擊目標為254
?這里其實我自己有個問題沒有搞定,生成的對抗樣本保存時為什么不用指定路徑,我無法直接保存到pycharm中,我是保存在遠程服務器上,但我從服務器上并不能打開這張圖片,是黑的,等于保存失敗......
總結
- 上一篇: TMDB电影数据分析报告
- 下一篇: Java开发利器Eclipse和IDEA