到目前为止,我们只讨论了具有一个单向隐藏层的循环神经网络。其中,隐变量和观测值与具体的函数形式的交互方式是相当随意的。只要我们可以对不同的交互类型建模具有足够的灵活性,这就不是一个大问题。然而,对于一个单层来说,这可能具有相当的挑战性。在线性模型的情况下,我们通过添加更多的层来解决这个问题。而在循环神经网络中,因为我们首先需要决定如何添加以及在哪里添加额外的非线性,因此这个问题有点棘手。
事实上,我们可以将多层循环神经网络堆叠在一起,通过对几个简单层的组合,产生了一个灵活的机制。特别是,数据可能与不同层的堆叠有关。例如,我们可能希望保持有关金融市场状况(熊市或牛市)的高层数据可用,而在底层数据就只记录较短期的时间动态。
下面是具有 L L L 个隐藏层的深度循环神经网络,每个隐藏状态都连续地传递到当前层的下一个时间步和下一层的当前时间步。(有趣的地方在于你将之前的RNN图片与之对比,可以发现 H 1 ( 1 ) 、 H 2 ( 1 ) H^{(1)}_1、H^{(1)}_2 H1(1)、H2(1)他们是之前对应的输出,现在只是变成了下一层神经元的输入)
我们可以对深度架构中的函数依赖关系形式化,这个架构是由 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)),
其中,权重 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 都是超参数。也就是说,它们可以由我们来调整或指定。另外,用门控循环单元或长短期记忆网络的隐藏状态来代替深度RNN中的隐藏状态进行计算,可以很容易地得到深度门控循环神经网络(沐神在课程中说使用LSTM和GRU就看个人爱好了,并没有本质上的差别,当然后面还有更NB的transformer可以应用),毕竟Attention is all you need 不是闹着玩儿的。
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)
Downloading ../data/timemachine.txt from http://d2l-data.s3-accelerate.amazonaws.com/timemachine.txt...
# 超参数架构决策与单层lstm中相同,需要输入和输出选择相同的数量,隐藏单元设置为256,只是多了一个隐藏层层数
vocab_size,num_hiddens,num_layers = len(vocab),256,2
num_inputs = vocab_size
device = d2l.try_gpu()
lstm_layers = nn.LSTM(num_inputs,num_hiddens,num_layers)
model = d2l.RNNModel(lstm_layers,len(vocab))
model = model.to(device)
num_epochs,lr=500,2
d2l.train_ch8(model,train_iter,vocab,lr,num_epochs,device)
perplexity 1.0, 161938.1 tokens/sec on cuda:0
time traveller for so it will be convenient to speak of himwas e
traveller with a slight accession ofcheerfulness really thi