Press "Enter" to skip to content

深度学习进阶:自然语言处理入门:第5章 RNN(2)

第5章 RNN(2)

 

第5章 RNN

 

5.2 RNN

 

5.2.1 循环的神经网络

 

RNN 的特征就在于拥有这样一个环路(或回路)。这个环路可以使数据不断循环。通过数据的循环,RNN一边记住过去的数据,一边更新到最新 的数据。

 

 

5.2.2 展开循环

 

 

这些RNN都是同一层

 

RNN具有状态h

 

h以下公式会被更新

tanh 函数(双曲 正切函数)

 

 

5.2.3 Backpropagation Through Time

 

 

问题:

计算资源
内存
梯度消失

通过该 BPTT,RNN 的学习似乎可以进行了,但是在这之前还有一个 必须解决的问题,那就是学习长时序数据的问题。因为随着时序数据的时间 跨度的增大,BPTT 消耗的计算机资源也会成比例地增大。另外,反向传播 的梯度也会变得不稳定

 

5.2.4 Truncated BPTT

 

在处理长时序数据时,通常的做法是将网络连接截成适当的长度。具 体来说,就是将时间轴方向上过长的网络在合适的位置进行截断,从而创建 多个小型网络,然后对截出来的小型网络执行误差反向传播法,这个方法称 为 Truncated BPTT(截断的 BPTT) 。

 

Truncated 是“被截断”的意思。Truncated BPTT 是指按适当长 度截断的误差反向传播法。

 

反向传播截断

假如有1000个单词的语料库(多个串联起来的句子当作一个时序数据)

 

长度为1000的时序数据

 

正向正常

 

反向以10个为单位

 

x0-x9

如图 5-12 所示,先进行正向传播,再进行反向传播,这样可以得到所 需的梯度。接着,对下一个块的输入数据 (x10, x11, ··· , x19) 执行误差反向传播法,如图 5-13 所示

这里,和第 1 个块一样,先执行正向传播,再执行反向传播。这里的重 点是,这个正向传播的计算需要前一个块最后的隐藏状态 h9,这样可以维 持正向传播的连接。

 

用同样的方法,继续学习第 3 个块,此时要使用第 2 个块最后的隐藏状 态 h19。像这样,在 RNN 的学习中,通过将数据按顺序输入,从而继承隐 藏状态进行学习。

 

可知 RNN 的学习流程如图 5-14 所示

5.2.5 Truncated BPTT的mini-batch学习

 

​ 为了执 行 mini-batch 学习,需要考虑批数据,因此,在输入数据的开始位置,需要在各个批次中进行“偏移”。

 

​ 为了说明“偏移”,我们仍用上一节的通过 Truncated BPTT 进行学习 的例子,**对长度为1000的时序数据,以时间长度10为单位进行截断。**此时, 如何将批大小设为 2 进行学习呢?在这种情况下,作为 RNN 层的输入数据, 第 1 笔样本数据从头开始按顺序输入,第 2 笔数据从第 500 个数据开始按顺 序输入。也就是说,将开始位置平移 500,如图 5-15 所示。

 

 

批次的第 1 个元素是 x0, ··· , x9,批次的第 2 个元素 是 x500, ··· , x509,将这个 mini-batch 作为 RNN 的输入数据进行学习。因 为要输入的数据是按顺序的,所以接下来是时序数据的第 10 ~ 19 个数据和 第 510 ~ 519 个数据。像这样,在进行 mini-batch 学习时,平移各批次输入 数据的开始位置,按顺序输入。此外,如果在按顺序输入数据的过程中遇到 了结尾,则需要设法返回头部。

 

如上所述,虽然 Truncated BPTT 的原理非常简单,但是关于数据的 输入方法有几个需要注意的地方。具体而言, 一是要按顺序输入数据,二是要平移各批次(各样本)输入数据的开始位置。

 

5.3 RNN的实现

 

实际上,我们要实现的是一个在水平方向上延伸的神经网络。另外,考虑到基于 Truncated BPTT 的学习,只需要创建一个在水平方向上长度固定的网络序列即可,如图 5-16 所示。

如图 5-16 所示,目标神经网络接收长度为 T 的时序数据(T 为任意值), 输出各个时刻的隐藏状态 T 个。这里,考虑到模块化,将图 5-16 中在水平 方向上延伸的神经网络实现为“一个层”,如图 5-17 所示

5.3.1 RNN层的实现

 

正向传播的数学公式

class RNN:
    def __init__(self, Wx, Wh, b):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None
    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)
        self.cache = (x, h_prev, h_next)
        return h_next
    def backward(self, dh_next):
        Wx, Wh, b = self.params
        x, h_prev, h_next = self.cache
        dt = dh_next * (1 - h_next ** 2)
        db = np.sum(dt, axis=0)
        dWh = np.dot(h_prev.T, dt)
        dh_prev = np.dot(dt, Wh.T)
        dWx = np.dot(x.T, dt)
        dx = np.dot(dt, Wx.T)
        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db
        return dx, dh_prev

 

**RNN 的初始化方法接收两个权重参数和一个偏置参数。**这里,将通过函 数参数传进来的模型参数设置为列表类型的成员变量 params。

 

以各个参数对应的形状初始化梯度,并保存在 grads 中。

 

最后,使用 None 对反向传 播时要用到的中间数据 cache 进行初始化。

 

正向传播的forward(x, h_prev) 方法接收两个参数: 从下方输入的 x 和从左边输入的 h_prev。

RNN 层的正向传播可由图 5-19 的计算图表示。这里进行的计算由矩阵 乘积“MatMul”、加法“+”和“tanh”这 3 种运算构成。此外,因为偏置 b 的加法运算会触发广播操作,所以严格地讲,这里还应该加上 Repeat 节 点。不过简单起见,这里省略了它(具体请参考 1.3.4.3 节)。

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注