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

复现论文《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),变成了 e = E ( x ) + r ^ e = E(x)+\hat{r} e=E(x)+r^,其中 x x x 为输入, r ^ \hat{r} 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} 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()

你可能感兴趣的:(自然语言处理)