一、RNN简介
循环神经网络(Recurrent Neural Network, RNN)是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络(recursive neural network。
对循环神经网络的研究始于二十世纪80-90年代,并在二十一世纪初发展为深度学习(deep learning)算法之一 ,其中双向循环神经网络(Bidirectional RNN, Bi-RNN)和长短期记忆网络(Long Short-Term Memory networks,LSTM)是常见的的循环神经网络 [3] 。
循环神经网络具有记忆性、参数共享并且图灵完备(Turing completeness),因此在对序列的非线性特征进行学习时具有一定优势 。循环神经网络在自然语言处理(Natural Language Processing, NLP),例如语音识别、语言建模、机器翻译等领域有应用,也被用于各类时间序列预报。引入了卷积神经网络(Convoutional Neural Network,CNN)构筑的循环神经网络可以处理包含序列输入的计算机视觉问题。

基础的神经网络只在层与层之间建立了权连接,RNN最大的不同之处就是在层之间的神经元之间也建立的权连接。
这是一个标准的RNN结构图,图中每个箭头代表做一次变换,也就是说箭头连接带有权值。左侧是折叠起来的样子,右侧是展开的样子,左侧中h旁边的箭头代表此结构中的“循环“体现在隐层。
在展开结构中我们可以观察到,在标准的RNN结构中,隐层的神经元之间也是带有权值的。也就是说,随着序列的不断推进,前面的隐层将会影响后面的隐层。图中O代表输出,y代表样本给出的确定值,L代表损失函数,我们可以看到,“损失“也是随着序列的推荐而不断积累的。
除上述特点之外,标准RNN的还有以下特点:
1、权值共享,图中的W全是相同的,U和V也一样。
2、每一个输入值都只与它本身的那条路线建立权连接,不会和别的神经元连接。
各个符号的含义:x是输入,h是隐层单元,o为输出,L为损失函数,y为训练集的标签。这些元素右上角带的t代表t时刻的状态,其中需要注意的是,因策单元h在t时刻的表现不仅由此刻的输入决定,还受t时刻之前时刻的影响。V、W、U是权值,同一类型的权连接权值相同。
有了上面的理解,前向传播算法其实非常简单,对于t时刻:

其中ϕ()为激活函数,一般来说会选择tanh函数,b为偏置。
t时刻的输出就更为简单:

最终模型的预测输出为:
其中σ为激活函数,通常RNN用于分类,故这里一般用softmax函数。
二、数据简介
根据已知的33个月的销售数据预测下个月每个产品和商店的总销售额。


三、代码实现
导入库
import torch.utils.data
from torch import optim, nn
import pandas as pd
import numpy as np
查看GPU是否可用
print('GPU: ', torch.cuda.is_available())

指定GPU设备
device = torch.device('cuda')
设置参数
num_time_steps = 34
input_size = 1
hidden_size = 16
output_size = 1
定义RNN网络结构
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.rnn = nn.RNN(
input_size=input_size,
hidden_size=hidden_size,
num_layers=1,
batch_first=True
)
self.liner = nn.Linear(hidden_size, output_size)
def forward(self, x, hidden_prev):
out, hidden_prev = self.rnn(x, hidden_prev)
out = out.view(-1, hidden_size)
out = self.liner(out)
out = out.unsqueeze(dim=0)
return out, hidden_prev
网络结构实体化、定义损失函数及梯度下降方式
model = Net().to(device)
criterion = nn.MSELoss().to(device)
optimizer = optim.Adam(model.parameters(), 1e-2)
hidden_prev = torch.zeros(1, 1, hidden_size).to(device)
读取数据并转化为tensor
test = pd.read_csv('/content/test.gz')
train = pd.read_csv('/content/train.gz')
ltest = np.array(test).tolist()
ltrain = np.array(train).tolist()
x_train = [torch.tensor(np.array(i[3:-1])).float().view(1, num_time_steps -1, 1).to(device) for i in ltrain]
y_train = [torch.tensor(np.array(i[4:])).float().view(1, num_time_steps -1, 1).to(device) for i in ltrain]
x_test = [torch.tensor(np.array(i[4:])).float().view(1, num_time_steps, 1).to(device) for i in ltest]
训练模型
for iter in range(10):
for i in range(len(x_train)):
output, hidden_prev = model(x_train[i], hidden_prev)
hidden_prev = hidden_prev.detach()
loss = criterion(output, y_train[i])
model.zero_grad()
loss.backward()
optimizer.step()
if i % 10000 == 0:
print("Iter: {} loss: {} ".format(iter, loss))
torch.save(model, 'model'+str(iter)+'.pth')
读取模型
注意这种方法需要在读取时也要定义网络结构
tm = torch.load('/content/model5.pth').to(device)
预测
predict = []
for i in range(len(x_test)):
input = 0
for _ in range(34):
input = x_test[i][0][0].view(1, 1, 1)
(pred, hidden_prev) = model(input, hidden_prev)
input = pred
predict.append(input)
















