本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com
当输入输出序列都是不定长的序列时,称为seq2seq问题,RNN的NVM模型可以解决该类问题
本文展示使用Encoder-Decoder方式来设计一个RNN的NVM模型,用于解决seq2seq问题
通过本文可以了解,如何应用RNN-NVM模型来解决seq2seq预测问题,以及如何实现代码
本节描述本案例所需要解决的问题,以及RNN模型的设计
RNN案例-Encoder-Decoder解决序列预测
我们以sin函数序列数据的预测为例,讲述如何使用Encoder-Decoder解决序列预测问题
以下是一个sin函数的曲线与序列数据,我们希望通过前5个数据预测之后10个时刻数据
RNN-NVM模型设计
我们将数据处理为以下的形式:
x1-x5是t时刻之前的5个数据,y1-y10是之后10个时刻的数据,我们使用x1-x5来预测y1-y10
我们设计一个结构为Encoder-Decoder的RNN模型如下:
上述模型的设计意义如下:
用Encoder按顺序将前五个时刻的数据更新到隐节点中,最后一个隐节点H5就承载了x1-x5的信息
然后将H5作为编码信息,使用Decoder将之后的10个时刻的信息按顺序推理出来
在这里作为Encoder的RNN是没有输出的,它的最终输出就是最后一个隐层H5,
而作为Decoder的RNN则是没有输入的,它只是把H5进行解码
我们可以设计得更加复杂一些,例如将H5先作一个变换,再输入到解码器,或者可以在Decoder中添加输入信息,
但这里我们作为学习,解决的问题本身就较为简单,在这里以简单上手为主要目的,这样的设计已经足以应对
本节使用pytorch实现上节所设计的模型,用于拟合sin函数接下来10个时刻的输出
RNN案例(Encoder-Decoder)-sin函数拟合代码实现
我们使用pytorch来实现上述所设计的Encoder-Decoder模型,以及训练模型
具体实现代码如下:
import torch
import random
import torch.nn as nn
# ----------------------数据生成---------------------------------
data = torch.sin(torch.arange(-10, 10,0.1)) # 生成sin序列数据
xLen = 5 # 利用前xLen个时刻的数据作为输入
yLen = 10 # 预测之后yLen个时刻的数据
sample_n = len(data)-1-xLen-yLen-1 # 样本个数
x = torch.zeros(xLen,sample_n,1) # 初始化x
y = torch.zeros(yLen,sample_n,1) # 初始化y
for i in range(sample_n): # 从序列数据中获取x与y
x[:,i,:] = data[i:i+xLen].unsqueeze(1) # 将前xLen个数据作为x
y[:,i,:] = data[i+xLen:i+xLen+yLen].unsqueeze(1) # 将后yLen个数据作为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
#--------------------模型结构--------------------------------------------
# 编码器
class Encoder(nn.Module):
def __init__(self,input_size,hiden_size):
super(Encoder, self).__init__()
self.hiden_size = hiden_size # 编码器的隐层个数
self.rnn = nn.RNN(input_size, hiden_size) # 编码器的RNN隐层
def forward(self, x,h0=None):
if(h0==None):
h0 = torch.zeros(1,x.shape[1],self.hiden_size) # 将隐层状态初始化为0
h,_ = self.rnn(x,h0) # 计算RNN隐层
h = h[-1,:,:].unsqueeze(0) # 只需返回最后一个隐层
return h
# 解码器
class Decoder(nn.Module):
def __init__(self,out_size,hiden_size):
super(Decoder, self).__init__()
# 解码器的RNN层,本来是没有输入的,
# 但pytroch不支持0输入,因为初始化为一个输入
self.rnn = nn.RNN(1, hiden_size) # 解码器的RNN层
self.fc = nn.Linear(hiden_size, out_size) # 解码器的输出层
def forward(self,T,C):
zeros_input = torch.zeros(T,C.shape[1],1) # 初始化一个全为0的输入
h,_ = self.rnn(zeros_input,C) # 将编码C作为隐层初始状态,计算RNN隐层
y = self.fc(h) # 计算输出层
return y,h
# seq2seq模型
class Seq2seq(nn.Module):
def __init__(self,input_size,out_size,hiden_size):
super(Seq2seq, self).__init__()
self.encoder = Encoder(input_size,hiden_size) # 初始化编码器
self.decoder = Decoder(out_size,hiden_size) # 初始化解码器
def forward(self, x,T,h0=None):
C = self.encoder(x,h0) # 获得编码器的隐节点作为编码
y,h = self.decoder(T,C) # 利用编码C解码出T时刻的数据
return y,h
#--------------------模型训练--------------------------------------------
# 模型设置
goal = 0.00001 # 训练目标
epochs = 100000 # 训练频数
model = Seq2seq(1,1,10) # 初始化模型,模型为1输入,1输出,10个隐节点
lossFun = torch.nn.MSELoss() # 定义损失函数为MSE损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.01,weight_decay=0.00001) # 初始化优化器
# 模型训练
for epoch in range(epochs):
optimizer.zero_grad() # 将优化器里的参数梯度清空
train_py,_ = model(train_x,yLen) # 计算模型的预测值
train_loss = lossFun(train_py, train_y) # 计算损失函数值
valid_py,_ = model(valid_x,yLen) # 计算模型的预测值
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,h = model(x,yLen)
print('loss:',lossFun(py, y).data) # 打印损失值
idx = 0 # 抽取一条样本
print('---smaple'+str(idx)+':trueValue and PredictValue:---') # 打印样本真实与预测结果
print(torch.cat([y[:,idx,:],py[:,idx,:]],dim=1).data) # 打印真实值与预测值进行对比
运行结果如下:
------当前epoch: 0 ----------
train_loss: tensor(0.5217)
valid_loss: tensor(0.5448)
------当前epoch: 1000 ----------
train_loss: tensor(0.1292)
valid_loss: tensor(0.1343)
------当前epoch: 2000 ----------
....
------当前epoch: 98000 ----------
train_loss: tensor(0.0002)
valid_loss: tensor(0.0002)
------当前epoch: 99000 ----------
train_loss: tensor(0.0002)
valid_loss: tensor(0.0002)
-----------------------------------------
loss: tensor(0.0002)
smaple0:trueValue and PredictValue:
tensor([[ 0.0752, 0.0600],
[-0.0248, -0.0370],
[-0.1245, -0.1395],
[-0.2229, -0.2352],
[-0.3191, -0.3318],
[-0.4121, -0.4233],
[-0.5010, -0.5095],
[-0.5849, -0.5915],
[-0.6630, -0.6679],
[-0.7344, -0.7380]])
可以看到,模型整体的损失值(MSE)已经极小,
从第0条样本的真实值与预测可以看到,真实值与预测值已经比较接近
说明模型已经能较好地预测未来10个时刻的数据
好了,以上就是RNN的NVM模型例子与代码实现了~
End