RNN 循环神经网络(上):计算图与网络设计模式

循环神经网络的三种经典结构

Posted by Xiaosheng on June 7, 2017

本文内容摘取自 《Deep Learning》,部分内容有修改。向辛勤翻译本书的 Exacity 团队致敬。

前言

循环神经网络(recurrent neural network,RNN)是一类用于处理序列数据的神经网络。就像卷积网络是专门用于处理网格化数据 $\mathbf{X}$(如一个图像)的神经网络,循环神经网络是专门用于处理序列 $\boldsymbol{x}^{(1)} ,\cdots, \boldsymbol{x}^{(\tau)}$ 的神经网络。正如卷积网络可以很容易地扩展到具有很大宽度和高度的图像,以及处理大小可变的图像,循环网络也可以扩展到更长的序列,大多数循环网络也能处理可变长度的序列。

循环网络充分利用了上世纪 80 年代机器学习和统计模型早期思想的优点:在模型的不同部分共享参数。参数共享使得模型能够扩展到不同形式的样本(这里指不同长度的样本)并进行泛化。

如果在每个时间点都有一个单独的参数,我们不但不能泛化到训练时没有见过序列长度,也不能在时间上共享不同序列长度和不同位置的统计强度。当信息的特定部分会在序列内多个位置出现时,这样的共享尤为重要。

例如,考虑这两句话:”I went to Nepal in 2009” 和 “In 2009, I went to Nepal”。如果让一个机器学习模型提取叙述者去 Nepal 的年份,无论 “2009” 是作为句子的第六个单词还是第二个单词出现,我们都希望模型能认出 “2009” 作为相关资料片段。

假设要训练一个处理固定长度句子的前馈网络,传统的全连接前馈网络会给每个输入特征分配一个单独的参数,所以需要分别学习句子每个位置的所有语言规则。相比之下,循环神经网络在几个时间步内共享相同的权重,不需要分别学习句子每个位置的所有语言规则。

时延神经网络的想法是在 $1$ 维时间序列上使用卷积。卷积操作允许网络跨时间共享参数,但是浅层的。卷积的输出是一个序列,其中输出中的每一项是相邻几项输入的函数。参数共享的概念体现在每个时间步中使用的相同的卷积核。

循环神经网络共享参数的方式为:输出的每一项是前一项的函数,输出的每一项对先前的输出应用相同的更新规则而产生,这种循环方式导致参数通过很深的计算图共享。

为简单起见,我们说的 $\text{RNN}$ 是指在序列上的操作,并且该序列在时刻 $t$(从 $1$ 到 $\tau$)包含向量 $\boldsymbol{x}^{(t)}$。在实际情况中,循环网络通常在序列的小批量上操作, 并且小批量的每项具有不同序列长度 $\tau$。时间步索引不一定是时间,有时仅表示序列中的位置。 $\text{RNN}$ 也可以应用于跨越两个维度的空间数据(如图像)。当应用于涉及时间的数据, 并且将整个序列提供给网络之前就能观察到整个序列时,该网络可具有关于时间向后的连接。

1. 展开计算图

计算图是形式化一组计算结构的方式,如那些涉及将输入和参数映射到输出和损失的计算。递归或循环计算得到的重复结构通常对应于一个事件链,展开(unfolding)这个计算图将导致深度网络结构中的参数共享。

经典动态系统

考虑动态系统的经典形式:

其中 $\boldsymbol{s}^{(t)}$ 称为系统的状态。$\boldsymbol{s}$ 在时刻 $t$ 的定义需要参考时刻 $t − 1$ 时同样的定义,因此上式是循环的。对有限时间步 $\tau$,$\tau − 1$ 次应用这个定义可以展开这个图,例如 $\tau = 3$,我们对上式展开,可以得到:

以这种方式重复应用定义,展开等式,就能得到不涉及循环的表达。下面我们使用传统的有向无环计算图呈现这样的表达。

1

上图将经典动态系统表示为展开的计算图:每个节点表示在某个时刻 $t$ 的状态,并且函数 $f$ 将 $t$ 处的状态映射到 $t + 1$ 处的状态。所有时间步都使用相同的参数(用于参数化 $f$ 的相同 $\boldsymbol{\theta}$ 值)。

