RNN的PyTorch实现

2023-02-14,

官方实现

PyTorch已经实现了一个RNN类,就在torch.nn工具包中,通过torch.nn.RNN调用。

使用步骤:

    实例化类;
    将输入层向量和隐藏层向量初始状态值传给实例化后的对象,获得RNN的输出。

在实例化该类时,需要传入如下属性:

input_size:输入层神经元个数;
hidden_size:每层隐藏层的神经元个数;
num_layers:隐藏层层数,默认设置为1层;
nonlinearity:激活函数的选择,可选是'tanh'或者'relu',默认设置为'tanh';
bias:偏置系数,可选是'True'或者'False',默认设置为'True';
batch_first:可选是'True'或者'False',默认设置为'False';
dropout:默认设置为0。若为非0,将在除最后一层的每层RNN输出上引入Dropout层,dropout概率就是该非零值;
bidirectional:默认设置为False。若为True,即为双向RNN。

RNN的输入有两个,一个是input,一个是h0。input就是输入层向量,h0就是隐藏层初始状态值。

若没有采用批量输入,则输入层向量的形状为(L, Hin);

若采用批量输入,且batch_first为False,则输入层向量的形状为(L, N, Hin);

若采用批量输入,且batch_first为True,则输入层向量的形状为(N, L, Hin);

对于(N, L, Hin),在文本输入时,可以按顺序理解为(每次输入几句话,每句话有几个字,每个字由多少维的向量表示)。

若没有采用批量输入,则隐藏层向量的形状为(D * num_layers, Hout);

若采用批量输入,则隐藏层向量的形状为(D * num_layers, N, Hout);

注意,batch_first的设置对隐藏层向量的形状不起作用。

RNN的输出有两个,一个是output,一个是hn。output包含了每个时间步最后一层的隐藏层状态,hn包含了最后一个时间步每层的隐藏层状态。

若没有采用批量输入,则输出层向量的形状为(L, D * Hout);

若采用批量输入,且batch_first为False,则输出层向量的形状为(L, N, D * Hout);

若采用批量输入,且batch_first为True,则输出层向量的形状为(N, L, D * Hout)。

参数解释:

N代表的是批量大小;
L代表的是输入的序列长度;
若是双向RNN,则D的值为2;若是单向RNN,则D的值为1;
Hin在数值上是输入层神经元个数;
Hout在数值上是隐藏层神经元个数。

import torch
import torch.nn as nn
rnn = nn.RNN(10, 20, 1, batch_first=True) # 实例化一个单向单层RNN
input = torch.randn(5, 3, 10)
h0 = torch.randn(1, 5, 20)
output, hn = rnn(input, h0)

手写复现

复现代码

import torch
import torch.nn as nn class MyRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super().__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.weight_ih = torch.randn(self.hidden_size, self.input_size) * 0.01
self.weight_hh = torch.randn(self.hidden_size, self.hidden_size) * 0.01
self.bias_ih = torch.randn(self.hidden_size)
self.bias_hh = torch.randn(self.hidden_size) def forward(self, input, h0):
N, L, input_size = input.shape
output = torch.zeros(N, L, self.hidden_size)
for t in range(L):
x = input[:, t, :].unsqueeze(2) # 获得当前时刻的输入特征,[N, input_size, 1]。unsqueeze(n),在第n维上增加一维
w_ih_batch = self.weight_ih.unsqueeze(0).tile(N, 1, 1) # [N, hidden_size, input_size]
w_hh_batch = self.weight_hh.unsqueeze(0).tile(N, 1, 1) # [N, hidden_size, hidden_size]
w_times_x = torch.bmm(w_ih_batch, x).squeeze(-1) # [N, hidden_size]。squeeze(n),在第n维上减小一维
w_times_h = torch.bmm(w_hh_batch, h0.unsqueeze(2)).squeeze(-1) # [N, hidden_size]
h0 = torch.tanh(w_times_x + self.bias_ih + w_times_h + self.bias_hh)
output[:, t, :] = h0
return output, h0.unsqueeze(0)

验证正确性

my_rnn = MyRNN(10, 20)
input = torch.randn(5, 3, 10)
h0 = torch.randn(5, 20)
my_output, my_hn = my_rnn(input, h0)
print(output.shape == my_output.shape, hn.shape == my_hn.shape)
True True

主要参考

官方说明文档

RNN的PyTorch实现的相关教程结束。

《RNN的PyTorch实现.doc》

下载本文的Word格式文档,以方便收藏与打印。