# 利用FGSM实现对抗样本攻击

## 对抗样本的线性解释

x ~ = x + η \tilde{\bm{x}}=\bm{x}+\bm{\eta}

w T x ~ = w T x + w T η \bm{w}^{T}\tilde{\bm{x}}=\bm{w}^{T}\bm{x}+\bm{w}^{T}\bm{\eta} 扰动 η \bm{\eta} 被增长激活通过 w T η \bm{w}^{T}\bm{\eta} 。我们最大化增长激活通过 η = s i g n ( w ) \bm{\eta}=sign(\bm{w}) 。因为加减1以内的数都是无法被感知的，所以采用 s i g n sign 函数是最大化的扰动值。这里假设 w \bm{w} 具有 n n 维，权重向量的平均数量级为 m m ，那么通过点乘之后激活被增加到 ϵ m n \epsilon m n .虽然 ϵ \epsilon 作为一个常数是不变的，但是维度 n n 会随着线性增长伴随着高维空间，此时对于输入的无穷小改变则会引起输出较大的改变。

## 对于非线性模型的线性扰动

class Attack(object):
def __init__(self, net):
self.net = net
self.criterion = F.cross_entropy

def fgsm(self, x, y, eps=0.03, x_val_min=-1, x_val_max=1):
cost = -self.criterion(logits, y)
cost.backward()



## 使用foolbox实现对pre-trained模型攻击

import foolbox
import torch
import torchvision.models as models
import numpy as np
import cv2
import os

# instantiate the model
resnet18 = models.resnet18(pretrained=True).eval()
if torch.cuda.is_available():
resnet18 = resnet18.cuda()
mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1))
std = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1))
fmodel = foolbox.models.PyTorchModel(
resnet18, bounds=(0, 1), num_classes=1000, preprocessing=(mean, std))

# get source image and label
image_path = 'val.JPEG'
image = cv2.resize(image, (224,) * 2)[..., ::-1].transpose((2, 0, 1)).astype(np.float32)
image = image / 255.  # because our model expects values in [0, 1]
image = np.expand_dims(image, axis=0)
label = np.array([0])

print('True label', label)
print('predicted class', np.argmax(fmodel.forward(image), axis=1))

# apply attack on source image
attack = foolbox.attacks.FGSM(fmodel)
# 如果攻击失败，返回全是nan的张量
adversarial = attack(image, label, epsilons=[0.1], max_epsilon=0)



True label 0
predicted class 0


call(self, input_or_adv, label=None, unpack=True, epsilons=1000, max_epsilon=1)
Parameters:

• label:int The reference label of the original input. Must be passed if a is a numpy.ndarray, must not be passed if a is an Adversarial instance.
• unpack:bool If true, returns the adversarial input, otherwise returns the Adversarial object.
• epsilons:int or Iterable[float] Either Iterable of step sizes in the direction of the sign of the gradient or number of step sizes between 0 and max_epsilon that should be tried.
• max_epsilon:float Largest step size if epsilons is not an iterable.

epsilons= np.linspace(0, max_epsilon, num=epsilons + 1)[1:]


epsilons=[0.1, 0.2, 0.3]


FGSM会从小的epsilon开始进行攻击，直至找到可以攻击成功的epsilon值。若攻击失败，则会返回全是nan的数组。若仅想要使用一个epsilon值来攻击，则可以设置epsilons=[0.1]max_epsilon=0，即：

adversarial = attack(image, label, epsilons=[0.001], max_epsilon=0)


### 遍历整个数据集得到攻击结果

import foolbox
import torch
import torchvision.models as models
import numpy as np
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import os

os.environ["CUDA_VISIBLE_DEVICES"] = '1,2'
# instantiate the model
model = models.resnet101(pretrained=True).cuda().eval()
model = torch.nn.DataParallel(model)
mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1))
std = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1))
fmodel = foolbox.models.PyTorchModel(
model, bounds=(0, 1), num_classes=1000, preprocessing=(mean, std))

# get source image and label

val_dir = '/home/ws/winycg/imagenet/ILSVRC2012_img_val/'
val_dataset = datasets.ImageFolder(val_dir, transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
]))
num_workers=16, pin_memory=(torch.cuda.is_available()))

# apply attack on source image
attack = foolbox.attacks.FGSM(fmodel)
# 记录未成功攻击的样本数
correct_num = 0
for i in range(output.size(0)):
if torch.isnan(output[i])[0, 0, 0]:
correct_num += 1
return correct_num

def acc_num(output, target, topk=(1,)):
"""Computes the precision@k for the specified values of k"""
maxk = max(topk)
_, pred = output.topk(maxk, 1, True, True)
pred = pred.t()
correct = pred.eq(target.view(1, -1).expand_as(pred))

number = []
for k in topk:
correct_k = correct[:k].view(-1).float().sum(0).item()
number.append(correct_k)
return number

total = 0
ori_top1_correct = 0

for batch_idx, (inputs, targets) in enumerate(val_loader):
inputs, targets = inputs, targets

ori_output = fmodel.forward(inputs.numpy())
ori_top1_correct += acc_num(torch.from_numpy(ori_output), targets, (1,))[0]

adversarial_input = attack(inputs.numpy(), targets.numpy(), epsilons=[0.3], max_epsilon=0)

total += targets.size(0)
ori_top1_acc = ori_top1_correct / total

print('original top1 accuracy:', ori_top1_acc)


### 可视化加噪声后的样本以及sign图

import matplotlib.pyplot as plt
import cv2
import numpy as np

plt.subplot(1, 3, 1)
plt.title('Original')
image_path = 'val.JPEG'
image = cv2.resize(image, (224,) * 2)[..., ::-1] / 255
plt.imshow(image)  # division by 255 to convert [0, 255] to [0, 1]
plt.axis('off')

plt.subplot(1, 3, 2)
plt.axis('off')

plt.subplot(1, 3, 3)
plt.title('Difference')
plt.imshow((sign- sign.min())/ (sign.max()-sign.min()))
plt.axis('off')

plt.show()


## 对抗性训练

J ~ ( θ , x , y ) = α J ( θ , x , y ) + ( 1 − α ) J ( θ , x + ϵ s i g n ( ∇ x J ( θ , x , y ) ) , y ) \tilde{J}(\bm{\theta},\bm{x},y)=\alpha J(\bm{\theta},\bm{x},y)+(1-\alpha)J(\bm{\theta},\bm{x}+\epsilon sign(\nabla_{\bm{x}}J(\bm{\theta},\bm{x},y)),y)

05-31 7194

07-02 2659
01-16 463
05-10 560
07-09 7万+
06-01
03-12 886