循环神经网络

作为另一个例子,让我们考虑由外部信号 $\boldsymbol{x}^{(t)}$ 驱动的动态系统,

我们可以看到,当前状态包含了整个过去序列的信息。

循环神经网络可以通过许多不同的方式建立。很多循环神经网络使用下式或类似的公式定义隐藏单元的值。为了表明状态是网络的隐藏单元,我们使用变量 $\boldsymbol{h}$ 代表状态:

如下图所示,典型 $\text{RNN}$ 会增加额外的架构特性,如读取状态信息 $\boldsymbol{h}$ 进行预测的输出层。

2

上图为没有输出的循环网络。此循环网络只处理来自输入 $\boldsymbol{x}$ 的信息,将其合并到经过时间向前传播的状态 $\boldsymbol{h}$。(左) 回路原理图:网络定义了实时操作的回路,其当前状态可以影响其未来的状态。黑色方块表明在时刻 $t$ 的状态到时刻 $t + 1$ 的状态单个时刻延迟中的相互作用。(右) 网络被视为展开的计算图:其中每一个组件由许多不同的变量表 示,每个时间步一个变量,表示在该时间点组件的状态。每个时间步的每个变量绘制为计算图的一个独立节点。

我们所说的展开是将左图中的回路映射为右图中包含重复组件的计算图的操作,展开图的大小取决于序列长度,可以用一个函数 $g^{(t)}$ 代表经 $t$ 步展开后的循环:

函数 $g^{(t)}$ 将全部的过去序列 $(\boldsymbol{x}^{(t)} , \boldsymbol{x}^{(t−1)} , \boldsymbol{x}^{(t−2)} , . . . , \boldsymbol{x}^{(2)} , \boldsymbol{x}^{(1)} )$ 作为输入来生成当前状态,但是展开的循环架构允许我们将 $g^{(t)}$ 分解为函数 $f$ 的重复应用。因此,展开过程引入两个主要优点:

  1. 无论序列的长度,学成的模型始终具有相同的输入大小,因为它指定的是从一 种状态到另一种状态的转移,而不是在可变长度的历史状态上操作。
  2. 我们可以在每个时间步使用相同参数的相同转移函数 $f$。

这两个因素使得学习在所有时间步和所有序列长度上操作单一的模型 $f$ 是可能的, 而不需要在所有可能时间步学习独立的模型 $g^{(t)}$ 。学习单一的共享模型允许泛化到没有见过的序列长度(没有出现在训练集中),并且估计模型所需的训练样本远远少于不带参数共享的模型。

无论是循环图和展开图都有其用途。循环图简洁。展开图能够明确描述其中的计算流程。展开图还通过显式的信息流动路径帮助说明信息在时间上向前(计算输出和损失)和向后(计算梯度)的思想。

与任务相关的有损摘要

当训练循环网络根据过去预测未来时,网络通常要学会使用 $\boldsymbol{h}^{(t)}$ 作为过去序列(直到 $t$)与任务相关方面的有损摘要。此摘要一般而言一定是有损的,因为其映射任意长度的序列 $(\boldsymbol{x}^{(t)} , \boldsymbol{x}^{(t−1)} , \boldsymbol{x}^{(t−2)} , \cdots , \boldsymbol{x}^{(2)} , \boldsymbol{x}^{(1)} )$ 到一固定长度的向量 $\boldsymbol{h}^{(t)}$。根据不同的训练准则,摘要可能选择性地精确保留过去序列的某些方面。例如,如果在统计语言建模中使用的 $\text{RNN}$,通常给定前一个词预测下一个词,可能没有必要存储时刻 $t$ 前输入序列中的所有信息,而仅仅存储足够预测句子其余部分的信息。最苛刻的情况是要求 $\boldsymbol{h}^{(t)}$ 足够丰富,并能大致恢复输入序列,如自编码器框架。

2. 循环神经网络

循环神经网络的设计模式

