1. 程式人生 > 實用技巧 ><快速梯度符號攻擊 (Fast Gradient Sign Attack)>

<快速梯度符號攻擊 (Fast Gradient Sign Attack)>

對抗樣本生成 — PyTorch Tutorials 1.4.0+cpu documentation

Note

Clickhereto download the full example code

對抗樣本生成

Author:Nathan Inkawhich

翻譯者:Antares 博士

如果你正在閱讀這篇文章,希望你能體會到一些機器學習模型是多麼的有效。研究不斷推動 ML 模型變得更快、更準確和更高效。 然而,設計和訓練模型的一個經常被忽視的方面是安全性和健壯性,特別是在面對希望欺騙模型的對手時。

本教程將提高您對 ML 模型的安全漏洞的認識,並將深入瞭解對抗性機器學習的熱門話題。 您可能會驚訝地發現,在影象中新增不可察覺的擾動會導致截然不同的模型效能。 鑑於這是一個教程,我們將通過一個影象分類器的例子來探討這個主題。 具體來說,我們將使用第一種也是最流行的攻擊方法 - 快速梯度符號攻擊 (Fast Gradient Sign Attack ,FGSM) 來欺騙 MNIST 分類器。 威脅模型(Threat Model) ————————-

有很多種類的對抗性攻擊,每種攻擊都有不同的目標和攻擊者的知識假設。但是,總體目標 是在輸入資料中增加最少的擾動量,以導致期望的錯誤分類。攻擊者的知識有幾種假設,其中兩種假設是:白盒子 (white-box)和黑盒子 (black-box)。白盒子攻擊假定攻擊者擁有對模型的全部知識和訪問許可權,包括體系結構、輸入、輸出和權重。黑盒子攻擊假設攻擊者只能訪問模型的輸入和輸出,而對底層架構或權重一無所知。 還有幾種目標型別,包括錯誤分類 (misclassification)和源 / 目標錯誤分類 (source/target misclassification)。錯誤分類的目標意味著對手只希望輸出分類是錯誤的,而不關心新的分類是什麼。源 / 目標錯誤分類

意味著對手希望更改最初屬於特定源類的影象,從而將其歸類為特定的目標類。

在這種情況下,FGSM 攻擊是以錯誤分類為目標的白盒攻擊。 有了這些背景資訊,我們現在可以詳細討論攻擊 (attack) 了。

快速梯度符號攻擊 (Fast Gradient Sign Attack)

迄今為止,第一次也是最流行的對抗性攻擊 (adversarial attacks) 之一被稱為快速梯度符號攻擊 (FGSM), 古德費爾特對此進行了描述:Explaining and Harnessing Adversarial Examples。 攻擊是非常強大的,但卻是直觀的。它是設計用來攻擊神經網路,利用他們的學習方式,梯度

。其思想很簡單, 不是通過調整基於反向傳播梯度的權重來最小化損失,而是基於相同的反向傳播梯度調整輸入資料, 使損失最大化。換句話說,攻擊使用損失 W.r.t 輸入資料的梯度,然後調整輸入資料以最大化損失。

在我們進入程式碼之前,讓我們看一下著名的FGSM熊貓示例,並提取一些記號 (notation)。

從圖片中,xx是被正確分類為 “panda” 的原始影象,yy是xx的真正的類標籤。θθ表示模型引數,並且J(θ,x,y)J(θ,x,y)用來 訓練網路的損失。 攻擊將梯度反向傳播回輸入資料以進行計算xJ(θ,x,y)∇xJ(θ,x,y)。 然後,它沿著使損失最大化的方向 (i.e.sign(xJ(θ,x,y))sign(∇xJ(θ,x,y))) 上 調整輸入資料一小步 (ϵϵ或0.0070.007在圖片中)。 由此產生的擾動影象 (perturbed image),xx′, 就會被目標網路誤分類 (misclassified)為 “gibbon”, 但事實上 被擾動的影象依然是個 “panda” 。

希望現在你已明瞭本教程的動機了,所以讓我們跳到它的具體實現吧。

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

實現

在這一小節中, 我們將討論輸入引數,定義在攻擊之下的模型,然後編寫攻擊程式碼然後將一些測試跑起來。

輸入

