深度学习-一篇入门

【代码】RNN案例-NVM模型(Encoder-Decoder)

作者 : 老饼 发表日期 : 2023-04-07 17:21:54 更新日期 : 2024-11-28 16:07:54
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



当输入输出序列都是不定长的序列时,称为seq2seq问题,RNN的NVM模型可以解决该类问题

本文展示使用Encoder-Decoder方式来设计一个RNN的NVM模型,用于解决seq2seq问题

通过本文可以了解,如何应用RNN-NVM模型来解决seq2seq预测问题,以及如何实现代码




     01. RNN案例-NVM模型(Encoder-Decoder)   





本节描述本案例所需要解决的问题,以及RNN模型的设计





      RNN案例-Encoder-Decoder解决序列预测      


我们以sin函数序列数据的预测为例,讲述如何使用Encoder-Decoder解决序列预测问题
以下是一个sin函数的曲线与序列数据,我们希望通过前5个数据预测之后10个时刻数据
 RNN案例-Encoder-Decoder的数据 





     RNN-NVM模型设计    


我们将数据处理为以下的形式:
 RNN案例-Encoder-Decoder用于构建模型的数据
x1-x5是t时刻之前的5个数据,y1-y10是之后10个时刻的数据,我们使用x1-x5来预测y1-y10
我们设计一个结构为Encoder-Decoder的RNN模型如下:
 RNN案例-Encoder-Decoder模型设计 
 上述模型的设计意义如下:
用Encoder按顺序将前五个时刻的数据更新到隐节点中,最后一个隐节点H5就承载了x1-x5的信息
然后将H5作为编码信息,使用Decoder将之后的10个时刻的信息按顺序推理出来
在这里作为Encoder的RNN是没有输出的,它的最终输出就是最后一个隐层H5,
而作为Decoder的RNN则是没有输入的,它只是把H5进行解码
 我们可以设计得更加复杂一些,例如将H5先作一个变换,再输入到解码器,或者可以在Decoder中添加输入信息,
但这里我们作为学习,解决的问题本身就较为简单,在这里以简单上手为主要目的,这样的设计已经足以应对









     02. RNN案例-NVM模型(Encoder-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 




联系老饼