(一)作业描述
使用FGSM算法得到攻击图像。FGSM算法地址
此次data文件夹中包含一个子文件夹和两个csv表格。
文件夹/images中包含有200张图片。
(二)作业实现
本次作业直接采用示例代码即可,不需要进行任何改动就可以直接跑通。
import os
import pandas as pd
from PIL import Image
import numpy as np
import torch
import torch.nn.functional as F
import torchvision.datasets as datasets
from torch.utils.data import Dataset, DataLoader
import torchvision.models as models
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
device = torch.device("cuda")
class Adverdataset(Dataset):
def __init__(self, root, label, transforms):
# 图片所在的文件夹
self.root = root
# 传入的label
self.label = torch.from_numpy(label).long()
# 由attacker传入的transform将输入的图片转换成符合预训练模型的形式
self.transforms = transforms
# 图片名称的list
self.fnames = []
for i in range(200):
self.fnames.append("{:03d}".format(i))
def __getitem__(self, idx):
# 利用路径读取图片
img = Image.open(os.path.join(self.root, self.fnames[idx] + '.png'))
# 将输入的图片转换成符合预测训练模型的形式
img = self.transforms(img)
# 图片相对应的label
label = self.label[idx]
return img, label
def __len__(self):
# 已知data中共有200张图片
return 200
class Attacker:
def __init__(self, img_dir, label):
# 载入预训练模型vgg16
self.model = models.vgg16(pretrained=True)
self.model.cuda()
self.model.eval()
self.mean = [0.485, 0.456, 0.406]
self.std = [0.229, 0.224, 0.225]
# 归一化,[0,1],平均值0,方差1
self.normalize = transforms.Normalize(self.mean, self.std, inplace=False)
transform = transforms.Compose([
transforms.Resize((224, 224), interpolation=3),
transforms.ToTensor(),
self.normalize
])
# 利用 Adverdataset
self.dataset = Adverdataset('./data/images', label, transform)
self.loader = torch.utils.data.DataLoader(
self.dataset,
batch_size=1,
shuffle=False)
# FGSM 攻击
def fgsm_attack(self, image, epsilon, data_grad):
# 找出 gradient 的方向
sign_data_grad = data_grad.sign()
perturbed_image = image + epsilon * sign_data_grad
return perturbed_image
def attack(self, epsilon):
# 存下一些成功攻击后的图片
adv_examples = []
wrong, fail, success = 0, 0, 0
for (data, target) in self.loader:
data, target = data.to(device), target.to(device)
data_raw = data;
data.requires_grad = True
# 将图片丢入model进行测试
output = self.model(data)
init_pred = output.max(1, keepdim=True)[1]
if init_pred.item() != target.item():
wrong += 1
continue
loss = F.nll_loss(output, target)
self.model.zero_grad()
loss.backward()
data_grad = data.grad.data
perturbed_data = self.fgsm_attack(data, epsilon, data_grad)
output = self.model(perturbed_data)
final_pred = output.max(1, keepdim=True)[1]
if final_pred.item() == target.item():
fail += 1
else:
success += 1
# 将攻击成功的图片进行保存
if len(adv_examples) < 5:
adv_ex = perturbed_data * torch.tensor(self.std, device=device).view(3, 1, 1) + torch.tensor(
self.mean, device=device).view(3, 1, 1)
adv_ex = adv_ex.squeeze().detach().cpu().numpy()
data_raw = data_raw * torch.tensor(self.std, device=device).view(3, 1, 1) + torch.tensor(self.mean,
device=device).view(
3, 1, 1)
data_raw = data_raw.squeeze().detach().cpu().numpy()
adv_examples.append((init_pred.item(), final_pred.item(), data_raw, adv_ex))
final_acc = (fail / (wrong + success + fail))
print("Epsilon: {}\tTest Accuracy = {} / {} = {}\n".format(epsilon, fail, len(self.loader), final_acc))
return adv_examples, final_acc
if __name__ == '__main__':
# 读入图片对应的label
df = pd.read_csv("./data/labels.csv")
df = df.loc[:, 'TrueLabel'].to_numpy()
label_name = pd.read_csv("./data/categories.csv")
label_name = label_name.loc[:, 'CategoryName'].to_numpy()
attacker = Attacker('./data/images', df)
epsilons = [0.1, 0.01]
accuracies, examples = [], []
# 进行攻击
for eps in epsilons:
ex, acc = attacker.attack(eps)
accuracies.append(acc)
examples.append(ex)
cnt = 0
plt.figure(figsize=(30, 30))
for i in range(len(epsilons)):
for j in range(len(examples[i])):
cnt += 1
plt.subplot(len(epsilons), len(examples[0]) * 2, cnt)
plt.xticks([], [], fontsize=5)
plt.yticks([], [], fontsize=5)
if j == 0:
plt.ylabel("Eps: {}".format(epsilons[i]), fontsize=10)
orig, adv, orig_img, ex = examples[i][j]
# plt.title("{} -> {}".format(orig, adv))
plt.title("original: {}".format(label_name[orig].split(',')[0]), fontsize=8)
orig_img = np.transpose(orig_img, (1, 2, 0))
plt.imshow(orig_img)
cnt += 1
plt.subplot(len(epsilons), len(examples[0]) * 2, cnt)
plt.title("adversarial: {}".format(label_name[adv].split(',')[0]), fontsize=8)
ex = np.transpose(ex, (1, 2, 0))
plt.imshow(ex)
plt.tight_layout()
plt.show()
实现结果: