この記事はブロガーが人工知能等の分野を勉強する際に、個人的な勉強・研究・鑑賞用に記録した勉強の抜粋・メモであり、ブロガーの人工知能等の分野に対する理解に基づいたものです。権利侵害につきましては、ご指摘がございましたら速やかに修正させていただきますので、ご理解賜りますようお願い申し上げます。Pytorchでの記事の分類:
Pytorch (3) --- 「 FGSM 敵対的攻撃アルゴリズムの実装」
FGSM敵対的攻撃アルゴリズムの実装
目次
1. 実験目的
高速勾配シンボル攻撃 (FGSM) の使用法をマスターして、前の実験で得た深層学習畳み込みニューラル ネットワーク CNN 手書き数字認識モデルに対して敵対的攻撃を実行し、MNIST 分類器を欺きます。
2. 実験内容
Fast Gradient Sign Attack (FGSM) は、Fast Gradient Sign Attack と呼ばれ、ニューラル ネットワークの学習方法である勾配更新を直接使用してニューラル ネットワークを攻撃します。この攻撃は、損失を最大化するために同じ逆伝播勾配に従って入力データを調整します。つまり、攻撃では、入力データに関連する勾配損失法を使用して、入力データを調整することで損失を最大化します。
参考文献: https://arxiv.org/abs/1412.6572
3. 原理の紹介
コードに入る前に、有名な FGSM pandas の例を見て、いくつかの表記を抜粋してみましょう。
上の図は、ディープ ネットワークに適用された高速敵対的な例を示しています。人間には知覚できない小さなベクトルを追加することにより、そのベクトルの値に、誤差に対する入力ピクセルの勾配値の符号値が ϵ 乗算されます。ここで ϵ=0.007 は、ディープ ネットワークによって実数に変換された後の 8 ビット画像エンコードの最小桁数と一致します。画像にノイズを追加した後、パンダはテナガザルとして識別され、信頼度は 99.3% であるため、この方法は高速勾配符号法 (略して FGSM) と呼ばれ、高速勾配降下法と呼ばれます。結果として生じる摂動画像 x' は、明らかに「パンダ」であるにもかかわらず、ターゲット ネットワークによって誤って「テナガザル」として分類されます。
次に、FGSM アルゴリズムの原理とコードによる敵対的攻撃の実装プロセスについて説明します。
4. コード分析
コードを実行する前に、実行プロセス中に使用される可能性のあるツールキットをインポートする必要があります。
# -*- coding: utf-8 -*-
import torch.nn as nn
import torch.nn.functional as F
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
use_cuda = True
device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")
モデル定義とテストデータのロード
まず、ネットワーク構造と入出力を含む分類モデルを定義する必要があります。ここでのネット定義とテスト データの読み込みは、前のセクションの 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() # dropout层
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) # dropout层
x = self.fc2(x) # 全连接层
return F.log_softmax(x, dim=1) # 输出
# MNIST 测试数据集和数据加载器声明
transform = transforms.Compose([transforms.ToTensor()]) #数据类型定义为Tensor张量
test_dataset = datasets.MNIST(root='./data/', train=False, download=False, transform=transform) # train=True训练集,=False测试集
test_loader = DataLoader(test_dataset, batch_size=1, shuffle=True) # 批大小为1,打乱数据
# 自动选择 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)
# 加载预训练模型,可以是自己训练好的模型,也可用本地提供的模型lenet_mnist_model.pth
# 添加加载预训练模型的相对路径(路径的前缀可用"./"代替)
pretrained_model = "./lenet_mnist_model.pth"
model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))
# 将模型设置为评估模式。在本例中,这是针对 Dropout layers 的
model.eval()
FGSM攻撃の実装
これで、次の方法で敵対的な例を作成する関数を定義できます。
fgsm_attach 関数には 3 つの入力が必要です。image は元のクリーンなイメージ (x)、epsilon はピクセルレベルの摂動 (ϵ)、data_grad は入力イメージの損失の勾配です。データの元の範囲を維持するために、摂動イメージは [0, 1] の範囲にトリミングされます。最後に、異なる外乱サイズの下での攻撃効果を調査するために、外乱の量に異なる値が設定されます。
ここで学生は、敵対的なサンプルを生成するためのコード行を追加する必要があります。
# FGSM attack code
def fgsm_attack(image, epsilon, data_grad): # 此函数的功能是进行fgsm攻击,需要输入三个变量,干净的图片,扰动量和输入图片梯度
sign_data_grad = data_grad.sign() # 梯度符号
# print(sign_data_grad)
#对抗样本perturbed_image由原始图像叠加epsilon像素级扰动量epsilon与梯度符号sign_data_grad的乘积组成
perturbed_image = image + epsilon*sign_data_grad
perturbed_image = torch.clamp(perturbed_image, 0, 1) # 为了保持图像的原始范围,将受干扰的图像裁剪到一定的范围【0,1】
return perturbed_image
epsilons = [0, .05, .1, .15, .2, .25, .3]
テスト攻撃効果
最後に、この実験の核となる結果はテスト モジュールから得られます。このテスト関数を呼び出すたびに、MNIST テスト セットに対して完全なテスト ステップが実行され、最終的な精度が報告されます。ただし、この関数はイプシロン入力も受け取ることに注意してください。これは、「テスト」関数が強力な敵対者 ϵ からの攻撃を受けているモデルの精度を報告するためです。より具体的には、テスト セット内のサンプルごとに、関数は入力データ (data_grad) の損失を計算し、「fgsm_攻撃」による摂動画像 (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)
data.requires_grad = True
#添加代码表示模型(model)的输出output,已知模型(model)的输入为data
# 此处的model已在前面定义过,此处直接调用即可
output = model(data)
init_pred = output.max(1, keepdim=True)[1] # 选取最大的类别概率
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
if len(adv_examples) < 6:
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
accuracies = []
examples = []
攻撃を実行する
実装の最後の部分は、実際に攻撃を実行することです。ここでは、イプシロン入力の ϵ の各値に対して完全なテスト ステップを実行します。各 ε について、最終的な精度といくつかの成功した結果も保存します。次のコンテンツでは、いくつかの敵対的な例を列挙します。ε の値が増加すると、モデルの精度が低下することに注意してください。また、ϵ=0 は元のテスト精度を表す、つまり攻撃が実行されないことにも注意してください。
# Run test for each epsilon
for eps in epsilons:
acc, ex = test(model, device, test_loader, eps)
accuracies.append(acc)
examples.append(ex)
結果分析
比較分析のために精度と ϵ を組み合わせます。最初の結果は、精度と ϵ の関係です。前述したように、実験の初期段階で ϵ が増加すると、テスト精度が低下すると予想されます。これは、ϵ が大きいほど、元の画像が勾配方向の損失を最大化することを意味するためです。実験結果も私たちの推測を裏付けました。
plt.figure(figsize=(5,5))
plt.plot(epsilons, accuracies, "*-")
plt.title("Accuracy vs Epsilon")
plt.xlabel("Epsilon")
plt.ylabel("Accuracy")
plt.plot(epsilons, accuracies)
敵対的な例を表示する
実験の最後に、さまざまな摂動値イプシロンの下で敵対的な例を視覚化できます。各敵対サンプルの上には、サンプルの元のラベルと攻撃後の分類ラベルが表示されており、図から、摂動値が 0.15 の場合、モデルに分類誤差があることが容易にわかり、攻撃が有効であることがわかります。 . 妨害値は増加し続け、攻撃成功率が高いほど攻撃効果が向上します。
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),color=("green" if orig==adv else "red"))
plt.imshow(ex, cmap="gray")
plt.tight_layout()
plt.show()
5. 運用結果
記事内に不適切や不正確な点がございましたら、ご理解の上ご指摘いただければ幸いです。一部の文章、画像等はインターネット上から取得したものであるため、出典が確認できませんので、紛争等がございましたらブロガーに連絡の上、削除していただきますようお願いいたします。間違い、質問、権利侵害がある場合は、コメントを残して著者に連絡するか、VX 公開アカウントRain21321をフォローして著者に連絡してください。