likes
comments
collection
share

在 TensorFlow 中使用循环神经网络 (RNN) 进行时间序列预测

作者站长头像
站长
· 阅读数 14

时间序列数据:时间序列中的每个数据点都链接到一个时间戳,该时间戳显示观察或记录数据的准确时间。包括许多领域:金融、经济、天气预报和机器学习,都经常使用此类数据。

时间序列数据经常显示随时间变化的模式或趋势,例如季节性或周期性模式,这是与之相关的一个基本特征。为了进行预测或更多地了解所观察到的潜在过程或事件,可以对这些模式进行分析和建模。

递归神经网络 (RNN)对数据中存在的时间依赖性进行建模,因为它包含先前输入的隐式记忆。因此 RNN 中经常使用本质上是连续的时间序列数据。为了处理 RNN 中的时间序列数据,TensorFlow 提供了许多 API 和工具,例如 tf.keras.layers.RNN API,它允许创建独特的 RNN 单元类并将其与数据一起使用。该 API 还支持多种 RNN 单元类型,包括 Basic RNN、LSTM 和 GRU。 

此示例使用股票价格数据,这是最流行的时间序列数据类型。

第1步: 导入所需的库。

  • Numpy 和 Pandas – 用于数据操作和分析
  • Matplotlib – 用于数据可视化。
  • Yahoo Finance – 提供财务数据进行分析。
  • Datetime – 用于处理日期和时间。
  • Math – 提供 Python 中的基本数学函数。
import numpy as np
import pandas as pd
import yfinance as yf
import datetime as dt
import matplotlib.pyplot as plt
import math

步骤 2: 此代码使用 yfinance 库的 yf.download() 方法从雅虎财经下载 Google 的历史股票数据。使用 datetime 模块的 dt.datetime() 方法,给出已获取数据的时间段的开始日期和结束日期。

然后使用 print() 函数显示下载的数据,其中使用 pd.set_option() 配置 Pandas DataFrame 的显示选项。

#开始和结束日期
start_date = dt.datetime(2020,4,1)
end_date = dt.datetime(2023,4,1)

#从雅虎金融加载
data = yf.download("GOOGL",start_date, end_date)

pd.set_option('display.max_rows', 4)
pd.set_option('display.max_columns',5)
print(data)

输出:

[*********************100%***********************]  1 of 1 completed
                  Open        High  ...   Adj Close    Volume
Date                                ...                      
2020-04-01   56.200001   56.471001  ...   55.105000  51970000
2020-04-02   55.000000   56.138500  ...   55.851501  56410000
...                ...         ...  ...         ...       ...
2023-03-30  100.910004  101.160004  ...  100.889999  33086200
2023-03-31  101.300003  103.889999  ...  103.730003  36823200

[756 rows x 6 columns]

步骤3: 接下来,我们将数据集按照80:20的比例分为训练和测试。使用 iloc[:,:1] 仅选择数据的第一列,并且 train_data 包含原始数据的第一个 training_data_len 行。 test_data 包含从 training_data_len 开始到末尾的原始数据的所有剩余行。

# 为训练设置80%的数据
training_data_len = math.ceil(len(data) * .8)
training_data_len

# 拆分数据集
train_data = data[:training_data_len].iloc[:,:1]
test_data = data[training_data_len:].iloc[:,:1]
print(train_data.shape, test_data.shape)

输出:

(605, 1) (151, 1)

步骤 4:此代码创建一个名为 dataset_train 的 numpy 数组,并用训练数据中的“Open”定价值填充它。然后将一维数组转换为二维数组。shape 属性,返回表示 dataset_train 数组的最终形状的元组 (num_rows, num_columns)。

# 选择未结价格值
dataset_train = train_data.Open.values
# 将 1D 阵列重塑为 2D 阵列
dataset_train = np.reshape(dataset_train, (-1,1))
dataset_train.shape

输出:

(605, 1)

步骤5: 归一化是数据预处理中的关键步骤,可增强机器学习模型的有效性和可解释性。因此,导入 sklearn 中的 MinMaxScaler 将数据集从 0 缩放到 1。使用 sklearn fit_transform() 方法,对训练数据集进行缩放。

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0,1))
# 缩放数据集
scaled_train = scaler.fit_transform(dataset_train)

