02 单变量时间序列回归

本博客介绍了如何利用循环神经网络(RNN)进行单变量时间序列预测,以预测沪标准普尔500指数。通过预处理数据,生成动态输入/输出对,构建LSTM层,并训练模型,最终得出模型在训练和测试集上的优秀表现,如相关系数接近1,RMSE较低。
摘要由CSDN通过智能技术生成

02 单变量时间序列回归

       本节演示了如何使用循环神经网络预测沪标准普尔500 指数。

导入和设置

import warnings
warnings.filterwarnings('ignore')
%matplotlib inline

from pathlib import Path

import numpy as np
import pandas as pd
import pandas_datareader.data as web
from scipy.stats import spearmanr

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

import tensorflow as tf
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM
from tensorflow import keras

import matplotlib.pyplot as plt
import seaborn as sns
gpu_devices = tf.config.experimental.list_physical_devices('GPU')
if gpu_devices:
    print('Using GPU')
    tf.config.experimental.set_memory_growth(gpu_devices[0], True)
else:
    print('Using CPU')
sns.set_style('whitegrid')#用来设置主题的,与数据可视化有关。
np.random.seed(42)
results_path = Path('results', 'univariate_time_series')
if not results_path.exists():
    results_path.mkdir(parents=True)

获取数据

       我们使用第 2 章市场和基本数据中介绍的 pandas_datareader 库获取 2011-2018 年的数据作为训练集,用2019年的数据作为测试集。

sp500 = web.DataReader('SP500', 'fred', start='2011', end='2020').dropna()
ax = sp500.plot(title='S&P 500',
           legend=False,
           figsize=(14, 4),#做出标准普尔500指数的图,宽14英寸,高4英寸
           rot=0)
ax.set_xlabel('')
sns.despine()

预处理

scaler = MinMaxScaler()#将每个元素转化成给定范围(0-1)的值,规范其格式,简化运算。
sp500_scaled = pd.Series(scaler.fit_transform(sp500).squeeze(), 
                         index=sp500.index)#找到均值和标准差,最小值最大值及分位数等基本指标,进行转换实现数据归一化
sp500_scaled.describe()

 从我们的时间序列中生成循环序列

       我们的时间序列是一个按时间索引的数字序列:。其中 {𝑥𝑡} 是周期 𝑡 中的数值,𝑇 是序列的总长度。为了应用 RNN 进行分类回归,我们使用窗口来构建一组动态的输入/输出对,供我们的模型学习。

        我们将生成 63 个交易日的序列,大约三个月,并使用具有 20 个隐藏单元的单个 LSTM 层提前一个时间段预测索引值。 每个 LSTM 层的输入必须具有三个维度,即:

样本:一个序列就是一个样本。 一个批次包含一个或多个样品。
时间段:一个时间段是样本中的一个观察点。
特征:一个特征是一个时间段的一个观察。

       我们的标准普尔 500 指数样本有 2,264 个观测值或时间段。 我们将使用每个包含 63 个观测值的窗口创建重叠序列。对于大小为 T = 5 的更简单的窗口,我们获得如下表所示的输入-输出对:

        一般对于滞后项S,我们设方程形式为,每个 𝑇−𝑆 滞后输入序列或向量的长度为 S,具有相应的标量输出。我们可以使用函数 create_univariate_rnn_data() 来生成使用动态窗口选择的序列:

def create_univariate_rnn_data(data, window_size):#适用于RNN的序列
    n = len(data)
    y = data[window_size:]
    data = data.values.reshape(-1, 1) #将value数据转化为一列
    X = np.hstack(tuple([data[i: n-j, :] for i, j in enumerate(range(window_size, 0, -1))]))
    #hstack是在水平把这些value组合在一起,enumerate遍历数据对象组合为索引序列,用循环结构获取,最后会输出表格形式。
    return pd.DataFrame(X, index=y.index), y#输出表格数据集

       我们将此函数应用于重新调整后的股票指数,以获得 window_size=63 的形状样本数 x 时间步数的二维数据集:

