本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com
NV1结构是RNN循环神经网络中的一种经典结构,可用于解决序列预测问题
本文展示一个使用NV1结构的RNN来解决sin函数拟合问题的代码案例,并讲解思路
通过本文可以了解,如何应用RNN-NV1结构来解决序列预测问题,以及如何实现代码
本节描述本案例所需要解决的问题,以及RNN模型的设计
RNN案例(Nv1)-sin函数拟合-问题描述
作为简单的入门,我们以sin函数为例,因为sin函数就是一个时序性非常强的函数,极具代表性
因此,本文以sin函数为例,简单上手与了解如何使用RNN解决序列数据的预测
如图所示,以下是一个sin函数的曲线与序列数据,我们希望通过前5个数据预测下一个数据
在本问题中,属于单时刻输出的预测,因此,是一种最简单的场景,我们以此来作为初步上手RNN
RNN模型设计
我们将数据处理为以下的形式:
x1-x5是t时刻之前的5个数据,y是本时刻的数据,我们使用x1-x5来预测y
我们设计一个Nv1的RNN模型如下:
它的意义就是,按顺序将前五个时刻的数据更新到隐节点中,
最后再使用承载了所有输入信息(x1-x5)的隐节点来拟合输出y
本节使用pytorch实现上节所设计的模型,用于拟合sin函数下一时刻的输出
RNN案例(Nv1)-sin函数拟合代码实现
我们使用pytorch来实现上述所设计的模型,以及训练模型
值得注意的是,在计算损失函数时,我们只需考虑最后一个时刻的预测值的损失
具体实现代码如下:
import torch
import random
import torch.nn as nn
import matplotlib.pyplot as plt
# ----------------------数据生成--------------------------
data = torch.sin(torch.arange(-10, 10,0.1)) # 生成sin序列数据
plt.plot(data)
seqLen = 5 # 利用前5个时刻预测下一时刻
sample_n = len(data)-1-seqLen-1 # 样本个数
x = torch.zeros(seqLen,sample_n,1) # 初始化x
y = torch.zeros(1,sample_n,1) # 初始化y
for i in range(sample_n): # 从序列数据中获取x与y
x[:,i,:] = data[i:i+seqLen].unsqueeze(1) # 将前5个数据作为x
y[:,i,:] = data[i+seqLen] # 将下一个数据作为y
valid_sample_n = round(sample_n*0.2) # 抽取20%的样本作为验证样本
idx = range(sample_n) # 生成一个序列,用于抽样
valid_idx = random.sample(idx, valid_sample_n) # 验证数据的序号
train_idx = [i for i in idx if i not in valid_idx] # 训练数据的序号
train_x = x[:,train_idx,:] # 抽取训练数据的x
train_y = y[:,train_idx,:] # 抽取训练数据的y
valid_x = x[:,valid_idx,:] # 抽取验证数据的x
valid_y = y[:,valid_idx,:] # 抽取验证数据的y
#--------------------模型结构----------------------------------
# RNN神经网络的结构
class RnnNet(nn.Module):
def __init__(self,input_size,out_size,hiden_size):
super(RnnNet, self).__init__()
self.rnn = nn.RNN(input_size, hiden_size)
self.fc = nn.Linear(hiden_size, out_size)
def forward(self, x):
h,_ = self.rnn(x) # 计算循环隐层
h = h[-1,:,:].unsqueeze(0) # 只需要最后一个时刻的隐节点
y = self.fc(h) # 计算输出
return y,h
#--------------------模型训练-----------------------------------
# 模型设置
goal = 0.0001 # 训练目标
epochs = 20000 # 训练频数
model = RnnNet(1,1,10) # 初始化模型,模型为1输入,1输出,10个隐节点
lossFun = torch.nn.MSELoss() # 定义损失函数为MSE损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 初始化优化器
# 模型训练
for epoch in range(epochs):
optimizer.zero_grad() # 将优化器里的参数梯度清空
train_py,_ = model(train_x) # 计算模型的预测值
train_loss = lossFun(train_py, train_y) # 计算损失函数值
valid_py,_ = model(valid_x) # 计算模型的预测值
valid_loss = lossFun(valid_py, valid_y) # 计算损失函数值
if(epoch%1000==0):
print('------当前epoch:',str(epoch),'----------') # 打印当前步数
print('train_loss:',train_loss.data) # 打印训练损失值
print('valid_loss:',valid_loss.data) # 打印验证损失值
if(train_loss<goal): # 如果训练已经达到目标
break # 则退出训练
train_loss.backward() # 更新参数的梯度
optimizer.step() # 更新参数
# ------------------展示结果--------------------------------------
py,_ = model(x) # 模型预测
loss= lossFun(py, y).data # 打印损失函数
print('整体损失值:',loss)
plt.figure() # 初始化画布
plt.plot( y[0,:,0], color='r', linestyle='-.',label='true_y') # 绘制真实曲线
plt.plot( py[0,:,0].detach(), color='b', linestyle='-.',label='predict_y') # 绘制预测曲线
plt.legend(loc=1,framealpha=1) # 展示图例
plt.show() # 展示图像
运行结果如下:
------当前epoch: 0 ----------
train_loss: tensor(0.9424)
valid_loss: tensor(0.8353)
------当前epoch: 1000 ----------
....
------当前epoch: 18000 ----------
train_loss: tensor(0.0002)
valid_loss: tensor(0.0003)
------当前epoch: 19000 ----------
train_loss: tensor(0.0002)
valid_loss: tensor(0.0002)
-----------------------------------------
整体损失值: tensor(0.0002)
可以看到,模型整体的损失值(MSE)已经极小,所预测的y与真实y已经几乎完全一样
好了,以上就是如何使用一个NV1结构RNN循环神经网络来进行序列预测的案例与代码了~
End