Press "Enter" to skip to content

ICML 2022 基于共轭梯度法的多样化对抗性攻击

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

本篇分享 ICML 2022 论文 『Diversified Adversarial Attacks based on Conjugate Gradient Method』 ,基于共轭梯度法的多样化对抗性攻击。

 

详细信息如下:

 

 

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

 

论文代码: https://github.com/yamamura-k/ACG

 

       01       

 

引言

 

深度学习模型容易受到对抗样本的攻击,尽管基于最速下降的现有方法已经取得了很高的攻击成功率,但优化的病态问题偶尔会降低它们的攻击性能。

 

为了解决这个问题,在该论文中作者借鉴对此类问题有效的共轭梯度方法,并提出了一种基于共轭梯度法方法新的对抗攻击算法。其实在大学的最优化课程里,会涉及学到最速下降法,共轭梯度法 ,以及拟牛顿法。作者很好的将共轭梯度法应用到了对抗攻击中去。

 

实验结果表明,对于大多数模型,论文提出的方法比现有的SOTA算法能够以更少的迭代次数找到更优的对抗样本,而且论文所提出方法的更多样化的搜索显着提高了对抗攻击的成功率。

 

       02       

 

对抗攻击

 

令局部可微函数 是一个 类分类器,分类标签为 。样本 被分类器 分类为 。给定距离函数 和扰动大小 ,对抗攻击可行域被定义为,对抗样本 定义为

 

令 为查找对抗样本 的目标函数。对抗攻击的数学形式如下所示:

 

上述公式使样本 对分类器 分类成 类的区分度降低。一般情况下,对抗攻击使用的距离有两种,分别是欧氏距离 ,均匀距离

 

且 。

 

PGD攻击是对抗攻击中非常重要的一种方法。给定分类器 ,PGD生成的对抗样本数学形式为 ,其中 对抗扰动的步长, 将样本投影到可行域 的操作。APGD攻击是PGD攻击的改进版,其在迭代过程中引入动量操作。令 为每一步迭代攻击的方向(即 ),APGD攻击具体形式如下所示:

 

其中 表示为正则化的一种方式, 是动量的强度系数,一般情况下取 。

 

       03        

 

共轭梯度攻击

 

共轭梯度法一般用于求解线性问题,之后又被延伸用于求解最小化凸二次型问题和一般的非线性问题。共轭梯度法可以用在无约束和投影有约束问题中。给定一个初始点 ,初始共轭梯度 被设置为 ,第 次搜索点 和共轭梯度 被更新为

其中

是从过去的搜索信息中计算出来的参数。

通常由满足类似于Wolfe条件线性搜索所决定的,因为求解以下问题比较困难

 

考虑最小化一个严格二次型凸函数问题

 

 

其中 是一个正定矩阵, 。在这种情况下,系数 的计算公式为

 

当目标函数是严格凸函数的时候,则由共轭梯度法可知,在少于 次迭代即能找到全局最优解。 对于非线性问题,有一些计算系数 公式被提出来,在该论文中作者使用系数计算公式如下所示:

 

其中 ,且 ,也会使用 来代替 。论文提出的ACG算法的具体形式如下所示:

 

其中攻击步长的搜索算法如下所示:

 

条件 表示的是增长点对应的目标函数的个数小于某个阈值时,则局部最大值点有很大概率在该区间产生。具体如下图示实例所示:

 

 

当某个间断点的函数值陡然升高并陡然降低时,则条件 也会有局部最大值点,具体实例如下图示实例所示:

 

综上所述,最终的算法流程图如下所示:

 

       04       

 

论文理解

 

论文作者提出的自动共轭梯度对抗攻击与原始的共轭梯度法有一些出入。对应于非线性优化问题,给定点 和共轭方向 ,原始的共轭梯度法首先会在点 处沿着方向共轭方向 上搜索使目标函数最大化的下一个迭代点 ,迭代步长即为 对于正定二次型问题,以上步长是可以直接求解出来的;对于非线性问题可以通过线性搜索或者其它搜索算法求得 。

 

然后再求得共轭方向 。而在该论文中,作者直接给出迭代步长和搜索间断点集合,然后随着迭代的进行在迭代间断点处不断缩小对抗扰动 的大小,从而最终求得最优对抗扰动。如果按照原始的共轭梯度法,基于共轭梯度法的对抗攻击的算法流程可以更改为如下所示:

 

step 0:给定初始点 ,神经网络 ,可行域 ,记初始的共轭梯度方向为

 

step 1:通过线性搜索在共轭方向 上搜索攻击步长 ,使得

 

step 2:求得第 步的对抗样本 为

 

step 3: 令 ,并转到step 1中去。

 

       05       

 

实验结果

 

