这里回顾dlsys第十八讲,本讲内容是序列建模和RNN。

课程主页:

大纲

  • 序列建模;
  • RNN;
  • LSTM;
  • 超越“简单”的序列模型;

序列建模

序列建模任务

在我们目前考虑的示例中,我们假设每个输入输出对$x^{(i)},y^{(i)}$是独立同分布的(i.i.d.)。

在实际中,很多情况下输入/输出对是以特定序列给出的,我们需要使用关于这个序列的信息来帮助我们进行预测:

例子:词性标注

给定一个单词序列,确定每个单词的词性:

单词的词性取决于使用它的上下文,而不仅仅是单词本身。

例子:语音to文本

给定一个音频信号(假设我们甚至知道词的边界,并将每个片段映射到一个固定大小的向量描述符),确定相应的变换:

同样,单词的上下文非常重要(例如,参见任何试图“wreck a nice beach”的糟糕语音识别系统)。

示例:自回归预测

序列预测的一种特殊情况,其中要预测的元素是序列中的下一个元素:

例如,在时间序列预测、语言建模和其他用例中。

RNN

递归神经网络(RNN)随着时间的推移保持隐藏状态,这是当前输入和先前隐藏状态的函数:

计算公式:

其中$f$和$g$是激活函数,$W_{hh}, W_{hx}, W_{yh}$是权重,$b_h, b_y$是偏置项。

如何训练你的RNN

给定输入和目标输出序列$\left(x_1, \ldots, x_T, y_1^{\star}, \ldots, y_T^{\star}\right)$,我们可以使用BPTT训练RNN,这只涉及在序列长度上“展开”RNN,然后主要依赖于autodiff:

opt = Optimizer(params = (W_hh, W_hx, W_yh, b_h, b_y))
h[0] = 0
l = 0
for t = 1,...,T:
    h[t] = f(W_hh * h[t-1] + W_hx * x[t] + b_h)
    y[t] = g(W_yh * h[t] + b_y)
    l += Loss(y[t], y_star[t])
l.backward()
opt.step()

堆叠RNN

就像普通的神经网络一样,RNN可以堆叠在一起,将一层的隐藏单元作为下一层的输入,形成“深度”RNN。
实际上,与其他架构相比,“非常深”的RNN的价值往往较低:

激活/梯度爆炸

训练RNN的挑战类似于训练深度MLP网络的挑战。因为我们在长序列上训练 RNN,如果RNN的权重/激活缩放不当,隐藏的激活(以及梯度)将随着序列长度无限增长:

上图表示激活函数为ReLU的单层RNN,使用权重初始化$W_{hh}\sim \mathcal N(0, 3/ n)$。回想一下$\sigma^2 = 2/n$是ReLU激活的“正确”初始化。

激活/梯度消失

类似地,如果权重太小,那么来自输入的信息将随着时间迅速衰减(这恰恰是我们经常希望用序列模型建模的“长距离”依赖性):

上图表示激活函数为ReLU的单层RNN,使用权重初始化$W_{hh}\sim \mathcal N(0, 1/ n)$。此处仅针对时间1提供非零输入,显示有关此输入的信息随时间衰减。

其他的激活函数

ReLU的一个明显问题是它可以无限增长; 使用有界激活能“解决”这个问题吗?

回答是否,如果激活函数有界,那么有部分区域大概率梯度很小,仍然会有问题。

LSTM

长短期记忆RNN

长短期记忆(LSTM)单元是隐藏单元更新的一种特殊形式,它避免了(部分)普通LSTM的问题。

第1步:将隐藏单元分为两个部分,称为隐藏状态(hidden sate)和单元状态(cell state):

第2步:使用一个非常具体的公式来更新隐藏状态和单元状态(使用一些其他名称,如“遗忘门”、“输入门”、“输出门”作为良好的衡量标准):

为什么LSTM有效?

关于“理解LSTM的工作原理”的论文/博客文章似乎数不胜数(我发现其中大部分都毫无帮助)。

关键是这里的这一行:

  • 我们通过按比例缩小$c_{t-1}$形成$c_t$(注意$f_t\in [0, 1]^n$),然后向其添加一项;
  • 如果$f_t$接近于1,那么$c_t$就接近于$c_{t-1}$;
  • 在更广泛的权重范围内,LSTM不会遭受梯度消失;

超越“简单”的序列模型

序列到序列的模型

为了了解除了“简单”序列预测之外,可以使用RNN/LSTM做些什么,请考虑尝试在语言之间进行翻译的任务。

可以将两个 RNN 连接在一起,一个“仅”处理序列以创建最终隐藏状态(即无损失函数); 然后一个接受这个初始隐藏状态的部分,并且“只”生成一个序列:

双向RNN

RNN只能使用直到时间$t$的序列信息来预测$y_t$:

  • 这有时是可取的(例如,自回归模型);
  • 但有时不够(例如,我们想要使用“整个”输入序列的语言翻译);

双向RNN:将正向运行的RNN与反向运行的RNN叠加:来自整个序列的信息传播到隐藏状态。