对抗训练:FGM与PGD方法介绍

对抗训练:FGM与PGD方法介绍

为什么会有对抗训练

当我们对一个事物进行建模或描述时,即使这种描述可能存在偏差,它仍然是对该事物本质的一种映射或表达。对于一张猫的图片来说,我们知道它是猫,但当我们对图片进行裁剪、缩放、遮掩和模糊后(对于模型而言,常见的数据增强(如裁剪、模糊等)是有意识地改变输入,但它们通常不会欺骗模型。而对抗样本 则是通过在输入中加入微小且人类难以察觉的扰动 ,以最大化模型的预测错误,从而检验模型的鲁棒性。这里只是为了方便理解故使用这些),我们可能不太能认出这是猫,但他实际上仍是猫。为了让模型能在这种情况下认出他是猫,所以我们认为构建这种人类难以察觉但会导致模型错误预测的微小扰动样本加入到模型的训练中,能使模型具备辨别的能力,而这种生成困难表示的方式就是对样本进行数据扰动,而在深度学习中,由于深度模型具有极高的拟合能力,它们很容易在训练数据上表现良好,但在面对稍有偏差的输入时却可能表现极差。而训练模型的数据往往本身就是有噪声且不充分的,所以我们需要引入对抗训练,让模型能优化到那些他原本可能薄弱的实例。

数据扰动的几种方式

1. FGM

在FGM中,数据扰动让输入或嵌入在最大化损失的方向上沿梯度单位方向扰动一次(通常要对梯度进行归一化以控制扰动幅度,常见的是使用L2归一,即将梯度除以它的L2范数),使得模型损失最大程度增长,让模型更能学到知识,至于这里的梯度是如何来的,如果你看过使用FGM的代码,你会发现在一个batch中调用了两次model和loss,第一次正是用来计算梯度帮助第二次生成对抗样本的
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x a d v = x + ε ⋅ ∇ x L ( x , y ) ∥ ∇ x L ( x , y ) ∥ x_{adv} = x + ε \cdot \frac{\nabla_x L(x, y)}{\|\nabla_x L(x, y)\|} </math>xadv=x+ε⋅∥∇xL(x,y)∥∇xL(x,y)

  • ε 扰动强度
  • ∇_x L(x, y) 损失函数L的梯度
ini 复制代码
 class FGM:
     def __init__(self, model):
         self.model = model
         self.backup = {}
 ​
     def attack(self, epsilon=1.0, emb_name='embed_tokens'): #例子是nlp文本生成的,其中embedding这里会给出每个token的向量,所以对他的参数进行扰动就能实现对样例的扰动
         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:
                     r_at = epsilon * param.grad / norm
                     param.data.add_(r_at)
 ​
     def restore(self, emb_name='embed_tokens'):
         for name, param in self.model.named_parameters():
             if param.requires_grad and emb_name in name and name in self.backup:
                 param.data = self.backup[name]
         self.backup = {}
 ​
 '''
 在batch中
 '''
 ​
 output=model(**batch)
 loss=output.loss
 loss.backward()
 fgm.attack() #进行数据扰动
 output2=model(**batch)
 loss2=output2.loss
 loss2.backward()
 fgm.restore() #恢复扰动数据,因为有些时候的扰动实际上是通过作作用于模型参数做的,如nlp,因为他的原始输入是文字等,无法扰动。所以攻击完后要复原,趁着参数还没更新

2. PGD

与 FGM 只执行一次线性扰动不同,PGD 会在一个局部邻域内多步迭代地进行扰动,每一步都在当前对抗样本基础上进一步最大化损失,并通过投影确保扰动不会超过指定的上限,从而更有效地逼近最坏情况。

第一个样本
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x 0 a d v = x + δ 0 , 其中 δ 0 ∼ U ( − ϵ , ϵ ) x_{0}^{adv} = x + δ_0,\quad \text{其中 } δ_0 \sim \mathcal{U}(-\epsilon, \epsilon) </math>x0adv=x+δ0,其中 δ0∼U(−ϵ,ϵ)

后续样本
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x t + 1 = x t + α ∗ s i g n ( ∇ x L ( x t , y ) ) x t + 1 = c l i p ( x t + 1 , x − ε , x + ε ) x_{t+1} = x_t + α * sign(∇x L(x_t, y))\\ x{t+1} = clip(x_{t+1}, x-ε, x+ε) </math>xt+1=xt+α∗sign(∇xL(xt,y))xt+1=clip(xt+1,x−ε,x+ε)

clip 对扰动进行裁剪防止过大或过小