print(scaled_train[:5])

输出:

[[0.01246754]
 [0.        ]
 [0.00764156]
 [0.01714287]
 [0.0607844 ]]

步骤6: 对测试数据进行同样的数据预处理。

# 选择未结价格值
dataset_test = test_data.Open.values
# 将1D阵列重塑为2D阵列
dataset_test = np.reshape(dataset_test, (-1,1))
# 规范化0和1之间的值
scaled_test = scaler.fit_transform(dataset_test)
print(*scaled_test[:5])

输出:

[0.98362881] [1.] [0.83867656] [0.84481572] [0.86118691]

步骤7: 本阶段时间序列数据必须分为训练集的X_train和y_train以及测试集的X_test和y_test。这样做的目的是将时间序列数据转化为可用于训练模型的监督学习问题。该循环在迭代时间序列数据时生成长度为 50 的输入/输出序列。使用这种方法,我们可以预测未来值,同时考虑数据对先前观测值的时间依赖性。

对于训练集:

X_train = []
y_train = []
for i in range(50, len(scaled_train)):
	X_train.append(scaled_train[i-50:i, 0])
	y_train.append(scaled_train[i, 0])
	if i <= 51:
		print(X_train)
		print(y_train)
		print()

输出:

[array([0.01246754, 0. , 0.00764156, 0.01714287, 0.0607844 , 
       0.05355843, 0.06139221, 0.05272728, 0.0727117 , 0.0761091 , 
       0.08682596, 0.0943896 , 0.08825454, 0.07413508, 0.0733039 , 
       0.08609869, 0.08051948, 0.09974024, 0.09516887, 0.12727273, 
       0.12018702, 0.11641037, 0.1081195 , 0.12337662, 0.13402599, 
       0.13574544, 0.14640004, 0.14378702, 0.16011432, 0.14345973, 
       0.12130912, 0.12896625, 0.13588574, 0.14830132, 0.15021299, 
       0.16155324, 0.15787013, 0.17764155, 0.16623377, 0.15584416, 
       0.16645714, 0.16919484, 0.17402597, 0.178026 , 0.17495062, 
       0.16396881, 0.16949613, 0.17934547 , 0.18779741, 0.17715843])] 
[0.16927791446834417]

[array([0.01246754, 0. , 0.00764156, 0.01714287, 0.0607844 , 
       0.05355843, 0.06139221, 0.05272728, 0.0727117 , 0.0761091 , 
       0.08682596, 0.0943896 , 0.08825454, 0.07413508, 0.0733039 , 
       0.08609869, 0.08051948, 0.09974024, 0.09516887, 0.12727273, 
       0.12018702, 0.11641037, 0.1081195 , 0.12337662, 0.13402599, 
       0.13574544, 0.14640004, 0.14378702, 0.16011432, 0.14345973, 
       0.12130912, 0.12896625, 0.13588574, 0.14830132, 0.15021299, 
       0.16155324, 0.15787013, 0.17764155, 0.16623377, 0.15584416, 
       0.16645714, 0.16919484, 0.17402597, 0.178026 , 0.17495062, 
       0.16396881, 0.16949613, 0.17934547 , 0.18779741, 0.17715843]),
array([0. , 0.00764156, 0.01714287, 0.0607844 , 0.05355843, 
       0.06139221, 0.05272728, 0.0727117 , 0.0761091 , 0.08682596, 
       0.0943896 , 0.08825454, 0.07413508, 0.0733039 , 0.08609869, 
       0.08051948, 0.09974024, 0.09516887, 0.12727273, 0.12018702, 
       0.11641037, 0.1081195 , 0.12337662, 0.13402599, 0.13574544, 
       0.14640004, 0.14378702, 0.16011432, 0.14345973, 0.12130912, 
       0.12896625, 0.13588574, 0.14830132, 0.15021299, 0.16155324, 
       0.15787013, 0.17764155, 0.16623377, 0.15584416, 0.16645714, 
       0.16919484, 0.17402597, 0.178026 , 0.17495062, 0.16396881, 
       0.16949613, 0.17934547, 0.18779741, 0.17715843, 0.16927791])] 
