循环神经网络RNN时间序列预测与MLP比较

1 序列数据

利用CNN构建图像识别模型,对每个样本的假设是独立同分布的,然而,大多数的数据并非如此。例如,文章中的单词是按顺序写的,如果顺序被随机地重排,就很难理解文章原始的意思。同样,视频中的图像帧、对话中的音频信号以及网站上的浏览行为都是有顺序的(称之为序列数据)。因此,针对此类数据而设计特定模型,可能效果会更好。

简言之,如果说卷积神经网络可以有效地处理空间信息,那么本章的循环神经网络(recurrent neural network,RNN)则可以更好地处理序列信息。循环神经网络通过引入状态变量存储过去的信息和当前的输入,从而可以确定当前的输出。[1]

2 从MLP到RNN 【2】

MLP和CNN的输出只考虑与当前输入的关系,同一层之间不存在关系,所以同层不存在与前一时刻或者后一时刻的关系。
在这里插入图片描述
在这里插入图片描述
RNN关注隐层每个神经元在时间维度上的不断成长与进步,与MLP相比,没有添加新的神经元,但是沿着时间维度recurrent,建立时序上的关联。图中的隐层神经元数量并没有增加,而是表示隐层不同时刻的状态。隐层之间的关联可以是全连接的,也可以是其他的形式。隐层不同时刻之间的关联共享一个矩阵 W s W_s Ws(减少训练参数)。
在这里插入图片描述
MLP的隐藏层只与当前的输入 X X X有关系。写成数学公式就是: S = f ( W i n X + b ) (1) S=f(W_{in}X+b) \tag{1} S=f(WinX+b)(1)
但是,RNN的隐藏层不仅与当前输入 X X X有关系,还与前一时刻的隐层状态 S t − 1 S_{t-1} St1有关系。写成数学公式就是: S t = f ( W i n X + W s S t − 1 + b ) (2) S_t=f(W_{in}X+W_sS_{t-1}+b) \tag{2} St=f(WinX+WsSt1+b)(2)
不同时刻隐层之间的关系通过矩阵 W s W_s Ws进行关联。让神经网络有了某种记忆的能力。

在这里插入图片描述

3 输入输出的不同形式会有不同的应用

3.1 看图说话(1toN)

在这里插入图片描述

3.2 句子分类(Nto1)

在这里插入图片描述

3.3 词性标定(等长NtoN)

在这里插入图片描述

3.4 Seq2Seq/Encoder-Decoder

先将输入数据编码成一个上下文向量,然后通过它预测输出的序列。机器翻译、文本理解、对话系统
在这里插入图片描述

4 代码

突出RNN与MLP在数据的预处理、输入、训练、输出等各个阶段的处理有什么不同

4.1 时间序列预测

4.1.1 下载数据并观察

#数据加载
import pandas_datareader as pdr
dji = pdr.DataReader('^DJI', 'stooq')
print(dji)

数据显示如下:

                Open      High       Low     Close        Volume
Date                                                            
2024-08-22  40932.23  41026.64  40584.47  40712.78  3.047464e+08
2024-08-21  40881.03  40974.40  40738.43  40890.49  2.696454e+08
2024-08-20  40874.52  40909.38  40756.65  40834.97  2.947137e+08
2024-08-19  40670.83  40907.32  40670.83  40896.53  2.708081e+08
2024-08-16  40528.86  40726.03  40453.58  40659.76  3.087936e+08...       ...       ...       ...           ...
2019-08-30  26476.39  26514.62  26295.59  26403.28  2.191756e+09
2019-08-29  26249.09  26408.84  26185.71  26362.25  2.086640e+08
2019-08-28  25712.99  26041.57  25637.43  26036.10  2.069843e+08
2019-08-27  26014.46  26054.02  25721.85  25777.90  2.635140e+08
2019-08-26  25826.05  25941.25  25716.39  25898.83  2.228613e+08[1257 rows x 5 columns]

四列分别对应着开盘、最高点、最低点和收盘。将收盘这一列拿出来画曲线图:

import matplotlib.pyplot as pltfig1=plt.figure(num=1)
plt.plot(dji['Close'])
plt.show()

在这里插入图片描述

