当前位置:   article > 正文

RNN—原理详解及股票预测实战_rnn预测模型

rnn预测模型

循环神经网络(RNN)是基于序列数据(如语言、语音、时间序列)的递归性质而设计的,是一种反馈类型的神经网络,其结构包含环和自重复,因此被称为“循环”。它专门用于处理序列数据,如逐字生成文本或预测时间序列数据(例如股票价格)。

图片

一、RNN网络类型

RNN以输入数m对应输出数n的不同,可以划分为5种基础结构类型:

图片

(1)one to one:其实和全连接神经网络并没有什么区别,这一类别算不上 RNN。

(2)one to many:输入不是序列,输出是序列。可用于按主题生成文章或音乐等。

(3)many to one:输入是序列,输出不是序列(为单个值),常用于文本分类、回归预测。

(4)many to many:输入和输出都是不定长的序列,这也就是Encoder-Decoder结构,常用于机器翻译。

(5)many to many(m==n):输入和输出都是等长的序列数据,这是 RNN 中最经典的结构类型,常用于NLP的命名实体识别、序列预测。

二、RNN原理

关于RNN模型,咱还是从数据、模型、学习目标、优化算法这几个要素展开解析,使用过程需要重点关注的是其输入和输出的差异(以经典的m==n的RNN结构为例)。

2.1 数据层面

不像传统的机器学习模型假设输入是独立的,RNN的输入数据元素有顺序及相互依赖的,并按时间步逐一的串行输入模型的。上一步的输入对下一步的预测是有影响的(如文字预测的任务,以“猫吃鱼”这段序列文字,上一步的输入“猫”:x(0)会影响下一步的预测“吃”:x(1)的概率,也会继续影响下下步的预测“鱼”:x(2)的概率),通过RNN结构就可以将历史的(上下文)的信息反馈到下一步。

2.2模型层面及前向传播

图片

如上图,RNN模型(如左侧模型,实际上也只有这一个物理模型),按各个时间步展开后(如右侧模型),可以看作是按时间步(t)串联并共享(U、W、V )参数的多个全连接神经网络。展开后的立体图如下: 

图片

RNN除了接受每一步的输入x(t),同时还会连接输入上一步的反馈信息——隐藏状态h(t-1),也就是当前时刻的隐藏状态h(t)由当前时刻的输入x(t)和上一时刻的隐藏状态h(t-1)共同决定。另外的,RNN神经元在每个时间步上是共享权重参数矩阵的(不同于CNN是空间上的参数共享),时间维度上的参数共享可以充分利用数据之间的时域关联性,如果在每个时间点都有一个单独的参数,不但不能泛化到训练时没有见过序列长度,也不能在时间上共享不同序列长度和不同位置的统计强度。

如下各时间步的前向传播计算流程图,接下来对计算流程逐步分解: 

图片

上图展开了两个时间步t-1及t的计算过程:

t取值为0~m(序列的长度);

x(t)是t时间步的输入向量;

U是输入层到隐藏层的权重矩阵; 

h(t)是t时间步隐藏层的输出状态向量,能表征历史输入(上下文)的反馈信息;

V是隐藏层到输出层的权重矩阵;b是偏置项;

o(t)是t时间步输出层的输出向量;

2.2.1 t时间步的输入过程

假设各时间步的状态h的维度为2,h初始值为[0,0],输入x和输出o维度为1。

将上一时刻的状态h(t-1),与当前时刻的输入x(t)拼接成一维向量作为全连接的隐藏层的输入,对应隐藏层的的输入维度为3(如下图的输入部分)。

图片

2.2.2 t时间步输出h(t) 并反馈到下一步的过程

对应到计算流程图上,t-1时刻输出的状态h(t-1)为[0.537, 0.462],t时刻的输入为[2.0],拼接之后为[0.537, 0.462, 2.0]输入全连接的隐藏层,隐藏层的权重矩阵U+W为[[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]],偏置项b1为[0.1, -0.1],经过隐藏层的矩阵运算为:h(t-1)拼接x(t) * 权重参数W拼接权重矩阵U + 偏置项(b1)再由tanh转换后输出为状态h(t)。接着h(t)与x(t+1)继续输入到下一步(t+1)的隐藏层。 

