循环神经网络,处理时间序列数据的老朋友了,虽然使用不当经常带来噪声放大的问题,,,,但效果还是很可以的,比如在机器翻译任务上。
这里简单介绍一下torch里面RNN模块和RNNCell模块的区别和使用,顺便回顾下循环神经网络。
torch.nn.RNN()调用的是循环神经网络最原始的形态,这种没法处理比较长的时间序列,后面的变体Lstm和GRU解决了这个问题,这里只是用torch.nn.RNN()展示一下循环神经网络的一些基本参数等信息,当然有些大神也是直接调用这个去搭建一些自己需要的网络结构。
import torch
from torch.autograd import Variable
from torch import nn
input_size = 10
batch_size = 5
hidden_size = 20
num_layers = 2
#定义一个最简单的循环神经网络,网络输入的特征维度为10,隐层的节点数目为20个,有2层
model = nn.RNN(input_size=input_size, hidden_size=hidden_size, num_layers=num_layers)
x = torch.rand(3, batch_size, input_size) #这里理解为生成batch_size条数据,每条数据长度为3,特征维度为input_size
h0 = torch.zeros(num_layers, batch_size, hidden_size) #这里定义最初始输入的记忆状态,直接设置为0就行
y, h = model(x, h0)
print('y.shape=', y.shape)
print('h.shape=', h.shape)
上面搭建了一个最简单的示例,解释下y和h的输出都表示什么,这对后面的使用影响比较大,循环神经网络是这样的,一条时间序列的长度为3,那么就对应了3个时刻,前面一个时刻模型的神经元的输出会作为下一个时刻模型神经元的输入,假设模型的所有神经元表示为H,h表示所有神经元的输出,h也叫记忆单元。那么在最开始的时间t0,计算过程就是
h1 = H(h0 + x0) #h0为初始化输入,可以直接设置为0
#因为时间序列每个元素都需要对应一个输出,于是0时刻,第1个元素对应的输出为
out1 = h1[-1] #也就是所有神经元输出的最后一层
开始循环如下:
h2 = H(h1 + x1)
out2 = h2[-1]
h3 = H(h2 + x2)
out3 = h3[-1]
最后训练结束,输出的h就是最后一个时刻模型所有神经元的输出,也就是h3,我们上面定义的模型有2层,每层神经元数目为20,于是得到的输出尺寸应该为[2, 20],又因为上面我设置了5条神经元数据,每条都对应一个[2, 20]的输出矩阵,所以最终的输出矩阵尺寸为[2, 5, 20],记得中间的维度是针对第几条数据,希望读者能理解输出和输入都是表示什么。
那么很好理解了吧,模型最后输出的y,就是每个时刻元素的输出,也就是[out1, out2, out3],每个out都是模型最后一层神经元的输出,尺寸为[1, 20],这里我设置的数据每条时间序列有3个元素,每个元素对应一个时刻的输出,于是尺寸为[3, 20],又因为有5条数据,所以最终尺寸为[3, 5, 20]。
这里注意下,RNN的输入格式不同于CNN,CNN模型的输入尺寸是batch_size放前面,也就是[batch_size, input_size_1, input_size_2],而RNN是放中间,也就是
[input_size_1, batch_size, input_size_2],希望各位同学注意下。
这里需要对比的是RNNCell,我们上面提到上述过程是一个循环的过程,唯一区别就是,RNN是读取了0时刻的隐层信息h0,剩下的过程是自动循环完成的,而RNNCell就需要自己写循环处理了,代码如下:
import torch
from torch.autograd import Variable
from torch import nn
input_size = 10
batch_size = 5
hidden_size = 20
num_layers = 2
#定义一个最简单的循环神经网络,网络输入的特征维度为10,隐层的节点数目为20个
model = nn.RNNCell(input_size=input_size, hidden_size=hidden_size)
x = torch.rand(3, batch_size, input_size) #这里理解为生成batch_size条数据,每条数据长度为3,特征维度为input_size
h0 = torch.zeros(batch_size, hidden_size) #这里定义最初始输入的记忆状态,直接设置为0就行
for i in range(3):
h0 = model(x[i], h0)
y = h0
print(y.shape)
RNNCell是单层结构,所以每次的输出,就是对应时刻元素x的输出。