任务就是通过最近一段时间的收盘价格,预测明天的收盘价格。“最近一段时间”指的是多长时间这个可以自己定义,在程序中就是通过变量seq_len指定。

为了对比MLP和RNN,下面对MLP和RNN分别进行定义

4.1.2 定义模型:MLP和RNN

###################################   MLP
class MLP(nn.Module):def __init__(self, input_size, output_size, num_hiddens):super().__init__()self.linear1 = nn.Linear(input_size, num_hiddens)self.linear3 = nn.Linear(num_hiddens, num_hiddens)self.linear2 = nn.Linear(num_hiddens, output_size)def forward(self, X):output = torch.relu(self.linear1(X))output = torch.relu(self.linear3(output))output = self.linear2(output)return output##############################     DRNN
class DRNN(nn.Module):def __init__(self, input_size, output_size, hidden_size, num_layers):super(DRNN, self).__init__()self.hidden_size = hidden_sizeself.num_layers = num_layersself.rnn = nn.RNN(input_size, hidden_size, num_layers, batch_first=True) # batch_first 为 True时output的tensor为(batch_size,seq_len,hidden_size),否则为(seq_len,batch_size,hidden_size)self.linear = nn.Linear(hidden_size, output_size)def forward(self, x):# 初始化隐藏状态和细胞状态state = torch.zeros(self.num_layers, x.size(0), self.hidden_size)# 计算输出和最终隐藏状态output, _ = self.rnn(x, state)output = self.linear(output)return output

通过模型定义可以看出以下几点:
(1)RNN不需要定义序列长度:RNN用于处理序列数据,但是在模型定义中并没有定义序列长度是多少。这一点和MLP不需要定义batch size是一样的,batch size序列长度都是等数据注入模型的时候才能决定。
(2)nn.RNN前向传播输入除了当前时刻输入(x)还需要上一时刻隐藏层的状态(state)作为输入,而nn.Linear不需要上一时刻隐藏层的状态(state)作为输入
(3)因为2,所以RNN需要有一个最初始的隐藏曾状态(state),这个state的形状是(隐藏层个数,batch size,hidden_size)
(4)nn.RNN的输出也是两个,一个是feature,一个是隐藏层状态。feature的形状要看在定义的时候batch_first的设置。如果batch_first=True,那么feature的形状为(batch_size,seq_len,hidden_size),否则为(seq_len,batch_size,hidden_size)。隐藏层状态的形状就是(隐藏层个数,batch size,hidden_size)

4.1.3 对数据进行预处理

import torch
from torch.utils.data import DataLoader, TensorDatasetnum = len(dji)                           # 总数据量
x = torch.tensor(dji['Close'].to_list()[::-1])  # 股价列表,把顺序倒过来,因为原来的时间顺序是从大到小的x = (x - torch.mean(x)) / torch.std(x)  #对数据进行归一化

这里有个需要注意的点,x把数据从dataframe里拿出来的时候,倒转了顺序,因为dataframe里的时间顺序是从大到小的

4.1.4 为训练做准备

因为要做MLP与RNN的对比,所以要准备两组数据,因为这两个模型对输入输出的要求不太一样

准备RNN的train_loader
seq_len = 16                               # 预测序列长度
batch_size = 32                            # 设置批大小X_feature = torch.zeros((num - seq_len, seq_len))      # 构建特征矩阵,num-seq_len行,seq_len列,初始值均为0
Y_label = torch.zeros((num - seq_len, seq_len))        # 构建标签矩阵,形状同特征矩阵for i in range(seq_len):X_feature[:, i] = x[i: num - seq_len + i]    # 为特征矩阵赋值Y_label[:, i] = x[i+1: num - seq_len + i + 1]    # 为标签矩阵赋值train_loader = DataLoader(TensorDataset(X_feature[:num-seq_len].unsqueeze(2), Y_label[:num-seq_len]),batch_size=batch_size, shuffle=True)  # 构建数据加载器,训练各种RNN
准备MLP的train_loader
y_label = x[seq_len:].reshape((-1, 1))           # 真实结果列表,用于MLPtrain_loader_mlp = DataLoader(TensorDataset(X_feature[:num-seq_len], y_label[:num-seq_len]), batch_size=batch_size, shuffle=True)  # 构建数据加载器,训练mlp
定义超参数
# 定义超参数
input_size = 1
output_size = 1
num_hiddens = 64
n_layers = 2
lr = 0.001
建立模型
# 建立模型
model = DRNN(input_size, output_size, num_hiddens, n_layers)
mlp = MLP(seq_len, output_size, num_hiddens)criterion = nn.MSELoss(reduction='none')trainer = torch.optim.Adam(model.parameters(), lr)
trainer_mlp = torch.optim.Adam(mlp.parameters(), lr)

