循环神经网络Recurrent Neural Network(RNN)概念及教程

推荐指数

本文最后由 Demo Marco 更新于 2025-02-22. 如有资源已失效,请留言反馈,将会及时处理。 【推荐:不翻墙访问被墙网站方法 | 自用高速专线机场 | 高速CN2线路 | 高质量家宽住宅IP

什么是循环神经网络(RNN)

循环神经网络 (RNN) 是一种人工神经网络(ANN),用于 Apple 的 Siri 和 Google 的语音搜索。RNN 会通过内部记忆记住过去的输入,这对于预测股票价格、生成文本、转录和机器翻译非常有用。

在传统神经网络中,输入和输出彼此独立,而 RNN 中的输出则取决于序列中的先前元素。循环网络还在网络的每一层之间共享参数。在前馈网络中,每个节点的权重不同。而 RNN 在网络的每一层之间共享相同的权重,并且在梯度下降过程中,权重和基础会单独调整以减少损失。循环神经网络 (RNN)

上图是循环神经网络的简单表示。如果我们使用简单数据 [45,56,45,49,50,…] 预测股票价格,则从X0Xt的每个输入都将包含一个过去值。例如,X0将有 45,X1将有 56,这些值用于预测序列中的下一个数字。

循环神经网络的工作原理

在 RNN 中,信息在循环中循环,因此输出由当前输入和之前收到的输入决定。

输入层X处理初始输入并将其传递给中间层A。中间层由多个隐藏层组成,每个隐藏层都有其激活函数、权重和偏差。这些参数在隐藏层中是标准化的,因此它不会创建多个隐藏层,而是创建一个并循环遍历。

循环神经网络不使用传统的反向传播,而是使用时间反向传播(BPTT) 算法来确定梯度。在反向传播中,模型通过计算从输出层到输入层的误差来调整参数。由于 RNN 在每一层上共享参数,因此 BPTT 会在每个时间步骤中对误差求和。有关 RNN 及其工作原理的更多信息,请参阅什么是循环神经网络?

循环神经网络的类型

前馈网络具有单一输入和输出,而循环神经网络则非常灵活,因为输入和输出的长度可以改变。这种灵活性使 RNN 能够生成音乐、进行情绪分类和机器翻译。

根据输入和输出的长度不同,RNN 可分为四种类型。

  • 一对一是一种简单的神经网络。它通常用于具有单个输入和输出的机器学习问题。
  • 一对多具有单个输入和多个输出。这用于生成图像标题。
  • 多对一采用一系列多个输入并预测单个输出。它在情绪分类中很流行,其中输入是文本,输出是类别。
  • 多对多需要多个输入和输出。最常见的应用是机器翻译。

RNN 的类型

CNN 与 RNN

卷积神经网络 (CNN) 是一种能够处理空间数据的前馈神经网络。它通常用于图像分类等计算机视觉应用。简单的神经网络擅长简单的二元分类,但无法处理具有像素依赖性的图像。CNN 模型架构由卷积层、ReLU层、池化层和全连接输出层组成。CNN 模型架构

CNN 和 RNN 之间的主要区别

  • CNN 适用于图像等稀疏数据。RNN 适用于时间序列和顺序数据。
  • 在训练模型时,CNN 使用简单的反向传播,而 RNN 使用随时间变化的反向传播来计算损失。
  • RNN 对输入和输出的长度没有限制,但 CNN 的输入和输出都是有限的。
  • CNN 有一个前馈网络,而 RNN 则通过循环来处理序列数据。
  • CNN 还可用于视频和图像处理。RNN 主要用于语音和文本分析。

RNN 的局限性

简单的 RNN 模型通常会遇到两个主要问题。这些问题与梯度有关,梯度是损失函数与误差函数的斜率。

  1. 当梯度变得太小以至于更新参数变得无关紧要时,就会出现消失梯度问题;最终算法停止学习。
  2. 当梯度过大时,就会出现梯度爆炸问题,这会使模型不稳定。在这种情况下,较大的误差梯度会累积,模型权重会变得过大。此问题会导致训练时间更长,模型性能较差。

解决这些问题的简单方法是减少神经网络中的隐藏层数量,这将降低 RNN 中的一些复杂性。这些问题也可以通过使用 LSTM 和 GRU 等高级 RNN 架构来解决。

