pytorch生成对抗示例
pytorch生成對抗示例
本文對ML(機器學習)模型的安全漏洞的認識,并將深入了解對抗性機器學習的熱門話題。圖像添加難以察覺的擾動會導致模型性能大不相同。通過圖像分類器上的示例探討該主題。使用第一種也是最流行的攻擊方法之一,即快速梯度符號攻擊算法(FGSM)來迷惑 MNIST 分類器。
1.威脅模型
對于上下文,有許多類別的對抗性攻擊,每種攻擊具有不同的目標和對攻擊者知識的假設。總體目標是向輸入數據添加最少量的擾動,引起期望的錯誤分類。對攻擊者的知識有幾種假設,其中兩種是:白盒子和黑盒子。白盒攻擊假定攻擊者具有對模型的全部知識和訪問權限,包括體系結構、輸入、輸出和權重。黑盒攻擊,假設攻擊者只能訪問模型的輸入和輸出,并且對底層架構或權重一無所知。還有幾種類型的目標,包括錯誤分類和源/目標錯誤分類。錯誤分類的目標,意味著攻擊者只希望輸出分類錯誤,但不關心新分類是什么。源/目標錯誤分類,意味著攻擊者想要更改最初屬于特定源類的圖像,以便將其歸類為特定目標類。
FGSM 攻擊是一種白盒攻擊,其目標是錯誤分類。有了這些背景信息,現在可以詳細討論攻擊。
2.FGSM(Fast Gradient Sign Attack)
快速梯度標志攻擊(FGSM),是迄今為止最早和最受歡迎的對抗性攻擊之一,由 Goodfellow 等人在[Explaining and Harnessing Adversarial Examples] (https://arxiv.org/abs/1412.6572)中提出,是一種簡單但是有效的對抗樣本生成算法。旨在通過利用模型學習的方式和漸變來攻擊神經網絡。想法很簡單,攻擊調整輸入數據,以基于相同的反向傳播梯度來最大化損失,而不是通過基于反向傳播的梯度,調整權重來最小化損失。 換句話說,攻擊是利用損失函數的梯度,然后調整輸入數據以最大化損失。
在進入代碼之前,先講一下著名的 FGSM 熊貓示例并提取一些符號。
從圖中可以看出,x 是正確分類為“熊貓”的原始輸入圖像, y是 x的基本事實標簽,
代表模型參數,
是用于訓練網絡的損失。攻擊是反向將梯度傳播回輸入數據以計算
。 然后,在一個方向上(即
)調整輸入數據(圖中的
或0.007),這將使損失最大化。當目標網絡仍然明顯是“熊貓”時,由此產生的擾動圖像被錯誤地分類為“長臂猿”。
3.實現
輸入參數,定義被攻擊的模型,然后編寫攻擊代碼并運行一些測試。
3.1 引入相關包
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
3.2 輸入
只有三個輸入,定義如下: * epsilons:用于運行的epsilon值列表。在列表中保留0非常重要,因為表示原始測試集上的模型性能。而且,期望epsilon越大,擾動就越明顯,但就降低模型精度方面而言攻擊越有效。由于此處的數據范圍為[0,1],因此epsilon值不應超過1。 * pretrained_model:pytorch/examples/mnist訓練的預訓練 MNIST 模型的路徑。為簡單起見,下載預訓練模型。 * use_cuda:如果需要使用CUDA的布爾標志。帶有CUDA的GPU并不重要,使用CPU不會花費太多時間。
epsilons = [0, .05, .1, .15, .2, .25, .3]
pretrained_model = “data/lenet_mnist_model.pth”
use_cuda=True
3.2 被攻擊的模型
如上所述,受攻擊的模型與pytorch/examples/mnist中的 MNIST 模型相同。可以訓練并保存自己的 MNIST 模型,也可以下載并使用提供的模型。此處的 Net 定義和測試數據加載器已從 MNIST 示例中復制。目的是定義模型和數據加載器,然后初始化模型并加載預訓練的權重。
定義LeNet模型
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)
#聲明 MNIST 測試數據集何數據加載
test_loader = torch.utils.data.DataLoader(
datasets.MNIST(’…/data’, train=False, download=True, transform=transforms.Compose([
transforms.ToTensor(),
])),
batch_size=1, shuffle=True)
定義正在使用的設備
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’))
在評估模式下設置模型。在這種情況下,這適用于Dropout圖層
model.eval()
? 輸出結果:
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to …/data/MNIST/raw/train-images-idx3-ubyte.gz
Extracting …/data/MNIST/raw/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to …/data/MNIST/raw/train-labels-idx1-ubyte.gz
Extracting …/data/MNIST/raw/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to …/data/MNIST/raw/t10k-images-idx3-ubyte.gz
Extracting …/data/MNIST/raw/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to …/data/MNIST/raw/t10k-labels-idx1-ubyte.gz
Extracting …/data/MNIST/raw/t10k-labels-idx1-ubyte.gz
Processing…
Done!
CUDA Available: True
3.3 FGSM算法攻擊
通過擾亂原始輸入來定義創建對抗性示例的函數。fgsm_attack函數有三個輸入,
是原始的勿擾亂 圖像
,
是像素方式的擾動量
,
是 輸入圖像的損失梯度
。然后該功能將擾動圖像創建為:
最后,為了保持數據的原始范圍,將擾動的圖像剪切到范圍[0,1]。
FGSM算法攻擊代碼
def fgsm_attack(image, epsilon, data_grad):
# 收集數據梯度的元素符號
sign_data_grad = data_grad.sign()
# 通過調整輸入圖像的每個像素來創建擾動圖像
perturbed_image = image + epsilon*sign_data_grad
# 添加剪切以維持[0,1]范圍
perturbed_image = torch.clamp(perturbed_image, 0, 1)
# 返回被擾動的圖像
return perturbed_image
3.4 測試函數
本文核心結果來自測試功能。每次調用此測試函數都會對 MNIST 測試集執行完整的測試步驟,并報告最終的準確性。此函數也需要輸入 。
test函數展示受到強度為
的攻擊下被攻擊模型的準確性。對于測試集中的每個樣本,該函數計算輸入數據
的損失梯度,用fgsm_attack(perturbed_data) 創建擾亂圖像,然后檢查擾動的例子是否是對抗性的。除了測試模型的準確性之外,該函數還保存并返回一些成功的對抗性示例,以便稍后可視化。
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] # get the index of the max log-probability# 如果初始預測是錯誤的,不打斷攻擊,繼續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] # get the index of the max log-probabilityif final_pred.item() == target.item():correct += 1# 保存0 epsilon示例的特例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) )# 計算這個epsilon的最終準確度
final_acc = correct/float(len(test_loader))
print("Epsilon: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))# 返回準確性和對抗性示例
return final_acc, adv_examples
3.5 運行攻擊
實現的最后一部分是實際運行攻擊。為 epsilons 輸入中的每個 epsilon 值運行一個完整的測試步驟。 對于每個epsilon,保存最終的準確性,并在接下來的部分中繪制一些成功的對抗性示例。注意隨著 epsilon 值的增加,打印精度會如何降低。另外,請注意ε= 0 的情況表示原始測試精度,沒有攻擊。
accuracies = []
examples = []
對每個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
4.結果
4.1 準確度 vs Epsilon
第一個結果是精度與 epsilon 圖。如前所述,隨著 epsilon 的增加,期望測試精度降低。這是因為較大的 epsilons 意味著,朝著最大化損失的方向邁出更大的一步。注意,即使 epsilon 值線性分布,曲線中的趨勢也不是線性的。例如,ε= 0.05 時的精度僅比 ε= 0 低 約 4%,但ε= 0.2 時的精度比 ε= 0.15 低 25%。另外,請注意在 ε= 0.25 和 ε= 0.3 之間模型的準確性達到10級分類器的隨機精度。
plt.figure(figsize=(5,5))
plt.plot(epsilons, accuracies, “*-”)
plt.yticks(np.arange(0, 1.1, step=0.1))
plt.xticks(np.arange(0, .35, step=0.05))
plt.title(“Accuracy vs Epsilon”)
plt.xlabel(“Epsilon”)
plt.ylabel(“Accuracy”)
plt.show()
4.2 樣本對抗性示例
正如天底下沒有免費午餐。在這種情況下,隨著 epsilon 增加,測試精度降低,擾動也在變得更容易察覺。實際上,在攻擊者必須考慮權衡,準確度降級和可感知性。展示了每個 epsilon 值的成功對抗性示例的一些例子。圖的每一行顯示不同的 epsilon 值。第一行是 ε= 0 的例子,代表沒有擾動的原始“干凈”圖像。每個圖像的標題顯示“原始分類 - >對抗性分類。”注意,擾動在 ε= 0.15 時開始變得明顯,并且在 ε= 0.3 時非常明顯。然而,在所有情況下,盡管增加了噪音,人類仍然能夠識別正確的類別。
在每個epsilon上繪制幾個對抗樣本的例子
cnt = 0
plt.figure(figsize=(8,10))
for i in range(len(epsilons)):
for j in range(len(examples[i])):
cnt += 1
plt.subplot(len(epsilons),len(examples[0]),cnt)
plt.xticks([], [])
plt.yticks([], [])
if j == 0:
plt.ylabel(“Eps: {}”.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()
5.展望
本文能夠深入了解對抗機器學習。在這里有很多潛在的方向。這次攻擊代表了對抗性攻擊研究的開始,因為后來有很多關于如何從對手攻擊和防御 ML 模型的想法。事實上,在NIPS 2017上有一場對抗性攻擊和防守比賽,文章:[Adversarial Attacks and Defences Competition] (https://arxiv.org/pdf/1804.00097.pdf)描述了競賽中使用的許多方法。防御方面的工作,讓萌發了使機器學習模型,在一般情況下更加健壯的想法,包括自然擾動和對抗性的輸入。
另一個方向,不同領域的對抗性攻擊和防御。對抗性研究不僅限于圖像領域,對語音到文本模型的攻擊。 但也許了解更多關于對抗性機器學習的最好方法就是動手實踐。嘗試從 NIPS 2017競賽中實施不同的攻擊,并了解與 FGSM 的區別。嘗試從自己的攻擊中保護模。
總結
以上是生活随笔為你收集整理的pytorch生成对抗示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PyTorch 进行 Neural-Tr
- 下一篇: 自定义算子高性能开发