window_size = 63
X, y = create_univariate_rnn_data(sp500_scaled, window_size=window_size)#调用create_univariate_rnn_data函数
X.head()
0 1 2 3 4 5 6 7 8 9 ... 53 54 55 56 57 58 59 60 61 62
DATE
2011-05-24 0.097240 0.096633 0.103069 0.106498 0.096740 0.097726 0.108250 0.103663 0.098515 0.103976 ... 0.120484 0.113439 0.116508 0.111426 0.107549 0.107320 0.112785 0.114149 0.109324 0.101897
2011-05-25 0.096633 0.103069 0.106498 0.096740 0.097726 0.108250 0.103663 0.098515 0.103976 0.103135 ... 0.113439 0.116508 0.111426 0.107549 0.107320 0.112785 0.114149 0.109324 0.101897 0.101388
2011-05-26 0.103069 0.106498 0.096740 0.097726 0.108250 0.103663 0.098515 0.103976 0.103135 0.091499 ... 0.116508 0.111426 0.107549 0.107320 0.112785 0.114149 0.109324 0.101897 0.101388 0.103345
2011-05-27 0.106498 0.096740 0.097726 0.108250 0.103663 0.098515 0.103976 0.103135 0.091499 0.095782 ... 0.111426 0.107549 0.107320 0.112785 0.114149 0.109324 0.101897 0.101388 0.103345 0.105783
2011-05-31 0.096740 0.097726 0.108250 0.103663 0.098515 0.103976 0.103135 0.091499 0.095782 0.092097 ... 0.107549 0.107320 0.112785 0.114149 0.109324 0.101897 0.101388 0.103345 0.105783 0.108310
y.head()

 

X.shape

(2166, 63)

 划分训练集

       为了尊重数据的时间序列性质,我们将样本末尾的数据留作保留或测试集。也就是说,我们将使用 2018 年前的数据作为训练集。做出转化后的指数价格图片。

ax = sp500_scaled.plot(lw=2, figsize=(14, 4), rot=0)#lw=2限制了线条宽度。
ax.set_xlabel('')#设置坐标轴标题
sns.despine()#去掉右边和上边框线

X_train = X[:'2018'].values.reshape(-1, window_size, 1)
y_train = y[:'2018']#取2011-2018为训练集,2019为测试集

X_test = X['2019'].values.reshape(-1, window_size, 1)
y_test = y['2019']
n_obs, window_size, n_features = X_train.shape
y_train.shape#输出训练集的大小

输出训练集的大小(1914,)

建立LSTM层

Keras 有几个内置的 RNN 层,在书中详细描述了各种配置选项。 

LSTM(units,
     activation='tanh',
     recurrent_activation='hard_sigmoid',
     use_bias=True,
     kernel_initializer='glorot_uniform',
     recurrent_initializer='orthogonal',
     bias_initializer='zeros',
     unit_forget_bias=True,
     kernel_regularizer=None,
     recurrent_regularizer=None,
     bias_regularizer=None,
     activity_regularizer=None,
     kernel_constraint=None,
     recurrent_constraint=None,
     bias_constraint=None,
     dropout=0.0,
     recurrent_dropout=0.0,
     implementation=1,
     return_sequences=False,
     return_state=False,
     go_backwards=False,
     stateful=False,
     unroll=False)

 定义模型架构

        在我们的时间序列中创建了输入/输出对并将其切割成训练/测试集后,我们现在可以开始设置我们的 RNN。 我们使用Keras快速搭建了一个如下规格的两隐藏层RNN。

       第 1 层使用带有 20 个隐藏单元的 LSTM 模块(注意这里的 input_shape = (window_size,1))
       第 2 层使用一个带有一个单元的全连接模块应该使用均方误差损失。

       这可以仅使用几行代码来构建,例如,请参阅 Keras 文档和 LSTM 文档,特别是有关如何快速使用 Keras 构建神经网络模型的示例。  keras 推荐的 RNN 方法要在初始化优化器后进行。

