循环神经网络的基本架构及其LSTM、GRU等变种。这些循环神经网 络PyTorch提供了相应的API,如单元版的有:nn.RNNCell、nn.LSTMCell、nn.GRUCell 等;封装版的有:nn.RNN、nn.LSTM、nn.GRU。单元版与封装版的最大区别就是输入, 前者是时间步或序列的一个元素,后者是一个时间步序列。利用这些API可以极大地提高 开发效率。
RNN实现
PyTorch提供了两个版本的循环神经网络接口,单元版的输入是每个时间步,或循 神经网络的一个循环,而封装版的是一个序列。下面我们从简单的封装版torch.nn.RNN开 始,其一般格式为:
torch.nn.RNN( args, * kwargs)
RNN状态输出
a
t
a_{t}
at的计算公式为
a
t
=
t
a
n
h
(
w
i
h
∗
x
t
+
b
i
h
+
w
h
h
∗
a
t
−
1
+
b
h
h
)
a_{t}=tanh(w_{ih}*x_{t}+b_{ih}+w_{hh}*a_{t}-1+b_{hh})
at=tanh(wih∗xt+bih+whh∗at−1+bhh)
nn.RNN函数中的参数说明如下
·input_size:输入x的特征数量。
·hidden_size:隐含层的特征数量。
·num_layers:RNN的层数。
·nonlinearity:指定非线性函数使用tanh还是relu。默认是tanh。
·bias:如果是False,那么RNN层就不会使用偏置权重bi和bh,默认是True。
·batch_first:如果True的话,那么输入Tensor的shape应该是(batch,seq,feature),输 出也是这样。默认网络输入是(seq,batch,feature),即序列长度、批次大小、特征维度。
·dropout:如果值非零(该参数取值范围为0~1之间),那么除了最后一层外,其他 层的输出都会加上一个dropout层,缺省为零。
·bidirectional:如果True,将会变成一个双向RNN,默认为False。
函数nn.RNN()的输入包括特征及隐含状态,记为(xt、h0),输出包括输出特征及输 出隐含状态,记为(outputt、hn)。
其中特征值xt的形状为(seq_len,batch,input_size),h0的形状为
(num_layers*num_directions,batch,hidden_size),其中num_layers为层数,num_directions
方向数,如果取2则表示双向(bidirectional,),取1则表示单向。
outputt的形状为 (seq_len,batch,num_directionshidden_size),hn的形状
(num_layersnum_directions,batch,hidden_size)。
首先建立一个简单循环神经网络,输入维度为10,隐含状态维度为20,单向两层网络。
rnn = nn.RNN(input_size=10, hidden_size=20,num_layers= 2)
因输入节点与隐含层节点是全连接的,根据输入维度、隐含层维度,可以推算出相关 权重参数的维度,wih应该是20×10,whh是20×20,bih和bhh都是hidden_size。以下我们通 过查询weight_ih_l0、weight_hh_l0等进行验证。
# 第一层相关权重参数形状
print("wih 形状{},whh 形状{},bih 形状{}".format(rnn.weight_ih_l0.shape,rnn.weight_hh_l0.shape,rnn.bias_hh_l0.shape))
# wih 形状torch.Size([20, 10]),whh 形状torch.Size([20, 20]),bih 形状torch.Size([20])
# 第二层相关权重参数形状
print("wih 形状{},whh 形状{},bih 形状{}".format(rnn.weight_ih_l1.shape,rnn.weight_hh_l1.shape,rnn.bias_hh_l1.shape))
# wih 形状torch.Size([20, 20]),whh 形状torch.Size([20, 20]),bih 形状torch.Size([20])
RNN网络已搭建好,接下来将输入(xt、h0)传入网络,根据网络配置及网络要求,来 生成输入数据。输入特征长度为100,批量大小为32,特征维度为10的张量。隐含状态按 网络要求,其形状为(2,32,20)。
# 生成输入数据
input = torch.randn(100, 32, 10)
h_0 = torch.randn(2, 32, 20)
将输入数据传入RNN网络,将得到输出及更新后隐含状态值。根据以上规则,输出 output的形状应该是(100,32,20),隐含状态的输出形状应该与输入的形状一致。
output, h_n = rnn(input, h_0)
print(output.shape, h_n.shape)
# torch.Size([100, 32, 20]) torch.Size([2, 32, 20])
import torch
import torch.nn as nn
import numpy as np
def test():
# at=tanh(wih*xt+bih+whh*at-1+bhh)
rnn = nn.RNN(input_size=10, hidden_size=20, num_layers=2)
# 因输入节点与隐含层节点是全连接的,根据输入维度、隐含层维度,可以推算出相关权重参数的维度,wih应该是20×10,whh是20×20,bih和bhh都是hidden_size
# 第一层相关权重参数形状
print("wih 形状{},whh 形状{},bih 形状{}".format(rnn.weight_ih_l0.shape,rnn.weight_hh_l0.shape,rnn.bias_hh_l0.shape))
# wih 形状torch.Size([20, 10]),whh 形状torch.Size([20, 20]),bih 形状torch.Size([20])
# 第二层相关权重参数形状
print("wih 形状{},whh 形状{},bih 形状{}".format(rnn.weight_ih_l1.shape,rnn.weight_hh_l1.shape,rnn.bias_hh_l1.shape))
# wih 形状torch.Size([20, 20]),whh 形状torch.Size([20, 20]),bih 形状torch.Size([20])
# 生成输入数据
input = torch.randn(100, 32, 10)
h_0 = torch.randn(2, 32, 20)
output, h_n = rnn(input, h_0)
print(output.shape, h_n.shape)
# torch.Size([100, 32, 20]) torch.Size([2, 32, 20])
if __name__ == '__main__':
test()