Deep Learning Systems Lecture 18 Sequence Modeling and Recurrent Networks
这里回顾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叠加:来自整个序列的信息传播到隐藏状态。