如下表所示为论文方法ACG与其它基于梯度方法APGD在CIFAR10数据集的攻击成功率。可以直观的发现,ACG的攻击成功率均高于APGD的攻击成功率。这些结果表明,无论使用的数据集或模型的架构如何,ACG 都表现出更高的攻击性能。此外,ACG方法不依赖随机数来选择初始点,因为初始点是可行区域的中心,由此可知,ACG 仅在确定性操作上优于要优于APGD。

 

 

下图显示了APGD和ACG上两个连续搜索点之间的2范数的转换情况。可以观察到 ACG 的搜索点比APGD 的搜索点移动得更远。此外,为了研究投影对APGD的影响,作者还计算了两个搜索点之间行进距离的比率,它表示投影浪费的更新距离量。与ACG相比,APGD 在行进距离中表现出更高的投影比率。由于引入了共轭方向,ACG比APGD移动得更多。

 

 

下图为共轭梯度攻击搜索多模态函数的可视化实例。初始搜索点由白星表示,搜索以白色方块结束。圆圈代表搜索点。黑色圆圈表示搜索多样化,白色圆圈表示搜索强度。左图中的“A”到“F”表示局部最优,“E”表示全局最优。

 

左侧和中间的示例分别是由于缺乏多样化或集约化而无法找到全局最优值的失败搜索。右侧的示例显示了由于多样化和集约化的适当平衡而成功找到全局最优值的搜索。

 

下图显示了强化搜索、多样化搜索和适当搜索的可视化,展示了多样化和强化之间的适当平衡。从图中函数的等高线可以观察到六个局部解,其中“E”位置的局部解是全局最优解。

 

 

       06       

 

 核心代码实例

 

论文中提供了算法源码,其代码有些复杂,以下代码是根据论文的核心算法重新编写的较为简单的核心代码。

 

class ACG_attack(object):
 # Input_x shape : R^n
 def __init__(self, input_x, input_y, model, N_iter, W_set, eta):
  self.input_x = input_x
  self.input_y = input_y
  self.model = model
  self.eta = eta
  self.N_iter = N_iter
  self.W_set = W_set
  self.rho = rho
 def judgement(self, k, x_new, x_old, eta_list, loss_list):
  flag = False
  CE = nn.CrossEntropyLoss()
  count = 0 
  j = self.W_set.index(k)
  for i in range(self.W_set[j-1], k-1):
   y_new = self.model(x_new)
   y_old = self.model(x_old)
   if CE(y_new, self.input_y) > CE(y_old, self.input_y):
    count += 1
  if count < self.rho * (k - self.W_set[j-1]):
   flag = True
  if eta_list[k] == eta_list[self.W_set[j-1]] and loss_list[k] == loss_list[self.W_set[j-1]]:
   flag = True
  return flag
 def attack(self):
  eta_list = []
  loss_list = []
  nabla_list = []
  s_list = []
  input_shape = self.input_x.shape
  self.input_x.requires_grad = True
  CE = nn.CrossEntropyLoss()
  x_adv = self.input_x.view(-1)
  x_pre = self.input_x.view(-1)
  x_adv.requires_grad = True
  x_pre.requires_grad = True
  beta = 0
  eta_old = self.eta
  # Compute the graident of f w.r.t x
  y = model(x_pre)
  loss = CE(y, self.input_y)
  loss.backward()
  s_0 = self.input_x.grad.detach().view(-1)
  s_pre = s_0
  # Auto conjugate gradient method
  x_old = x_pre
  s_old = s_pre
  loss_list.append(loss)
  eta_list.append(self.eta)
  nabla_list.append(s_pre)
  s_list.append(s_pre)
  for k in range(self.N_iter):
   x_new = torch.clamp( x_old + eta_pre * torch.sign(s_old) , 0, 1)
   x_new.requires_grad = True
   y_new = self.model(x_new)
   y_adv = self.model(x_adv)
   loss_new = CE(y_new, self.input_y)
   loss_old = CE(y_adv, self.input_y)
   loss_new.backward()
   loss_list.append(loss_new)
   y_list = []
   if CE(y_new, self.input_y) > CE(y_adv, self.input_y):
    x_adv = x_new
    x_pre = x_old
    s_pre = s_old
   eta_new = eta_old
   eta_list.append()
   nabla_list.append(x_new.grad.data)
   if k in self.W_set[1:]:
    if self.judgement(k, x_new, x_old, eta_list, loss_list):
     eta_new = eta_old / 2 
     eta_list[-1] = eta_new
     x_new = x_adv 
     x_old = x_pre
     s_old = s_pre
   y_list.append(nabla_list[-1].view(-1)-nabla_list[-2].view(-1))
   beta = torch.dot(-nabla_list[-1].view(-1), y_list[-1].view(-1))/(torch.dot(s_list[-1].view(-1)), y_list[-1].view(-1))
   beta_list = beta
   s_new = nabla_list[-1] + beta * s_list[-1]
   s_list.append(s_new)
 return x_adv

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注