- 知道seq2seq的常见应用场景
- 能够说出常见的seq2seq的结构
- 能够使用代码完成基础的seq2seq的结构
是由和两个RNN的组成的。其中负责对输入句子的理解,转化为,负责对理解后的句子的向量进行处理,解码,获得输出。上述的过程和我们大脑理解东西的过程很相似,
那么此时,就有一个问题,在的过程中得到的作为的输入,那么这样一个输入,怎么能够得到多个输出呢?
其实就是那么循环什么时候停止呢?
在训练数据集中,可以再输出的最后面添加一个结束符,如果遇到该结束符,则可以终止循环这个结束符只是一个标记,很多人也会使用
总之:模型中的接受一个长度为M的序列,得到1个 ,之后把这一个转化为长度为N的序列作为输出,从而构成一个的模型,能够处理很多不定长输入输出的问题,比如:
下面,我们通过一个简单的列子,来看看普通的模型应该如何实现。
需求:完成一个模型,实现往模型输入一串数字字符,输出这串数字字符+0(不靠字符串拼接)
例如:
- 输入,输出;
- 输入,输出
2.1 实现流程
- 准备数据集和,其中使用下面2中的转换序列函数
- 编写文本转化为序列函数(即数字字符串转化为数字序列,)
- 完成编码器
- 完成解码器
- 完成seq2seq模型
- 完成模型训练函数,进行训练
- 完成模型评估函数,进行模型评估
2.2 准备数据集和
这里,我们使用随机创建的的整型,来准备数据集
输出结果如下:
通过随机数的结果,可以看到,大部分的数字长度为8,如果在目标值后面添加上0和EOS之后,最大长度为10
所以常见config配置文件,添加上,方便后续的修改
2.3 数字字符串转化为序列
由于输入的是数字字符串,为了把这些数字字符串中单个数字字符和词典中的真实数字进行对应
那么我们需要做的就是:
- 把字符串对应为数字序列
- 把数字序列转化为字符串
完成逻辑和之前文本情感分类中相同,实现上述逻辑如下:
输出结果如下:
此时在和文件添加修改代码如下,使得在数据集加载获得后每个batch数据就已经序列化:
其中collate_fn方法全部需要注意:
- 需要对batch中的数据进行排序,根据数据的真实长度进行降序排序(后面需要用到)
- 需要调用的方法,把文本进行序列化的操作,同时target需要进行的操作
- 最后返回序列的LongTensor格式一遍后续可以记录梯度
运行文件结果如下:
这里解释下,以batch中一个样本数字字符串为例:
原本样本input为:[‘8’, ‘2’, ‘0’, ‘7’, ‘6’, ‘3’, ‘1’, ‘2’]
填充到max_len=10长度后为:[‘8’, ‘2’, ‘0’, ‘7’, ‘6’, ‘3’, ‘1’, ‘2’,‘PAD’,‘PAD’]
num2sequence后为:[12, 6, 4, 11, 10, 7, 5, 6, 1, 1]原本样本target为:[‘8’, ‘2’, ‘0’, ‘7’, ‘6’, ‘3’, ‘1’, ‘2’,‘0’]
填充到max_len=10长度后为:[‘8’, ‘2’, ‘0’, ‘7’, ‘6’, ‘3’, ‘1’, ‘2’,‘0’,‘EOS’]
num2sequence后为:[12, 6, 4, 11, 10, 7, 5, 6, 4, 3]
2.4 准备编码器
编码器(encoder)的目的就是为了对文本进行编码,把编码后的结果交给后续的程序使用,所以在这里我们可以使用的结构来使用,使用最后一个的输出()作为
注意点:
- Embedding和GRU的参数,这里我们让GRU中batch放在前面
- 输出结果的形状
- 在LSTM和GRU中,每个的输入会进行计算,得到结果,整个过程是一个和句子长度相关的一个循环,手动实现速度较慢
- pytorch中实现了 对padding后的句子进行打包的操作能够更快获得LSTM或GRU的结果
- 同时实现了对打包的内容进行解包的操作
- 使用过程中需要对batch中的内容按照句子的长度降序排序
关于上述两个api详细介绍请见:pytorch中如何处理RNN输入变长序列padding
实现代码如下:
其中pack原理即encoder使得batch样本数据形状变化和获得数据如下图:
2.5 实现解码器
解码器主要负责实现对编码之后结果的处理,得到预测值,为后续计算损失做准备
此时需要思考:
- 使用什么样的损失函数,预测值需要是什么格式的
- 结合之前的经验,我们可以理解为当前的问题是一个分类的问题,即每次的输出其实对选择一个概率最大的词
- 真实值的形状是,从而我们知道输出的结果需要是一个的形状
- 即预测值的最后一个维度进行计算log_softmax,然后和真实值进行相乘,从而得到损失
- 如何把编码结果进行操作,得到预测值。解码器也是一个RNN,即也可以使用LSTM or GRU的结构,所以在解码器中:
- 通过循环,每次计算的一个time step的内容
- 编码器的结果作为初始的隐层状态,另外定义一个的全为的数据作为最开始的输入,告诉解码器,要开始工作了
- 通过解码器预测一个输出(最后结束之后会通过一个全连接层进行形状的调整,调整为),把这个输出作为输入再使用解码器进行解码
- 上述是一个循环,循环次数就是句子的最大长度,那么就可以得到个输出
- 把所有输出的结果进行concate,得到存放在中
- 在RNN的训练过程中,使用前一个预测的结果作为下一个step的输入,可能会导致,如果提高模型的收敛速度?
- 可以考虑在训练的过程中,把真实值作为下一步的输入,这样可以避免
- 同时在使用真实值的过程中,仍然使用预测值作为下一步的输入,两种输入随机使用
- 上述这种机制我们把它称为,就像是一个指导老师,在每一步都会对我们的行为进行纠偏,从而达到在多次训练之后能够需要其中的规律
输出结果如下(这里刚开始仅第一轮第一个batch跑一遍Rnn,还没训练,所以误差很大):
2.6 完成seq2seq模型
调用之前的和,完成模型的搭建
2.7 完成训练逻辑
思路流程和之前相同:
输出结果如下(loss后期出现震荡,猜想可能这一个batch数据是脏数据):
2.8 完成模型评估与预测逻辑
完成评估逻辑,和decoder中的训练过程稍微不同,可以在其中新建的方法,传入,得到预测的结果
之后再seq2seq的model中,添加的逻辑
创建,完成模型评估的逻辑
在model训练完5个epoch后,评估输出如下:
3.1
2.
3.
4.
5.
6.
7.
8.
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/6074.html