循环神经网络模型 (RNN)(TensorFlow)
我们学习了
其中hidden state
),也称为隐藏变量(hidden variable
),它存储了到时间步
对于函数recurrent neural networks,RNNs
)是具有隐状态的神经网络。
无隐状态的循环神经网络
让我们来看一看只有但隐藏层的多层感知机。设隐藏层的激活函数为
在上面公式中,我们拥有的隐藏层权重参数为
其中,
有隐状态的循环神经网络
有了隐状态后,情况就完全不同了。假设我们在时间步
多添加了一项hidden state
)。由于在当前时间步中,隐状态使用的定义与前一个时间步中使用的定义相同,因此是循环的(recurrent
)。于是基于循环计算的隐状态神经网络被命名为 循环神经网络(recurrent neural network
)。在循环神经网络中执行计算的层称为循环层(recurrent layer
)。有许多不同的方法可以构建循环神经网络,输出层的输出类似于多层感知机中的计算:
循环神经网络的参数包括隐藏层的权重
下图展示了循环神经网络在三个相邻时间步的计算逻辑。在任意时间步
- 拼接当前时间步
的输入 和前一时间步 的隐状态 ; - 将拼接的结果送入带有激活函数
的全连接层。全连接层的输出是当前时间步 的隐状态 。
在本例中,模型参数是和 的拼接,以及 的偏置。当前时间步隐状态 将参与计算下一时间步 的隐状态 。而且 还将送入全连接输出层,用于计算当前时间步 的输出 。
基于循环神经网络的字符级语言模型
回想一下语言模型,我们的目标是根据过去的和当前的词元预测下一个词元,因此我们将原始序列移位一个词元作为标签。Bengio
等人首先提出使用神经网络进行语言建模。接下来,我们看一下如何使用循环神经网络来构建语言模型。设小批量大小为1
,批量中的文本序列为“machine”
。为了简化后续部分的训练,我们考虑使用字符级语言模型(character-level language model
),将文本词元化为字符而不是单词。下图演示了如何通过基于字符级语言建模的循环神经网络,使用当前的和先前的字符预测下一个字符。
在训练过程中,我们对每个时间步的输出层的输出进行softmax
操作,然后利用交叉熵损失计算模型输出和标签之间的误差。由于隐藏层中隐状态的循环计算,上图中的第3个时间步的输出“m”“a”
和“c”
确定。由于训练数据中这个文本序列的下一个字符是“h”
,因此第3
个时间步的损失将取决于下一个字符的概率分布,而下一个字符是基于特征序列“m”“a”“c”
和这个时间步的标签“h”
生成的。在实践中,我们使用的批量大小为
困惑度(Perplexity)
最后,让我们讨论如何度量语言模型的质量。我们在引入·回归时定义了熵、惊异和交叉熵,并在信息论中讨论了更多的信息论知识。如果想要压缩文本,我们可以根据当前词元集预测的下一个词元。一个更好的语言模型应该能让我们更准确地预测下一个词元。因此,它应该允许我们在压缩序列时花费更少的比特。所以我们可以通过一个序列中所有的
其中perplexity
)的量。
困惑度的最好的理解是“下一个词元的实际选择数的调和平均数”。
- 在最好的情况下,模型总是完美地估计标签词元的概率为
1
。在这种情况下,模型的困惑度为1
。 - 在最坏的情况下,模型总是预测标签词元的概率为
0
。在这种情况下,困惑度是正无穷大。 - 在基线上,该模型的预测是词表的所有可用词元上的均匀分布。在这种情况下,困惑度等于词表中唯一词元的数量。事实上,如果我们在没有任何压缩的情况下存储序列,这将是我们能做的最好的编码方式。因此,这种方式提供了一个重要的上限,而任何实际模型都必须超越这个上限。
通过时间反向传播
到目前为止,我们已经反复提到像梯度爆炸或梯度消失,以及需要对循环神经网络分离梯度。循环神经网络中的前向传播相对简单。通过时间反向传播(backpropagation through time,BPTT
)实际上是循环神经网络中反向传播技术的一个特定应用。它要求我们将循环神经网络的计算图一次展开一个时间步,以获得模型变量和参数之间的依赖关系。然后,基于链式法则,应用反向传播来计算和存储梯度。由于序列可能相当长,因此依赖关系也可能相当长。例如,某个1000
个字符的序列,其第一个词元可能会对最后位置的词元产生重大影响。这在计算上是不可行的(它需要的时间和内存都太多了),并且还需要超过1000
个矩阵的乘积才能得到非常难以捉摸的梯度。这个过程充满了计算与统计的不确定性。
循环神经网络的梯度分析
我们从一个描述循环神经网络工作原理的简化模型开始,此模型忽略了隐状态的特性及其更新方式的细节。这里的数学表示没有像过去那样明确地区分标量、向量和矩阵,因为这些细节对于分析并不重要, 反而只会使本小节中的符号变得混乱。在简化模型中我们将时间步
其中
对于反向传播,问题则有点棘手,特别是当我们计算目标函数
在上图中乘积的第一项和第二项很容易计算,而第三项
为了导出上述梯度,假设我们有三个序列
基于下列公式替换
以上公式中梯度计算,满足
虽然我们可以使用链式法则递归地计算
完全计算
显然,我们可以仅仅计算上面公式中的全部总和,然而,这样的计算非常缓慢,并且可能会发生梯度爆炸,因为初始条件的微小变化就可能会对结果产生巨大的影响。也就是说,我们可以观察到类似于蝴蝶效应的现象,即初始条件的很小变化就会导致结果发生不成比例的变化。这对于我们想要估计的模型而言是非常不可取的。毕竟,我们正在寻找的是能够很好地泛化高稳定性模型的估计器。因此,在实践中,这种方法几乎从未使用过。
截断时间步
或者,我们可以在
随机截断
最后,我们可以用一个随机变量替换
从
遗憾的是,虽然随机截断在理论上具有吸引力,但很可能是由于多种因素在实践中并不比常规截断更好。首先,在对过去若干个时间步经过反向传播后,观测结果足以捕获实际的依赖关系。其次,增加的方差抵消了时间步数越多梯度越精确的事实。第三,我们真正想要的是只有短范围交互的模型。因此,模型需要的正是截断的通过时间反向传播方法所具备的轻度正则化效果。
通过时间反向传播的细节
我们看一下通过时间反向传播问题的细节。下面我们将展示如何计算目标函数相对于所有分解模型参数的梯度。为了保持简单,我们考虑一个没有偏置参数的循环神经网络,其在隐藏层中的激活函数使用恒等映射(
其中权重参数为
为了在循环神经网络的计算过程中可视化模型变量和参数之间的依赖关系,我们可以为模型绘制一个计算图,如下图所示。例如,时间步3
的隐状态
正如刚才所说,上图中的模型参数是
现在,我们可以计算目标函数关于输出层中参数
其中
当目标函数
为了进行分析,对于任何时间步
我们可以从以上公式中看到,这个简单的线性例子已经展现了长序列模型的一些关键问题:它陷入到1
的特征值将会消失,大于1
的特征值将会发散。这在数值上是不稳定的,表现形式为梯度消失或梯度爆炸。解决此问题的一种方法是按照计算方便的需要截断时间步长的尺寸。实际上,这种截断是通过在给定数量的时间步之后分离梯度来实现的。从上边的公式表明:目标函数
其中,
总结
对隐状态使用循环计算的神经网络称为循环神经网络(RNN
)。循环神经网络的隐状态可以捕获直到当前时间步序列的历史信息。循环神经网络模型的参数数量不会随着时间步的增加而增加。我们可以使用循环神经网络创建字符级语言模型。我们可以使用困惑度来评价语言模型的质量。“通过时间反向传播”仅仅适用于反向传播在具有隐状态的序列模型。截断是计算方便性和数值稳定性的需要。截断包括:规则截断和随机截断。矩阵的高次幂可能导致神经网络特征值的发散或消失,将以梯度爆炸或梯度消失的形式表现。为了计算的效率,“通过时间反向传播”在计算期间会缓存中间值。