系列文章目录
文章目录
深度循环神经网络
🏷sec_deep_rnn
到目前为止,我们只讨论了具有一个单向隐藏层的循环神经网络。但是RNN中一个隐藏层不能做很宽,会很过拟合 。
在 RNN(递归神经网络)中,隐藏层的宽度(即隐藏单元的数量)对模型的表现有重要影响。宽度过大的隐藏层可能导致过拟合,原因如下:
1. 模型复杂性增加
- 隐藏层的宽度增加意味着模型的参数数量显著增加。更多的参数可以捕捉到训练数据中的复杂模式,但这也使得模型容易记住训练数据的噪声,而不是学习到通用的特征。
2. 训练数据不足
- 如果训练数据量相对较小,宽的隐藏层可能会导致模型在训练数据上表现很好,但在未见数据(测试数据)上的泛化能力差。过拟合通常发生在模型对训练数据的依赖过强,无法适应新的数据分布。
3. 梯度消失和爆炸
- RNN 本身就容易出现梯度消失或爆炸的问题,尤其在处理长序列时。宽的隐藏层可能加剧这一问题,使得训练过程不稳定,从而影响模型的学习效果。
4. 正则化不足
- 如果模型过于复杂而没有适当的正则化(如 dropout、L2 正则化等),则会更容易过拟合。宽隐藏层在缺乏正则化的情况下,可能会导致对训练数据的过度拟合。
5. 特征冗余
- 宽的隐藏层可能导致特征冗余,许多隐藏单元可能学习到相似的特征,导致模型效率低下,并增加过拟合的风险。
总结
为了避免过拟合,通常需要在模型设计中平衡隐藏层的宽度与训练数据的数量和质量。可以考虑使用正则化方法、减少隐藏单元数量、增加训练数据量或使用更复杂的模型架构(如 LSTM 或 GRU)来提高模型的泛化能力。
其中,隐变量和观测值与具体的函数形式的交互方式是相当随意的。
只要交互类型建模具有足够的灵活性,这就不是一个大问题。
然而,对一个单层来说,这可能具有相当的挑战性。
之前在线性模型中,我们通过添加更多的层来解决这个问题。
而在循环神经网络中,我们首先需要确定如何添加更多的层,以及在哪里添加额外的非线性,因此这个问题有点棘手。
事实上,我们可以将多层循环神经网络堆叠在一起,通过对几个简单层的组合,产生了一个灵活的机制。
特别是,数据可能与不同层的堆叠有关。例如,我们可能希望保持有关金融市场状况(熊市或牛市)的宏观数据可用,而微观数据只记录较短期的时间动态。下图描述了一个具有 L L L个隐藏层的深度循环神经网络,每个隐状态都连续地传递到当前层的下一个时间步和下一层的当前时间步。
🏷fig_deep_rnn
函数依赖关系
我们可以将深度架构中的函数依赖关系形式化,这个架构是由 :numref:fig_deep_rnn
中描述了 L L L个隐藏层构成。
后续的讨论主要集中在经典的循环神经网络模型上,但是这些讨论也适应于其他序列模型。
假设在时间步 t t t有一个小批量的输入数据 X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} Xt∈Rn×d(样本数: n n n,每个样本中的输入数: d d d)。
同时,将 l t h l^\mathrm{th} lth隐藏层( l = 1 , ... , L l=1,\ldots,L l=1,...,L)的隐状态设为 H t ( l ) ∈ R n × h \mathbf{H}_t^{(l)} \in \mathbb{R}^{n \times h} Ht(l)∈Rn×h(隐藏单元数: h h h),输出层变量设为 O t ∈ R n × q \mathbf{O}_t \in \mathbb{R}^{n \times q} Ot∈Rn×q(输出数: q q q)。
设置 H t ( 0 ) = X t \mathbf{H}_t^{(0)} = \mathbf{X}_t Ht(0)=Xt,第 l l l个隐藏层的隐状态使用激活函数 ϕ l \phi_l ϕl,则:
H t ( l ) = ϕ l ( H t ( l − 1 ) W x h ( l ) + H t − 1 ( l ) W h h ( l ) + b h ( l ) ) , \mathbf{H}t^{(l)} = \phi_l(\mathbf{H}t^{(l-1)} \mathbf{W}{xh}^{(l)} + \mathbf{H}{t-1}^{(l)} \mathbf{W}_{hh}^{(l)} + \mathbf{b}_h^{(l)}), Ht(l)=ϕl(Ht(l−1)Wxh(l)+Ht−1(l)Whh(l)+bh(l)),
:eqlabel:eq_deep_rnn_H
其中,权重 W x h ( l ) ∈ R h × h \mathbf{W}{xh}^{(l)} \in \mathbb{R}^{h \times h} Wxh(l)∈Rh×h, W h h ( l ) ∈ R h × h \mathbf{W}{hh}^{(l)} \in \mathbb{R}^{h \times h} Whh(l)∈Rh×h和偏置 b h ( l ) ∈ R 1 × h \mathbf{b}_h^{(l)} \in \mathbb{R}^{1 \times h} bh(l)∈R1×h都是第 l l l个隐藏层的模型参数。
最后,输出层的计算仅基于第 l l l个隐藏层最终的隐状态:
O t = H t ( L ) W h q + b q , \mathbf{O}_t = \mathbf{H}t^{(L)} \mathbf{W}{hq} + \mathbf{b}_q, Ot=Ht(L)Whq+bq,
其中,权重 W h q ∈ R h × q \mathbf{W}_{hq} \in \mathbb{R}^{h \times q} Whq∈Rh×q和偏置 b q ∈ R 1 × q \mathbf{b}_q \in \mathbb{R}^{1 \times q} bq∈R1×q都是输出层的模型参数。
与多层感知机一样,隐藏层数目 L L L和隐藏单元数目 h h h都是超参数。也就是说,它们可以由我们调整的。
另外,用门控循环单元或长短期记忆网络的隐状态来代替 :eqref:eq_deep_rnn_H
中的隐状态进行计算,可以很容易地得到深度门控循环神经网络或深度长短期记忆神经网络。
简洁实现
实现多层循环神经网络所需的许多逻辑细节在高级API中都是现成的。
简单起见,我们仅示范使用此类内置函数的实现方式。
以长短期记忆网络模型为例,该代码与之前在LSTM中使用的代码非常相似,实际上唯一的区别是我们指定了层的数量,而不是使用单一层这个默认值。
像往常一样,我们从加载数据集开始。
python
import torch
from torch import nn
from d2l import torch as d2l
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
像选择超参数这类架构决策也跟 :numref:sec_lstm
中的决策非常相似。
因为我们有不同的词元,所以输入和输出都选择相同数量,即vocab_size
。
隐藏单元的数量仍然是 256 256 256。
唯一的区别是,我们现在(通过num_layers
的值来设定隐藏层数)。
python
vocab_size, num_hiddens, num_layers = len(vocab), 256, 2
num_inputs = vocab_size
device = d2l.try_gpu()
lstm_layer = nn.LSTM(num_inputs, num_hiddens, num_layers)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
训练与预测
由于使用了长短期记忆网络模型来实例化两个层,因此训练速度被大大降低了。
python
num_epochs, lr = 500, 2
d2l.train_ch8(model, train_iter, vocab, lr*1.0, num_epochs, device)
perplexity 1.0, 224250.2 tokens/sec on cuda:0
time travelleryou can show black is white by argument said filby
travelleryou can show black is white by argument said filby
<Figure size 350x250 with 1 Axes>
小结
- 在深度循环神经网络中,隐状态的信息被传递到当前层的下一时间步和下一层的当前时间步。
- 有许多不同风格的深度循环神经网络,
如长短期记忆网络、门控循环单元、或经典循环神经网络。
这些模型在深度学习框架的高级API中都有涵盖。 - 总体而言,深度循环神经网络需要大量的调参(如学习率和修剪)
来确保合适的收敛,模型的初始化也需要谨慎。
练习
- 基于我们在 :numref:
sec_rnn_scratch
中讨论的单层实现,
尝试从零开始实现两层循环神经网络。 - 在本节训练模型中,比较使用门控循环单元替换长短期记忆网络后模型的精确度和训练速度。
- 如果增加训练数据,能够将困惑度降到多低?
- 在为文本建模时,是否可以将不同作者的源数据合并?有何优劣呢?