2.2.3 t时间步h(t) 到输出o(t)的过程

隐藏层输出状态h(t)为[0.86, 0.884],输出层权重矩阵为[[1.0], [2.0]],偏置项b2为[0.1],h(t)经由输出层的矩阵运算为:h(t) * V+偏置项(b2)后,输出o(t)。

上述过程从初始输入(t=0)遍历到序列结束(t=m),就是一个完整的前向传播过程,可以看出权重矩阵U、W、V和偏置项在不同时刻都是同一组,这也说明RNN在不同时刻中是共享参数的。

可以将这RNN计算过程简要概述为两个公式:

状态h(t) = f( U * x(t) + W * h(t-1) + b1),f为激活函数,上图隐藏层用的是tanh。隐藏层激活函数常用tanh、relu。

输出o(t) = g( V * h(t) + b2),g为激活函数,上图输出层做回归预测,没有用非线性激活函数。当用于分类任务,输出层一般用softmax激活函数。

2.3 学习目标

RNN模型将输入 x(t)序列映射到输出值 o(t)后, 同全连接神经网络一样,可以衡量每个 o(t) 与相应的训练目标 y 的误差(如交叉熵、均方误差)作为损失函数,以最小化损失函数L(U,W,V)作为学习目标(也可以称为优化策略)。

图片

2.4 优化算法

RNN的优化过程与全连接神经网络没有本质区别,通过误差反向传播,多次迭代梯度下降优化参数,得到合适的RNN模型参数U,W,V(此处忽略偏置项)。区别在于RNN是基于时间反向传播,所以RNN的反向传播有时也叫做BPTT(back-propagation through time),BPTT会对不同时间步的梯度求和,由于所有的参数在序列的各个位置是共享的,反向传播时更新的是相同的参数组。如下BPTT示意图及U,W,V求导(梯度)的过程。

图片

优化参数V相对简单,求参数V的偏导数,并对不同时间步的梯度求和:

图片

W和 U的偏导的求解由于需要涉及到历史数据,其偏导求起来相对复杂,假设只有三个时刻(t==3),那么在第三个时刻L对W的偏导数为:

图片

相应的,L在第三个时刻对U的偏导数为:

图片

根据上面两个式子可以写出L在t时刻对W和U偏导数的通式

图片

  • RNN优化的难点

当把激活函数(sigmoid、tanh)代入,分析上述通式的中间累乘的那部分:

图片

sigmoid函数的导数范围是(0,0.25],tanh函数的导数范围是(0,1]。累乘的过程中,如果取sigmoid函数作为激活函数的话,随着时间步越长,较小导数累乘就会导致该时间步梯度越来越小直到接近于0(历史时间步的信息距离当前时间步越长,反馈的梯度信号就会越弱),这也就是“梯度消失”。同理,也可能会导致“梯度爆炸”。

图片

2.5 RNN的局限性

  • 上述展示的都是单向的 RNN,单向 RNN 有个缺点是在 t 时刻,无法使用 t+1 及之后时刻的序列信息,所以就有了双向循环神经网络(bidirectional RNN)。

  • 理论上RNN能够利用任意长序列的信息,但是实际中它能记忆的长度是有限的,经过一定的时间后将导致梯度爆炸或者梯度消失(如上节),即长期依赖(long-term dependencies)问题。一般的,使用传统RNN常需要对序列限定个最大长度、设定梯度截断以及引导信息流的正则化,或者使用门控RNN 如GRU、LSTM 以改善长期依赖问题。

三、RNN预测股票

