赞
踩
代码地址:https://github.com/keon/seq2seq
一、参数定义模块
epochs --- 迭代次数(default = 100)
batch_size --- 批处理参数,并行处理数据的数量(default = 32)
lr --- 学习率(default = 0.0001),是超参数
grad_clip --- 梯度阈值(default = 10.0)
hide_size = 512 ---隐层大小
embed_size = 256 --- embedding层大小
二、数据加载模块
train_iter, val_iter, test_iter --- 分别为训练集、验证集、测试集的迭代器
len(train_iter): 907 (dataset:29000)
len(test_iter): 32 (dataset:1000)
DE,EN --- 语言模型:英语和德语
de_size = 8014, 德语词典里的词数
en_size = 10004 英语词典里的词数
词典里的几个特殊标记:
'<pad>': 1, ->填充符,填充句子使其长度增加,用来统一句子的长度
'<sos>': 2, ->开始符,标志一个句子的开始
'<eos>': 3 ->结束符,标志一个句子的结束
三、模型初始化
1.定义编码器
Encoder(de_size, embed_size, hidden_size, n_layers=2, dropout=0.5)
self.input_size = de_size = 8014
self.hidden_size = hidden_size = 512
self.embed_size = embed_size = 256
self.embed = Embedding(8014, 256)
2.定义解码器
Decoder(embed_size, hidden_size, en_size, n_layers=1, dropout=0.5)
self.embed_size = embed_size = 256
self.hidden_size = hidden_size = 512
self.output_size = en_size = 10004
self.embed = Embedding(10004, 256)
四、模型训练
通过train_iter迭代训练集(训练集的数据大小为29000,每次处理32个句子,需循环907次)
每次从训练集中随机取出32个句子,以32个句子中词数最多的为准。所以每次句子的长度可能不同
这里就详细介绍第一次迭代过程(调试代码,观察数据是如何变化的)
此次的句子长度为23
src = tensor(23*32) //这32个句子的词数统一为23,小于23的用<pad>填充 (德)
len_src = tensor(1*32) //32个句子的实际词数,取值:[1,23] +1
trg = tensor(22*32) //这32个句子的词数统一为22,小于22的用<pad>填充(英)
len_trg = tensor(1*32) //32个句子的实际词数,取值:[1,22]+1
//进入Seq2Seq模型计算,input为输入,output为输出
input : 第0维: torch.Size([23*32]) ----src(德语)
第1维: torch.Size([22*32]) ----trg(英语)
outputs: torch.Size([22,32,10004]) // 先初始化为全0
1.通过Encoder对输入input[0]进行编码,输出为encoder_output,hidden
encoder的输入为:input[0]: torch.Size([23*32])
embedded: torch.Size([23*32*256]) //转为词向量,词典的每个词对应一个大小256的向量
//将转化后的输入传递给gru,它会自动为整个序列重复计算hidden(隐藏状态),初始为None
gru的输入:embedded:torch.Size([23*32*256]) , hidden:None
gru的输出:outputs: torch.Size([23*32*256]) hidden: torch.Size([4,32,512])
因为这里的gru是双向的,所以要把两层的结果加起来,然后返回
encoder的输出为:encoder_output: torch.Size([23, 32, 512])
hidden: torch.Size([4, 32, 512])
2.通过Decoder进行逐词解码,最后的输出为output, hidden, attn_weights(注意力权重)
将Encoder输出来的hidden取出第1维,作为Decoder的初始隐状态
Decoder的输入:encoder_output: torch.Size([23, 32, 512])
hidden: torch.Size([1, 32, 512])
刚开始input形状是torch.Size([32]),经embed转化后变为torch.Size([32,256]),再在0轴上升1维后得到embedded,形状为torch.Size([1,32,256])
然后对embedded张量dropout(随机丢弃)一些值
通过attention计算注意力权重,输出attn_weights
attention的输入:encoder_output: torch.Size([23, 32, 512])
last_hidden 即传入Decoder的hidden(初始隐状态)[-1]:torch.Size([32, 512])
h:torch.Size([32,23,512])
encoder_output转置之后变为torch.Size([32,23,512])
然后传入score函数计算
energy: torch.Size([32,23,512]) 转置之后为 torch.Size([32,512,23])
v:torch.Size([32,1,512])
将energy跟v相乘(bmm)后 energy:torch.Size([32,1,23])
将energy降维后返回结果
所以attn_energies为torch.Size([32,23])
attention的输出:attn_weights:torch.Size([32,23])
context:torch.Size([32, 1, 512]) 然后转置为 torch.Size([1, 32, 512])
rnn_input:torch.Size([1, 32, 768])
进入gru(Decoder的)网络计算,输出output,hidden
gru的输入:rnn_input:torch.Size([1, 32, 768])
last_hidden:torch.Size([32, 512])
gru的输出:output = torch.Size([1, 32, 512]) (返回)
hidden = torch.Size([1, 32, 512]) (返回)
将output和context降维之后再合并,进入out函数计算
out函数输出的output:torch.Size([32,10004])
然后送入激活函数log_softmax计算之后 返回结果
Decoder的输出:output:torch.Size([32, 10004])
hidden:torch.Size([1, 32, 512])
attn_weights:torch.Size([32,23])
将句子的第t个词的预测结果放入到outputs中对应的位置
为了避免此次预测不准确而影响后面的预测,加入了is_teacher,若is_teacher为True,则使用trg.data[t](真实值),否则用预测值
每个词都预测完之后,返回outputs
Seq2Seq的输出(即返回的outputs): output:torch.Size([22, 32, 10004])
通过损失函数nll_loss()求预测值与实际值的误差,根据误差对模型参数求梯度,通过设置梯度阈值(grad_clip=10)解决过度拟合,更新模型参数,并更新整个模型。
累计误差值
每100次打印1次误差值,先将累计的误差值除以100再打印出来
训练完1轮后,用验证集验证模型
第一轮训练或者误差值比上轮更小就保存这个模型
训练完100轮之后,保存的最后那个模型就是最优的,我们就可以用这个模型来预测了,结果应该比较准确了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。