元学习—对抗式元学习(ADML)

it2025-01-31  15

元学习—对抗式元学习(ADML)

在之前的文章中,我们介绍了MAML模型,有兴趣的读者可以参考元学习—模型不可知元学习(MAML)。在下面的文章中,我们将介绍MAML模型在对抗式学习中的应用,即MAML模型的一个变体ADML模型。

1 FGSM方法

在对抗式的学习中,需要同时使用到真实样本和对抗样本。对于对抗样本的生成,有很多种方法,我们下面来看其中的一种方法,即基于梯度的攻击算法(FGSM)。

一般情况下,我们会计算模型参数的梯度值来更新模型参数,以求得使得模型的Loss最小,在FGSM中,为了获取对抗样本,我们计算输入数据的关于Loss的梯度结果。在实际的计算中,我们只计算一次梯度下降步骤,以此来保证计算的有效性。计算完之后,我们利用符号函数来进一步计算结果。首先,符号函数的定义如下: s i g n ( x ) = { 1 x > 0 0 x = 0 − 1 x < 0 sign(x)=\left\{ \begin{aligned} 1 & & x>0 \\ 0 && x=0\\ -1& & x<0 \\ \end{aligned} \right. sign(x)=101x>0x=0x<0 最终,在输入样本为x的基础之上,我们可以生成其对抗样本 X a d v X_{adv} Xadv,即: X a d v = x + ε s i g n ( ▽ x J ( x , y t r u e ) ) X_{adv}=x+εsign(▽_xJ(x,y_{true})) Xadv=x+εsign(xJ(x,ytrue)) 其中 J ( ) J() J()表示当前样本x的损失。以一个 图像的样本为例,我们可以得到如下图所示的对抗样本:

2 ADML算法

现在,我们已经可以通过FGSM算法来获取对抗样本了,下一步则是利用ADML算法进行学习。在ADML中,我们将使用真实数据和对抗数据来训练元学习的模型。这种对抗式的学习方式有助于我们寻找出更加健壮的参数 θ θ θ,通过在内层和外层循环中(这个部分如果有疑问,请参考我开头提到的博客。)来使用真实数据和对抗数据来计算损失,更新参数。ADML利用了真实样本和对抗样本来获取了一个更好的,更具健壮性的模型初始化参数。该参数可以被应用到其他的任务之上。

简单的回顾一下MAML算法,首先,我们需要一个任务集合T,并且任务集合中的任务以概率 p ( T ) p(T) p(T)分布。进一步,我们按照概率分布来采样任务 T i T_i Ti,同时,对于每一个任务,我们对于其训练集和测试集各采样k个样本点。同样的方式,在ADML算法,我们还需要为对抗样本的训练集和测试集各采样K个样本点。即: D c l e a n i t r a i n , D a d v i t r a i n , D c l e a n i t e s t , D a d v i t e s t D_{clean_i}^{train},D_{adv_i}^{train},D_{clean_i}^{test},D_{adv_i}^{test} DcleanitrainDadvitrainDcleanitestDadvitest

现在,我们计算训练集的Loss,并且通过梯度下降算法进行最小化损失,并且寻找最优参数 θ ′ θ' θ。因为我们拥有真实和对抗数据,我们可以同时为两个数据集计算出最优参数 θ c l e a n i ′ θ_{clean_i}' θcleani θ a d v i ′ θ_{adv_i}' θadvi,具体定义的形式如下: θ c l e a n i ′ = θ − α 1 ▽ θ L T i ( f θ , D c l e a n i t r a i n ) θ'_{clean_i}=θ-α_1▽_θL_{T_i}(f_θ,D_{clean_i}^{train}) θcleani=θα1θLTi(fθDcleanitrain) θ a d v i ′ = θ − α 1 ▽ θ L T i ( f θ , D a d v i t r a i n ) θ'_{adv_i}=θ-α_1▽_θL_{T_i}(f_θ,D_{adv_i}^{train}) θadvi=θα1θLTi(fθDadvitrain) 现在,我们进行元学习的训练阶段,通过最小化测试集的损失以及优化的参数 θ i ′ θ_i' θi来寻找最优的模型初始化参数 θ θ θ。具体的,根据之前计算出来的 θ c l e a n i ′ θ_{clean_i}' θcleani θ a d v i ′ θ_{adv_i}' θadvi,我们可以计算出两组最优的模型初始化参数,即: θ = θ − β 1 ▽ θ ∑ T i   − p ( T ) L T i ( f θ c l e a n i ′ , D a d v i t e s t ) θ=θ-β_1▽_θ∑_{T_i~-p(T)}L_{T_i}(f_{θ_{clean_i}'},D_{adv_i}^{test}) θ=θβ1θTi p(T)LTi(fθcleani,Dadvitest) θ = θ − β 2 ▽ θ ∑ T i   − p ( T ) L T i ( f θ a d v i ′ ) , D c l e a n i t e s t θ=θ-β_2▽_θ∑_{T_i~-p(T)}L_{T_i}(f_{θ_{adv_i}'}),D_{clean_i}^{test} θ=θβ2θTi p(T)LTi(fθadvi),Dcleanitest 最终,我们使用真实数据与对抗样本对参数进行优化。下面,我们给出ADML算法的算法描述:

下面,我们来简单的分析一下,ADML算法中内存循环与外层循环:

在内层循环的过程中,我们基于训练样本 D a d v i D_{adv_i} Dadvi D c l e a n i D_{clean_i} Dcleani,使用梯度下降算法,计算新的模型参数,即 θ a d v i ′ θ_{adv_i}' θadvi θ c l e a n i ′ θ_{clean_i}' θcleani。在外层循环,即元学习的过程中,我们使用上一步计算出来的参数 θ a d v i ′ θ_{adv_i}' θadvi θ c l e a n i ′ θ_{clean_i}' θcleani来优化损失函数 L i ( f θ a d v i ′ ) L_i(f_{θ_{adv_i}'}) Li(fθadvi) L i ( f θ c l e a n i ′ ) L_i(f_{θ_{clean_i}'}) Li(fθcleani),以此来更新θ。具体的公式定义如下: 从上述公式中不难返现,我们在元学习更新的过程中,使用的是任务i中真实样本组成的测试集来对应的任务i中由对抗样本产生的参数 θ a d v i ′ θ_{adv_i}' θadvi,以此来就散损失,同时使用任务i中的对抗样本组成的测试集来对应由真实样本产生的参数 θ c l e a n i ′ θ_{clean_i}' θcleani。这是一个交叉操作的过程,最终对参数θ进行更新。我们使用下图描述一下这个过程:

最终,我们来分析一下这种对于参数θ更新过程的设计:

对于每一个任务 T i T_i Ti,在内层的梯度更新中,ADML首先通过对样本来对参数 θ a d v i θ_{adv_i} θadvi来进行更新,使其能够适应于对抗样本的特征。同时对于真实样本也采用了相同的操作。最后能够获取到两个参数 θ a d v i ′ θ_{adv_i}' θadvi θ c l e a n i ′ θ_{clean_i}' θcleani,即上图的粉色和紫色的点。然后进入外层的元更新阶段,在这一个阶段,ADML算法使用上面获取的最优参数来更新初始参数θ。并且希望参数θ能够到达最优的位置,即 θ i ∗ θ_i^* θi,其处于两个样本空间的交叉的位置,同时能够适应真实样本与对抗样本,最终能够提高全局的效果。上图中,我们给出的是通过一个任务进行更新的流程。当使用任务集合中的所有任务时,θ可以被优化到所有任务对应的所有样本空间中(包括每一个任务的真实样本空间与对抗样本空间)的交集中,以此来完成对于所有任务,所有样本的支持。

2 ADML算法的实现(基于Pytorch)

#encoding=utf-8 import numpy as np import torch import torch.nn as nn import torch.nn.functional as F from torch.autograd import Variable import torch.optim as optim #生成数据 def sample_point(k): x = np.random.rand(k,50) y = np.random.choice([0,1],size=k,p=[.5,.5]).reshape([-1,1]) x = torch.from_numpy(x) x = x.float() y = torch.from_numpy(y) y = y.float() return x,y class FGSM(nn.Module): def __init__(self,input_dim,hidden_dim): super(FGSM, self).__init__() self.input_dim = input_dim self.hidden_dim = hidden_dim self.linear = nn.Linear(input_dim,hidden_dim) def forward(self,x): #x = Variable(x,requires_grad=True) return self.linear(x).reshape(-1,1) class ADML(nn.Module): def __init__(self,input_dim,hidden_dim): super(ADML, self).__init__() self.input_dim = input_dim self.hidden_dim = hidden_dim self.W = nn.Parameter(torch.zeros(size=[input_dim,hidden_dim])) def forward(self,x): y_predict = torch.matmul(x,self.W).reshape(-1,1) return y_predict fgsmModel = FGSM(50,1) admlModel = ADML(50,1) optimerf = optim.Adam(fgsmModel.parameters(),lr=0.01,weight_decay=1e-5) optimera = optim.Adam(admlModel.parameters(),lr=0.01,weight_decay=1e-5) loss_functionf = nn.MSELoss() loss_functiona = nn.MSELoss() #定义一些相关的超参数 epoches = 100 tasks = 10 betac = 0.0001 betaa = 0.0001 theta_martix_clean = torch.zeros(size=[10,50,1]) theta_martix_clean = theta_martix_clean.float() theta_martix_adv = torch.zeros(size=[10,50,1]) theta_martix_adv = theta_martix_adv.float() ori_theta = torch.rand(size=[50,1]) ori_theta = ori_theta.float() meta_gradient_adv = torch.zeros_like(ori_theta) meta_gradient_clean = torch.zeros_like(ori_theta) epsilon = 0.001 def train(epoch): global ori_theta,meta_gradient, meta_gradient_adv, meta_gradient_clean loss_sum_clean = 0.0 loss_sum_adv = 0.0 for i in range(tasks): ''' 对每一个任务进行迭代 ''' #首先,生成原始样本,调用FGSM生成对抗样本 x_train,y_train = sample_point(10) x_train = Variable(x_train,requires_grad=True) optimerf.zero_grad() y_fgsm_predict = fgsmModel(x_train) loss_fgsm_pre = loss_functionf(y_train,y_fgsm_predict) loss_fgsm_pre.backward() optimerf.step() x_adv = x_train + epsilon * torch.sign(x_train.grad.detach_()) # 调用真实样本和对抗样本来训练ADML模型,注意这里对于每一个任务中的真实样本和对抗样本需要分别训练,顺序不影响最终结果 #先训练真实数据 admlModel.W.data = ori_theta.data optimera.zero_grad() y_predict = admlModel(x_train) loss_adml_clean = loss_functiona(y_train,y_predict) loss_sum_clean = loss_sum_clean + loss_adml_clean.item() loss_adml_clean.backward() optimera.step() #保存参数结果 theta_martix_clean[i,:] = admlModel.W #然后,训练对抗样本集合 admlModel.W.data = ori_theta.data optimera.zero_grad() y_predict = admlModel(x_adv) loss_adml_adv = loss_functiona(y_train,y_predict) loss_sum_adv = loss_sum_adv + loss_adml_adv.item() loss_adml_adv.backward() optimera.step() theta_martix_adv[i,:] = admlModel.W for i in range(tasks): ''' 下面开始测试过程:同理,我们需要测试用的真实样本集合与对抗样本集合 ''' #首先,生成真实样本和对抗样本 x_test, y_test = sample_point(10) x_test = Variable(x_test, requires_grad=True) optimerf.zero_grad() y_fgsm_predict = fgsmModel(x_test) loss_fgsm_pre = loss_functionf(y_test, y_fgsm_predict) loss_fgsm_pre.backward() optimerf.step() x_adv_test = x_test + epsilon * torch.sign(x_test.grad.detach_()) # 进一步,我们需要使用真实样本的参数来计算对抗样本组成的测试集 # 同时,我们使用对抗样本生成的参数来计算真实样本组成的测试集 # 首先,我们用真实集的参数来计算对抗样本 admlModel.W.data = theta_martix_clean[i] optimera.zero_grad() y_adv_predict_test = admlModel(x_adv_test) loss_adml_adv_test = loss_functiona(y_test,y_adv_predict_test) loss_adml_adv_test.backward() optimera.step() meta_gradient_adv = meta_gradient_adv + admlModel.W #然后,我们使用对抗集参数来计算真实演变 admlModel.W.data = theta_martix_adv[i] optimera.zero_grad() y_predict_test = admlModel(x_test) loss_adml_test = loss_functiona(y_test, y_predict_test) loss_adml_test.backward() optimera.step() meta_gradient_clean = meta_gradient_clean + admlModel.W #最后,我们来更新原始的参数 ori_theta = ori_theta - betac * meta_gradient_clean ori_theta = ori_theta - betaa * meta_gradient_adv print("the Epoch is {:04d}".format(epoch),"the loss clean is {:.4f}".format(loss_sum_clean),"the loss adv is {:.4f}".format(loss_sum_adv)) if __name__ == "__main__": for epoch in range(epoches): train(epoch)
最新回复(0)