拟通过创建单层隐藏层的RNN模型,输入前60个交易日(时间步)股票开盘价的时间序列数据,预测下一个(60+1)交易日的股票开盘价。

  1. # 导入股票数据,选取股票开盘价的时间序列数据
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. import pandas as pd
  5. dataset_train = pd.read_csv('./NSE-TATAGLOBAL.csv')
  6. dataset_train = dataset_train.sort_values(by='Date').reset_index(drop=True)
  7. training_set = dataset_train.iloc[:, 1:2].values
  8. print(dataset_train.shape)
  9. dataset_train.head()

  1. # 对训练数据max-min归一化,加速网络收敛
  2. # max-min离差标准化帮助转化不同比例上的数据,消除特殊特征的主导。并且它不需要对数据的分布进行假设(比如k近邻和人工神经网络)。但是,归一化(离差标准化)不能很好地处理异常值。相反,标准化(中心标准化)可以更好地处理异常值,以及加速诸如梯度下降等算法的收敛。所以通常选择中心标准化。
  3. from sklearn.preprocessing import MinMaxScaler,StandardScaler
  4. # sc = StandardScaler()
  5. sc = MinMaxScaler(feature_range = (0, 1))
  6. training_set_scaled = sc.fit_transform(training_set)
  1. # 数据整理为样本及标签:60 timesteps and 1 output
  2. # 每条样本含60个时间步,对应下一时间步作为标签值
  3. X_train = []
  4. y_train = []
  5. for i in range(60, 2035):
  6. X_train.append(training_set_scaled[i - 60:i, 0])
  7. y_train.append(training_set_scaled[i, 0])
  8. X_train, y_train = np.array(X_train), np.array(y_train)
  9. print(X_train.shape)
  10. print(y_train.shape)

 

  1. # Reshaping
  2. X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
  3. print(X_train.shape)

 

  1. # 利用Keras创建RNN模型
  2. from keras.models import Sequential
  3. from keras.layers import Dense
  4. from keras.layers import SimpleRNN,LSTM
  5. from keras.layers import Dropout
  6. # 初始化顺序模型
  7. regressor = Sequential()
  8. # 定义输入层及带5个神经元的隐藏层
  9. regressor.add(SimpleRNN(units=5, input_shape=(X_train.shape[1], 1)))
  10. # 定义线性的输出层
  11. regressor.add(Dense(units=1))
  12. # 模型编译:定义优化算法adam, 目标函数均方根MSE
  13. regressor.compile(optimizer='adam', loss='mean_squared_error')
  14. # 模型训练
  15. history = regressor.fit(X_train, y_train, epochs=500, batch_size=100, validation_split=0.3)
  16. regressor.summary()

  1. plt.plot(history.history['loss'], c='yellow') # 黄色线训练集损失
  2. plt.plot(history.history['val_loss'], c='pink') # 粉色线验证集损失
  3. plt.show()

 

  1. # 测试数据
  2. dataset_test = pd.read_csv('./tatatest.csv')
  3. dataset_test = dataset_test.sort_values(by='Date').reset_index(drop=True)
  4. real_stock_price = dataset_test.iloc[:, 1:2].values
  5. dataset_total = pd.concat((dataset_train['Open'], dataset_test['Open']), axis = 0)
  6. inputs = dataset_total[len(dataset_total) - len(dataset_test) - 60:].values
  7. inputs = inputs.reshape(-1, 1)
  8. inputs = sc.transform(inputs)
  9. # 提取测试集
  10. X_test = []
  11. for i in range(60, 76):
  12. X_test.append(inputs[i - 60:i, 0])
  13. X_test = np.array(X_test)
  14. X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))
  15. # 模型预测
  16. predicted_stock_price = regressor.predict(X_test)
  17. # 逆归一化
  18. predicted_stock_price = sc.inverse_transform(predicted_stock_price)
  19. # 模型评估
  20. print('预测与实际差异MSE',sum(pow((predicted_stock_price - real_stock_price), 2)) / predicted_stock_price.shape[0])
  21. print('预测与实际差异MAE',sum(abs(predicted_stock_price - real_stock_price)) / predicted_stock_price.shape[0])

 

  1. # 预测与实际差异的可视化
  2. plt.plot(real_stock_price, color = 'yellow', label = 'Real TATA Stock Price')
  3. plt.plot(predicted_stock_price, color = 'pink', label = 'Predicted TAT Stock Price')
  4. plt.title('TATA Stock Price Prediction')
  5. plt.xlabel('samples')
  6. plt.ylabel('TATA Stock Price')
  7. plt.legend()
  8. plt.show()

 

通过测试集评估,预测与实际差异MSE:42.54190808,预测与实际差异MAE :5.34363956。可视化预测值与实际值的差异情况,整体比较一致。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/341999?site
推荐阅读
相关标签
  

闽ICP备14008679号