基于上面所说的图展开和参数共享的思想,我们可以设计各种循环神经网络。循环神经网络中一些重要的设计模式包括以下几种:

  1. 每个时间步都有输出,并且隐藏单元之间有循环连接的循环网络,如图 A 所示。
  2. 每个时间步都产生一个输出,只有当前时刻的输出到下个时刻的隐藏单元之间有循环连接的循环网络,如图 B 所示。
  3. 隐藏单元之间存在循环连接,但读取整个序列后产生单个输出的循环网络,如图 C 所示。

3

图 A 计算循环网络(将 $\boldsymbol{x}$ 值的输入序列映射到输出值 $\boldsymbol{o}$ 的对应序列) 训练损失的计算图。损失 $L$ 衡量每个 $\boldsymbol{o}$ 与相应的训练目标 $\boldsymbol{y}$ 的距离。当使用 $\text{softmax}$ 输出时,我们假设 $\boldsymbol{o}$ 是未归一化的对数概率,损失 $L$ 内部计算 $\hat{\boldsymbol{y}} = \text{softmax}(\boldsymbol{o})$,并将其与目标 $\boldsymbol{y}$ 比较。$\text{RNN}$ 输入到隐藏的连接由权重矩阵 $\boldsymbol{U}$ 参数化,隐藏到隐藏的循环连接由权重矩阵 $\boldsymbol{W}$ 参数化以及隐藏到输出的连接由权重矩阵 $\boldsymbol{V}$ 参数化。(左) 使用循环连接绘制的 RNN 和它的损失。(右) 同一网络被视为展开的计算图,其中每个节点与一个特定的时间实例相关联。

4

图 B 此类 $\text{RNN}$ 的唯一循环是从输出到隐藏层的反馈连接。在每个时间步 $t$,输入为 $\boldsymbol{x}^{(t)}$,隐藏层激活为 $\boldsymbol{h}^{(t)}$,输出为 $\boldsymbol{o}^{(t)}$,目标为 $\boldsymbol{y}^{(t)}$,损失为 $L^{(t)}$。(左) 回路原理图。(右) 展开的计算图。这样的 $\text{RNN}$ 没有图 A 表示的 $\text{RNN}$ 那样强大(只能表示更小的函数集合),图 A 中的 $\text{RNN}$ 可以选择将其想要的关于过去的任何信息放入隐藏表示 $\boldsymbol{h}$ 中并且将 $\boldsymbol{h}$ 传播到未来。而该图中的 $\text{RNN}$ 被训练为将特定输出值放入 $\boldsymbol{o}$ 中,$\boldsymbol{o}$ 是允许传播到未来的唯一信息,$\boldsymbol{h}$ 仅通过产生的预测间接地连接到当前。$\boldsymbol{o}$ 通常缺乏过去的重要信息,除非它非常高维且内容丰富。这使得该图中的 $\text{RNN}$ 不那么强大,但是它更容易训练,因为每个时间步可以与其他时间步分离训练,允许训练期间更多的并行化。

5

图 C 关于时间展开的循环神经网络,在序列结束时具有单个输出。这样的网络可以用于概括序列并产生用于进一步处理的固定大小的表示。在结束处可能存在目标(如此处所示),或者通过更下游模块的反向传播来获得输出 $\boldsymbol{o}^{(t)}$ 上的梯度。

RNN 的前向传播公式

现在我们研究图 A 中 $\text{RNN}$ 的前向传播公式,假设隐藏单元的激活函数为双曲正切激活函数,并且输出是离散的,如用于预测词或字符的 $\text{RNN}$,表示离散变量的常规方式是把输出 $\boldsymbol{o}$ 作为每个离散变量可能值的非标准化对数概率。然后可以应用 $\text{softmax}$ 函数后续处理,获得标准化后概率的输出向量 $\hat{\boldsymbol{y}}$。

$\text{RNN}$ 从特定的初始状态 $\boldsymbol{h}^{(0)}$ 开始前向传播。从 $t = 1$ 到 $t = \tau$ 的每个时间步,应用以下更新方程:

