Pytorch学习记录-使用神经网络训练Seq2Seq总结
昨天的学习记录,想想还是总结一下流程看。
1. 数据预处理流程
1.1 tokenize使用加载好的分词器进行分词(如果使用spacy这类分词器,先加载分词器)
1.2 将分词结果放入Field中
1.3 加载平行语料库,使用splits将语料库拆分为train、valid、test,同时加上src和trg标签
1.4 使用build_vocab构建词汇表,将SRC、TRG两个Field转为词汇表
1.5 构建迭代器,使用BucketIterator.splits将train、valid、test三个数据集转为迭代器 在预处理阶段有两组数据:(1)训练好的平行语料库、(2)要处理的语料
- 训练好的平行语料库做了处理,拆分成了traindata/validdata/testdata,然后做成迭代器trainiter/validiter/testiter。
- 要处理的语料进行分词处理,放入Field成为SRC和TRG,最后生成词汇表
2. 构建模型
2.1 构建Encoder 相关参数
- input_dim输入encoder的one-hot向量维度,这个和输入词汇大小一致
- emb_dim嵌入层的维度,这一层将one-hot向量转为密度向量
- hid_dim隐藏层和cell状态维度
- n_layersRNN的层数
- dropout是要使用的丢失量。这是一个防止过度拟合的正则化参数。
class Encoder(nn.Module):
def __init__(self, input_dim, emb_dim, hid_dim, n_layers, dropout):
super(Encoder,self).__init__()
self.input_dim=input_dim
self.emb_dim=emb_dim
self.hid_dim=hid_dim
self.n_layers=n_layers
self.dropout=dropout
self.embedding=nn.Embedding(input_dim,emb_dim)
self.rnn=nn.LSTM(emb_dim,hid_dim,n_layers,dropout=dropout)
self.dropout=nn.Dropout(dropout)
def forward(self, src):
embedded=self.dropout(self.embedding(src))
outputs, (hidden,cell)=self.rnn(embedded)
return hidden ,cell
2.2 构建Decoder 相关参数和Encoder类似
- output_dim是encoder输出的one-hot向量维度,作为Decoder的输入
- emb_dim嵌入层的维度,这一层将one-hot向量转为密度向量
- hid_dim隐藏层和cell状态维度
- n_layersRNN的层数
- dropout是要使用的丢失量。这是一个防止过度拟合的正则化参数。
class Decoder(nn.Module):
def __init__(self, output_dim, emb_dim, hid_dim, n_layers, dropout):
super(Decoder,self).__init__()
self.emb_dim=emb_dim
self.hid_dim=hid_dim
self.output_dim=output_dim
self.n_layers=n_layers
self.dropout=dropout
self.embedding=nn.Embedding(output_dim,emb_dim)
self.rnn=nn.LSTM(emb_dim,hid_dim,n_layers,dropout=dropout)
self.out=nn.Linear(hid_dim,output_dim)
self.dropout=nn.Dropout(dropout)
def forward(self, input,hidden,cell):
input=input.unsqueeze(0)
embedded=self.dropout(self.embedding(input))
output, (hidden,cell)=self.rnn(embedded,(hidden,cell))
prediction=self.out(output.squeeze(0))
return prediction,hidden ,cell
2.3 构建Seq2Seq
在这里有一个有意思的Teacher Forcing,这是个训练的小技巧。因为 Seq2Seq 会把前一个时间点的输出当成后一个时间点的输入,如果我们在前个时间点做了错误的结论,那往后所有的时间点都会受到这个错误影响,这个连锁反应会让训练容易摆荡不定。 于是我们就想到对于用于生成式的 RNN,如果他在第 t 个时间搞错了,没关系,我们派个老师把错误的答案给偷偷纠正成对的,尽管学生记忆中仍觉得之前说的是对的,但在看到了正确的输入后,至少还有浪子回头的可能。 不过,学生最终还是得踏出校园,自己探求人生的答案。同样的,在测试阶段,我们并没有正确答案,模型必须试着自立自强可惜这往往会遇到些困难,因为在训练时被老师过度保护了,输出与输入间的递归性并没被妥善地训练到。
确认之前Encoder和Decoder层数一致。
class Seq2Seq(nn.Module):
def __init__(self, encoder, decoder, device):
super(Seq2Seq,self).__init__()
self.encoder = encoder
self.decoder = decoder
self.device = device
assert encoder.hid_dim == decoder.hid_dim, \
"Hidden dimensions of encoder and decoder must be equal!"
assert encoder.n_layers == decoder.n_layers, \
"Encoder and decoder must have equal number of layers!"
def forward(self, src,trg,teacher_forcing_ratio=0.5):
# src = [src sent len, batch size]
# trg = [trg sent len, batch size]
# teacher_forcing_ratio是使用教师强制的概率
# 例如。如果teacher_forcing_ratio是0.75,我们75%的时间使用groundtruth输入
batch_size=trg.shape[1]
max_len=trg.shape[0]
trg_vocab_size=self.decoder.output_dim
outputs = torch.zeros(max_len, batch_size, trg_vocab_size).to(self.device)
hidden, cell=self.encoder(src)
input=trg[0,:]
for t in range(1, max_len):
output, hidden, cell = self.decoder(input, hidden, cell)
outputs[t] = output
teacher_force = random.random() < teacher_forcing_ratio
top1 = output.max(1)[1]
input = (trg[t] if teacher_force else top1)
return outputs