背景
我们知道,对抗学习最初在生成模型中应用,比如最简单的GAN,能够生成以假乱真的图像等;后来该模型被用于判别模型中,因为普通的图像分类器泛化能力差,易受攻击导致分类错误,通过增加对抗训练,能够有效提高模型的鲁棒性,同时也能提高模型的泛化能力。
对抗训练中关键的是需要找到对抗样本,通常是对原始的输入添加一定的扰动来构造,然后放给模型训练,这样模型就有了识别对抗样本的能力。其中的关键技术在于如果构造扰动,使得模型在不同的攻击样本中均能够具备较强的识别性。
本文针对自然语言中的对抗学习,针对攻击的“一阶扰动”场景,总结了最近的工作进展,涉及到的知识包括:基本单步算法FGM,“一阶扰动”最强多步算法PGD,以及针对时耗等改进的FreeAT,YOPO和FreeLB,其中FreeLB成为了目前刷榜的SOA。
基础知识
对抗训练,简单来说,就是在原始输入样本x加上一个扰动 r a d v r_{adv} radv,得到对抗样本后,用其进行训练,2018年Madry在ICLR中[1]针对对抗学习,将此类问题定义为一个Min-Max的公式,即:
m i n θ E ( x , y ) ∼ D [ m a x r a d v ∈ S L ( θ , x + r a d v , y ) ] − − − ( 1 ) \underset {\theta}{min}\mathbb E(x,y)\sim D[\underset{r_{adv\in S}}{max}L(\theta, x+r_{adv}, y)] --- (1) θminE(x,y)∼D[radv∈SmaxL(θ,x+radv,y)]−−−(1)
该公式分为两个部分,一个是内部损失函数的最大化,一个是外部风险的最小化。
- 内部max,L为定义的损失函数,S为扰动的空间,此时我们的目的是求得让判断失误最多情况下扰动的量,即求得最佳的攻击参数;
- 外部min,针对上述的攻击,找到最鲁邦的模型参数,也就是防御,进一步优化模型参数,使得在整个数据分布的期望还是最小。
至于公式(1)的定义是否合理,内部通过max优化找到对抗样本是否足够,遇到其他的对抗样本模型是否依然鲁棒?Madry在论文中给了证明,并认为:通过PGD优化找到的对抗样本,将其进行训练使得在这些对抗样本上神经网络的loss很小,那么这个神经网络也就可以抵抗其他的对抗样本,并且说针对内部非凸的优化,PGD能够成功将其解决。
FGM
在PGD提出之前,Goodfellow 在17年提出FGM,其增加的扰动为:
r a d v = ϵ ⋅ g / ∣ ∣ g ∣ ∣ 2 g = ▽ x L ( θ , x , y ) r_{adv} = \epsilon \cdot g/||g||_2\\ g = \triangledown_x L(\theta,x,y) radv=ϵ⋅g/∣∣g∣∣2g=▽xL(θ,x,y)
新增的对抗样本为
x a d v = x + r a d v x_{adv} = x + r_{adv} xadv=x+radv
关键代码实现部分按照【训练技巧】功守道:NLP中的对抗训练 + PyTorch实现
class FGM():
def attack(self, epsilon=1., emb_name='emb.'):
# emb_name这个参数要换成你模型中embedding的参数名
for name, param in self.model.named_parameters():
if param.requires_grad and emb_name in name:
self.backup[name] = param.data.clone()
norm = torch.norm(param.grad)
if norm != 0 and not torch.isnan(norm):
r_at = epsilon * param.grad / norm
param.data.add_(r_at)
---------------------------------------------
fgm = FGM(model)
for batch_input, batch_label in data:
# 正常训练
loss = model(batch_input, batch_label)
loss.backward() # 反向传播,得到正常的grad
# 对抗训练
fgm.attack() # 在embedding上添加对抗扰动
loss_adv = model(batch_input, batch_label)
loss_adv.backward() # 反向传播,并在正常的grad基础上,累加对抗训练的梯度
fgm.restore() # 恢复embedding参数
optimizer.step()# 梯度下降,更新参数
model.zero_grad()
PGD
Project Gradient Descent 是一种迭代攻击,相比于普通的FGM 仅做一次迭代,PGD是做多次迭代,每次走一小步,每次迭代都会将扰动投射到规定范围内。
r a d v t + 1 = Π ∣ ∣ r a d v ∣ ∣ F ≤ ϵ ( r a d v t + α g ( r a d v t ) / ∣ ∣ g ( r a d v t ) ∣ ∣ 2 ) r_{adv}^{t+1} = \Pi_{||r_{adv}||_F\leq \epsilon}(r_{adv}^t + \alpha g(r_{adv}^t)/||g(r_{adv}^t)||_2) radvt+1=Π∣∣radv∣∣F≤ϵ(radvt+αg(radvt)/∣∣g(radvt)∣∣