其中的参数的偏置向量 $\boldsymbol{b}$ 和 $\boldsymbol{c}$ 连同权重矩阵 $\mathbf{U}$、$\mathbf{V}$ 和 $\mathbf{W}$,分别对应于输入到隐藏、 隐藏到输出和隐藏到隐藏的连接。这个循环网络将一个输入序列映射到相同长度的 输出序列。与 $\boldsymbol{x}$ 序列配对的 $\boldsymbol{y}$ 的总损失就是所有时间步的损失之和。例如,$L^{(t)}$ 为给定的 $\boldsymbol{x}^{(1)} , \cdots , \boldsymbol{x}^{(t)}$ 后 $\boldsymbol{y}^{(t)}$ 的负对数似然,则

其中 $p_{\text{model}} (y^{(t)} \mid {\boldsymbol{x}^{(1)} , \cdots , \boldsymbol{x}^{(t)}})$ 需要读取模型输出向量 $\hat{\boldsymbol{y}}^{(t)}$ 中对应于 $y^{(t)}$ 的项。关于各个参数计算这个损失函数的梯度是计算成本很高的操作。梯度计算涉及执行一次前向传播(如在图 A 展开图中从左到右的传播),接着是由右到左的反向传播。运行时间是 $O(\tau)$,并且不能通过并行化来降低,因为前向传播图是固有循序的,每个时间步只能一前一后地计算。前向传播中的各个状态必须保存,直到它们在反向传播中被再次使用,因此内存代价也是 $O(\tau)$。应用于展开图且代价为 $O(\tau)$ 的反向传播算法称为通过时间反向传播(back-propagation through time, BPTT)。因此隐藏单元之间存在循环的网络非常强大但训练代价也很大。

导师驱动过程和输出循环网络

仅在一个时间步的输出和下一个时间步的隐藏单元间存在循环连接的网络(图 B)确实没有那么强大。因为这个网络缺少隐藏到隐藏的循环,它要求输出单元捕捉用于预测未来的关于过去的所有信息,而输出单元被明确地训练成匹配训练集的目标,因此它们不太能捕获关于过去输入历史的必要信息。消除隐藏到隐藏循环的优点在于,任何基于比较时刻 $t$ 的预测和时刻 $t$ 的训练目标的损失函数中的所有时间步都解耦了,因此训练可以并行化,即在各时刻 $t$ 分别计算梯度。因为训练集提供输出的理想值,所以不必先计算前一时刻的输出。

由输出反馈到模型而产生循环连接的模型可用导师驱动过程(teacher forcing)进行训练。训练模型时,导师驱动过程不再使用最大似然准则,而是在时刻 $t + 1$ 接收真实值 $y^{(t)}$ 作为输入(可以通过检查两个时间步的序列得知)。条件最大似然准则是

在这个例子中,同时给定迄今为止的 $\boldsymbol{x}$ 序列和来自训练集的前一 $\boldsymbol{y}$ 值。可以看到在时刻 $t = 2$ 时,模型被训练为最大化 $\boldsymbol{y}^{(2)}$ 的条件概率。因此最大似然在训练时指定正确反馈,而不是将自己的输出反馈到模型。如下图所示:

6

导师驱动过程是一种训练技术,适用于输出与下一时间步的隐藏状态存在连接的 $\text{RNN}$。(左) 训练时,我们将训练集中正确的输出 $\boldsymbol{y}^{(t)}$ 反馈到 $\boldsymbol{h}^{(t+1)}$。(右) 当模型部署后,真正的输出通常是未知的,因此用模型的输出 $\boldsymbol{o}^{(t)}$ 近似正确的输出 $\boldsymbol{y}^{(t)}$,并反馈回模型。

使用导师驱动过程的最初动机是为了在缺乏隐藏到隐藏连接的模型中避免通过时间反向传播。只要模型一个时间步的输出与下一时间步计算的值存在连接, 导师驱动过程仍然可以应用到这些存在隐藏到隐藏连接的模型。然而,只要隐藏单元成为较早时间步的函数,BPTT 算法是必要的。因此训练某些模型时要同时使用导师驱动过程和 BPTT。