本教程只有三個輸入,定義如下:

  • epsilons- 要用於執行的 epsilon 值列表。在列表中保持 0 很重要,因為它代表了原始測試集上的模型效能。而且,從直覺上說, 我們認為 epsilon 越大,擾動越明顯,但攻擊越有效,降低了模型的準確性。由於 資料的範圍是[0,1][0,1],任何 epsilon 值都不應超過 1。
  • pretrained_model- 通向預先訓練過的 MNIST 模型的路徑,該模型是用pytorch/examples/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

受攻擊模型 (Model Under Attack)

如前所述,受攻擊的模型是與pytorch/examples/mnist相同的 MNIST 模型。您可以訓練和儲存自己的 MNIST 模型,也可以下載和使用所提供的模型。 這裡的網路定義和測試 dataloader 是從 MNIST 示例中複製的。本節的目的是定義 model 和 dataloader, 然後初始化模型並載入預先訓練的權重。

# LeNet Model definition
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 dataset 和 dataloader 宣告
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 layers。
model.eval()

FGSM Attack

現在,我們可以通過擾動原始輸入來定義建立對抗性樣例 (adversarial examples) 的函式。fgsm_attack函式接收三個輸入:image是原始的乾淨影象 (xx),epsilon是 逐畫素擾動量 (ϵϵ), 而data_grad是損失相對於 (w.r.t) 輸入影象的梯度: (xJ(θ,x,y)∇xJ(θ,x,y)) 。 有了這三個輸入,該函式就會按下述方法 建立擾動影象 (perturbed image):

perturbed_image=image+epsilonsign(data_grad)=x+ϵsign(xJ(θ,x,y))perturbed_image=image+epsilon∗sign(data_grad)=x+ϵ∗sign(∇xJ(θ,x,y))

最後, 為了保持資料的原始範圍,將擾動影象裁剪到[0,1][0,1]範圍內。

# FGSM 攻擊程式碼
def fgsm_attack(image, epsilon, data_grad):
    # Collect the element-wise sign of the data gradient
    sign_data_grad = data_grad.sign()
    # Create the perturbed image by adjusting each pixel of the input image
    perturbed_image = image + epsilon*sign_data_grad
    # Adding clipping to maintain [0,1] range
    perturbed_image = torch.clamp(perturbed_image, 0, 1)
    # Return the perturbed image
    return perturbed_image

測試函式

最後,本教程的中心結果來自於test函式。每次呼叫該測試函式都會在 MNIST 測試集上執行完整的測試步驟, 並報告最終的準確性。但是,請注意,此函式也接受epsilon輸入。這是因為test函式報告了一個模型的準確性, 該模型正受到來自實力ϵϵ的對手的攻擊。更具體地說,對於測試集中的每個樣本, 該函式計算 loss w.r.t the input data (data_graddata_grad),用fgsm_attack(perturbed_dataperturbed_data) 建立一個受擾動的影象,然後檢查被擾動的樣例是否是對抗性的。除了測試模型的準確性外, 該函式還儲存並返回了一些成功的對抗性樣例,以供以後視覺化。