rnn = Sequential([#Sequential指的是序贯模型,是keras的内置模型之一,层与层之间只有相邻关系,没有跨层链接,模型速度更快。
    LSTM(units=10, 
         input_shape=(window_size, n_features), name='LSTM'),
    Dense(1, name='Output')#dense是经过非线性变化,发现前面提取的特征之间的关联,最后映射到输出空间上,可以在这一步就是先卷积操作。
])

      输出结果显示该模型有 1,781 个参数:

rnn.summary()

 训练模型

       我们使用推荐用于 RNN 的 RMSProp 优化器和默认设置来训练模型,并针对这个回归问题使用均方误差编译模型:

optimizer = keras.optimizers.RMSprop(lr=0.001,
                                     rho=0.9,
                                     epsilon=1e-08,
                                     decay=0.0)#参数设定
rnn.compile(loss='mean_squared_error', 
            optimizer=optimizer)#用均方误差来做损失函数

        我们定义了一个 EarlyStopping 回调找到停止点并训练模型最多 150 次。

rnn_path = (results_path / 'rnn.h5').as_posix()
checkpointer = ModelCheckpoint(filepath=rnn_path,#文件路径
                               verbose=1,#及时输出进度条记录
                               monitor='val_loss',#监控loss函数的变化
                               save_best_only=True)#只保存结果最好的
early_stopping = EarlyStopping(monitor='val_loss', 
                              patience=20,
                              restore_best_weights=True)
lstm_training = rnn.fit(X_train,
                        y_train,
                        epochs=150,#epoch迭代次数
                        batch_size=20,#一次训练所选取的样本数为20
                        shuffle=True,
                        validation_data=(X_test, y_test),#生成的有效测试数据集
                        callbacks=[early_stopping, checkpointer],
                        verbose=1)#及时输出进度条记录

        下面展示了有效运行次数124次,每一次执行程序的损失函数值及变化。

Epoch 1/150
95/96 [============================>.] - ETA: 0s - loss: 0.0162
Epoch 00001: val_loss improved from inf to 0.00766, saving model to results/univariate_time_series/rnn.h5
96/96 [==============================] - 1s 12ms/step - loss: 0.0161 - val_loss: 0.0077
Epoch 2/150
95/96 [============================>.] - ETA: 0s - loss: 5.0726e-04
Epoch 00002: val_loss improved from 0.00766 to 0.00135, saving model to results/univariate_time_series/rnn.h5
96/96 [==============================] - 1s 9ms/step - loss: 5.0613e-04 - val_loss: 0.0014
Epoch 3/150
94/96 [============================>.] - ETA: 0s - loss: 4.2700e-04
Epoch 00003: val_loss did not improve from 0.00135
96/96 [==============================] - 1s 9ms/step - loss: 4.2515e-04 - val_loss: 0.0033
Epoch 4/150
94/96 [============================>.] - ETA: 0s - loss: 4.0140e-04
Epoch 00004: val_loss did not improve from 0.00135
96/96 [==============================] - 1s 10ms/step - loss: 3.9946e-04 - val_loss: 0.0022
Epoch 5/150
91/96 [===========================>..] - ETA: 0s - loss: 3.7595e-04
Epoch 00005: val_loss did not improve from 0.00135
96/96 [==============================] - 1s 9ms/step - loss: 3.6440e-04 - val_loss: 0.0033
Epoch 6/150
96/96 [==============================] - ETA: 0s - loss: 3.4672e-04
Epoch 00006: val_loss improved from 0.00135 to 0.00068, saving model to results/univariate_time_series/rnn.h5
96/96 [==============================] - 1s 9ms/step - loss: 3.4672e-04 - val_loss: 6.7836e-04
Epoch 7/150
95/96 [============================>.] - ETA: 0s - loss: 3.1172e-04
Epoch 00007: val_loss did not improve from 0.00068
96/96 [==============================] - 1s 9ms/step - loss: 3.1417e-04 - val_loss: 0.0047
Epoch 8/150
93/96 [============================>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值