# 10.9 编码器—解码器(seq2seq)
我们已经在前两节中表征并变换了不定长的输入序列。但在自然语言处理的很多应用中,输入和输出都可以是不定长序列。以机器翻译为例,输入可以是一段不定长的英语文本序列,输出可以是一段不定长的法语文本序列,例如
> 英语输入:“They”、“are”、“watching”、“.”
> 法语输出:“Ils”、“regardent”、“.”
当输入和输出都是不定长序列时,我们可以使用编码器—解码器(encoder-decoder)[1] 或者seq2seq模型 [2]。这两个模型本质上都用到了两个循环神经网络,分别叫做编码器和解码器。编码器用来分析输入序列,解码器用来生成输出序列。
图10.8描述了使用编码器—解码器将上述英语句子翻译成法语句子的一种方法。在训练数据集中,我们可以在每个句子后附上特殊符号“<eos>”(end of sequence)以表示序列的终止。编码器每个时间步的输入依次为英语句子中的单词、标点和特殊符号“<eos>”。图10.8中使用了编码器在最终时间步的隐藏状态作为输入句子的表征或编码信息。解码器在各个时间步中使用输入句子的编码信息和上个时间步的输出以及隐藏状态作为输入。我们希望解码器在各个时间步能正确依次输出翻译后的法语单词、标点和特殊符号"<eos>"。需要注意的是,解码器在最初时间步的输入用到了一个表示序列开始的特殊符号"<bos>"(beginning of sequence)。
:-: ![](https://img.kancloud.cn/18/3e/183e20c4ec122fc8a66889b697136bc5.svg)
<div align=center>图10.8 使用编码器—解码器将句子由英语翻译成法语。编码器和解码器分别为循环神经网络</div>
接下来,我们分别介绍编码器和解码器的定义。
## 10.9.1 编码器
编码器的作用是把一个不定长的输入序列变换成一个定长的背景变量`$ \boldsymbol{c} $`,并在该背景变量中编码输入序列信息。常用的编码器是循环神经网络。
让我们考虑批量大小为1的时序数据样本。假设输入序列是`$ x_1,\ldots,x_T $`,例如`$ x_i $` 是输入句子中的第`$ i $`个词。在时间步`$ t $`,循环神经网络将输入`$ x_t $`的特征向量`$ \boldsymbol{x}_t $`和上个时间步的隐藏状态`$ \boldsymbol{h}_{t-1} $`变换为当前时间步的隐藏状态`$ \boldsymbol{h}_t $`。我们可以用函数`$ f $`表达循环神经网络隐藏层的变换:
```[tex]
\boldsymbol{h}_t = f(\boldsymbol{x}_t, \boldsymbol{h}_{t-1}).
```
接下来,编码器通过自定义函数$q$将各个时间步的隐藏状态变换为背景变量
```[tex]
\boldsymbol{c} = q(\boldsymbol{h}_1, \ldots, \boldsymbol{h}_T).
```
例如,当选择`$ q(\boldsymbol{h}_1, \ldots, \boldsymbol{h}_T) = \boldsymbol{h}_T $`时,背景变量是输入序列最终时间步的隐藏状态`$ \boldsymbol{h}_T $`。
以上描述的编码器是一个单向的循环神经网络,每个时间步的隐藏状态只取决于该时间步及之前的输入子序列。我们也可以使用双向循环神经网络构造编码器。在这种情况下,编码器每个时间步的隐藏状态同时取决于该时间步之前和之后的子序列(包括当前时间步的输入),并编码了整个序列的信息。
## 10.9.2 解码器
刚刚已经介绍,编码器输出的背景变量 `$ \boldsymbol{c} $`编码了整个输入序列` $x_1, \ldots, x_T $`的信息。给定训练样本中的输出序列`$ y_1, y_2, \ldots, y_{T'} $`,对每个时间步`$ t' $`(符号与输入序列或编码器的时间步`$ t $`有区别),解码器输出`$ y_{t'} $`的条件概率将基于之前的输出序列`$ y_1,\ldots,y_{t'-1} $`和背景变量`$ \boldsymbol{c} $`,即`$ P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}) $`。
为此,我们可以使用另一个循环神经网络作为解码器。在输出序列的时间步$t^\prime$,解码器将上一时间步的输出`$ y_{t^\prime-1} $`以及背景变量`$ \boldsymbol{c} $`作为输入,并将它们与上一时间步的隐藏状态`$ \boldsymbol{s}_{t^\prime-1} $`变换为当前时间步的隐藏状态`$ \boldsymbol{s}_{t^\prime} $`。因此,我们可以用函数`$ g $`表达解码器隐藏层的变换:
```[tex]
\boldsymbol{s}_{t^\prime} = g(y_{t^\prime-1}, \boldsymbol{c}, \boldsymbol{s}_{t^\prime-1}).
```
有了解码器的隐藏状态后,我们可以使用自定义的输出层和softmax运算来计算`$ P(y_{t^\prime} \mid y_1, \ldots, y_{t^\prime-1}, \boldsymbol{c}) $`,例如,基于当前时间步的解码器隐藏状态 `$ \boldsymbol{s}_{t^\prime} $`、上一时间步的输出`$ y_{t^\prime-1} $`以及背景变量`$ \boldsymbol{c} $`来计算当前时间步输出`$ y_{t^\prime} $`的概率分布。
## 10.9.3 训练模型
根据最大似然估计,我们可以最大化输出序列基于输入序列的条件概率
```[tex]
\begin{aligned}
P(y_1, \ldots, y_{T'} \mid x_1, \ldots, x_T)
&= \prod_{t'=1}^{T'} P(y_{t'} \mid y_1, \ldots, y_{t'-1}, x_1, \ldots, x_T)\\
&= \prod_{t'=1}^{T'} P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}),
\end{aligned}
```
并得到该输出序列的损失
```[tex]
-\log P(y_1, \ldots, y_{T'} \mid x_1, \ldots, x_T) = -\sum_{t'=1}^{T'} \log P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}),
```
在模型训练中,所有输出序列损失的均值通常作为需要最小化的损失函数。在图10.8所描述的模型预测中,我们需要将解码器在上一个时间步的输出作为当前时间步的输入。与此不同,在训练中我们也可以将标签序列(训练集的真实输出序列)在上一个时间步的标签作为解码器在当前时间步的输入。这叫作强制教学(teacher forcing)。
## 小结
* 编码器-解码器(seq2seq)可以输入并输出不定长的序列。
* 编码器—解码器使用了两个循环神经网络。
* 在编码器—解码器的训练中,可以采用强制教学。
## 参考文献
[1] Cho, K., Van Merriënboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., & Bengio, Y. (2014). Learning phrase representations using RNN encoder-decoder for statistical machine translation. arXiv preprint arXiv:1406.1078.
[2] Sutskever, I., Vinyals, O., & Le, Q. V. (2014). Sequence to sequence learning with neural networks. In Advances in neural information processing systems (pp. 3104-3112).
-----------
> 注:本节与原书基本相同,[原书传送门](https://zh.d2l.ai/chapter_natural-language-processing/seq2seq.html)
- Home
- Introduce
- 1.深度学习简介
- 深度学习简介
- 2.预备知识
- 2.1环境配置
- 2.2数据操作
- 2.3自动求梯度
- 3.深度学习基础
- 3.1 线性回归
- 3.2 线性回归的从零开始实现
- 3.3 线性回归的简洁实现
- 3.4 softmax回归
- 3.5 图像分类数据集(Fashion-MINST)
- 3.6 softmax回归的从零开始实现
- 3.7 softmax回归的简洁实现
- 3.8 多层感知机
- 3.9 多层感知机的从零开始实现
- 3.10 多层感知机的简洁实现
- 3.11 模型选择、反向传播和计算图
- 3.12 权重衰减
- 3.13 丢弃法
- 3.14 正向传播、反向传播和计算图
- 3.15 数值稳定性和模型初始化
- 3.16 实战kaggle比赛:房价预测
- 4 深度学习计算
- 4.1 模型构造
- 4.2 模型参数的访问、初始化和共享
- 4.3 模型参数的延后初始化
- 4.4 自定义层
- 4.5 读取和存储
- 4.6 GPU计算
- 5 卷积神经网络
- 5.1 二维卷积层
- 5.2 填充和步幅
- 5.3 多输入通道和多输出通道
- 5.4 池化层
- 5.5 卷积神经网络(LeNet)
- 5.6 深度卷积神经网络(AlexNet)
- 5.7 使用重复元素的网络(VGG)
- 5.8 网络中的网络(NiN)
- 5.9 含并行连结的网络(GoogLeNet)
- 5.10 批量归一化
- 5.11 残差网络(ResNet)
- 5.12 稠密连接网络(DenseNet)
- 6 循环神经网络
- 6.1 语言模型
- 6.2 循环神经网络
- 6.3 语言模型数据集(周杰伦专辑歌词)
- 6.4 循环神经网络的从零开始实现
- 6.5 循环神经网络的简单实现
- 6.6 通过时间反向传播
- 6.7 门控循环单元(GRU)
- 6.8 长短期记忆(LSTM)
- 6.9 深度循环神经网络
- 6.10 双向循环神经网络
- 7 优化算法
- 7.1 优化与深度学习
- 7.2 梯度下降和随机梯度下降
- 7.3 小批量随机梯度下降
- 7.4 动量法
- 7.5 AdaGrad算法
- 7.6 RMSProp算法
- 7.7 AdaDelta
- 7.8 Adam算法
- 8 计算性能
- 8.1 命令式和符号式混合编程
- 8.2 异步计算
- 8.3 自动并行计算
- 8.4 多GPU计算
- 9 计算机视觉
- 9.1 图像增广
- 9.2 微调
- 9.3 目标检测和边界框
- 9.4 锚框
- 10 自然语言处理
- 10.1 词嵌入(word2vec)
- 10.2 近似训练
- 10.3 word2vec实现
- 10.4 子词嵌入(fastText)
- 10.5 全局向量的词嵌入(Glove)
- 10.6 求近义词和类比词
- 10.7 文本情感分类:使用循环神经网络
- 10.8 文本情感分类:使用卷积网络
- 10.9 编码器--解码器(seq2seq)
- 10.10 束搜索
- 10.11 注意力机制
- 10.12 机器翻译