一、概念介绍
DRNN:
深度循环神经网络(Deep Recurrent Neural Network)。在实际应用中,仅使用一层隐藏层的循环神经网络肯定是不够的,拓展网络的深度可以让模型增加更多的非线性特征,从而提高模型的性能。
BRNN:
双向循环神经网络(Bidirectional Recurrent Neural Network)。对于标准的rnn模型而言,我们只能利用过去的信息来预测当前的输出,无法考虑到当前时间点之后的信息。而在某些情景下,我们可能需要同时结合上下文信息来做预测,比如:完形填空。双向循环神经网络就可以帮助我们解决这样的问题。由于BRNN输入的是一段上下文信息,所以它并不适合做推理预测,而适合在有完整句子的情况下对句子做特征提取(翻译、语义识别)。
二、实现思路
DRNN:
下图简单展示了深度循环神经网络的模型架构。
需要注意的是,上一隐藏层的输出在往后一层传递时,并不需要经过输出层处理,可以直接作为下一隐藏层的输入。
BRNN:
下图简单展示了双向循环神经网络的模型架构。
从图中可以看到,网络中包含了两个rnn隐藏层,一个是从左往右(前向)推理的,另一个是从右往左(反向)推理的,这两个隐藏层互相独立、互不影响,网络最终的输出则是这两个隐藏层输出组合(concatenate)。
三、代码实现
使用pytorch的api可以方便简洁地实现深度循环神经网络。
rnnExtension.py:
导入依赖包:
import torch
import seqDataLoader as loader
from torch import nn
from torch.nn import functional as F
from rnnModel import trainRNN
继承nn.Module,定义一个rnn模型基类:
class RNNModel(nn.Module):
"""The RNN model."""
def __init__(self, rnn_layer, vocab_size, **kwargs):
super(RNNModel, self).__init__(**kwargs)
self.rnn = rnn_layer
self.vocab_size = vocab_size
self.num_hiddens = self.rnn.hidden_size
# 对于双向循环神经网络而言,num_directions=2
if not self.rnn.bidirectional:
self.num_directions = 1
self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
else:
self.num_directions = 2
self.linear = nn.Linear(self.num_hiddens * 2, self.vocab_size)
def forward(self, inputs, state):
X = F.one_hot(inputs.T.long(), self.vocab_size)
X = X.to(torch.float32)
Y, state = self.rnn(X, state)
# The fully connected layer will first change the shape of `Y` to
# (`num_steps` * `batch_size`, `num_hiddens`). Its output shape is
# (`num_steps` * `batch_size`, `vocab_size`).
output = self.linear(Y.reshape((-1, Y.shape[-1])))
return output, state
def begin_state(self, device, batch_size=1):
if not isinstance(self.rnn, nn.LSTM):
# `nn.GRU` takes a tensor as hidden state
return torch.zeros((self.num_directions * self.rnn.num_layers,
batch_size, self.num_hiddens),
device=device)
else:
# `nn.LSTM` takes a tuple of hidden states
return (torch.zeros((
self.num_directions * self.rnn.num_layers,
batch_size, self.num_hiddens), device=device),
torch.zeros((
self.num_directions * self.rnn.num_layers,
batch_size, self.num_hiddens), device=device))
主函数测试深度循环神经网络:
if __name__=='__main__':
batch_size, num_steps = 3, 10
train_iter, vocab = loader.loadData(batch_size, num_steps, 'loseyourself.txt')
vocab_size, num_hiddens, device, num_layers = len(vocab), 256, torch.device('cpu'), 2
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens, num_layers, bidirectional=False)
model = RNNModel(lstm_layer, vocab_size)
model = model.to(device)
num_epochs, lr = 50, 2
trainRNN(model, train_iter, vocab, lr, num_epochs, device)
主函数测试双向循环神经网络:
if __name__=='__main__':
batch_size, num_steps = 3, 10
train_iter, vocab = loader.loadData(batch_size, num_steps, 'loseyourself.txt')
vocab_size, num_hiddens, device, num_layers = len(vocab), 256, torch.device('cpu'), 2
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens, num_layers, bidirectional=True)
model = RNNModel(lstm_layer, vocab_size)
model = model.to(device)
num_epochs, lr = 20, 2
trainRNN(model, train_iter, vocab, lr, num_epochs, device)
可以看到,构造双向循环神经网络时只要把bidirectional设置为True就好了。运行一遍可以发现双向循环神经网络的预测效果并不好,不适合当前场景使用。
参考链接:
《动手学深度学习》 — 动手学深度学习 2.0.0 documentationhttps://zh-v2.d2l.ai/