[0.16927791446834417, 0.15038444221793834]

对于测试集:

X_test = []
y_test = []
for i in range(50, len(scaled_test)):
	X_test.append(scaled_test[i-50:i, 0])
	y_test.append(scaled_test[i, 0])

步骤8: 在这一步中,数据被转换成适合输入到RNN的格式。*np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1)) *将原本是形状(样本、特征)的二维数组的 X_train 数组转换为 3 维形状数组(样本、时间步长、特征),其中时间步长表示输入序列中的时间步数,特征表示输入数据中的特征数。大小 1 是一个附加维度,用于指示每个时间步仅具有单个特征。

*y_train 数组通过np.reshape(y_train, (y_train.shape[0], 1)) *从形状 (samples) 的一维数组转换为形状 (samples, 1) 的二维数组,其中每个row 表示某个时间步的输出值。 

对于训练集:

# 数据转换为Numpy数组
X_train, y_train = np.array(X_train), np.array(y_train)

# 重塑
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1],1))
y_train = np.reshape(y_train, (y_train.shape[0],1))
print("X_train :",X_train.shape,"y_train :",y_train.shape)

输出:

X_train : (555, 50, 1) y_train : (555, 1)

对于测试集:

# 数据转换为numpy数组
X_test, y_test = np.array(X_test), np.array(y_test)

# 重塑
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1],1))
y_test = np.reshape(y_test, (y_test.shape[0],1))
print("X_test :",X_test.shape,"y_test :",y_test.shape)

输出:

X_test : (101, 50, 1) y_test : (101, 1)

步骤9: 这一步创建了三个RNN模型。导入模型所需的库。

# 导入库
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import SimpleRNN
from keras.layers import Dropout
from keras.layers import GRU, Bidirectional
from keras.optimizers import SGD
from sklearn import metrics
from sklearn.metrics import mean_squared_error

简单RNN模型: 

此代码使用 Keras API 创建一个循环神经网络 (RNN),其中包含四层基本 RNN 和一个密集输出层。它利用 tanh 双曲正切激活函数。为了避免过拟合,引入了速率为0.2的dropout层。它采用优化器作为Adam,均方误差作为损失函数,准确性作为编译时的评估指标。批量大小为 2 时,模型适合 20 个时期的训练数据。模型架构摘要中列出了每层参数的数量以及模型中参数的总数。

# 初始化RNN
regressor = Sequential()

# 添加RNN层和丢弃正则化
regressor.add(SimpleRNN(units = 50,
						activation = "tanh",
						return_sequences = True,
						input_shape = (X_train.shape[1],1)))
regressor.add(Dropout(0.2))

regressor.add(SimpleRNN(units = 50,
						activation = "tanh",
						return_sequences = True))

regressor.add(SimpleRNN(units = 50,
						activation = "tanh",
						return_sequences = True))

regressor.add( SimpleRNN(units = 50))

# 添加输出层
regressor.add(Dense(units = 1,activation='sigmoid'))

# 编译RNN
regressor.compile(optimizer = SGD(learning_rate=0.01,
								decay=1e-6,
								momentum=0.9,
								nesterov=True),
				loss = "mean_squared_error")

# 拟合模型
regressor.fit(X_train, y_train, epochs = 20, batch_size = 2)
regressor.summary()

输出:

Epoch 1/20
278/278 [==============================] - 13s 39ms/step - loss: 0.0187
Epoch 2/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0035
Epoch 3/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0031
Epoch 4/20
278/278 [==============================] - 12s 42ms/step - loss: 0.0028
Epoch 5/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0027
Epoch 6/20
278/278 [==============================] - 10s 36ms/step - loss: 0.0023
Epoch 7/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0026
Epoch 8/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0023
Epoch 9/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0021
Epoch 10/20
278/278 [==============================] - 11s 40ms/step - loss: 0.0021
Epoch 11/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 12/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 13/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0022
Epoch 14/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 15/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0019
Epoch 16/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0018
Epoch 17/20
278/278 [==============================] - 10s 36ms/step - loss: 0.0019
Epoch 18/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0017
Epoch 19/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0016
Epoch 20/20
278/278 [==============================] - 11s 39ms/step - loss: 0.0016
Model: "sequential_6"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 simple_rnn_24 (SimpleRNN)   (None, 50, 50)            2600      
                                                                 
 dropout_6 (Dropout)     