4.1.5 开始训练

# 训练轮次
num_epochs = 20
mlp_loss_history = []
rnn_loss_history = []
#######################  MLP训练
for epoch in tqdm(range(num_epochs)):# print('##########################epoch:',epoch)# 批量训练for X, y in train_loader_mlp:# print('%%%%%%%%%%%')# print('X.shape:',X.shape)#####X.shape: torch.Size([32, 16])# print('y.shape:',y.shape)######y.shape: torch.Size([32, 1])trainer_mlp.zero_grad()y_pred = mlp(X)# print('y_pred.shape:',y_pred.shape)######y_pred.shape: torch.Size([32, 1])loss = criterion(y_pred, y)# print('loss:',loss)loss.sum().backward()trainer_mlp.step()# 输出损失with torch.no_grad():total_loss = 0for X, y in train_loader_mlp:y_pred = mlp(X)loss = criterion(y_pred, y)total_loss += loss.sum()/loss.numel()avg_loss = total_loss / len(train_loader)print(f'Epoch {epoch+1}: Validation loss = {avg_loss:.4f}')# loss_history.append(avg_loss.detach().numpy())mlp_loss_history.append(avg_loss)#######################  DRNN训练for epoch in tqdm(range(num_epochs)):# 批量训练for X, Y in train_loader:trainer.zero_grad()y_pred = model(X)##X.shape: torch.Size([32, 16,1]),其中1是input_size;#Y.shape: torch.Size([32, 16])###y_pred.shape: torch.Size([32, 16, 1]),其中1是output_sizeloss = criterion(y_pred.squeeze(), Y.squeeze())loss.sum().backward()trainer.step()# 输出损失with torch.no_grad():total_loss = 0for X, Y in train_loader:y_pred = model(X)loss = criterion(y_pred.squeeze(), Y.squeeze())total_loss += loss.sum()/loss.numel()avg_loss = total_loss / len(train_loader)print(f'Epoch {epoch+1}: Validation loss = {avg_loss:.4f}')rnn_loss_history.append(avg_loss)# 绘制损失曲线图
# import matplotlib.pyplot as plt
# plt.plot(loss_history, label='loss')
fig2=plt.figure(num=2)
plt.plot(mlp_loss_history, label='MLP_loss')
plt.plot(rnn_loss_history, label='RNN_loss')
plt.legend()
plt.show()

损失曲线图如图所示:
在这里插入图片描述
可以看到RNN的损失更低一些。
需要注意几点:
(1)MLP预测时间序列的输入输出形式与rnn不同,mlp如下:
在这里插入图片描述
RNN如下:
在这里插入图片描述
MLP的input_size就是序列长度,输出size是1;RNN的输入size与输出size都是1

