【注】参考自邱锡鹏的《神经网络与深度学习》。
1. 简介
循环神经网络(RNN)是一类具有短期记忆能力的神经网络。在循环神经网络中,神经元不但可以接受其他神经元的信息,也可以接受自身的信息,形成具有环路的网络结构。而前馈网络是一种静态网络,不具备记忆能力。
- RNN 能够用于处理时序数据的神经网络,被广泛应用于语音识别、语言模型以及自然语言生成等任务上。
时序数据的长度一般是不固定的,而前馈神经网络要求输入和输出的维数都是固定的,不能任意改变。
- 理论上,前馈神经网络可以模拟任何连续函数,而循环神经网络可以模拟任何程序。
2. 给网络增加记忆能力
为了处理这些时序数据并利用其历史信息,我们需要让网络具有短期记忆能力,一般有三种方法可以给网络增加短期记忆能力:
2.1 延时神经网络
一种简单的利用历史信息的方法是建立一个额外的延时单元,用来存储网络的历史信息(可以包括输入、输出、隐状态等)。这种方式的代表模型为延时神经网络:
- 延时神经网络(TDNN)是在前馈网络中的非输出层都添加一个延时器,记录神经元的最近几次活性值。
- 在第 t 个时刻,第 l 层神经元的活性值依赖于第 l - 1 层神经元的最近 K 个时刻的活性值,即
\begin{array}{c}
\boldsymbol{h}_t^{(l)} = f(\boldsymbol{h}_t^{(l-1)}, \boldsymbol{h}_{t-1}^{(l-1)}, \cdots, \boldsymbol{h}_{t-K}^{(l-1)})
\end{array}
其中,\boldsymbol{h}_t^{(l)} \in \mathbb{R}^{M_l} 表示第 l 层神经元在时刻 t 的活性值,M_l 为第 l 层神经元的数量。
2.2 有外部输入的非线性自回归模型
- 自回归模型(AR)是统计学上常用的一类时间序列模型,用一个变量 \boldsymbol{y}_t 的历史信息来预测自己:
\begin{array}{c}
\boldsymbol{y}_t = w_0 + \sum_{k=1}^K w_k \boldsymbol{y}_{t-k} + \epsilon_t
\end{array}
其中,K 为超参数,w_0, \cdots, w_K 为可学习参数,\epsilon_t \sim \mathcal{N}(0, \sigma^2) 为第 t 个时刻的噪声,方差 \sigma^2 和时间无关。
- 有外部输入的非线性自回归模型(NARX)是自回归模型的扩展,在每个时刻 t 都有一个外部输入 \boldsymbol{x}_t ,产生一个输出 \boldsymbol{y}_t 。NARX 通过一个延时器记录最近 K_x 次的外部输入和最近 K_y 次的输出,第 t 个时刻的输出 \boldsymbol{y}_t 为:
\begin{array}{c}
\boldsymbol{y}_t = f(\boldsymbol{x}_t, \boldsymbol{x}_{t-1}, \cdots, \boldsymbol{x}_{t-K_x}, \boldsymbol{y}_{t-1}, \boldsymbol{y}_{t-2}, \cdots, \boldsymbol{y}_{t-K_y})
\end{array}
其中,f(\cdot) 表示非线性函数,K_x 和K_y 为超参数。
2.3 循环神经网络
循环神经网络(RNN)通过使用带自反馈的神经元,能够处理任意长度的时序数据。
给定一个输入序列 \boldsymbol{x}_{1:T} = (\boldsymbol{x}_1, \boldsymbol{x}_2, \cdots, \boldsymbol{x}_t, \cdots, \boldsymbol{x}_T) ,循环神经网络通过以下公式更新带反馈边的隐藏层的活性值 \boldsymbol{h}_t :
\begin{array}{c}
\boldsymbol{h}_t = f(\boldsymbol{h}_{t-1}, \boldsymbol{x}_t)
\end{array}
其中,\boldsymbol{h}_0 = 0 ,f(\cdot) 为一个非线性函数。
3. 简单循环网络
简单循环网络(SRN)是只有一个隐藏层的神经网络。
令向量\boldsymbol{x}_t \in \mathbb{R}^M 表示在时刻 t 时网络的输入,\boldsymbol{h}_t \in \mathbb{R}^D 表示隐藏层状态,则 \boldsymbol{h}_t 不仅和当前时刻的输入 \boldsymbol{x}_t 相关,也和上一个时刻的隐藏层状态 \boldsymbol{h}_{t-1} 相关,SRN 在时刻 t 的更新公式为:
\begin{array}{c}
\boldsymbol{z}_t = \boldsymbol{U}\boldsymbol{h}_{t-1} + \boldsymbol{W} \boldsymbol{x}_t + \boldsymbol{b} \\
\boldsymbol{h}_t = f(\boldsymbol{z}_t)
\end{array}
其中 \boldsymbol{z}_t 为隐藏层的净输入, \boldsymbol{U} \in \mathbb{R}^{D \times D} 为状态 - 状态权重矩阵,\boldsymbol{W} \in \mathbb{R}^{D \times M} 为状态 - 输入权重矩阵,\boldsymbol{b} \in \mathbb{R}^{D} 为偏置向量,f(\cdot) 为非线性激活函数。
- 如果将每个时刻的状态都看作前馈神经网络的一层,循环神经网络可以看作在时间维度上权值共享的神经网络。
循环神经网络的拟合能力也十分强大。一个完全连接的循环网络是任何非线性动力系统的近似器。
3.1 循环神经网络的通用近似定理
如果一个完全连接的循环神经网络有足够数量的 sigmoid 型隐藏单元,它可以以任意的准确度去近似任何一个非线性动力系统
\begin{array}{c}
\boldsymbol{s}_t = g(\boldsymbol{s}_{t-1}, \boldsymbol{x}_t) \\
\boldsymbol{y}_t = o(\boldsymbol{s}_t)
\end{array}
其中 \boldsymbol{s}_t 为每个时刻的隐状态,\boldsymbol{x}_t 是外部输入,g(\cdot) 是可测的状态转换函数,o(\cdot) 是连续输出函数,并且对状态空间的紧致性没有限制。
证明:
定义一个全连接的 SRN,其输入为 \boldsymbol{x}_t ,输出为 \boldsymbol{y}_t ,则:
\begin{array}{c}
\boldsymbol{h}_t = f(\boldsymbol{U} \boldsymbol{h}_{t-1} + \boldsymbol{W} \boldsymbol{x}_t + \boldsymbol{b}) \\
\boldsymbol{y}_t = \boldsymbol{V} \boldsymbol{h}_t
\end{array}
其中 \boldsymbol{h} 为隐状态,f(\cdot) 为非线性激活函数,\boldsymbol{U},\boldsymbol{W},\boldsymbol{b} 为网络参数。
3.2 图灵完备定理
所有的图灵机都可以被一个由使用 Sigmoid 型激活函数的神经元构成的全连接循环网络来进行模拟。
4. 循环神经网络应用分类
4.1 序列到类别模式
序列到类别模式主要用于序列数据的分类问题:输入为序列,输出为类别。
- 假设一个样本 \boldsymbol{x}_{1:T} = (\boldsymbol{x}_1, \cdots, \boldsymbol{x}_T) 为一个长度为 T 的序列,输出为一个类别 y \in \{1,\cdots,C\} ,则可以将样本 \boldsymbol{x} 按不同时刻输入到循环神经网络中,并得到不同时刻的隐藏状态 \boldsymbol{h}_1, \cdots, \boldsymbol{h}_T 。
- 我们可以将 \boldsymbol{h}_T 看作整个序列的最终表示输入给分类器 g(\cdot)
进行分类:
\begin{array}{c}
\hat{y} = g(\boldsymbol{h}_T)
\end{array}
或者对整个序列的所有状态进行平均并用这个平均状态来作为整个序列的表示输入给分类器 g(\cdot) 进行分类:
\begin{array}{c}
\hat{y} = g(\frac{1}{T} \sum_{t=1}^T \boldsymbol{h}_t)
\end{array}
4.2 同步的序列到序列模式
同步的序列到序列模式主要用于序列标注(Sequence Labeling)任务,即每一时刻都有输入和输出,输入序列和输出序列的长度相同。
- 在同步的序列到序列模式中,输入为一个长度为 T 的序列 \boldsymbol{x}_{1:T} = (\boldsymbol{x}_1, \cdots, \boldsymbol{x}_T) ,输出为序列 y_{1:T} = (y_1,\cdots,y_T) 。样本 \boldsymbol{x} 按不同时刻输入到循环神经网络中,并得到不同时刻的隐状态 \boldsymbol{h}_1, \cdots, \boldsymbol{h}_T 。每个时刻的隐状态 \boldsymbol{h}_t 代表了当前时刻和历史的信息,并输入给分类器 g(\cdot) 得到当前时刻的标签 \hat{y}_t :
\begin{array}{c}
\hat{y}_t = g(\boldsymbol{h}_t), \quad \forall t \in [1, T]
\end{array}
4.3 异步的序列到序列模式
异步的序列到序列模式也称为编码器 - 解码器(Encoder-Decoder)模型,即输入序列和输出序列不需要有严格的对应关系,也不需要保持相同的长度。
- 在异步的序列到序列模式中,输入为一个长度为T 的序列 \boldsymbol{x}_{1:T} = (\boldsymbol{x}_1, \cdots, \boldsymbol{x}_T) ,输出为长度为 M 的序列 y_{1:M} = (y_1,\cdots,y_M) 。
- 异步的序列到序列模式一般通过先编码后解码的方式来实现。先将样本 \boldsymbol{x} 按不同时刻输入到一个循环神经网路(编码器)中,并得到其编码 \boldsymbol{h}_T ;然后再使用另一个循环神经网络(解码器)得到输出序列 \hat{y}_{1:M} 。
- 为了建立输出序列之间的依赖关系,在解码器中通常使用非线性的自回归模型。令 f(\cdot) 和 f(\cdot) 分别为用作编码器和解码器的循环神经网络,则编码器 - 解码器模型可以写为:
\begin{array}{c}
\boldsymbol{h}_t = f_1(\boldsymbol{h}_{t-1}, \boldsymbol{x}_t), \quad \forall t \in [1,T] \\
\boldsymbol{h}_{T+t} = f_2(\boldsymbol{h}_{T+t-1}, \hat{y}_{t-1}), \quad \forall t \in [1,M] \\
\hat{y}_t = g(\boldsymbol{h}_{T+t}) \quad \forall t \in [1, M]
\end{array}
其中 g(\cdot) 为分类器,\hat{\boldsymbol{y}}_t 为预测输出 \hat{\boldsymbol{y}}_t 的向量表示。在解码器通常采用自回归模型,每个时刻的输入为上一时刻的预测结果 \hat{y}_{t-1} 。
5. 计算梯度
以同步的序列到序列模式为例,给定一个训练样本 (\boldsymbol{x}, \boldsymbol{y}) ,其中 \boldsymbol{x}_{1:T} = (\boldsymbol{x}_1, \cdots, \boldsymbol{x}_T) 为长度是 T 的输入序列,y_{1:T} = (y_1, \cdots, y_T) 是长度为 T 的标签序列。
\begin{array}{c}
\mathcal{L}_t = \mathcal{L}(y_t, g(\boldsymbol{h}_t))
\end{array}
其中g(\boldsymbol{h}_t) 为第 t 时刻的输出,\mathcal{L} 为可微分的损失函数。则整个序列的损失函数为:
\begin{array}{c}
\mathcal{L} = \sum_{t=1}^T \mathcal{L}_t
\end{array}
- 整个序列的损失函数 \mathcal{L} 关于参数 \boldsymbol{U}, \boldsymbol{W}, \boldsymbol{b} 的梯度为:
\begin{array}{c}
\frac{\partial \mathcal{L}}{\partial \boldsymbol{U}} = \sum_{t=1}^T \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{U}} \\
\frac{\partial \mathcal{L}}{\partial \boldsymbol{W}} = \sum_{t=1}^T \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{W}} \\
\frac{\partial \mathcal{L}}{\partial \boldsymbol{b}} = \sum_{t=1}^T \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{b}}
\end{array}
即每个时刻损失 \mathcal{L}_t 对参数\boldsymbol{U}, \boldsymbol{W}, \boldsymbol{b} 的偏导数之和。
5.1 随时间反向传播算法(BPTT)
BPTT 算法将循环神经网络看作一个展开的多层前馈网络,其中「每一层」对应循环网络中的「每个时刻」;这样,循环神经网络就可以按照前馈网络中的反向传播算法计算参数梯度。
\frac{\partial \mathcal{L}_t}{\partial \boldsymbol{U}}, \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{W}}, \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{b}} 隐藏层每个时刻 k(1 \leq k \leq t) 的净输入 \boldsymbol{z}_k = \boldsymbol{U}\boldsymbol{h}_{k-1} + \boldsymbol{W}\boldsymbol{x}_k + \boldsymbol{b} ,因此第 t 时刻的损失函数 \mathcal{L}_t 关于参数 u_{ij} 的梯度为:
\begin{array}{c}
\frac{\partial \mathcal{L}_t}{\partial u_{ij}} = \sum_{k=1}^t \frac{\partial^+ \boldsymbol{z}_k}{\partial u_{ij}} \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{z}_k}
\end{array}
其中,\frac{\partial^+ \boldsymbol{z}_k}{\partial u_{ij}} 表示「直接」偏导数,即公式 \boldsymbol{z}_k = \boldsymbol{U}\boldsymbol{h}_{k-1} + \boldsymbol{W}\boldsymbol{x}_k + \boldsymbol{b} 保持 \boldsymbol{h}_{k-1} 不变对 \boldsymbol{h}_{k-1} 求导(因为 \boldsymbol{h}_{k-1} 已经将过去的所有信息都对u_{ij} 进行了求导),从而有
\begin{array}{c}
\frac{\partial^+ \boldsymbol{z}_k}{\partial u_{ij}} = [0, \cdots, [\boldsymbol{h}_{k-1}], \cdots, 0] \triangleq \mathbb{I}_i([\boldsymbol{h}_{k-1}]_j)
\end{array}
其中 [\boldsymbol{h}_{k-1}]_j 为第 k-1 时刻隐状态的第 j 维,\mathbb{I}_{i}(x) 除了第 i 列值为 x 外,其余都为 0 的行向量。
定义误差项 \delta_{t,k} = \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{z}_k} 为第 t 时刻的损失对第 k 时刻隐藏神经层的净输入 \boldsymbol{z}_k 的导数,则当 1 \leq k \lt t 时
\begin{array}{c}
\delta_{t,k} = \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{z}_k} = \frac{\partial \boldsymbol{h}_k}{\partial \boldsymbol{z}_k} \frac{\partial \boldsymbol{z}_{k+1}}{\partial \boldsymbol{h}_k} \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{z}_{k+1}} = \mathrm{diag}(f^{'}(\boldsymbol{z}_k)) \boldsymbol{U}^T \delta_{t,k+1}
\end{array}
将误差项带入前面的公式可得
\begin{array}{c}
\frac{\partial \mathcal{L}_t}{\partial u_{ij}} = \sum_{k=1}^t [\delta_{t,k}]_i [\boldsymbol{h}_{k-1}]_j
\end{array}
写成矩阵形式为
\begin{array}{c}
\frac{\partial \mathcal{L}_t}{\partial \boldsymbol{U}} = \sum_{k=1}^t \delta_{t,k} \boldsymbol{h}_{k-1}^T
\end{array}
最终得到参数梯度为
\begin{array}{c}
\frac{\partial \mathcal{L}}{\partial \boldsymbol{U}} = \sum_{t=1}^T \sum_{k=1}^t \delta_{t,k} \boldsymbol{h}_{k-1}^T \\
\frac{\partial \mathcal{L}}{\partial \boldsymbol{W}} = \sum_{t=1}^T \sum_{k=1}^t \delta_{t,k} \boldsymbol{x}_k^T \\
\frac{\partial \mathcal{L}}{\partial \boldsymbol{b}} = \sum_{t=1}^T \sum_{k=1}^t \delta_{t,k}
\end{array}
5.2 实时循环学习算法(RTRL)
RTRL 通过前向传播的方式来计算梯度。假设循环神经网络中第 t+1 时刻的状态 \boldsymbol{h}_{t+1} 为
\begin{array}{c}
\boldsymbol{h}_{t+1} = f(\boldsymbol{z}_{t+1}) = f(\boldsymbol{U}\boldsymbol{h}_t + \boldsymbol{W}\boldsymbol{x}_{t+1} + \boldsymbol{b})
\end{array}
则其关于参数 \boldsymbol{h}_{t+1} 的偏导数为
\begin{array}{c}
\frac{\partial \boldsymbol{h}_{t+1}}{\partial u_{ij}} = (\frac{\partial^+ \boldsymbol{z}_{t+1}}{\partial u_{ij}} + \frac{\partial \boldsymbol{h}_t}{\partial u_{ij}}\boldsymbol{U}^T) \mathrm{diag}(f^{'}(\boldsymbol{z}_{t+1})) = (\mathbb{I}_i([\boldsymbol{h}_t]_j) + \frac{\partial \boldsymbol{h}_t}{\partial u_{ij}}\boldsymbol{U}^T) \odot (f^{'}(\boldsymbol{z}_{t+1}))^T
\end{array}
其中 \mathbb{I}_i(x) 是除了第 i 列值为 x 外其余值都为 0 的行向量。
最终可得到参数梯度的前向传播计算公式为
\begin{array}{c}
\frac{\partial \mathcal{L}_t}{\partial u_{ij}} = \frac{\partial \boldsymbol{h}_t}{\partial u_{ij}} \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{h}_t} \\
\frac{\partial \mathcal{L}_t}{\partial w_{ij}} = \frac{\partial \boldsymbol{h}_t}{\partial w_{ij}} \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{h}_t} \\
\frac{\partial \mathcal{L}_t}{\partial b_i} = \frac{\partial \boldsymbol{h}_t}{\partial b_i} \frac{\partial \mathcal{L}_t}{\partial \boldsymbol{h}_t}
\end{array}
6. 基于门控的循环神经网络
6.1 长短期记忆网络
长短期记忆网络(LSTM)引入了门控机制来控制信息传递的路径,LSTM 引入了三个门,即「输入门」\boldsymbol{I}_t ,「遗忘门」\boldsymbol{I}_t 和「输出门」\boldsymbol{O}_t :
- 遗忘门 \boldsymbol{F}_t :控制上一个时刻的内部状态 \boldsymbol{C}_{t-1} 需要遗忘多少信息。
- 输入门 \boldsymbol{I}_t :控制当前时刻的候选状态 \tilde{\boldsymbol{C}}_t 有多少信息需要保存。
- 输出门 \boldsymbol{O}_t :控制当前时刻的内部状态 \boldsymbol{C}_t 有多少信息需要输出给外部状态 \boldsymbol{H}_t 。
LSTM 的循环单元结构如下:
6.2 门控循环单元网络
门控循环单元(GRU)网络和 LSTM 不同,GRU 不引入额外的记忆单元,GRU 中包含两个门,即「重置门」\boldsymbol{R}_t 和「更新门」\boldsymbol{R}_t :
- 重置门 \boldsymbol{R}_t :控制了上⼀时间步的隐藏状态如何流⼊当前时间步的候选隐藏状态。重置⻔有助于捕捉时间序列⾥短期的依赖关系 。
- 更新门 \boldsymbol{Z}_t :控制隐藏状态应该如何被包含当前时间步信息的候选隐藏状态所更新。更新⻔有助于捕捉时间序列⾥⻓期的依赖关系 。
GRU 循环单元结构如下:
7. 深层循环神经网络
7.1 堆叠循环神经网络
\begin{array}{c}
\boldsymbol{h}_t^{(l)} = f(\boldsymbol{U}^{(l)} \boldsymbol{h}_{t-1}^{(l)} + \boldsymbol{W}^{(l)} \boldsymbol{h}_t^{(l-1)} + \boldsymbol{b}^{(l)})
\end{array}
堆叠循环神经网络(SRNN)结构如下:
7.2 双向循环神经网络
\begin{array}{c}
\boldsymbol{h}_t^{(1)} = f(\boldsymbol{U}^{(1)} \boldsymbol{h}_{t-1}^{(1)} + \boldsymbol{W}^{(1)} \boldsymbol{x}_t + \boldsymbol{b}^{(1)}) \\
\boldsymbol{h}_t^{(2)} = f(\boldsymbol{U}^{(2)} \boldsymbol{h}_{t+1}^{(2)} + \boldsymbol{W}^{(2)} \boldsymbol{x}_t + \boldsymbol{b}^{(2)}) \\
\boldsymbol{h}_t = \boldsymbol{h}_t^{(1)} \oplus \boldsymbol{h}_t^{(2)}
\end{array}
双向循环神经网络结构(Bi-RNN)如下: