论文复现《Effective Adversarial Regularization for Neural Machine Translation》
复现论文《Effective Adversarial Regularization for Neural Machine Translation》,遇到的一些pytorch的技巧(坑),总结一下。
原文是基于另一个库Chainer实现的,我在fairseq框架上加以复现,基于pytorch >= 1.0
论文中主要用公式来介绍主要思想,主要集中在word embedding和loss部分的修改:
- word embedding部分主要是使原本的 ,变成了 ,其中 为输入, 为扰动项
- loss部分,使用两种损失函数计算loss,再求和,去反向传播
裁剪数据
为了快速解决修改模型的bug,可以把训练数据裁小,减少数据加载和训练时间
-
先取前100行
head -100 train.en > sub_train.en
-
再用
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
这一部分
论文中没有提及,实际上代码中对于每一组数据,过程是这样的:
- 走一遍原本的模型,产生
net_output
- 计算
loss
(根据LabelSmoothedCrossEntropyCriterion
) - 计算
loss_first
- 根据
losss_first
,反向传播optimizer.backward(loss_first)
- 再走一遍模型,这时在encoder 和decoder 加入扰动项 ,得到
net_output_v
- 计算
loss_vat
- 返回
loss = loss + loss_vat
- 回到
fairseq.task.fairseq_task.train_step()
里,反向传播optimizer.backward(loss)
论文代码的klloss
和nn.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))
自己实现的部分有以**意点:
-
net_output是一个元组,其中
net_output[0]
为输出结果,tensor类型,net_output[1]
为attn等信息,dict类型 -
反向传播会默认删除计算图,只需加入
retain_graph = True
参数即可不删除图,进行多次反向传播assert optimizer is not None, 'optimizer is None!' optimizer.backward(loss_vat_first, retain_graph=True)
-
改代码不能只改
criterion
和model
,fairseq.task.fairseq_task.valid_step()
同样要改。如果只针对train_step
做了修改,而valid_step
仍使用原模型,那么在criterion.forward()
里添加参数is_train=True
即可
其他
在阅读原码中,也学到了一些api的使用 :
-
torch.gather(input, dim, index, out=None) → Tensor
-
torch.normal()
-
nn.KLDivLoss
-
optimizer.backward()
上一篇: 你今天作弊了吗