查看原文
其他

一个符号引发的讨论,对抗攻击算法FGSM的纯粹版:FGNS,附代码

孙裕道 我爱计算机视觉 2022-07-23



关注公众号,发现CV技术之美





 1 引言


是基于梯度迭代攻击中生成对抗样本的开创性工作。我第一次接触相关工作的时候,给我困惑最多的就是论文中为什么要给梯度加上这个符号函数,因为这会导致生成的对抗扰动的方向与最速梯度方向有一个锐角偏移。

在斯坦福大学的讲座里对梯度符号中加入符号给出了一个基于实证的解释,即在线性假设下,给定一个数据集的样本,当样本中对抗扰动分量方向与梯度分量的方向相同的个数如果多余某个常数时,该样本沿着对抗扰动的方向即可进入到对抗子区域中。
这个解释是在说明加入符号后的扰动方向依然具有攻击性,但从原理上来说这并不是最好的攻击方向。最近看到了一篇文章,就讨论了该问题,论文作者通过原理分析实验验证,发现当梯度方向加入符号后会使得攻击效率比较低。论文的代码链接失效,我根据论文中的算法流程图重新编写了一下代码。


论文链接:https://arxiv.org/abs/2110.12734


 2 理论分析


给定一个样本,其对应的标签为,损失函数为,其中第步生成的对抗样本为。根据多元函数的泰勒展开公式可以得到如下方程组:


根据以上方程组可以得到如下公式:



,进而则有:


假设,,且。令,则此时的余弦值表示为:


因为



又因为,在向量所有的范数中1范数是最大的,即,此时则有:



进而则有:


令新的对抗扰动为,且此时该扰动的方向与梯度方向一致即:



为了能够使得与的扰动步长范围一致,则有:

进而则有:



根据以上公式则可以推导出:



给定的范围,第步的对抗扰动为:



根据上述推导可以得到具体的算法流程图如下所示:


 3 实验结果


如下图所示,为原始梯度方向和梯度方向的可视化剪头图。从下图发现原始梯度更快更高效收敛到最优点中。然而施加到梯度上会使得它推离最优方向,导致要以更多的迭代次数才能达到最优点。



如下两图所示为不同方法的平均攻击成功率比较。作者使用作为白盒,并计算其它四种黑盒模型(-)的平均攻击成功率,可以发现在各个攻击迭代方法下,应用论文中的方法会取得更好的效果。





 4 程序代码


论文中给出的相关代码链接失效,以下是自己根据算法流程图编写的简化的完整程序。
from torchvision import datasets, transformsfrom torch.utils.data import DataLoader, Datasetimport torchimport torch.nn as nnfrom torch.autograd import Variableimport torch.optim as optimimport torch.nn.functional as Fimport os
class CNN(nn.Module): def __init__(self): super().__init__() self.Sq1 = nn.Sequential( nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=2), # output: (16, 28, 28) nn.ReLU(), nn.MaxPool2d(kernel_size=2), # (16, 14, 14) ) self.Sq2 = nn.Sequential( nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, stride=1, padding=2), # (32, 14, 14) nn.ReLU(), nn.MaxPool2d(2), # (32, 7, 7) ) self.out = nn.Linear(32 * 7 * 7, 10)
def forward(self, x): x = self.Sq1(x) x = self.Sq2(x) x = x.view(x.size(0), -1) output = self.out(x) return output
def FGM_attack(inputs, targets, net, alpha, epsilon, attack_type): delta = torch.zeros_like(inputs) delta.requires_grad = True outputs = net(inputs + delta) loss = nn.CrossEntropyLoss()(outputs, targets) loss.backward() grad = delta.grad.detach() if type == 'FGSN': zeta = (torch.norm(inputs, p=0, dim=(2,3), keepdim=True) / torch.norm(inputs, p=2, dim=(2,3), keepdim=True)) * torch.ones(inputs.shape) delta.data = torch.clamp(delta + alpha * zeta * grad, -epsilon, epsilon) else: delta.data = torch.clamp(delta + alpha * torch.sign(grad), -epsilon, epsilon) delta = delta.detach() return delta
def main(): alpha = 0.2 epsilon = 0.5 total = 0 correct1 = 0 correct2 = 0 model = CNN() model.load_state_dict(torch.load('model/model.pt')) use_cuda = torch.cuda.is_available() mnist_train = datasets.MNIST("mnist-data", train=True, download=True, transform=transforms.ToTensor()) train_loader = torch.utils.data.DataLoader(mnist_train, batch_size= 5, shuffle=True)
for batch_idx, (inputs, targets) in enumerate(train_loader): if use_cuda: inputs, targets = inputs.cuda(), targets.cuda() inputs, targets = Variable(inputs), Variable(targets) total += targets.size(0)
delta1 = FGM_attack(inputs, targets, model, alpha, epsilon, 'FGNM') adv_image1 = torch.clamp(inputs + delta1, 0, 1) outputs1 = model(adv_image1) _, predicted1 = torch.max(outputs1.data, 1) correct1 += predicted1.eq(targets.data).cpu().sum().item() print('The FGNM accuracy:', correct1, total, correct1/total)
delta2 = FGM_attack(inputs, targets, model, alpha, epsilon, 'FGSM') adv_images2 = torch.clamp(inputs + delta1, 0, 1) outputs2 = model(adv_images2) _, predicted2 = torch.max(outputs2.data, 1) correct2 += predicted2.eq(targets.data).cpu().sum().item() print('The FGSM accuracy:', correct2, total, correct2/total) print('The FGNM accuracy:', correct1) print('The FGSM accuracy:', correct2)
if __name__ == '__main__': main()

当给定的攻击步长,则有如下实验结果:



END




欢迎加入「对抗攻击」交流群👇备注:对抗




您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存