当前位置:   article > 正文

Projected Gradient Descent(PGD)对抗训练

projected gradient descent

PGD对抗训练什么是:

之前说到了FGM对抗训练,FGM 解决的思路其实就是梯度上升,那么 FGM 简单粗暴的“一步到位”,是不是有可能并不能走到约束内的最优点呢?当然是有可能的。于是,一个很 intuitive 的改进诞生了:Projected Gradient Descent(PGD)的方法,简单的说,就是“小步走,多走几步”,如果走出了扰动半径为 ϵ 的空间,就映射回“球面”上,以保证扰动不要过大.


伪代码:

pgd整个对抗训练的过程如下,伪代码如下:

1.计算x的前向loss、反向传播得到梯度并备份;
2.对于每步t:

 a.根据embedding矩阵的梯度计算出r,并加到当前embedding上,相当于x+r(超出范围则投影回epsilon内);
  1. if t 不是最后一步,则进行b步骤:
  2. 将模型梯度归0,根据a的x+r计算前后向并得到梯度,继续a步骤;
  3. if t 是最后一步,则进行c步骤:
  4. 恢复(1)的梯度,根据a的x+r计算前后向得到梯度并将梯度累加到(1)的梯度上,跳出循环;

3.将embedding恢复为(1)时的值;

4.根据2 中 c 的梯度对参数进行更新。


PGD代码实现(基于pytorch):

  1. class PGD:
  2. def __init__(self, model, eps=1., alpha=0.3):
  3. self.model = (
  4. model.module if hasattr(model, "module") else model
  5. )
  6. self.eps = eps
  7. self.alpha = alpha
  8. self.emb_backup = {}
  9. self.grad_backup = {}
  10. def attack(self, emb_name='word_embeddings', is_first_attack=False):
  11. for name, param in self.model.named_parameters():
  12. if param.requires_grad and emb_name in name:
  13. if is_first_attack:
  14. self.emb_backup[name] = param.data.clone()
  15. norm = torch.norm(param.grad)
  16. if norm != 0 and not torch.isnan(norm):
  17. r_at = self.alpha * param.grad / norm
  18. param.data.add_(r_at)
  19. param.data = self.project(name, param.data)
  20. def restore(self, emb_name='word_embeddings'):
  21. for name, param in self.model.named_parameters():
  22. if param.requires_grad and emb_name in name:
  23. assert name in self.emb_backup
  24. param.data = self.emb_backup[name]
  25. self.emb_backup = {}
  26. def project(self, param_name, param_data):
  27. r = param_data - self.emb_backup[param_name]
  28. if torch.norm(r) > self.eps:
  29. r = self.eps * r / torch.norm(r)
  30. return self.emb_backup[param_name] + r
  31. def backup_grad(self):
  32. for name, param in self.model.named_parameters():
  33. if param.requires_grad and param.grad is not None:
  34. self.grad_backup[name] = param.grad.clone()
  35. def restore_grad(self):
  36. for name, param in self.model.named_parameters():
  37. if param.requires_grad and param.grad is not None:
  38. param.grad = self.grad_backup[name]

PGD训练代码实现(基于pytorch):

  1. pgd = PGD(model)
  2. K = 3
  3. for batch_input, batch_label in data:
  4. # 正常训练
  5. loss = model(batch_input, batch_label)
  6. loss.backward() # 反向传播,得到正常的grad
  7. pgd.backup_grad()
  8. # 对抗训练
  9. for t in range(K):
  10. pgd.attack(is_first_attack=(t==0)) # 在embedding上添加对抗扰动, first attack时备份param.data
  11. if t != K-1:
  12. model.zero_grad()
  13. else:
  14. pgd.restore_grad()
  15. loss_adv = model(batch_input, batch_label)
  16. loss_adv.backward() # 反向传播,并在正常的grad基础上,累加对抗训练的梯度
  17. pgd.restore() # 恢复embedding参数
  18. # 梯度下降,更新参数
  19. optimizer.step()
  20. model.zero_grad()

总结:

 一般来说,PGD的攻击效果比FGSM要好,首先,如果目标模型是一个线性模型,那么用FGSM就可以了,因为此时loss对输入的导数是固定的,换言之,使得loss下降的方向是明确的,即使你多次迭代,扰动的方向也不会改变。而对于一个非线性模型,仅仅做一次迭代,方向是不一定完全正确的,这也是为什么FGSM的效果一般的原因了。
 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/508465
推荐阅读
相关标签
  

闽ICP备14008679号