写在前面
之前一直纠结于DL框架的选用,直到看到了《Practical Time Series Analysis》这本书,了解到Keras的简洁与高效,使我更加坚定了使用Keras的决心,不过由于其高度模块化,对一些功能的定制不支持,所以熟练使用后还是要Torch|Tensorflow进行训练(个人理解)。
本文代码部分基于Packt出版社的《Practical Time Series Analysis》一书第五章——深度学习以进行时间序列预测,做了一些更改,数据使用上证指数(本文使用网易财经的免费下载接口->下载地址),需要本书Ebook的可以私信我。
代码及其解释
# 用于训练&测试数据的分割(设置时间点)
from datetime import datetime
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 使用sklearn中的MinMaxScaler函数处理数据(归一化)
from sklearn.preprocessing import MinMaxScaler
# 使用sklearn中的MAE函数,计算平均绝对误差
from sklearn.metrics import mean_absolute_error as MAE
# 使用tk库绘制图形,便于图形的调整
import matplotlib as mpl
mpl.use('TkAgg')
# 加载数据&格式化数据
def import_csv(stock_code, rows):
df = pd.read_csv(stock_code + '.csv', encoding='gbk')
df.rename(columns={'日期': 'date', '收盘价': 'close'}, inplace=True)
df.sort_values(by='date', inplace=True)
df = df[['date', 'close']][-rows:]
df['date'] = pd.to_datetime(df['date'])
df.reset_index(drop=True, inplace=True)
return df
stock_code = '000001'
# 选取全部数据的后7000个值作为模型的数据
df = import_csv(stock_code, 7000)
# 箱形图绘制,用于可视化收盘价的中心趋势和离散程度
# import seaborn as sns
# plt.figure()
# fig1 = sns.boxplot(df['close'])
# fig1.set_title('Box plot of %s'%stock_code)
# plt.show()
# 数据正规化处理
scaler = MinMaxScaler(feature_range=(0, 1))
df['scaled_close'] = scaler.fit_transform(np.array(df['close']).reshape(-1, 1))
# 数据集划分处理
split_date = datetime(year=2019, month=9, day=27)
df_train = df.loc[df['date'] < split_date]
df_val = df.loc[df['date'] >= split_date]
df_val.reset_index(drop=True, inplace=True)
# print(df_train.shape, df_val.shape)
# exit()
def makeXy(df, time_steps):
# 用于生成训练模型的数组数据
# 使用过去time_steps长度的数据来预测下一天的数据
X = []
y = []
for i in range(time_steps, df.shape[0]):
X.append(list(df.loc[i - time_steps:i - 1]))
y.append(df.loc[i])
X, y = np.array(X), np.array(y)
return X, y
time_steps = 5
# 生成训练数据
X_train, y_train = makeXy(df_train['scaled_close'], time_steps)
X_val, y_val = makeXy(df_val['scaled_close'], time_steps)
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
# print(X_train.shape, y_train.shape)
# print(X_val.shape, y_val.shape)
# exit()
def train_model():
from keras.layers import Dense, Input, Dropout
from keras.layers.recurrent import LSTM
from keras.models import Model
from keras.optimizers import Adam
from keras.callbacks import ModelCheckpoint
# 定义LSTM神经网络层结构(利用函数式AP构建)
input_layer = Input(shape=(time_steps, 1), dtype='float32')
lstm_layer1 = LSTM(64, input_shape=(time_steps, 1), return_sequences=True)(input_layer)
lstm_layer2 = LSTM(32, input_shape=(time_steps, 64), return_sequences=False)(lstm_layer1)
dropout_layer = Dropout(.5)(lstm_layer2)
output_layer = Dense(1, activation='linear')(dropout_layer)
optimizer = Adam(lr=0.05, beta_1=0.9, beta_2=0.99, epsilon=0.1, decay=0.0, amsgrad=False)
ts_model = Model(inputs=input_layer, outputs=output_layer)
# 编译模型
ts_model.compile(loss='mean_absolute_error', optimizer=optimizer)
# ts_model.summary();exit() # 输出模型层结构
# 存储损失函数最小值时的模型为hdf文件
save_all = ModelCheckpoint('%s_LSTM_weights.{epoch:02d}-{val_loss:.4f}.h5' % stock_code,
monitor='val_loss', verbose=0, save_best_only=True,
save_weights_only=False, mode='min', period=1)
# 开始训练模型(拟合)
ts_model.fit(x=X_train, y=y_train, batch_size=16,
epochs=25, verbose=2, callbacks=[save_all],
validation_data=(X_val, y_val), shuffle=True)
# 首次运行程序得到误差最小的模型权重
# 第二次运行注释掉下面两行代码,自行添加最优模型的文件名
# 即可得到模型的预测结果,最后绘制成图
train_model()
exit()
# 读取最优模型
from keras.models import load_model
best_model = load_model('%s_LSTM_weights.21-0.0053.h5' % stock_code)
# 预测
preds = best_model.predict(X_val)
# 处理预测的数据(降维,便于2D绘图)
pred_close = np.squeeze(scaler.inverse_transform(preds))
# 输出MAE信息
mae = MAE(df_val['close'].loc[time_steps:], pred_close)
print('MAE : ', round(mae, 4))
# 设置绘制的图形的步长
plot_steps = 100
plt.figure()
plt.plot(range(plot_steps), df_val['close'].loc[time_steps:plot_steps + time_steps - 1], linewidth=1., color='r')
plt.plot(range(plot_steps), pred_close[:plot_steps], linewidth=1., color='b')
# 设置图例
plt.legend(['Actual', 'Predicted'], loc=2)
plt.title('Actual & Predicted %s close_price' % stock_code)
# 设置标签
plt.ylabel('close_price')
plt.xlabel('Index')
plt.show()
# 存图
plt.savefig('LSTM_%s_.png' % stock_code, format='png', dpi=300)
模型的缺点&分析
-
由于输入的数据只有收盘价信息,导致这个模型的输出出现了很严重的滞后性。如果选用股价波动幅度不大的股票进行预测,其滞后性会小一些,但依然存在。
-
影响股价波动的因素有很多,如市场信息、政策调整、复权拆股等,从这些方面进行分析可能会改进模型。
-
股价波动是一种随机游走现象,这个观点已经被证明,所以使用框架进行预测只能得到一个大致的趋势信息,不具有普适性,鉴于此,可以通过金融学的相关知识来分析,可能会得到一些好的结果。(上面提到的那本书中前几章有提到)
-
可以使用Tensorflow&PyTorch框架进一步定制模型,使用更好的优化算法等使得模型Robust性加强。
⋯ ⋯ \cdots\cdots ⋯⋯
后记
时间序列预测的滞后性问题比较明显,针对这个问题需要结合具体数据进行分析,找到关键的一两个点,就会有所突破。
Ubuntu训练模型确实快。还没调成GPU速度就已经比Windows下用GPU快很多了。。