Data

Traindata
image.png
Newdata
Dataset

image.png

Model


  1. class EncoderRNN(nn.Module):
  2. def __init__(self, input_size, hidden_size):
  3. super(EncoderRNN, self).__init__()
  4. self.hidden_size = hidden_size
  5. self.embedding = nn.Embedding(input_size, hidden_size)
  6. self.gru = nn.GRU(hidden_size, hidden_size)
  7. def forward(self, input, hidden):
  8. embedded = self.embedding(input).view(1, 1, -1)
  9. output = embedded
  10. output, hidden = self.gru(output, hidden)
  11. return output, hidden
  12. def initHidden(self):
  13. return torch.zeros(1, 1, self.hidden_size, device=device)
  1. class AttnDecoderRNN(nn.Module):
  2. def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):
  3. super(AttnDecoderRNN, self).__init__()
  4. self.hidden_size = hidden_size
  5. self.output_size = output_size
  6. self.dropout_p = dropout_p
  7. self.max_length = max_length
  8. self.embedding = nn.Embedding(self.output_size, self.hidden_size)
  9. self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
  10. self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
  11. self.dropout = nn.Dropout(self.dropout_p)
  12. self.gru = nn.GRU(self.hidden_size, self.hidden_size)
  13. self.out = nn.Linear(self.hidden_size, self.output_size)
  14. def forward(self, input, hidden, encoder_outputs):
  15. embedded = self.embedding(input).view(1, 1, -1)
  16. embedded = self.dropout(embedded)
  17. attn_weights = F.softmax(
  18. self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)
  19. attn_applied = torch.bmm(attn_weights.unsqueeze(0),
  20. encoder_outputs.unsqueeze(0))
  21. output = torch.cat((embedded[0], attn_applied[0]), 1)
  22. output = self.attn_combine(output).unsqueeze(0)
  23. output = F.relu(output)
  24. output, hidden = self.gru(output, hidden)
  25. output = F.log_softmax(self.out(output[0]), dim=1)
  26. return output, hidden, attn_weights
  27. def initHidden(self):
  28. return torch.zeros(1, 1, self.hidden_size, device=device)

比较值得借鉴的就是这里使用了Attention机制
关于Attention机制可以看我先前的笔记
这里相当于对之前的理论的一次实践(之前其实没完全看懂图片例子,这里对应就可以理解)
Attention Note

对应的是上面的代码20-23行的位置。
可以看到利用嵌入向量和隐藏状态全连接层规约到hiddensize之后利用softmax进行计算权重
然后利用权重比对encode得到的全句序列向量加权(22行的代码)
这里补充torch.bmm用法
Torch.bmm(A,B)
下面是Attention图例

Seq2Seq Translation - 图3

Training

比较诡异的就是训练的时候,每一次输入一个句子
然后一个字一个字的输入模型,每次把输出的向量放在列表里
所以就会很慢,想改成用Dataloader直接多batchsize的计算,但是不知道怎么改。

  1. def train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH):
  2. encoder_hidden = encoder.initHidden()
  3. encoder_optimizer.zero_grad()
  4. decoder_optimizer.zero_grad()
  5. input_length = input_tensor.size(0)
  6. target_length = target_tensor.size(0)
  7. encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)
  8. loss = 0
  9. for ei in range(input_length):
  10. encoder_output, encoder_hidden = encoder(
  11. input_tensor[ei], encoder_hidden)
  12. encoder_outputs[ei] = encoder_output[0, 0]
  13. decoder_input = torch.tensor([[SOS_token]], device=device)
  14. decoder_hidden = encoder_hidden
  15. use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False
  16. if use_teacher_forcing:
  17. # Teacher forcing: Feed the target as the next input
  18. for di in range(target_length):
  19. decoder_output, decoder_hidden, decoder_attention = decoder(
  20. decoder_input, decoder_hidden, encoder_outputs)
  21. loss += criterion(decoder_output, target_tensor[di])
  22. decoder_input = target_tensor[di] # Teacher forcing
  23. else:
  24. for di in range(target_length):
  25. decoder_output, decoder_hidden, decoder_attention = decoder(
  26. decoder_input, decoder_hidden, encoder_outputs)
  27. topv, topi = decoder_output.topk(1)
  28. decoder_input = topi.squeeze().detach() # detach from history as input
  29. loss += criterion(decoder_output, target_tensor[di])
  30. if decoder_input.item() == EOS_token:
  31. break
  32. #print("not use decoder_output.size() ", decoder_output.size())
  33. loss.backward()
  34. encoder_optimizer.step()
  35. decoder_optimizer.step()
  36. return loss.item() / target_length

Result

原数据集数据较大
有135824个句子对
训练仅仅是用了75000个,本地GPU大约1小时半
同时数据集的句式相对分布集中,感觉对结果有一定的影响
(例如:下面的句子的句式都是人称代词,然后be动词,然后接一些句子,于是在训练的过程中很明显的,训练没多久就可以做到人称代词和be动词翻译出来,但是后面翻译的很糟,要么不对,要么缺少词语)

下图是随机抽取句子进行预测翻译
观察训练结果,可以发现句子基本翻译和Label一致,少数的翻译有不同,但是意思基本一致或者是比较相近
image.png

其他

有点想魔改一下中英翻译
中英翻译的数据集也已经找好了
双语的语句对的数据集,30+M,还是很大的。
毕竟法语看不懂,也没啥直观的感受,也没啥实用性(。。。)