python 复制代码
 class PGD:
     def __init__(self, model, emb_name='model.encoder.embed_tokens', epsilon=1.0, alpha=0.3, K=3):
         self.model = model
         self.emb_name = emb_name  # 目标embedding名称,因为例子是nlp文本生成的,其中embedding这里会给出每个token的向量,所以对他的参数进行扰动就能实现对样例的扰动
         self.epsilon = epsilon    # 扰动半径
         self.alpha = alpha        # 每步步长
         self.K = K                # 攻击步数
         self.emb_backup = {}      # 原始embedding备份
         self.grad_backup = {}
 ​
     def attack(self, is_first_attack=False):
         for name, param in self.model.named_parameters():
             if param.requires_grad and self.emb_name in name:
                 if is_first_attack:
                     self.emb_backup[name] = param.data.clone()
                 norm = torch.norm(param.grad)
                 if norm != 0 and not torch.isnan(norm):
                     r_at = self.alpha * param.grad / norm
                     param.data.add_(r_at)
                     param.data = self.project(name, param.data)
 ​
     def restore(self):
         for name, param in self.model.named_parameters():
             if param.requires_grad and self.emb_name in name:
                 assert name in self.emb_backup
                 param.data = self.emb_backup[name]
         self.emb_backup = {}
 ​
     def project(self, param_name, param_data):
         r = param_data - self.emb_backup[param_name]
         if torch.norm(r) > self.epsilon:
             r = self.epsilon * r / torch.norm(r)
         return self.emb_backup[param_name] + r
 ​
     def backup_grad(self):
         for name, param in self.model.named_parameters():
             if param.requires_grad and param.grad is not None:
                 self.grad_backup[name] = param.grad.clone()
 ​
     def restore_grad(self):
         for name, param in self.model.named_parameters():
             if param.requires_grad and param.grad is not None:
                 param.grad = self.grad_backup[name]
 ​
 ​
 '''
 在batch中
 '''
 outputs = model(input_ids=batch['input_ids'], attention_mask=batch['attention_mask'], labels=batch['labels'])
 loss = outputs.loss
 epoch_loss += loss.item()
 loss.backward()
 ​
 pgd.backup_grad() #保留原梯度,用来后续还原
 for t in range(pgd.K):
     pgd.attack(is_first_attack=(t==0)) #数据扰动
     if t != pgd.K - 1:
         model.zero_grad() #清除中间梯度防止污染累计梯度
     else:
         pgd.restore_grad() #最后一次攻击后恢复梯度
     outputs_adv = model(input_ids=batch['input_ids'], attention_mask=batch['attention_mask'], labels=batch['labels'])
     adv_loss = outputs_adv.loss
     adv_loss.backward() #求梯度用于优化
 pgd.restore() #恢复扰动数据

PGD多次生成对抗样本并优化的优势

  1. 攻击能力更强
  2. 搜索更充分
  3. 克服局部最优解

缺点显而易见,太耗时间了,所以对于PGD而言,对抗次数是一个重要的超参数,需要平衡性能与效果

为什么对抗训练有效

  • 对抗训练不仅仅是鲁棒性提升,它本质上是一种正则化方法,可以减少过拟合;
  • 它也迫使模型在局部邻域保持一致预测(类似一致性正则项);
  • 可以提高模型对分布外样本(out-of-distribution)更稳定。

因此,对抗训练不仅是一种提升鲁棒性的技术手段,更是一种提升泛化能力、缓解过拟合的正则化策略,尤其在面对现实世界中非理想输入时,它表现出明显优势。

相关推荐
SHIPKING3932 小时前
【机器学习&深度学习】LMDeploy的分布式推理实现
人工智能·深度学习
兔子的倔强4 小时前
Transformer在文本、图像和点云数据中的应用——经典工作梳理
人工智能·深度学习·transformer
lxmyzzs5 小时前
【图像算法 - 21】慧眼识虫:基于深度学习与OpenCV的农田害虫智能识别系统
人工智能·深度学习·opencv·算法·yolo·目标检测·计算机视觉
AI人工智能+5 小时前
表格识别技术:通过图像处理与深度学习,将非结构化表格转化为可编辑结构化数据,推动智能化发展
人工智能·深度学习·ocr·表格识别
codeyanwu6 小时前
nanoGPT 部署
python·深度学习·机器学习
欧阳小猜7 小时前
深度学习②【优化算法(重点!)、数据获取与模型训练全解析】
人工智能·深度学习·算法
fsnine7 小时前
深度学习——神经网络
人工智能·深度学习·神经网络
泡泡茶壶_ovo10 小时前
RORPCAP: retrieval-based objects and relations prompt for image captioning
人工智能·深度学习·计算机视觉·语言模型·prompt·多模态·imagecaptioning
魔乐社区13 小时前
OpenAI重新开源!gpt-oss-20b适配昇腾并上线魔乐社区
人工智能·gpt·深度学习·开源·大模型
Coovally AI模型快速验证15 小时前
全景式综述|多模态目标跟踪全面解析:方法、数据、挑战与未来
人工智能·深度学习·算法·机器学习·计算机视觉·目标跟踪·无人机