RNN 高级架构

简单的 RNN 重复模块具有一个带有单个 tanh 层的基本结构。RNN 简单结构存在短记忆问题,即难以在较大的序列数据中保留前一个时间步长的信息。这些问题可以通过长短期记忆 (LSTM) 和门控循环单元 (GRU) 轻松解决,因为它们能够记住长时间的信息。简单的 RNN 单元

长短期记忆网络(LSTM)

长短期记忆 (LSTM) 是 RNN 的高级类型,旨在防止梯度衰减和梯度爆炸问题。与 RNN 一样,LSTM 具有重复模块,但结构不同。LSTM 不是单层 tanh,而是有四个相互通信的交互层。这种四层结构有助于 LSTM 保留长期记忆,可用于多个连续问题,包括机器翻译、语音合成、语音识别和手写识别。您可以按照指南获得 LSTM 的实践经验:用于股票预测的 Python LSTM。LSTM 单元

门控循环单元 (GRU)

门控循环单元 (GRU) 是 LSTM 的一个变体,因为两者的设计相似,并且在某些情况下,它们产生的结果也相似。GRU 使用更新门重置门来解决消失梯度问题。这些门决定哪些信息是重要的,并将其传递到输出。这些门可以训练为存储很久以前的信息,而不会随着时间的推移而消失或删除不相关的信息。

与 LSTM 不同,GRU 没有单元状态Ct。它只有隐藏状态ht,由于架构简单,与 LSTM 模型相比,GRU 的训练时间更短。GRU 架构易于理解,因为它采用输入xt和来自前一个时间戳ht-1 的隐藏状态并输出新的隐藏状态ht 。您可以在了解 GRU 网络中获得有关 GRU 的深入知识。GRU细胞

使用 LSTM 和 GRU 预测万事达卡股票价格

在这个项目中,我们将使用 Kaggle 的万事达卡股票数据集(从 2006 年 5 月 25 日到 2021 年 10 月 11 日),并训练 LSTM 和 GRU 模型来预测股价。这是一个基于项目的简单教程,我们将在其中分析数据、预处理数据以在高级 RNN 模型上进行训练,最后评估结果。

该项目需要使用PandasNumpy进行数据处理、使用Matplotlib.pyplot进行数据可视化、使用scikit-learn进行缩放和评估以及使用TensorFlow进行建模。我们还将设置种子以实现可重复性。

# Importing the libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout, GRU, Bidirectional
from tensorflow.keras.optimizers import SGD
from tensorflow.random import set_seed

set_seed(455)
np.random.seed(455)

数据分析

在本部分中,我们将通过将日期列添加到索引并将其转换为日期时间格式来导入 MasterCard 数据集。我们还将从数据集中删除不相关的列,因为我们只对股票价格、交易量和日期感兴趣。

数据集以日期为索引,以开盘价、最高价、最低价、收盘价和成交量为列。看来我们已经成功导入了清理后的数据集。

dataset = pd.read_csv(
"data/Mastercard_stock_history.csv", index_col="Date", parse_dates=["Date"]
).drop(["Dividends", "Stock Splits"], axis=1)
print(dataset.head())
Open High Low Close Volume
Date
2006-05-25 3.748967 4.283869 3.739664 4.279217 395343000
2006-05-26 4.307126 4.348058 4.103398 4.179680 103044000
2006-05-30 4.183400 4.184330 3.986184 4.093164 49898000
2006-05-31 4.125723 4.219679 4.125723 4.180608 30002000
2006-06-01 4.179678 4.474572 4.176887 4.419686 62344000

.describe() 函数可帮助我们深入分析数据。让我们重点关注High列,因为我们将使用它来训练模型。我们还可以为模型特征选择CloseOpen列,但High更有意义,因为它为我们提供了股票价值在特定日期上涨多少的信息。

最低股价为 4.10 美元,最高股价为 400.5 美元。平均值为 105.9 美元,标准差为 107.3 美元,这意味着股票的方差较大。

