《动手学深度学习 Pytorch版》 9.3 深度循环神经网络
将多层循环神经网络堆叠在一起,通过对几个简单层的组合,产生一个灵活的机制。其中的数据可能与不同层的堆叠有关。
9.3.1 函数依赖关系
将深度架构中的函数依赖关系形式化,第 个隐藏层的隐状态表达式为:
参数字典:
-
表示第 个隐藏层的激活函数
-
表示小批量输入
-
表示样本个数
-
表示输入个数
-
-
表示 隐藏层 的隐状态
-
表示隐藏单元个数
-
设置
-
-
表示输出层变量
- 表示输出数
-
表示第 个隐藏层的权重参数
-
表示第 个隐藏层的偏重参数
最后,输出层的计算仅基于第 个隐藏层最终的隐状态:
其中 和 表示输出层的模型参数
9.3.2 简洁实现
手撸多层循环神经网络有点过于麻烦了,在此仅简单实现。
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)
vocab_size, num_hiddens, num_layers = len(vocab), 256, 2 # 用 num_layers 来设定隐藏层数
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)
9.3.3 训练与预测
num_epochs, lr = 500, 2
d2l.train_ch8(model, train_iter, vocab, lr*1.0, num_epochs, device) # 多了一层后训练速度大幅下降
perplexity 1.0, 116173.5 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
练习
(1)基于我们在 8.5 节中讨论的单层实现,尝试从零开始实现两层循环神经网络。
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
def get_params_bilayer(vocab_size, num_hiddens, device):
num_inputs = num_outputs = vocab_size
def normal(shape):
return torch.randn(size=shape, device=device) * 0.01
# 隐藏层1参数
W_xh1 = normal((num_inputs, num_hiddens))
W_hh1 = normal((num_hiddens, num_hiddens))
b_h1 = torch.zeros(num_hiddens, device=device)
# 新增隐藏层2参数
W_hh2 = normal((num_hiddens, num_hiddens))
b_h2 = torch.zeros(num_hiddens, device=device)
# 输出层参数
W_hq = normal((num_hiddens, num_outputs))
b_q = torch.zeros(num_outputs, device=device)
# 附加梯度
params = [W_xh1, W_hh1, b_h1, W_hh2, b_h2, W_hq, b_q]
for param in params:
param.requires_grad_(True)
return params
def init_rnn_state_bilayer(batch_size, num_hiddens, device):
return (torch.zeros((batch_size, num_hiddens), device=device),
torch.zeros((batch_size, num_hiddens), device=device)) # 新增第二个隐状态初始化张量
def rnn_bilayer(inputs, state, params): # inputs的形状:(时间步数量,批量大小,词表大小)
W_xh1, W_hh1, b_h1, W_hh2, b_h2, W_hq, b_q = params # 新增第二层参数
H1, H2 = state
outputs = []
for X in inputs: # X的形状:(批量大小,词表大小) 前面转置是为了这里遍历
H1 = torch.tanh(torch.mm(X, W_xh1) + torch.mm(H1, W_hh1) + b_h1) # 计算隐状态1
H2 = torch.tanh(torch.mm(H1, W_hh2) + b_h2) # 计算隐状态2
Y = torch.mm(H2, W_hq) + b_q # 计算输出
outputs.append(Y)
return torch.cat(outputs, dim=0), (H1, H2) # 沿时间步拼接
num_hiddens = 512
net_rnn_bilayer = d2l.RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params_bilayer,
init_rnn_state_bilayer, rnn_bilayer)
num_epochs, lr = 500, 1
d2l.train_ch8(net_rnn_bilayer, train_iter, vocab, lr, num_epochs, d2l.try_gpu())
perplexity 1.0, 63514.3 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
(2)在本节训练模型中,比较使用门控循环单元替换长短期记忆网络后模型的精确度和训练速度。
vocab_size, num_hiddens, num_layers = len(vocab), 256, 2 # 用 num_layers 来设定隐藏层数
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))
gru_layer = nn.GRU(num_inputs, num_hiddens)
model_gru = d2l.RNNModel(gru_layer, len(vocab))
model_gru = model_gru.to(device)
num_epochs, lr = 500, 2
d2l.train_ch8(model_gru, train_iter, vocab, lr*1.0, num_epochs, device) # 换 gru 后更快了
perplexity 1.0, 230590.6 tokens/sec on cuda:0
time traveller for so it will be convenient to speak of himwas e
travelleryou can show black is white by argument said filby
(3)如果增加训练数据,能够将困惑度降到多低?
已经是 1 了,没得降了。
(4)在为文本建模时,是否可以将不同作者的源数据合并?有何优劣呢?
不同作者的数据源之间可能没有什么关系,拼在一起可能效果反而下降。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了