1.seq2seq模型
目标:
seq2seq意思即向模型输入一个sequence,模型自己决定输出一个sequence。应用于文本翻译任务,我们期望告诉模型一段英语,模型输出一段翻译的结果,翻译的内容和长度都是由机器自己决定的。
模型
包含两部分,分别为encoder和decoder。下面尝试在翻译任务的语境中解释这个模型。
encoder(编码器):将输入的一段英文序列(machine learning)编码为向量H,H 包含了input的语义信息。
decoder(解码器):负责解析向量H,并逐步生成一个翻译结果序列。具体说来,我们期望decoder先接收一个事先约定好的start向量和初始向量H,输出‘机’字,我们再将输出向量‘机’反馈给decoder,模型输出‘器’向量… 如此迭代,直到模型输出END(事先约定好的向量),表示输出结束。
实现
RNN满足上述场景需求。记RNN的外部记忆状态状态为H。Encoder主要包含一个RNN,输入为一段英文序列词向量,将最后一个词向量对应的外部记忆H作为模型的输出。注意这里的RNN迭代次数固定为输入序列长度,每次迭代完成后,即清空外部记忆H。重新开始另一个sequence的学习/预测。
decoder同样主要包含一个RNN。RNN的input为H和start向量,只需迭代一步,即获得 output “机”,同时更新了外部记忆H,再将“机”输入模型,迭代一步,获得“器”,同时更新外部记忆H。如此往复,直到获得END向量,或达到最大长度。
2.实践
如何训练?
为加速模型收敛,在训练阶段,采用teach forcing的办法。
直接将label(期望的翻译结果)输入Decoder,取Decoder的输出作为Output,和label计算loss。注意,前一个label的后面用于计算的label差一列。
举例来说,输入的label为 “<START> 机 器 学 习",用于计算损失的loss为“机 器 学 习 <END>”。
在预测阶段,我们期望模型行为为:①根据encoder提取的英文序列的语义信息,判断出第一个字应该是“机”。②假设这个判断正确,将它作为条件,再判断下一个字应该是“器”…
如何处理一个batch内不同长度的序列?
有两种解决办法:
①直接设batch为1.
②用batch中最大长度序列补齐其他向量,同时记住每个序列的原始长度。
在encoder的forward函数中,根据每个序列的原始长度,采样对应长度的隐藏状态作为传递给decoder的外部记忆H。
decoder中也类似,将超出label序列原始长度的时间步输出直接置0,确保不会影响损失计算。
代码
注:这个简洁实现只是为了说明原理,故没有划分训练集。
数据集:https://download.csdn.net/download/yao_20020402/86398278
from collections import Counter
import nltk
import random
import numpy as np
import torch.nn as nn
import torch
import torch.optim as optim
import torch.nn.functional as F
# nltk.download('punkt')
device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
with open("./train.txt",encoding='utf-8',mode='r') as f:
content=[i.split('\t') for i in f.read().split("\n") if len(i)>0]
cn_corpus=''
en_corpus=''
for i in content:
en_corpus+=' '
en_corpus+=i[0]
cn_corpus+=i[1]
#建立词表
en_word_fre_dict=Counter(nltk.word_tokenize(en_corpus))
en_word_fre=[[word,en_word_fre_dict[word]] for word in en_word_fre_dict]
en_word_fre_sorted=sorted(en_word_fre,key=lambda x:x[1],reverse=True)
en_id_word=['','BE']
en_id_word+=[i[0] for i in en_word_fre_sorted]
en_word_id={
word:id for id,word in enumerate(en_id_word)}
cn_word_fre_dict=Counter([i for i in cn_corpus])
cn_word_fre=[[word,cn_word_fre_dict[word]] for word in cn_word_fre_dict]
cn_word_fre_sorted=sorted(cn_word_fre,key=lambda x:x[1],reverse=True)
cn_id_word=['','BE']
cn_id_word+=[i[0] for i in cn_word_fre_sorted]
cn_word_id={
word:id for id,word in enumerate(cn_id_word)}
content_id=list(