LSTM RNN 模型:  

此代码创建一个具有三层和密集输出层的 LSTM 模型。它采用优化器作为Adam,均方误差作为损失函数,准确性作为编译时的评估指标。批量大小为 1 时,模型将适合 10 个时期的训练数据。模型架构摘要中列出了每层参数的数量以及模型中参数的总数。

#初始化模型
regressorLSTM = Sequential()

#添加LSTM层
regressorLSTM.add(LSTM(50,
					return_sequences = True,
					input_shape = (X_train.shape[1],1)))
regressorLSTM.add(LSTM(50,
					return_sequences = False))
regressorLSTM.add(Dense(25))

#添加输出层
regressorLSTM.add(Dense(1))

#编译模型
regressorLSTM.compile(optimizer = 'adam',
					loss = 'mean_squared_error',
					metrics = ["accuracy"])

#拟合模型
regressorLSTM.fit(X_train,
				y_train,
				batch_size = 1,
				epochs = 12)
regressorLSTM.summary()

输出:

Epoch 1/12
555/555 [==============================] - 18s 25ms/step - loss: 0.0050
Epoch 2/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0024
Epoch 3/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0018
Epoch 4/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0017
Epoch 5/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0013
Epoch 6/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0013
Epoch 7/12
555/555 [==============================] - 14s 26ms/step - loss: 0.0010
Epoch 8/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0010
Epoch 9/12
555/555 [==============================] - 14s 25ms/step - loss: 9.8315e-04
Epoch 10/12
555/555 [==============================] - 15s 26ms/step - loss: 0.0011
Epoch 11/12
555/555 [==============================] - 14s 25ms/step - loss: 0.0011
Epoch 12/12
555/555 [==============================] - 14s 24ms/step - loss: 9.1305e-04
Model: "sequential_15"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_12 (LSTM)              (None, 50, 50)            10400     
                                                                 
 lstm_13 (LSTM)              (None, 50)                20200     
                                                                 
 dense_19 (Dense)            (None, 25)                1275      
                                                                 
 dense_20 (Dense)            (None, 1)                 26        
                                                                 
=================================================================
Total params: 31,901
Trainable params: 31,901
Non-trainable params: 0
_________________________________________________________________

GRU RNN 模型: 

此代码使用 Keras 中的 GRU(门控循环单元)层定义循环神经网络 (RNN) 模型。它由四个堆叠的 GRU 层组成,后面跟着一个输出层。它利用“tanh”双曲正切激活函数。为了避免过拟合,引入了速率为0.2的dropout层。它采用随机梯度下降(SGD)优化器,学习率为 0.01,衰减率为 1e-7,动量为 0.9,Nesterov 设置为 False。均方误差是损失函数,准确率是编译时的评估指标。批量大小为 2 时,模型适合 20 个时期的训练数据。模型架构摘要中列出了每层参数的数量以及模型中参数的总数。

#初始化模型
regressorGRU = Sequential()

# 带Dropout正则化的GRU层
regressorGRU.add(GRU(units=50,
					return_sequences=True,
					input_shape=(X_train.shape[1],1),
					activation='tanh'))
regressorGRU.add(Dropout(0.2))

regressorGRU.add(GRU(units=50,
					return_sequences=True,
					activation='tanh'))

regressorGRU.add(GRU(units=50,
					return_sequences=True,
					activation='tanh'))

regressorGRU.add(GRU(units=50,
					activation='tanh'))

# 输出层
regressorGRU.add(Dense(units=1,
					activation='relu'))
# 编译RNN
regressorGRU.compile(optimizer=SGD(learning_rate=0.01,
								decay=1e-7,
								momentum=0.9,
								nesterov=False),
					loss='mean_squared_error')

# 拟合数据
regressorGRU.fit(X_train,y_train,epochs=20,batch_size=1)
regressorGRU.summary()

输出:

