卷友们好鸭 🦆
今天给大家分享一篇关于对抗训练的文章,这篇文章提出的 AWP 技术 在最近非常多的NLP比赛的TOP方案中都有出现,笔者使用之后也发现的确会有提升,但是痛点在于参数比较难调,因此本篇文章参考提出该方法的论文探究一下参数背后的具体含义,以及如何起的作用,希望和大家一起进步 🏄🏿♀️
文章大纲
paper: https://arxiv.org/pdf/2004.05884.pdf
code: https://github.com/csdongxian/AWP
在如今AI遍及各行各业的情况下,现在不论是搞科研还是做比赛,一个非常重要的问题就是提升模型的robust,让训练出来的模型能更好的泛化到一个从未见过的测试集上,以此减小线上和线下的gap。
- 对抗训练 Adversarial training
先简单介绍一下对抗训练
我们知道模型训练是一个ERM(经验风险最小化,Empirical Risk Minimization)的过程,而对抗训练就是为了增强模型的抗干扰能力。
顾名思义,对抗训练就是用对抗样本去训练模型,而通过对原始训练数据添加噪声便得到了对抗样本。从优化的角度来看,对抗训练可以被形式化为一个min-max优化问题,即内层最大化,外层最小化 问题,公式如下
*经验风险最小化 (ERM)*是统计学习理论里的一项原则,该原则下有一系列学习算法 ,经验风险最小化用于为这些算法的性能提供理论上的界。核心思想是,我们无法确切知道算法在实际中的运行情况(真正的“风险”),因为我们不知道算法将在其上运行的数据的真实分布,但借助经验风险最小化,我们可以在一组已知的训练数据(“经验”风险)上衡量其性能。
其中,表示损失函数,θ表示模型,表示原始数据样本,表示标签,表示对抗样本。
从上述公式就能清晰的看出对抗训练的流程,和正常训练类似,比如训练n轮,选取一个mini-batch的数据,对这个batch内的每一个数据点生成一个对抗样本,然后作为对抗训练的数据,代入到模型里更新模型的参数。
而关键的min-max优化问题即为
- 内层最大化
我们希望通过最大化损失函数去生成一个对抗样本,只是这里的数据扰动会有一定的限制,即,这里的表示约束添加的噪声 2. 外层最小化
外层最小化是经典的标准训练过程,只是这里用到的是对抗样本进行训练。我们希望利用内层最大化求出来得解,去训练模型的参数,即训练出来一个更加robust的模型
1.1 如何求解内层最大化
对于神经网络而言,模型的损失函数一般都是非凸的,所以内层最大化问题很难去得到一个精确的解,一般会采用对抗攻击的方式去找到内层最大化的近似解,也就是去产生对抗样本。比较典型的方法包括最早提出的FGSM,通过一步的梯度迭代产生对抗样本。
- Fast Gradient Sign Method(FGSM)Goodfellow et al., 2014
FGSM通过一步的梯度迭代 产生对抗样本,首先计算模型的损失函数对于输入的梯度,然后把梯度用sign函数进行归一化,这是因为限制添加扰动的规模满足范数的约束
这个方法的问题是训练的模型很容易去overfit到产生的对抗样本上,即用这种方法得到的模型对于其他的攻击比如PGD等并不具备很好的鲁棒性。
- Projected Gradient Descent (PGD) Madry et al., 2018
在FGSM一步梯度迭代的基础上,后来就出现了多步 梯度迭代的方法。每一步都去计算模型的损失函数对于输入的梯度,然后乘以一个比较小的步长,添加到当前的对抗噪声上,还需要把超出扰动范围的再映射回来,进而得到对抗样本
1.2 如何求解外层最小化
我们的目标是希望训练一个更加robust的模型,即优化得到一个最佳模型参数 θ,但是min-max不是那么容易直观的求解,所以对抗训练的一个理论基础是基于Danskin's 定理,即对于一个min-max问题,如果我们要计算内层的目标函数对于模型参数的梯度,其实等于先对内层的最大化问题求解,得到一个最优的值,然后用这个解代入到模型的损失函数中,然后再来对模型的参数求梯度 。
有了这个定理,那么对抗训练就可以用一个简单的方式来实现,先去求解内层最大化问题,找到最优解,然后代入到模型当前的损失函数,用对抗样本去更新模型的参数。这也是使用比较多的对抗训练方式,流程同前面的公式一致。
当然这个理论也有一些假设,比如要求内层的优化空间s需要是一个非空的空间,同时模型的损失函数是连续可微分的,但是神经网络可能并不具备这些性质,而且对内层求解用的攻击方法也未必能找到最优解,所以一般我们通常会去做这样的假设,然后用这种方式去做对抗训练。
- 对抗训练存在的问题
- 训练速度(Training speed) , 因为对抗训练需要去求解内层最大化问题来产生对抗样本,而通常PGD的方式一般需要10步到20步的迭代,因此相对于正常训练,对抗训练会慢10倍到20倍,所以对抗训练在大数据集上很难采用。
- 对不同攻击的泛化能力(Attack generalization) , 因为做对抗训练往往使用某种特定的攻击去产生对抗样本,但是这么训练得到的模型可能只对当前的攻击有防御效果,最典型的就是FGSM。
- 较大的泛化误差(Large Generalization Gap) ,正常训练很容易在训练集上得到一个很高的准确率,而且在测试集上的表现也不差。但是对抗训练往往会面临一个问题,在训练集上的效果非常好,但是在测试集上却没有很好地结果
上述几个问题我们重点来看第三个,正常训练的结果如下,此时的generalization gap很小
标准训练gap
而对抗训练则会出现一种情况,model在训练集上表现非常好,但是面对未知的测试集表现不尽如人意,如此就产生了比较大的generalization gap
对抗训练gap
这个gap来自哪里,AWP这篇论文就是去探讨这个问题,从一个新的角度研究weight loss landscape(loss 随 weight 的变化情况),确定了 weight loss landscape的平坦性和robust generalization gap之间的关系。
现在介绍了关于对抗训练的基本问题,接下来一起来看一下论文中是如何探究模型的鲁棒泛化性的问题
3 Abstract
近年来,关于提高深度神经网络对于对抗样本鲁棒性的研究迅速增长。其中,对抗性训练是最有希望的一种,它使用对抗样本进行训练,使input loss landscape(loss 随input的变化情况)变得平坦。然而,广泛使用的weight loss landscape(loss 随 weight 的变化情况) 在对抗训练中的表现却很少被探讨。本论文从一个新的角度研究了weight loss landscape,并确定了weight loss landscape的平坦性和robust generalization gap(鲁棒泛化性差距)之间的明显关联 。几个公认的对抗训练的改进,如early stopping(早停)、designing new objective functions(设计新的目标函数)或leveraging unlabeled data(利用无标签数据),都隐含地使weight loss landscape变平坦。基于这些观察,论文中提出了一个简单而有效的对抗性权重扰动(Adversarial Weight Perturbation,AWP)来明确地规范weight loss landscape的平坦性,在对抗性训练框架中形成一个双扰动机制,对输入和权重进行对抗性扰动。 广泛的实验表明,AWP确实带来了更平坦的weight loss landscape,并且可以很容易地纳入各种现有的对抗训练方法中,以进一步提高其对抗鲁棒性。
接着文章review了一些对抗训练的过程,然后指出如何缓解robus generalization gap成为提高对抗训练鲁棒性的关键 ,即上面我们提到的第三个关键问题
为了方便阅读,笔者后面会将weight loss landscape称为权重损失或权重损失情况,
input loss landscape同样称为输入损失, 而robust generalization gap 称为鲁棒泛化差距
- Main contributions
回顾一下,在标准训练场景下,权重损失是一个广泛使用的表征标准泛化差距的指标 ,然而,在对抗性训练下的探索很少,之前有学者试图使用预先生成的对抗样本来探索,但未能得出预期的结论。在论文中,我们使用即时生成的对抗样本 来探索对抗训练下的权重损失,并确定了权重损失的平坦性和鲁棒泛化差距之间的紧密联系 。几个公认的对抗性训练改进,即带有早停的AT、TRADES、MART和RST,都隐含地使权重损失变平,以缩小鲁棒泛化性差距。受此启发,论文作者提出了一种明确的权重损失正则化,即对抗性失重扰动(AWP),以直接限制权重损失的平坦性。与随机扰动不同,AWP注入了最坏情况下的权重扰动,在对抗性训练框架中形成了双扰动机制(即输入和权重都受到对抗性扰动)。 AWP是通用的,可以很容易地纳入现有的对抗训练方法,而且开销很小。论文的主要贡献总结如下:
- 通过使用即时生成的对抗样本来表征权重损失情况,发现在对抗训练中,更平坦的权重损失通常能得到更小的鲁棒泛化差距
- 提出Adversarial Weight Perturbation(AWP)来明确规范化对抗训练的权重损失,形成一种双扰动机制,注入最坏情况下的输入和权重扰动
- 通过广泛实验,证明了AWP能够持续地提高SOTA方法的对抗训练鲁棒性,而且幅度很大
上述中有提到之前有学者试图使用预先生成的对抗样本 来探索,但未能得出预期的结论。论文作者借鉴了NeurIPS 2018 年上发表的论文中提出的可视化方法,用来绘制损失函数的变化情况,进而去探索对抗训练的鲁棒泛化差距的问题。
这个可视化的方法是给定一个训练好的model,然后给model扰动之后重新画出损失图。于是把训练样本换成对抗样本就可以画出对抗训练中的损失图,但是论文作者这么去做发现得不出损失图和鲁棒泛化差距之间的关联。于是论文作者去思考这个泛化性到底是什么,关键点在于,他们的方法是预先生成的对抗样本。这就是一个很大的问题,前文中我们介绍了对抗训练的一般流程,可以看的出,对抗样本的生成是一个min-max过程,而这个求解过程完全依赖于当前模型,因此这里在给了model一定的扰动之后,需要去重新基于当前model生成新的对抗样本 ,也就是AWP这篇论文中提到的——即时生成对抗样本。
- 权重损失和鲁棒泛化差距之间的联系
如此,论文中使用即时生成的对抗样本来去探索权重损失和鲁棒泛化差距之间的关系,提出了一种新的方法来描述权重损失,并从两个角度来研究它。
1)在对抗训练的训练过程中
2)在不同的对抗训练方法中
5.1 可视化
通过绘制沿随机方向 移动权重 时的对抗损失变化来直观地显示权重损失的变化情况,其幅度为α。
其中, 是从高斯分布中采样,并通过 是d的第l层的第j个滤波器, 表示Frobenius规范)进行滤波归一化,以消除DNN的缩放不变性。在这里,通过PGD为当前的扰动模型 α 即时生成对抗样本,然后计算其交叉熵损失。 与以往工作的关键区别在于用于可视化的对抗样本,同前文描述的即时生成 ,在此基础上,研究weight loss landscape和robust generalization gap之间的联系。
5.2 在对抗训练学习过程中的联系
上图中(a)展示了在对抗训练,权重损失是如何随着鲁棒性泛化差距而变化的。论文作者在CIFAR-10上用vanilla AT训练了一个PreAct ResNet-18,训练200个epochs,并采用了piece-wise learning rate schedule(初始学习率为0.1,在第100和150个epoch时除以10)。训练和测试攻击都是10步的PGD(PGD-10),步长2/255,最大 扰动∊=8/255。
在该学习曲线中可以看到,其中 "bset"(最高的测试鲁棒性)是在第103个epoch。在 "best "之前,测试鲁棒性接近于训练鲁棒性,因此鲁棒性泛化差距(绿线)很小。同时,在 "best "之前的权重(每20个epoch绘制)也非常平坦。在 "最佳 "之后,随着训练的继续,鲁棒性泛化差距(绿线)变得更大,而权重损失同时变得更尖锐。
因此,在训练过程中,权重损失的平坦程度与鲁棒性泛化差距有很大关系
5.3 不同对抗训练方法之间的联系
此外,论文作者还探讨了在不同的对抗训练方法中,权重损失和鲁棒泛化差距之间的关系是否仍然存在。在相同的设置下,使用几种SOTA对抗训练方法来训练PreAct ResNet-18。上图中(b)展示了它们的训练/测试鲁棒性 和 权重损失。 与vanilla AT相比,所有的方法都有较小的鲁棒性泛化差距和较平坦的权重损失 。 尽管这些SOTA的方法利用各种技术提高了对抗训练鲁棒性,但它们都隐含地使权重损失变平。还可以看到, 一种方法实现的泛化差距越小,它的权重损失就越平坦 。这一观察结果与训练过程中的观察结果是一致的,它验证了权重损失与鲁棒性泛化差距有很强的关联性。
5.4 更平坦的权重损失是否一定会得到更高的测试鲁棒性?
重新审视上图(b),AT-ES具有最平坦的权重损失(也是最小的鲁棒泛化差距),但没有获得最高的测试鲁棒性。由于鲁棒泛化差距被定义为训练和测试鲁棒性之间的差异,AT-ES的低测试鲁棒性是由低训练鲁棒性引起的。这表明早停技术没有充分利用整个训练过程,例如,它只在第100个历时左右停止训练,训练鲁棒性为60%,比第200个历时低20%。因此, 更平坦的权重损失确实直接导致了更小的鲁棒性泛化差距,但只有在训练过程充分(即训练鲁棒性高)的条件下,才有利于最终测试的鲁棒性 。
这也是为什么在实际使用AWP的时候,如果对全部权重进行扰动,模型的训练损失会很高,笔者认为也是因为对全部权重进行扰动会使得训练的过程异常艰难,导致训练不充分,模型学不到东西
5.5 为什么我们需要权重损失
如前所述,对抗训练已经通过对对抗样本的学习优化了输入损失。然而,对抗样本是通过在每个单独的样本上注入输入扰动以获得最高的对抗损失而产生的,这是一个样本上的 "局部 "最坏情况,没有考虑对多个样本的整体影响。 DNN的权重可以影响所有样本的损失,因此它可以被扰动以获得一个模型上的 "全局 "最坏情况(多个样本的最高对抗损失)。权重扰动可以作为输入扰动的良好补充。另外,对扰动的权重进行优化(即,即使在权重上增加扰动,损失仍然很小)可以获得一个平坦的权重损失,这将进一步缩小鲁棒泛化的差距 。
这也是该论文的第二个contribution,形成一种双扰动机制,注入最坏情况下的输入和权重扰动
那么如何去对权重加扰动呢?我们继续看论文作者如何去做的
- 对抗权重扰动AWP
对抗性权重扰动(Adversarial Weight Perturbation,AWP),通过向DNN注入最坏情况下的权重扰动来明确地使权重损失更加平坦。如上所述,为了提高测试鲁棒性,我们需要关注训练鲁棒性和鲁棒泛化差距(由权重损失的平坦程度表示)。因此,我们有这样的目标:
其中 ρ是原始对抗损失, ρρ用来表征权重损失的平坦程度, 是需要仔细选择的权重扰动
6.1 Weight Perturbation
6.1.1 Perturbation Direction (扰动方向)
与常用的随机权重扰动(随机方向采样)不同,论文中提出的对抗性权重扰动(AWP),沿着这个方向,对抗损失急剧增加。这就是
其中 是扰动 的可行区域。与对抗性输入扰动类似,AWP也在 周围的小区域注入权重上的最坏情况。请注意,对 的最大化取决于整个mini-batch的样本,以使整个损失(而不是每个样本的损失)达到最大,因此这两个最大化是不能交换的。
这个公式就表明了,我们希望让邻域内的损失最大,在此基础上找出最小的w值。
6.1.2 Perturbation Size(扰动大小)
按照权重扰动的方向,我们需要确定应该注入多少扰动。与对抗性输入的固定值约束不同,这里用权重扰动
与第
层权重
的相对大小来限制:
其中 是对权重扰动大小的约束。使用相对大小来约束权重扰动的原因在于两个方面。
1)各层权重的数值分布是不同的,所以不可能用一个固定的值来约束不同层的权重;
2)权重存在尺度不变性,例如,当使用非线性ReLU时,如果我们把一层的权重乘以10,然后在下一层除以10,网络仍然是不变的。
6.2 Optimization
一旦确定了权重扰动的方向和大小,论文作者提出一种算法来优化公式中的双扰动对抗训练问题。对于这两个最大化问题,我们循环生成对抗性例子 ,然后用PGD来更新权重扰动 ,都是经验性的。基于AWP的vanilla AT的程序,命名为AT-AWP,如下
-
Input Perturbation .
使用PGD方法攻击 来生成对抗样本其中 Π(·)是投影函数,第一次迭代时 v 为0。
-
Weight Perturbation .
根据生成的对抗样本 来计算对抗权重的扰动:
其中 是批次大小, 是逐层更新的。与通过FGSM(一步法)或PGD(多步法)生成对抗样本 类似, 也可以通过一步法或多步法解决。然后,我们可以交替生成 和计算 的迭代次数A。论文后面也有描述A的一个迭代和 的一个步骤(默认设置)足以获得良好的鲁棒性改进。
-
Model Training .
最后,在输入扰动和权重扰动之后,我们可以像正常学习一样进行反推,使用SGD更新被扰动模型 的参数。请注意,在优化了landscape上的一个扰动点的损失后,我们应该再次回到中心点进行下一次启动。因此,实际的参数更新如下
- 论文总结
在该篇论文中,作者使用即时生成的对抗样本来描述weight loss landscape,并发现weight loss landscape与robust generalization gap之间的相关性。几个公认的对抗训练变体都引入了更平坦的权重损失,尽管它们使用不同的技术来提高对抗训练的鲁棒性。基于这些发现,作者提出了对抗性权重扰动(Adversarial Weight Perturbation,AWP)来直接使权重损失变得平坦,并在对抗训练框架中开发了一个双扰动(对抗性扰动输入和权重)机制。综合实验表明,AWP是通用的,可以在不同的对抗训练方法、网络结构、威胁模型和基准数据集中提高最先进的对抗鲁棒性。
该论文重点关注对对抗样本的鲁棒性,但从经验上看,它也提高了对未知数据的泛化性。
Kaggle中使用的AWP实现不进行输入扰动(即不创建对抗样本),而只进行权重扰动。
以上便是论文中的核心内容了,还剩下一些实验分析,大家有兴趣的还是去看原论文详细的内容吧。
- Code
伪代码
经典的训练循环
包含AWP的训练循环
attack_backward
attack_step
Args:
-
adv_param (str): 要攻击的layer name,一般攻击第一层 或者全部weight参数效果较好
-
adv_lr (float): 攻击步长,这个参数相对难调节,如果只攻击第一层embedding,一般用1比较好,全部参数用0.1比较好。
-
adv_eps (float): 参数扰动最大幅度限制,范围(0~ +∞),一般设置(0,1)之间相对合理一点。
-
start_epoch (int): (0~ +∞)什么时候开始扰动,默认是0,如果效果不好可以调节值模型收敛一半的时候再开始攻击。
实现代码
class AWP:
"""
Implements weighted adverserial perturbation
adapted from: https://www.kaggle.com/code/wht1996/feedback-nn-train/notebook
"""
def \_\_init\_\_(self, model, optimizer, adv_param="weight", adv_lr=1, adv_eps=0.0001):
self.model = model
self.optimizer = optimizer
self.adv_param = adv_param
self.adv_lr = adv_lr
self.adv_eps = adv_eps
self.backup = {}
self.backup_eps = {}
def attack\_backward(self, inputs, labels):
if self.adv_lr == 0:
return
self._save()
self._attack_step()
y_preds = self.model(inputs)
adv_loss = self.criterion(y_preds, labels)
self.optimizer.zero_grad()
return adv_loss
def \_attack\_step(self):
e = 1e-6
for name, param in self.model.named_parameters():
if param.requires_grad and param.grad is not None and self.adv_param in name:
norm1 = torch.norm(param.grad)
norm2 = torch.norm(param.data.detach())
if norm1 != 0 and not torch.isnan(norm1):
# 在损失函数之前获得梯度
r_at = self.adv_lr * param.grad / (norm1 + e) * (norm2 + e)
param.data.add_(r_at)
param.data = torch.min(
torch.max(param.data, self.backup_eps[name][0]), self.backup_eps[name][1]
)
def \_save(self):
for name, param in self.model.named_parameters():
if param.requires_grad and param.grad is not None and self.adv_param in name:
if name not in self.backup:
self.backup[name] = param.data.clone()
grad_eps = self.adv_eps * param.abs().detach()
self.backup_eps[name] = (
self.backup[name] - grad_eps,
self.backup[name] + grad_eps,
)
def \_restore(self,):
for name, param in self.model.named_parameters():
if name in self.backup:
param.data = self.backup[name]
self.backup = {}
self.backup_eps = {}
"""
使用AWP的训练过程
"""
# 初始化AWP
awp = AWP(model, loss_fn, optimizer, adv_lr=awp_lr, adv_eps=awp_eps)
for step, batch in enumerate(train_loader):
inputs, labels = batch
# 将模型的参数梯度初始化为0
optimizer.zero_grad()
# forward + backward + optimize
predicts = model(inputs) # 前向传播计算预测值
loss = loss_fn(predicts, labels) # 计算当前损失
loss.backward() # 反向传播计算梯度
# 指定从第几个epoch开启awp,一般先让模型学习到一定程度之后
if awp_start >= epoch:
loss = awp.attack_backward(inputs, labels)
loss.backward()
awp._restore() # 恢复到awp之前的model
optimizer.step() # 更新所有参数
注意:使用AWP训练时间大概是原来的两倍
附加上kaggle一个TOP方案对于AWP超参数的解释
link 超参数的调整非常重要!!!主要的超参数是adv_lr,在过去的比赛中,TOP方案经常调整adv_eps。但是,仔细阅读原论文后,我们得出结论,adv_lr更重要。在我们的理解中,adv_eps可以adv_lr如下图所示。(但是,我们遇到的一些体验与预期的行为有些不同,因此某处可能存在错误。)
以上就是本文的全部内容了,希望能给你一点点的帮助
如果感觉还不错的话,欢迎点赞和关注🍑
分享知识,记录生活,一起进步
From zero to hero
Reference
https://www.bilibili.com/s/video/BV1pt4y1Y7uU
https://www.kaggle.com/code/wht1996/feedback-nn-train/notebook
https://b.hatena.ne.jp/ent ry/s/speakerdeck.com /masakiaota/kaggledeshi-yong-sarerudi-dui-xue-xi-fang-fa-awpnolun-wen-jie-shuo-toshi-zhuan g-jie-shuo-advers arial-w eight-perturbation-helps-robust-generalization
https://www.kaggle.com/competitions/us-patent-phrase-to-phrase-matching/discussion/332492#1828747