之前在学习RNN的时候,总是零零散散的搜一些东西。这次想要将关于RNN的知识总结起来,包括各种RNN网络的结构、输入输出以及pytorch代码实现。
单向RNN网络简介
我们首先介绍单向RNN网络的结构,如下图所示。时间步
i
i
i 的输入为:
x
i
x_i
xi和
s
i
−
1
s_{i-1}
si−1,输出是
y
i
y_i
yi和
s
i
s_i
si。这里我们统一使用
s
s
s 代表隐藏层状态表示,
y
y
y 则表示模型输出值。那么,单个神经元的运算为:
(
y
t
,
s
t
)
=
f
t
(
x
t
,
s
t
−
1
)
(y_t, s_t) = f_t(x_t, s_{t-1})
(yt,st)=ft(xt,st−1)。其中,
f
t
f_t
ft 表示的该层的第
t
t
t 个神经元中的运算。
在上面介绍的基础上,接下来,会介绍三种RNN网络,分别是Vanilla RNN(即简单RNN),GRU,LSTM。对于这三种网络,我会从单个神经元、单层以及多层的方面展开介绍。因为Vanilla RNN和GRU的结构上是一样的,只是内部计算不同,所以这里将这两个放在一起介绍
Vanilla RNN / GRU 的结构
首先给出简单RNN/ GRU的单层和多层的网络结构,这里从单个神经元进行分析。网络结构图如下:
简介中我们用
s
t
s_t
st表示隐层状态,
y
t
y_t
yt表示
t
t
t 时刻输出。那么,单层Vanilla RNN / GRU的隐层状态和输出就是:
s
t
=
h
t
s_t = h_t
st=ht,
y
t
=
h
t
y_t = h_t
yt=ht。可以看出,Vanilla RNN / GRU的隐层状态和该时刻的输出是一样的。
对于多层Vanilla RNN / GRU来说,隐层状态和输出分别为: s t = ( h t − 1 ( 1 ) , h t − 1 ( 2 ) , ⋯ , h t − 1 ( l ) ) s_t = (h_{t-1}^{(1)}, h_{t-1}^{(2)}, \cdots, h_{t-1}^{(l)}) st=(ht−1(1),ht−1(2),⋯,ht−1(l)), y t = h t ( l ) y_t = h_t^{(l)} yt=ht(l)。中间层的输出 h t ( i ) h_{t}^{(i)} ht(i)直接用于下一层的输入,不会被直接输出出来。
LSTM 的结构
首先给出单层和多层LSTM的结构图,可以看出,相比Vanilla RNN和GRU,LSTM多了一个
c
t
−
1
c_{t-1}
ct−1的输入,这个表示cell的状态。
在单层LSTM中,隐层状态和输出分别对应于:
s
t
=
(
h
t
,
c
t
)
s_t = (h_t, c_t)
st=(ht,ct),
y
t
=
h
t
y_t = h_t
yt=ht。
在多层LSTM中,隐层状态和输出分别对应于: s t = ( ( h t ( 1 ) , c t ( 1 ) ) , ( h t ( 2 ) , c t ( 2 ) ) , ⋯ , ( h t ( l ) , c t ( l ) ) ) s_t = ((h_t^{(1)}, c_t^{(1)}), (h_t^{(2)}, c_t^{(2)}), \cdots, (h_t^{(l)}, c_t^{(l)})) st=((ht(1),ct(1)),(ht(2),ct(2)),⋯,(ht(l),ct(l))), y t = h t ( l ) y_t = h_t^{(l)} yt=ht(l)。
双向RNN网络简介
双向RNN就相当于在上面所讲单向RNN基础之上,再增加一层方向的相同结构网络。图示如下:
双向RNN就会出现两个输出,分别为
y
t
→
\overrightarrow{y_t}
yt和
y
t
←
\overleftarrow{y_t}
yt,通常这两个输出会concat在一起,作为整体输出。
知道了单向和双向RNN的结构之后,我们就来看看pytorch里怎样实现这样的网络,这里以LSTM为例,GRU类似,只是输入和输出的不同。
单向和双向LSTM的pytorch代码实现
单向LSTM
rnn = nn.LSTM(input_size=10, hidden_size=20, num_layers=2)#(input_size,hidden_size,num_layers)
input = torch.randn(5, 3, 10)#(seq_len, batch, input_size)
h0 = torch.randn(2, 3, 20) #(num_layers,batch,output_size)
c0 = torch.randn(2, 3, 20) #(num_layers,batch,output_size)
output, (hn, cn) = rnn(input, (h0, c0))
那么,output和hn,cn的维度是多少呢?
output.shape #(seq_len, batch, output_size)
torch.Size([5, 3, 20])
hn.shape #(num_layers, batch, output_size)
torch.Size([2, 3, 20])
cn.shape #(num_layers, batch, output_size)
torch.Size([2, 3, 20])
双向LSTM
rnn = nn.LSTM(input_size=10, hidden_size=20, num_layers=2,bidirectional=True)#(input_size,hidden_size,num_layers)
input = torch.randn(5, 3, 10)#(seq_len, batch, input_size)
h0 = torch.randn(4, 3, 20) #(num_layers,batch,output_size)
c0 = torch.randn(4, 3, 20) #(num_layers,batch,output_size)
output, (hn, cn) = rnn(input, (h0, c0))
output和hn,cn的维度是多少呢?
output.shape #(seq_len, batch, output_size*2)
torch.Size([5, 3, 40])
hn.shape #(num_layers*2, batch, output_size)
torch.Size([4, 3, 20])
cn.shape #(num_layers*2, batch, output_size)
torch.Size([4, 3, 20])