如果之后网络在开环(open-loop)模式下使用, 即网络输出(或输出分布的样本)反馈作为输入,那么完全使用导师驱动过程进行训练的缺点就会出现,因为训练期间该网络看到的输入与测试时看到的会有很大的不同。

一种方法是同时使用导师驱动过程和自由运行的输入进行训练,例如在展开循环的输出到输入路径上预测几个步骤的正确目标值,这样网络可以学会考虑在训练时没有接触到的输入条件(如自由运行模式下,自身生成自身),以及将状态映射回使网络几步之后生成正确输出的状态。

另外一种方式是通过随意选择生成值或真实的数据值作为输入以减小训练时和测试时看到的输入之间的差别。这种方法利用了课程学习策略,逐步使用更多生成值作为输入。

基于上下文的 RNN 序列建模

之前已经讨论了将 $t = 1, \cdots , \tau$ 的向量 $\boldsymbol{x}^{(t)}$ 序列作为输入的 $\text{RNN}$,另一种选择是只使用单个向量 $\boldsymbol{x}$ 作为输入。当 $\boldsymbol{x}$ 是一个固定大小的向量时,可以简单地将其看作产生 $\boldsymbol{y}$ 序列 $\text{RNN}$ 的额外输入,将额外输入提供到 $\text{RNN}$ 的一些常见方法是:

  1. 在每个时刻作为一个额外输入
  2. 作为初始状态 $\boldsymbol{h}^{(0)}$
  3. 结合两种方式

第一个也是最常用的方法如下图所示。将固定长度的向量 $\boldsymbol{x}$ 映射到序列 $\boldsymbol{Y}$ 上分布的 $\text{RNN}$。这类 $\text{RNN}$ 适用于很多任务如图注,其中单个图像作为模型的输入,然后产生描述图像的词序列。观察到的输出序列的每个元素 $\boldsymbol{y}^{(t)}$ 同时用作输入(对于当前时间步)和训练期间的目标(对于前一时间步)。

7

输入 $\boldsymbol{x}$ 和每个隐藏单元向量 $\boldsymbol{h}^{(t)}$ 之间的相互作用是通过新引入的权重矩阵 $\boldsymbol{R}$ 参数化的,乘积 $\boldsymbol{x}^{\top}\boldsymbol{R}$ 在每个时间步作为隐藏单元的一个额外输入。我们可以认为 $\boldsymbol{x}$ 的选择(确定 $\boldsymbol{x}^{\top}\boldsymbol{R}$ 值),是有效地用于每个隐藏单元的一个新偏置参数。权重与输入保持独立。

$\text{RNN}$ 可以接收向量序列 $\boldsymbol{x}^{(t)}$ 作为输入, 而不是仅接收单个向量 $\boldsymbol{x}$ 作为输入。$\text{RNN}$ 的前向传播公式描述的 $\text{RNN}$ 对应条件分布 $P(\boldsymbol{y}^{(1)} , \cdots , \boldsymbol{y}^{(\tau)} \mid \boldsymbol{x}^{(1)} , \cdots , \boldsymbol{x}^{(\tau)})$,并在条件独立的假设下这个分布分解为:

为去掉条件独立的假设,我们可以在时刻 $t$ 的输出到时刻 $t + 1$ 的隐藏单元添加连接,如下图所示。

8

将可变长度的 $\boldsymbol{x}$ 值序列映射到相同长度的 $\boldsymbol{y}$ 值序列上分布的条件循环神经网络。对比图 A,此 $\text{RNN}$ 包含从前一个输出到当前状态的连接,这些连接允许此 $\text{RNN}$ 对给定 $\boldsymbol{x}$ 的序列后相同长度的 $\boldsymbol{y}$ 序列上的任意分布建模。图 A 的 $\text{RNN}$ 仅能表示在给定 $\boldsymbol{x}$ 值的情况下,$\boldsymbol{y}$ 值彼此条件独立的分布。

本文内容摘取自 《Deep Learning》,部分内容有修改。向辛勤翻译本书的 Exacity 团队致敬。