(五)循环神经网络 -- 2 循环神经网络

2. 循环神经网络

在上一节的 n n n元语法中,时间步 t t t的词 w t w_t wt基于前面所有词的条件概率只考虑了最近时间步的 n − 1 n-1 n1个词。
若要考虑比 t − ( n − 1 ) t-(n-1) t(n1)更早时间步的词对 w t w_t wt的可能影响,需要增大 n n n,但模型参数的数量将随之呈指数级增长。

本节将介绍的循环神经网络,并非刚性地记忆所有固定长度的序列,而是通过隐藏状态来存储之前时间步的信息。

以下内容将围绕,多层感知机的回顾以及如何通过添加隐藏状态形成循环神经网络来展开。


2.1 不含隐藏状态的神经网络

对于一个含单隐藏层的多层感知机:给定样本数为 n n n、输入个数(特征数或特征向量维度)为 d d d的小批量数据样本 X ∈ R n × d \boldsymbol{X} \in \mathbb{R}^{n \times d} XRn×d,设隐藏层的激活函数为 ϕ \phi ϕ,那么,有隐藏层的输出 H ∈ R n × h \boldsymbol{H} \in \mathbb{R}^{n \times h} HRn×h为:

H = ϕ ( X W x h + b h ) \boldsymbol{H} = \phi(\boldsymbol{X} \boldsymbol{W}_{xh} + \boldsymbol{b}_h) H=ϕ(XWxh+bh)

其中,隐藏层权重参数为 W x h ∈ R d × h \boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h} WxhRd×h,隐藏层偏差参数为 b h ∈ R 1 × h \boldsymbol{b}_h \in \mathbb{R}^{1 \times h} bhR1×h,隐藏单元个数为 h h h

将隐藏变量 H \boldsymbol{H} H作为输出层的输入,且设输出个数为 q q q(如分类问题中的类别数),则输出层的输出为:

O = H W h q + b q \boldsymbol{O} = \boldsymbol{H} \boldsymbol{W}_{hq} + \boldsymbol{b}_q O=HWhq+bq

其中,输出变量为 O ∈ R n × q \boldsymbol{O} \in \mathbb{R}^{n \times q} ORn×q, 输出层权重参数为 W h q ∈ R h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q} WhqRh×q, 输出层偏差参数为 b q ∈ R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q} bqR1×q
若为分类问题,可以使用 softmax ( O ) \text{softmax}(\boldsymbol{O}) softmax(O)来计算输出类别的概率分布。



2.2 含隐藏状态的循环神经网络

2.2.1 概念

现在考虑输入数据存在时间相关性的情况。

假设 X t ∈ R n × d \boldsymbol{X_t} \in \mathbb{R}^{n \times d} XtRn×d是序列中时间步 t t t的小批量输入, H t ∈ R n × h \boldsymbol{H_t} \in \mathbb{R}^{n \times h} HtRn×h是该时间步的隐藏变量。
与多层感知机不同,这里保存上一时间步的隐藏变量 H t − 1 \boldsymbol{H_{t-1}} Ht1,并引入一个新的权重参数 W h h ∈ R h × h \boldsymbol{W_{hh}} \in \mathbb{R}^{h \times h} WhhRh×h,该参数用来描述在当前时间步如何使用上一时间步的隐藏变量。

具体来说,时间步 t t t的隐藏变量的计算由当前时间步的输入和上一时间步的隐藏变量共同决定:

H t = ϕ ( X t W x h + H t − 1 W h h + b h ) \boldsymbol{H_t} = \phi(\boldsymbol{X_t} \boldsymbol{W}_{xh} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} + \boldsymbol{b}_h) Ht=ϕ(XtWxh+Ht1Whh+bh)

与多层感知机相比,这里添加了 H t − 1 W h h \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} Ht1Whh一项。

由上式中相邻时间步的隐藏变量 H t \boldsymbol{H}_t Ht H t − 1 \boldsymbol{H}_{t-1} Ht1之间的关系可知,这里的隐藏变量能够捕捉截至当前时间步的序列的历史信息,类似于神经网络当前时间步的状态或记忆,因此,该隐藏变量也称为隐藏状态。

由于隐藏状态在当前时间步的定义使用了上一时间步的隐藏状态,上式的计算是循环的,使用循环计算的网络即循环神经网络(recurrent neural network)。


循环神经网络存在多种不同的构造方法,含上式所定义的隐藏状态的循环神经网络是极为常见的一种。若无特别说明,本章中的循环神经网络均基于上式中隐藏状态的循环计算。

在时间步 t t t,输出层的输出与多层感知机中的计算类似:
O t = H t W h q + b q \boldsymbol{O}_t = \boldsymbol{H}_t \boldsymbol{W}_{hq} + \boldsymbol{b}_q Ot=HtWhq+bq

