动手学深度学习 6.2 循环神经网络

前言

从零开始学习ai文章系列计划是个人在《动手学深度学习》和《磨菇书》两本书的学习中的个人笔记,文章也会以课本中的章节分开,即每个章节一片笔记。我会尽量的把主要内容以及遇到的难点进行记录与解决,如果哪里有错误的欢迎指正。或者不清晰的可以直接查看原文部分。

《动手学深度学习》原文(课本):https://tangshusen.me/Dive-into-DL-PyTorch/#/

《动手学深度学习》代码:https://github.com/ShusenTang/Dive-into-DL-PyTorch

(由于有时候公式太多,可能会直接贴图片)


1. 不含隐藏状态的神经网络

让我们考虑一个含单隐藏层的多层感知机。

给定样本数为n、输入个数(特征数或特征向量维度)为d的小批量数据样本X (大小为n*d)。设隐藏层的激活函数为ϕ,那么隐藏层的输出H (n*h)计算为 \[ H=ϕ(XW_{xh}+b_h) \] 输出层的输出为 \[ O=HW_{hq}+b_q \] 如果是分类问题,我们可以使用 softmax(O) 来计算输出类别的概率分布

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

现在我们考虑输入数据存在时间相关性的情况。假设 \(X_t\) 是序列中时间步t的小批量输入,\(H_t\) 是该时间步的隐藏变量。

与多层感知机不同的是,这里我们保存上一时间步的隐藏变量 \(H_{t−1}\) ,并引入一个新的权重参数 \(W_h\) ,该参数用来描述在当前时间步如何使用上一时间步的隐藏变量。

具体来说,时间步t的隐藏变量的计算由当前时间步的输入和上一时间步的隐藏变量共同决定: \[ H_t=ϕ(X_tW_{xh}+H_{t-1}W_{hh}+b_{h}) \] 与多层感知机相比,我们在这里添加了 \(H_{t-1}W_{hh}\) 一项。

由上式中相邻时间步的隐藏变量 \(H_t\) 之间的关系可知,这里的隐藏变量能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。

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

历史信息指的是什么。在循环网络里它不是“历史输入的原样存档”,而是与任务相关的、从过去输入中提炼出来的特征表示。

也就是说 \(h_t\) 是前 \(t\) 个输入序列 \({x_1, x_2, \dots, x_t}\) 的某种函数结果。

所以这里的 历史信息 就是:截至时间 t 为止,所有输入对当前预测的影响。

一句话总结:

RNN 里的“历史信息”就是截至当前时刻之前所有输入序列对当前任务有用的总结表示,不是完整的输入记录,而是压缩后的语境/上下文

在时间步t,输出层的输出和多层感知机中的计算类似: \[ O_t=H_tW_{hq}+b_{q} \] 图6.1展示了循环神经网络在3个相邻时间步的计算逻辑。

接下来手动实现\(X_tW_{xh}+H_{t-1}W_{hh}\)的计算。

将矩阵X和H按列(维度1)连结 与对应的两个W权重按行连接 进行矩阵乘法,其结果不变

隐藏状态中 \(X_tW_{xh}+H_{t-1}W_{hh}\) 的计算等价于 \(X_t\)\(H_{t-1}\) 连结后的矩阵乘以 \(W_{xh}\)\(W_{hh}\) 连结后的矩阵。

原本计算如下

拼接后计算如下

1
2
3
4
5
6
7
8
9
10
11
12
import torch

X, W_xh = torch.randn(3, 1), torch.randn(1, 4)
H, W_hh = torch.randn(3, 4), torch.randn(4, 4)

r1 = torch.matmul(X, W_xh) + torch.matmul(H, W_hh)
print(r1,'\n')

r2 = torch.matmul(torch.cat((X, H), dim=1), torch.cat((W_xh, W_hh), dim=0))
print(r2,'\n')

print(r1==r2)

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

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

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

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