(2)注入MLP的数据与注入RNN的数据的形状不同:注入MLP的数据形状是(batch_size, input_size=seq_len),注入RNN的数据形状是(batch_size, seq_len, input_size=1)
(3)MLP输出形状和RNN输出形状不同:MLP输出形状:(batch_size, output_size)RNN输出形状(batch_size, seq_len, output_size=1)。这个时候,我们就可以看出MLP与RNN的数据的区别了,MLP少了一个seq_len的维度,而这个维度是在数据侧决定的,不是在模型侧
(4)RNN是可以输出隐藏层的状态的,只是这里没有输出。(RNN的输入需要X和state,输出是Y和state,state看需要,可以不输出

所以为了能够正确的训练和预测,需要对数据的形状进行整理,需要用到unsqueeze()和squeeze(),这两个函数的原理请参考【3】

另外MSEloss也需要注意,reduction='none’就是每个元素进行差和平方,保留数据形状,不加和不平均。可参考【4】

4.1.6 预测

fig3=plt.figure(num=3)
rnn_preds = model(X_feature.unsqueeze(2))
mlp_preds = mlp(X_feature)
rnn_preds.squeeze()
time = torch.arange(1, num+1, dtype= torch.float32)  # 时间轴plt.plot(time[:num-seq_len], x[seq_len:num], label='dji')
# plt.plot(time[:num-seq_len], preds.detach().numpy(), label='preds')
plt.plot(time[:num-seq_len], mlp_preds.detach().numpy(), label='mlp_preds')
plt.plot(time[:num-seq_len], rnn_preds[:,seq_len-1].detach(), label='RNN_preds')######只取了序列最后节点的输出
plt.legend()
plt.show()

这里需要注意的是,预测的时候,RNN的预测值只是取了最后时刻节点的输出。
在这里插入图片描述

4.2 文本

参考:
【1】动手学深度学习
【2】【循环神经网络】5分钟搞懂RNN,3D动画深入浅出
【*】PyTorch nn.MSELoss() 均方误差损失函数详解和要点提醒
【3】unsqueeze()和squeeze()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/407246.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware Workstation Pro 下载

文章目录 VMware Workstation ProVMware下载与安装 VMware Workstation Pro VMware Workstation Pro 对个人用户已经完全免费! VMware下载与安装 第一步:进入vmware的官网 VMWare已被收购,因此它会跳到, Broadcom 注册页面&…

命令模式:如何利用命令模式实现手游后端架构?

成长路上不孤单😊【14后boy,C爱好者,持续分享所学,如有需要欢迎收藏转发😊😊😊😊😊😊😊!!!接上篇博文&#xf…

【React原理 - 任务调度和时间分片详解】

概述 在React15的时候,React使用的是从根节点往下递归的方式同步创建虚拟Dom,由于递归具有同步不可中断的特性,所以当执行长任务时(通常以60帧为标准,即16.6ms)就会长时间占用主线程长时间无响应,导致页面卡顿&#x…

通过C# 读取PDF页面大小、方向、旋转角度

在处理PDF文件时,了解页面的大小、方向和旋转角度等信息对于PDF的显示、打印和布局设计至关重要。本文将介绍如何使用免费.NET 库通过C#来读取PDF页面的这些属性。 文章目录 C# 读取PDF页面大小(宽度、高度)C# 判断PDF页面方向C# 检测PDF页面…

批发部小程序怎么制作 批发配送系统开发方法

很多领导想要做一个自己公司的批发部小程序系统,但是不知道该怎么做,本次瀚林就为大家详细介绍一下各种批发部小程序系统的开发制作方法为大家做参考。 目前市面上的批发部有很多类型例如常见的:食品、鲜花、零售批发商、冻品、百货、批发城、…

实现BeanPostProcessor

文章目录 1.实现初始化方法1.目录2.InitializingBean.java3.MonsterService.java 实现初始化接口4.SunSpringApplicationContext.java 调用初始化方法5.测试 2.实现后置处理器1.目录2.BeanPostProcessor.java 后置处理器接口3.SunBeanProcessor.java 自定义后置处理器4.SunSpri…

【Python】函数的定义和调用、形参和实参、函数的返回值、多元赋值、全局和局部变量

文章目录 函数的定义函数的调用形参和实参函数的返回值一个 return多个 return多元赋值 变量作用域函数内的变量全局变量和局部变量修改全局变量 函数的定义 函数的定义:分配任务 def 函数名(形参列表):函数体return 返回值def:define,定义…

AI革新下的社交媒体:揭秘Facebook如何利用智能算法

在社交媒体领域,Facebook一直走在技术创新的前沿。随着人工智能(AI)的飞速发展,Facebook通过智能算法不断革新用户体验、提升平台效率,并推动社交互动的新形式。本文将详细探讨Facebook如何利用AI技术,从个…

ElasticSearch IK分词器的MySQL热部署字典(Docker)

1.下载插件源码 找到自己对应ES版本的下载 Releases infinilabs/analysis-ik GitHub 2.添加mysql驱动依赖 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version><…

C++模拟实现priority_queue(优先级队列)

一、priority_queue的函数接口 从上图我们可以看出&#xff0c; priority_queue也是一个容器适配器&#xff0c;我们使用vector容器来模拟实现priority_queue。 namespace bit{#include<vector>#include<functional>template <class T, class Container vector…

【数据结构】动态顺序表的实现

1.什么是数据结构 数据结构就是把数据元素按照一定的关系组织起来的集合&#xff0c;用来组织和存储数据。通过数据结构&#xff0c;能够有效的将数据组织和管理在一起&#xff0c;按照我们的方式任意对数据进行增删查改等操作。 2.数据结构的分类 数据结构大概可分为逻辑结构…

Selenium + Python 自动化测试19(补充-读取各种文件数据操作)

我们的目标是&#xff1a;按照这一套资料学习下来&#xff0c;大家可以独立完成自动化测试的任务。 上一篇我们讨论了数据驱动测试中如何完成重复的测试实例&#xff0c;今天我们补充一些读取各种文件的方法。 本篇文章我们讨论一下如何使用读取txt、CSV、Excel文件&#xff0…

14-17岁未成年如何办理能一直用的手机卡?

14-17岁未成年如何办理能一直用的手机卡&#xff1f; 有些姐妹要去外面上学&#xff0c;都想要一张属于自己的手机卡。 但是因为反诈的原因&#xff0c;对于手机卡的申领特别严格。 很多不满18岁的人能申领的卡&#xff0c;都是物联卡或者纯流量卡&#xff0c;只能上网&#x…

如何评估Redis的性能

如果系统中出现了大 key、热 key 等&#xff0c;往往会导致 Redis 变慢&#xff0c;但是这个慢该如何界定&#xff1f;多久算慢&#xff1f;1秒还是3秒&#xff1f; 这个肯定是没有标准答案&#xff0c;因为这个和你的硬件设备有关。 硬件差一些&#xff0c;平时响应时间都是…

css 宫格样式内容上下结构

结构 <div class"sc-content-group"><div class"sc-content-item"><div class"sc-item-img"><el-image :src"src" :preview-src-list"[src]"></el-image></div><div class"s…

前程无忧搜索接口 JS 逆向:阿里系acw_sc__v2和Sign加密

&#x1f4ca; 前程无忧搜索接口 JS 逆向&#xff1a;阿里系acw_sc__v2和Sign加密 &#x1f50d; 观察网页加密规律&#xff1a;阿里系acw_sc__v2 在分析前程无忧的搜索接口时&#xff0c;我们首先需要关注网页的加密规律。特别是阿里系的 acw_sc__v2 加密机制。这个加密机制通…

图论 最短路

文章目录 单源最短路朴素Dijkstra代码 堆优化Dijkstra代码 Bellman-ford代码 spfaspfa求最短路代码 spfa判断负环代码 多源最短路Floyd代码 单源最短路 朴素Dijkstra 给定一个 n n n 个点 m m m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正…

龙格-库塔法(Matlab实现)

四阶龙格-库塔法介绍 在各种龙格&#xff0d;库塔法当中有一个方法十分常用&#xff0c;以至于经常被称为“RK4”或者就是“龙格&#xff0d;库塔法”。该方法主要是在已知方程导数和初始值时&#xff0c;利用计算机的仿真应用&#xff0c;省去求解微分方程的复杂过程。 令初…

场景库之高精度地图编辑器

一、背景介绍 高精度地图编辑器是场景库生产所需的必要工具&#xff0c;地图编辑器基于JS开发&#xff0c;可对指定的地图进行描绘&#xff0c;生成数字高精度地图。 二、功能介绍 路网元素支持&#xff1a; 类别元素图片交叉口交叉口安全岛交通岛导流岛道路中心圈路口边缘线…

msvcp110.dll丢失修复?教你几招简单易懂的修复msvcp110.dll指南

msvcp110.dll错误通常出现在Windows操作系统中&#xff0c;表明系统缺少或损坏了该msvcp110.dll文件&#xff0c;这是Microsoft Visual C 2012 Redistributable程序包的一部分。下面列出了几种彻底解决此问题的全面方法&#xff0c;以确保解决从简单文件丢失到系统级问题的多种…