循环神经网络的参数包括:隐藏层的权重 W x h ∈ R d × h \boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h} WxhRd×h W h h ∈ R h × h \boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h} WhhRh×h和偏差 b h ∈ R 1 × h \boldsymbol{b}_h \in \mathbb{R}^{1 \times h} bhR1×h,以及输出层的权重 W h q ∈ R h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q} WhqRh×q和偏差 b q ∈ R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q} bqR1×q

值得一提的是,即便在不同时间步,循环神经网络也始终使用这些模型参数。因此,循环神经网络模型参数的数量不随时间步的增加而增长。


循环神经网络在3个相邻时间步的计算逻辑,如下图所示:

在时间步 t t t,隐藏状态的计算可以看成是将输入 X t \boldsymbol{X}_t Xt和前一时间步隐藏状态 H t − 1 \boldsymbol{H}_{t-1} Ht1连结后输入一个激活函数为 ϕ \phi ϕ的全连接层。
该全连接层的输出就是当前时间步的隐藏状态 H t \boldsymbol{H}_t Ht,且模型参数为 W x h \boldsymbol{W}_{xh} Wxh W h h \boldsymbol{W}_{hh} Whh的连结,偏差为 b h \boldsymbol{b}_h bh
当前时间步 t t t的隐藏状态 H t \boldsymbol{H}_t Ht将参与下一个时间步 t + 1 t+1 t+1的隐藏状态 H t + 1 \boldsymbol{H}_{t+1} Ht+1的计算,并输入到当前时间步的全连接输出层。


2.2.2 代码示例

隐藏状态中 X t W x h + H t − 1 W h h \boldsymbol{X}_t \boldsymbol{W}_{xh} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} XtWxh+Ht1Whh的计算等价于 X t \boldsymbol{X}_t Xt H t − 1 \boldsymbol{H}_{t-1} Ht1连结后的矩阵乘以 W x h \boldsymbol{W}_{xh} Wxh W h h \boldsymbol{W}_{hh} Whh连结后的矩阵。代码验证示例如下:

import tensorflow as tf
print(tf.__version__)
2.0.0

(1) 构造矩阵XW_xhHW_hh,其形状分别为(3, 1)、(1, 4)、(3, 4)和(4, 4)。将XW_xhHW_hh分别相乘,再将两个乘法运算的结果相加,得到形状为(3, 4)的矩阵。

X, W_xh = tf.random.normal(shape=(3, 1)), tf.random.normal(shape=(1, 4))
H, W_hh = tf.random.normal(shape=(3, 4)), tf.random.normal(shape=(4, 4))

tf.matmul(X, W_xh) + tf.matmul(H, W_hh)

输出:

<tf.Tensor: id=93, shape=(3, 4), dtype=float32, numpy=
array([[ 0.52738583,  3.6930585 , -1.185591  ,  0.1229157 ],
       [-0.5417428 , -1.1258523 , -2.894788  , -2.441412  ],
       [-1.7703915 , -2.5031934 ,  2.02733   , -0.6239855 ]],
      dtype=float32)>

(2) 将矩阵XH按列(axis=1)连结,得到形状为(3, 5)的矩阵;将矩阵W_xhW_hh按行(axis=0)连结,得到形状为(5, 4)的矩阵;最后,将两个连结后的矩阵相乘,得到与上述代码输出相同的形状为(3, 4)的矩阵。

# (3,5) * (5,4)
tf.matmul(tf.concat([X, H], axis=1), tf.concat([W_xh, W_hh], axis=0))

输出:

<tf.Tensor: id=98, shape=(3, 4), dtype=float32, numpy=
array([[ 0.52738583,  3.6930587 , -1.185591  ,  0.12291575],
       [-0.54174286, -1.1258523 , -2.8947878 , -2.441412  ],
       [-1.7703915 , -2.5031934 ,  2.0273302 , -0.62398565]],
      dtype=float32)>


2.3 应用:基于字符级循环神经网络的语言模型

介绍如何应用循环神经网络来构建一个语言模型。

设小批量中样本数为1,文本序列为“想”“要”“有”“直”“升”“机”。如何使用循环神经网络基于当前和过去的字符来预测下一个字符,如下图所示:

在训练时,对每个时间步的输出层输出使用softmax运算,然后使用交叉熵损失函数来计算它与标签的误差。

在上图中,由于隐藏层中隐藏状态的循环计算,时间步3的输出 O 3 \boldsymbol{O}_3 O3取决于文本序列“想”“要”“有”。
由于训练数据中该序列的下一个词为“直”,时间步3的损失,将取决于该时间步基于序列“想”“要”“有”生成下一个词的概率分布,以及该时间步的标签“直”。

因为每个输入词是一个字符,因此,该模型称为字符级循环神经网络(character-level recurrent neural network)。
由于不同字符的个数远小于不同词的个数(对于英文尤其如此),所以,字符级循环神经网络的计算通常更加简单。

在后续小节的内容中,将介绍它的具体实现。



参考

《动手学深度学习》(TF2.0版)

猜你喜欢

转载自blog.csdn.net/m0_38111466/article/details/107285157