def test( model, device, test_loader, epsilon ):

    # Accuracy counter
    correct = 0
    adv_examples = []

    # Loop over all examples in test set
    for data, target in test_loader:

        # Send the data and label to the device
        data, target = data.to(device), target.to(device)

        # Set requires_grad attribute of tensor. Important for Attack
        data.requires_grad = True

        # Forward pass the data through the model
        output = model(data)
        init_pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability

        # If the initial prediction is wrong, dont bother attacking, just move on
        if init_pred.item() != target.item():
            continue

        # Calculate the loss
        loss = F.nll_loss(output, target)

        # Zero all existing gradients
        model.zero_grad()

        # Calculate gradients of model in backward pass
        loss.backward()

        # Collect datagrad
        data_grad = data.grad.data

        # Call FGSM Attack
        perturbed_data = fgsm_attack(data, epsilon, data_grad)

        # Re-classify the perturbed image
        output = model(perturbed_data)

        # Check for success
        final_pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability
        if final_pred.item() == target.item():
            correct += 1
            # Special case for saving 0 epsilon examples
            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:
            # Save some adv examples for visualization later
            if len(adv_examples) < 5:
                adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
                adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )

    # Calculate final accuracy for this epsilon
    final_acc = correct/float(len(test_loader))
    print("Epsilon: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))

    # Return the accuracy and an adversarial example
    return final_acc, adv_examples

執行 Attack

實現的最後一部分是實際執行攻擊。在這裡,我們對epsilons輸入中的每個 epsilon 值執行一個完整的測試步驟。 對於每個 epsilon,我們還儲存了最終的準確性和一些成功的對抗性樣例,將在接下來繪製出來。 注意列印精度是如何隨著 epsilon 值的增加而降低的。另外,請注意ϵ=0ϵ=0表示原始測試的準確性,沒有任何攻擊。

accuracies = []
examples = []

# Run test for each epsilon
for eps in epsilons:
    acc, ex = test(model, device, test_loader, eps)
    accuracies.append(acc)
    examples.append(ex)

結果

Accuracy vs Epsilon

第一個結果是 accuracy vs epsilon 的圖。正如前面提到的,隨著 epsilon 的增加,我們預計測試的準確性會下降。 這是因為更大的 epsilon 意味著我們朝著最大化損失的方向邁出了更大的一步。注意,即使 epsilon 值是線性的, 曲線中的趨勢也不是線性的。例如,在ϵ=0.05ϵ=0.05處的準確度僅比ϵ=0.15ϵ=0.15低 4%, 而ϵ=0.2ϵ=0.2的準確度比ϵ=0.15ϵ=0.15低 25%。 另外,注意模型的精度對 10 類分類器的隨機精度影響在ϵ=0.25ϵ=0.25和ϵ=0.3ϵ=0.3之間。

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()

一些對抗性樣本

還記得沒有免費午餐的思想嗎?在這種情況下,隨著 epsilon 的增加,測試精度降低,但擾動變得更容易察覺。 實際上,攻擊者必須考慮的是準確性、程度和可感知性之間的權衡。在這裡,我們展示了在每個 epsilon 值下 一些成功的對抗性樣例。圖中的每一行都顯示不同的 epsilon 值。第一行是ϵ=0ϵ=0示例, 它表示原始的 “乾淨” 影象,沒有任何擾動。每幅影象的標題顯示 “原始分類 -> 對抗性分類”。 注意,當ϵ=0.15ϵ=0.15時,擾動開始變得明顯,在ϵ=0.3ϵ=0.3時非常明顯。 然而,在所有情況下,人類仍然能夠識別正確的類別,儘管增加了噪音。

# Plot several examples of adversarial samples at each 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()

下一步去哪裡?

希望本教程能提供一些關於對抗性機器學習主題的見解。這裡有許多潛在的方向可走。 這種攻擊代表了對抗性攻擊研究的開始,並且由於有許多關於如何攻擊和保護 ML 模型不受對手攻擊的想法。 實際上,在 NIPS 2017 的比賽中,存在著一種對抗性的攻防競爭,本文介紹了在這場比賽中所採用的許多方法:對抗攻擊和防禦競爭。 防禦方面的工作也帶來了使機器學習模型在一般情況下更加健壯的想法, 使機器學習模型既具有自然的擾動性,又具有對抗性的輸入。

另一個方向是不同領域的對抗攻擊和防禦。對抗性研究並不侷限於影象領域,請看這個對語音到文字模型的攻擊。 但是也許瞭解更多對抗性機器學習的最好方法是弄髒你的手 (意思是讓你動手嘗試)。 嘗試實現來自 NIPS 2017 競賽的不同的攻擊策略,看看它與 FGSM 有何不同。然後,試著保護模型不受你自己的攻擊。

Total running time of the script:(0 minutes 0.000 seconds)

DownloadPythonsourcecode:fgsm_tutorial.py DownloadJupyternotebook:fgsm_tutorial.ipynb

Gallery generated by Sphinx-Gallery

全文完

本文由簡悅 SimpRead優化,用以提升閱讀體驗 使用了全新的簡悅詞法分析引擎beta點選檢視詳細說明

對抗樣本生成快速梯度符號攻擊 (Fast Gradient Sign Attack)實現輸入受攻擊模型 (Model Under Attack)FGSM Attack測試函式執行 Attack結果Accuracy vs Epsilon一些對抗性樣本下一步去哪裡?