Epoch 1/20
555/555 [==============================] - 32s 46ms/step - loss: 0.0155
Epoch 2/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0027
Epoch 3/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0030
Epoch 4/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0026
Epoch 5/20
555/555 [==============================] - 26s 47ms/step - loss: 0.0022
Epoch 6/20
555/555 [==============================] - 26s 48ms/step - loss: 0.0025
Epoch 7/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0021
Epoch 8/20
555/555 [==============================] - 26s 46ms/step - loss: 0.0021
Epoch 9/20
555/555 [==============================] - 25s 46ms/step - loss: 0.0021
Epoch 10/20
555/555 [==============================] - 27s 49ms/step - loss: 0.0021
Epoch 11/20
555/555 [==============================] - 27s 49ms/step - loss: 0.0022
Epoch 12/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0022
Epoch 13/20
555/555 [==============================] - 28s 50ms/step - loss: 0.0022
Epoch 14/20
555/555 [==============================] - 28s 50ms/step - loss: 0.0021
Epoch 15/20
555/555 [==============================] - 26s 48ms/step - loss: 0.0018
Epoch 16/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0021
Epoch 17/20
555/555 [==============================] - 27s 49ms/step - loss: 0.0021
Epoch 18/20
555/555 [==============================] - 26s 47ms/step - loss: 0.0019
Epoch 19/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0019
Epoch 20/20
555/555 [==============================] - 27s 48ms/step - loss: 0.0018
Model: "sequential_17"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 gru_4 (GRU)                 (None, 50, 50)            7950      
                                                                 
 dropout_8 (Dropout)         (None, 50, 50)            0         
                                                                 
 gru_5 (GRU)                 (None, 50, 50)            15300     
                                                                 
 gru_6 (GRU)                 (None, 50, 50)            15300     
                                                                 
 gru_7 (GRU)                 (None, 50)                15300     
                                                                 
 dense_22 (Dense)            (None, 1)                 51        
                                                                 
=================================================================
Total params: 53,901
Trainable params: 53,901
Non-trainable params: 0
_________________________________________________________________

步骤 10: 然后使用 X_test 数据对所有三个模型进行预测。

# X_test 数据预测
y_RNN = regressor.predict(X_test)
y_LSTM = regressorLSTM.predict(X_test)
y_GRU = regressorGRU.predict(X_test)

步骤 11: 使用 inverse_transform() 函数将预测值从归一化状态转换回原始比例。

# 从0-1缩减到原始
y_RNN_O = scaler.inverse_transform(y_RNN)
y_LSTM_O = scaler.inverse_transform(y_LSTM)
y_GRU_O = scaler.inverse_transform(y_GRU)

第 12 步: 使用 matplotlib 将预测价格可视化。

fig, axs = plt.subplots(3,figsize =(18,12),sharex=True, sharey=True)
fig.suptitle('Model Predictions')

# RNN预测图
axs[0].plot(train_data.index[150:], train_data.Open[150:], label = "train_data", color = "b")
axs[0].plot(test_data.index, test_data.Open, label = "test_data", color = "g")
axs[0].plot(test_data.index[50:], y_RNN_O, label = "y_RNN", color = "brown")
axs[0].legend()
axs[0].title.set_text("Basic RNN")

# LSTM预测图
axs[1].plot(train_data.index[150:], train_data.Open[150:], label = "train_data", color = "b")
axs[1].plot(test_data.index, test_data.Open, label = "test_data", color = "g")
axs[1].plot(test_data.index[50:], y_LSTM_O, label = "y_LSTM", color = "orange")
axs[1].legend()
axs[1].title.set_text("LSTM")

# GRU预测图
axs[2].plot(train_data.index[150:], train_data.Open[150:], label = "train_data", color = "b")
axs[2].plot(test_data.index, test_data.Open, label = "test_data", color = "g")
axs[2].plot(test_data.index[50:], y_GRU_O, label = "y_GRU", color = "red")
axs[2].legend()
axs[2].title.set_text("GRU")

plt.xlabel("Days")
plt.ylabel("Open price")

plt.show()

输出:

在 TensorFlow 中使用循环神经网络 (RNN) 进行时间序列预测