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

元学习—对抗式元学习(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算法中内存循环与外层循环:

  1. 在内层循环的过程中,我们基于训练样本 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
  2. 在外层循环,即元学习的过程中,我们使用上一步计算出来的参数 θ 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
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值