第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