欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

论文复现《Effective Adversarial Regularization for Neural Machine Translation》

程序员文章站 2022-06-12 07:52:19
...

复现论文《Effective Adversarial Regularization for Neural Machine Translation》,遇到的一些pytorch的技巧(坑),总结一下。

原文是基于另一个库Chainer实现的,我在fairseq框架上加以复现,基于pytorch >= 1.0

论文中主要用公式来介绍主要思想,主要集中在word embedding和loss部分的修改:

  1. word embedding部分主要是使原本的 e=E(x)e = E(x),变成了 e=E(x)+r^e = E(x)+\hat{r},其中 xx 为输入,r^\hat{r} 为扰动项
  2. loss部分,使用两种损失函数计算loss,再求和,去反向传播

裁剪数据

为了快速解决修改模型的bug,可以把训练数据裁小,减少数据加载和训练时间

  1. 先取前100行

    head -100 train.en > sub_train.en

  2. 再用preprocess.py

    python preprocess.py --source-lang ch --target-lang en --trainpref /data/slwu/datas/test-ldc-bin/train --validpref /data/slwu/datas/ldc-data/valid --testpref /data/slwu/datas/ldc-data/test --destdir /data/slwu/datas/test-ldc-bin

    注意,--trainpref是指训练文件前缀,因此要在路径后面追加train。同理--validpref也要追加valid

word embedding

在word embedding部分,由于torch.tensor​numpy.array可以相互转换,因此可以通过

perturbation = np.random.normal(size=x.shape)
perturbation = torch.tensor(perturbation,dtype=torch.float32,device='cuda')

间接生成均值为0,方差为1的tensor

另外,normalization可以使用torch.nn.LayerNorm来实现

技巧:在创建tensor的时候,使用参数 device='cuda'比调用 perturbation.cuda()运行的速度要快

loss

loss部分主要是修改fairseq.criterion.LabelSmoothedCrossEntropyCriterion这一部分

论文中没有提及,实际上代码中对于每一组数据,过程是这样的:

  1. 走一遍原本的模型,产生net_output
  2. 计算loss(根据LabelSmoothedCrossEntropyCriterion
  3. 计算loss_first
  4. 根据losss_first,反向传播optimizer.backward(loss_first)
  5. 再走一遍模型,这时在encoder 和decoder 加入扰动项 r^\hat{r},得到net_output_v
  6. 计算loss_vat
  7. 返回loss = loss + loss_vat
  8. 回到fairseq.task.fairseq_task.train_step()里,反向传播optimizer.backward(loss)

论文代码的kllossnn.KLDivLoss的结果不一样,所以我采用论文的写法。

def kl_loss(self, p_, q_):
    p_logit = p_.float()
    q_logit = q_.float()
    p = torch.softmax(p_logit, -1)
    _kl = torch.sum(p * (torch.log_softmax(p_logit, -1) 
    		- torch.log_softmax(q_logit, -1)), -1)
    return torch.sum(_kl) / np.prod(np.array(_kl.shape))

自己实现的部分有以**意点:

  1. net_output是一个元组,其中net_output[0]为输出结果,tensor类型,net_output[1]为attn等信息,dict类型

  2. 反向传播会默认删除计算图,只需加入retain_graph = True参数即可不删除图,进行多次反向传播

    assert optimizer is not None, 'optimizer is None!'
    optimizer.backward(loss_vat_first, retain_graph=True)
    
  3. 改代码不能只改criterionmodelfairseq.task.fairseq_task.valid_step()同样要改。如果只针对train_step做了修改,而valid_step仍使用原模型,那么在criterion.forward()里添加参数is_train=True即可

其他

在阅读原码中,也学到了一些api的使用 :

  1. torch.gather(input, dim, index, out=None) → Tensor

    链接:torch.gather()

  2. torch.normal()

    链接:torch.normal()

  3. nn.KLDivLoss

  4. optimizer.backward()