print(dataset.describe())
Open High Low Close Volume
count 3872.000000 3872.000000 3872.000000 3872.000000 3.872000e+03
mean 104.896814 105.956054 103.769349 104.882714 1.232250e+07
std 106.245511 107.303589 105.050064 106.168693 1.759665e+07
min 3.748967 4.102467 3.739664 4.083861 6.411000e+05
25% 22.347203 22.637997 22.034458 22.300391 3.529475e+06
50% 70.810079 71.375896 70.224002 70.856083 5.891750e+06
75% 147.688448 148.645373 146.822013 147.688438 1.319775e+07
max 392.653890 400.521479 389.747812 394.685730 3.953430e+08

通过使用 .isna().sum() 我们可以确定数据集中的缺失值。看起来数据集没有缺失值。

dataset.isna().sum()
Open 0
High 0
Low 0
Close 0
Volume 0
dtype: int64

train_test_plot 函数接受三个参数:datasettstarttend,并绘制一个简单的折线图。tstart 和 tend 是年限。我们可以更改这些参数来分析特定时期。折线图分为两部分:训练和测试。这将使我们能够确定测试数据集的分布。

万事达卡股价自 2016 年以来一直呈上涨趋势。2020 年第一季度股价有所下跌,但下半年股价趋于稳定。我们的测试数据集包含一年,从 2021 年到 2022 年,其余数据集用于训练。

tstart = 2016
tend = 2020

def train_test_plot(dataset, tstart, tend):
dataset.loc[f"":f"", "High"].plot(figsize=(16, 4), legend=True)
dataset.loc[f"{tend+1}":, "High"].plot(figsize=(16, 4), legend=True)
plt.legend([f"Train (Before {tend+1})", f"Test ({tend+1} and beyond)"])
plt.title("MasterCard stock price")
plt.show()

train_test_plot(dataset,tstart,tend)

数据预处理

train_test_split 函数将数据集分为两个子集:training_set 和 test_set。

def train_test_split(dataset, tstart, tend):
train = dataset.loc[f"":f"", "High"].values
test = dataset.loc[f"{tend+1}":, "High"].values
return train, test
training_set, test_set = train_test_split(dataset, tstart, tend)

我们将使用 MinMaxScaler 函数来标准化我们的训练集,这将帮助我们避免异常值或异常。您还可以尝试使用 StandardScaler 或任何其他标量函数来规范化您的数据并提高模型性能。

sc = MinMaxScaler(feature_range=(0, 1))
training_set = training_set.reshape(-1, 1)
training_set_scaled = sc.fit_transform(training_set)

split_sequence 函数使用训练数据集并将其转换为输入(X_train)和输出(y_train)。

例如,如果序列为 [1,2,3,4,5,6,7,8,9,10,11,12] 且 n_step 为 3,那么它会将序列转换为三个输入时间戳和一个输出,如下所示:

1,2,34
2,3,45
3,4,56
4,5,67

在这个项目中,我们使用 60 个 n_steps。我们还可以减少或增加步骤数来优化模型性能。

def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)


n_steps = 60
features = 1
# split into samples
X_train, y_train = split_sequence(training_set_scaled, n_steps)

我们处理的是单变量序列,因此特征数量为 1,我们需要重塑 X_train 以适应 LSTM 模型。X_train 具有 [样本、时间步长],我们将重塑为 [样本、时间步长、特征]。

# Reshaping X_train for model
X_train = X_train.reshape(X_train.shape[0],X_train.shape[1],features)

LSTM 模型

该模型由一个 LSTM 隐藏层和一个输出层组成。您可以尝试不同的单元数,因为单元越多,结果越好。对于此实验,我们将 LSTM 单元设置为 125,使用 tanh 作为激活,并设置输入大小。

作者注:Tensorflow 库非常易于使用,因此我们无需从头开始创建 LSTM 或 GRU 模型。我们只需使用 LSTM 或 GRU 模块即可构建模型。

最后,我们将使用 RMSprop 优化器和均方误差作为损失函数来编译模型。

# The LSTM architecture
model_lstm = Sequential()
model_lstm.add(LSTM(units=125, activation="tanh", input_shape=(n_steps, features)))
model_lstm.add(Dense(units=1))
# Compiling the model
model_lstm.compile(optimizer="RMSprop", loss="mse")

