对抗训练fgm、fgsm和pgd原理和源码分析
當(dāng)前,在各大NLP競賽中,對抗訓(xùn)練已然成為上分神器,尤其是fgm和pgd使用較多,下面來說說吧。對抗訓(xùn)練是一種引入噪聲的訓(xùn)練方式,可以對參數(shù)進(jìn)行正則化,提升模型魯棒性和泛化能力。
fgm
FGM的全稱是Fast Gradient Method, 出現(xiàn)于Adversarial Training Methods for Semi-supervised Text Classification這篇論文,FGM是根據(jù)具體的梯度進(jìn)行scale,得到更好的對抗樣本:
整個對抗訓(xùn)練的過程如下,偽代碼如下:
- 1.計算x的前向loss、反向傳播得到梯度;
- 2.根據(jù)embedding矩陣的梯度計算出r,并加到當(dāng)前embedding上,相當(dāng)于x+r;
- 3.計算x+r的前向loss,反向傳播得到對抗的梯度,累加到(1)的梯度上;
- 4.將embedding恢復(fù)為(1)時的值;
- 5.根據(jù)(3)的梯度對參數(shù)進(jìn)行更新。
fgm代碼實現(xiàn)如下:
class FGM:def __init__(self, model: nn.Module, eps=1.):self.model = (model.module if hasattr(model, "module") else model)self.eps = epsself.backup = {}# only attack word embeddingdef attack(self, emb_name='word_embeddings'):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 and not torch.isnan(norm):r_at = self.eps * param.grad / normparam.data.add_(r_at)def restore(self, emb_name='word_embeddings'):for name, para in self.model.named_parameters():if para.requires_grad and emb_name in name:assert name in self.backuppara.data = self.backup[name]self.backup = {}fgm應(yīng)用代碼如下:
##對應(yīng)第一步 loss = model(**batch_data)[0] loss.backward() ##對應(yīng)第二步 fgm.attack() #對應(yīng)第三步 loss_adv = model(**batch_data)[0] loss_adv.backward() #對應(yīng)第四步 fgm.restore() #對應(yīng)第五步 optimizer.step()fgsm
FGSM的全稱是Fast Gradient Sign Method. 如果要說FGSM和FGM的區(qū)別,核心區(qū)別在計算擾動的方式不一樣,FGSM擾動的計算方式如下:
FGSM的其他算法流程跟FGM一樣,這里不再贅述。
pgd
FGM直接通過epsilon參數(shù)一下子算出了對抗擾動,這樣得到的可能不是最優(yōu)的。因此PGD進(jìn)行了改進(jìn),多迭代幾次,慢慢找到最優(yōu)的擾動。
引用:
FGM簡單粗暴的“一步到位”,可能走不到約束內(nèi)的最優(yōu)點。PGD則是“小步走,多走幾步”,如果走出了擾動半徑為epsilon的空間,就映射回“球面”上,以保證擾動不要過大
并且
pgd整個對抗訓(xùn)練的過程如下,偽代碼如下:
- 1.計算x的前向loss、反向傳播得到梯度并備份;
- 2.對于每步t:
- a.根據(jù)embedding矩陣的梯度計算出r,并加到當(dāng)前embedding上,相當(dāng)于x+r(超出范圍則投影回epsilon內(nèi));
- if t 不是最后一步,則進(jìn)行b步驟:將模型梯度歸0,根據(jù)a的x+r計算前后向并得到梯度,繼續(xù)a步驟;if t 是最后一步,則進(jìn)行c步驟:恢復(fù)(1)的梯度,根據(jù)a的x+r計算前后向得到梯度并將梯度累加到(1)的梯度上,跳出循環(huán);
- 3.將embedding恢復(fù)為(1)時的值;
- 4.根據(jù)2c的梯度對參數(shù)進(jìn)行更新。
可以看到,在循環(huán)中r是逐漸累加的,要注意的是最后更新參數(shù)只使用最后一個x+r算出來的梯度。
pgd代碼實現(xiàn)如下:
pgd應(yīng)用代碼如下:
loss = model(**batch_data)[0] 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()loss_adv = model(**batch_data)[0]loss_adv.backward() pgd.restore() optimizer.step()注:在torch中,每次迭代時,如果不把模型的梯度清零,會默認(rèn)將模型每次迭代的梯度累加的。
FreeAT
FreeAT (Free Adversarial Training):來源于NIPS2019的一篇論文,從FGSM到PGD,主要是優(yōu)化對抗擾動的計算,但計算量也一步步增加。對于每個樣本,FGSM和FGM都只用計算兩次,一次是計算x的前后向,一次是計算x+r的前后向。而PGD則計算了K+1次,消耗了更多的計算資源。因此FreeAT被提了出來,在PGD的基礎(chǔ)上進(jìn)行訓(xùn)練速度的優(yōu)化。
FreeAT的思想是在對每個樣本x連續(xù)重復(fù)m次訓(xùn)練,計算r時復(fù)用上一步的梯度,為了保證速度,整體epoch會除以m。r的更新公式為:
整個對抗訓(xùn)練的過程如下,偽代碼如下:
以上所述的對抗訓(xùn)練方法在不同的訓(xùn)練數(shù)據(jù)上表現(xiàn)大同小異,需要根據(jù)具體場景和具體數(shù)據(jù)去選擇對應(yīng)的算法,沒有最好的,只有當(dāng)下場景最適合的算法。
詳細(xì)代碼實現(xiàn)參見github代碼實現(xiàn): 對抗訓(xùn)練.
總結(jié)
以上是生活随笔為你收集整理的对抗训练fgm、fgsm和pgd原理和源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 20种PLC对应Modbus地址表
- 下一篇: 数据可视化 基于TMDB数据集的电影数据