model_lstm.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 125) 63500
_________________________________________________________________
dense (Dense) (None, 1) 126
=================================================================
Total params: 63,626
Trainable params: 63,626
Non-trainable params: 0
_________________________________________________________________

该模型将以 32 个批次大小进行 50 个周期的训练。您可以更改超参数以缩短训练时间或改善结果。模型训练已成功完成,损失最小。

model_lstm.fit(X_train, y_train, epochs=50, batch_size=32)
Epoch 50/50
38/38 [==============================] - 1s 30ms/step - loss: 3.1642e-04

结果

我们将重复预处理并规范化测试集。首先,我们将转换数据集,然后将数据集拆分为样本,重塑它,进行预测,并将预测逆转换为标准形式。

dataset_total = dataset.loc[:,"High"]
inputs = dataset_total[len(dataset_total) - len(test_set) - n_steps :].values
inputs = inputs.reshape(-1, 1)
#scaling
inputs = sc.transform(inputs)

# Split into samples
X_test, y_test = split_sequence(inputs, n_steps)
# reshape
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], features)
#prediction
predicted_stock_price = model_lstm.predict(X_test)
#inverse transform the values
predicted_stock_price = sc.inverse_transform(predicted_stock_price)

plot_predictions 函数将绘制实际值与预测值的折线图。这将帮助我们直观地看到实际值和预测值之间的差异。

return_rmse 函数接受测试和预测参数并打印出均方根误差 (rmse) 度量。

def plot_predictions(test, predicted):
plt.plot(test, color="gray", label="Real")
plt.plot(predicted, color="red", label="Predicted")
plt.title("MasterCard Stock Price Prediction")
plt.xlabel("Time")
plt.ylabel("MasterCard Stock Price")
plt.legend()
plt.show()


def return_rmse(test, predicted):
rmse = np.sqrt(mean_squared_error(test, predicted))
print("The root mean squared error is {:.2f}.".format(rmse))

根据下面的线图,单层 LSTM 模型表现良好。

plot_predictions(test_set,predicted_stock_price)

该模型在测试数据集上获得了 6.70 rmse,结果看起来很有希望。

return_rmse(test_set,predicted_stock_price)
>>> The root mean squared error is 6.70.

GRU 模型

我们将保持一切不变,只需用 GRU 层替换 LSTM 层即可正确比较结果。模型结构包含一个具有 125 个单元的 GRU 层和一个输出层。

model_gru = Sequential()
model_gru.add(GRU(units=125, activation="tanh", input_shape=(n_steps, features)))
model_gru.add(Dense(units=1))
# Compiling the RNN
model_gru.compile(optimizer="RMSprop", loss="mse")

model_gru.summary()
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
gru_4 (GRU) (None, 125) 48000
_________________________________________________________________
dense_5 (Dense) (None, 1) 126
=================================================================
Total params: 48,126
Trainable params: 48,126
Non-trainable params: 0
_________________________________________________________________

该模型已成功训练了 50 个时期和 32 的批次大小。

model_gru.fit(X_train, y_train, epochs=50, batch_size=32)
Epoch 50/50
38/38 [==============================] - 1s 29ms/step - loss: 2.6691e-04

结果

我们可以看到,真实值和预测值比较接近。预测折线图几乎与实际值相符。

GRU_predicted_stock_price = model_gru.predict(X_test)
GRU_predicted_stock_price = sc.inverse_transform(GRU_predicted_stock_price)
plot_predictions(test_set, GRU_predicted_stock_price)

GRU模型在测试数据集上得到了5.50的rmse,这比LSTM模型有所改进。

return_rmse(test_set,GRU_predicted_stock_price)
>>> The root mean squared error is 5.50.

结论

世界正在走向混合解决方案,数据科学家在图像字幕、情绪检测、视频字幕和 DNA 测序领域使用 CNN-RNN 混合网络。混合网络为模型提供视觉和时间特征。

本教程的前半部分介绍了循环神经网络的基础知识、其局限性以及更高级架构形式的解决方案。本教程的后半部分是关于使用 LSTM 和 GRU 模型开发万事达卡股票价格预测。结果清楚地表明,在结构和超参数相似的情况下,GRU 模型的表现优于 LSTM。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

You May Also Like