目录

一、概述:

二、股票数据准备

三、股票数据预处理

1、数据特征归一化(标准化)

2、将数据集转化为有监督学习问题

四、股票数据划分为训练集和测试集

五、模型构建及其预测

1、搭建LSTM模型并绘制损失图

2、预测并反转数据(反归一化)

3、绘制模型预测结果图

六、模型评估


一、概述:

传统的线性模型难以解决多变量或多输入问题,而神经网络如LSTM则擅长于处理多个变量的问题,该特性使其有助于解决时间序列预测问题。

本文将初步探究 LSTM 在股票市场的应用。通过使用LSTM对股票收益的预测,可以了解到:(1)如何将原始数据集转换为可用于时间序列预测的数据。(2)如何准备数据并使LSTM适合多变量时间序列预测问题。(3)如何进行预测并将结果重新调整回原始数据。

本文以600000.SH股票数据为基准进行分析,以2016年3月1日至2017年12月31日为回测期,进行收益率的预测模拟。

本实验所需要导入的库如下:

import pandas as pd
# 核心代码,设置显示的最大列、宽等参数,消掉打印不完全中间的省略号
pd.set_option('display.max_columns', 1000)
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth', 1000)

import matplotlib.pyplot as plt
from pandas import read_excel
import numpy as np
from pandas import DataFrame
from pandas import concat
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM,Dense,Dropout
from numpy import concatenate
from sklearn.metrics import mean_squared_error,mean_absolute_error,r2_score
from math import sqrt

二、股票数据准备

前两篇博客对股票数据进行了详细分析,根据股票指标间的相关关系,删除成交量市值pb这三个指标,实现了股票数据的降维。具体股票数据如下:

#定义字符串转换为浮点型(此处是转换换手率)
def str_to_float(s):
    s=s[:-1]
    s_float=float(s)
    return s_float

## 读取excel文件,并将‘日期’列解析为日期时间格式,并设为索引
stock_data=read_excel('stock_data/600000.SH.xlsx',parse_dates=['日期'],index_col='日期')
stock_data.drop(['交易日期','成交量','市值','pb'],axis=1, inplace=True) #删除多列数据
stock_data['换手率']=stock_data['换手率'].apply(str_to_float)
#对股票数据的列名重新命名
stock_data.columns=['open','high','low','close','turnover','pe']
stock_data.index.name='date' #日期为索引列
#将数据按日期这一列排序(保证后续计算收益率的正确性)
stock_data=stock_data.sort_values(by='date')
# 增加一列'earn_rate', 存储每日的收益率
stock_data['earn_rate'] = stock_data['close'].pct_change()
#缺失值填充
stock_data['earn_rate'].fillna(method='bfill',inplace=True)
# 打印数据的前5行
print(stock_data.head())

 部分数据如下:

三、股票数据预处理

采用LSTM模型时,需要对数据进行适配处理,其中包括归一化变量(包括输入和输出值)和将数据集转化为有监督学习问题,使其能够实现通过前一个时刻(t-1)的股票数据和收益率预测当前时刻(t)的股票收益率。

1、数据特征归一化(标准化)

LSTM对输入数据的规模很敏感,特别是当使用sigmoid(默认)或tanh激活函数时。需要将数据重新调整到0到1的范围(也称为标准化)。本实验使用scikit-learn库中的MinMaxScaler预处理类实现数据集的规范化。

#获取DataFrame中的数据,形式为数组array形式
values=stock_data.values
#确保所有数据为float类型
values=values.astype('float32')

# 特征的归一化处理
scaler = MinMaxScaler(feature_range=(0, 1))
scaled = scaler.fit_transform(values)
print(scaled)

归一化结果如下:

2、将数据集转化为有监督学习问题

在本文中,定义一个名为series_to_supervised()函数该函数采用单变量或多变量时间序列并将其构建为监督学习数据集。

#定义series_to_supervised()函数
#将时间序列转换为监督学习问题
def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
	"""
	Frame a time series as a supervised learning dataset.
	Arguments:
		data: Sequence of observations as a list or NumPy array.
		n_in: Number of lag observations as input (X).
		n_out: Number of observations as output (y).
		dropnan: Boolean whether or not to drop rows with NaN values.
	Returns:
		Pandas DataFrame of series framed for supervised learning.
	"""
	n_vars = 1 if type(data) is list else data.shape[1]
	df = DataFrame(data)
	cols, names = list(), list()
	# input sequence (t-n, ... t-1)
	for i in range(n_in, 0, -1):
		cols.append(df.shift(i))
		names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
	# forecast sequence (t, t+1, ... t+n)
	for i in range(0, n_out):
		cols.append(df.shift(-i))
		if i == 0:
			names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
		else:
			names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
	# put it all together
	agg = concat(cols, axis=1)
	agg.columns = names
	# drop rows with NaN values
	if dropnan:
		agg.dropna(inplace=True)
	return agg

该函数有四个参数:

  • data:输入数据需要是列表或二维的NumPy数组的观察序列。
  • n_in:输入的滞后观察数(X)。值可以在[1..len(data)]之间,可选的。默认为1。
  • n_out:输出的观察数(y)。值可以在[0..len(data)-1]之间,可选的。默认为1。
  • dropnan:Bool值,是否删除具有NaN值的行,可选的。默认为True。

该函数返回一个值:

  • 返回:用于监督学习的Pandas DataFrame。

新数据集构造为DataFrame,每列适当地由变量编号和时间步骤命名。使其可以根据给定的单变量或多变量时间序列设计各种不同的时间步长序列类型预测问题。

本文股票数据转换为监督学习问题如下:

#将时间序列转换为监督学习问题
reframed = series_to_supervised(scaled, 1, 1)
# 删除不想预测的特征列,这里只预测收益率
reframed.drop(reframed.columns[[7,8,9,10,11,12]], axis=1, inplace=True)
# 打印数据的前5行
print(reframed.head())

结果如下:

四、股票数据划分为训练集和测试集

将处理后的数据集划分为训练集和测试集。我们将2016年3月1日至2017年12月31日的股票数据作为测试集,其余作为训练集。将训练集和测试集的最终输入(X)转换为为LSTM的输入格式,即[samples,timesteps,features]

Keras LSTM层的工作方式是通过接收3维(N,W,F)的数字阵列,其中N是训练序列的数目,W是序列长度,F是每个序列的特征数目。

# 划分训练集和测试集
values = reframed.values
train = np.concatenate([values[:774, :],values[1219:,:]])
test = values[774:1219, :]
# 划分训练集和测试集的输入和输出
train_X, train_y = train[:, :-1], train[:, -1]
test_X, test_y = test[:, :-1], test[:, -1]
#转化为三维数据
# reshape input to be 3D [samples, timesteps, features]
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))
print(train_X.shape, train_y.shape)
print(test_X.shape, test_y.shape)

训练集和测试集的输入格式如下:

五、模型构建及其预测

1、搭建LSTM模型并绘制损失图

本实验使用keras深度学习框架对模型进行快速搭建。建立Sequential模型,向其中添加LSTM层,设定Dropout为0.5,加入Dense层将其维度聚合为1,激活函数使用relu(也用了sigmoid作为激活函数,但实验效果不如relu),损失函数定为均方差Mean Absolute Error(MAE)。优化算法采用Adam,模型采用50个epochs并且每个batch的大小为100。 

其中:隐藏层有64个神经元,输出层1个神经元(回归问题),输入变量是一个时间步(t-1)的特征。在fit()函数中设置validation_data参数,记录训练集和测试集的损失。

# 搭建LSTM模型
model = Sequential()
model.add(LSTM(64, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dropout(0.5))
model.add(Dense(1,activation='relu'))
model.compile(loss='mae', optimizer='adam')
# fit network
history = model.fit(train_X, train_y, epochs=50, batch_size=100, validation_data=(test_X, test_y), verbose=2,shuffle=False)

# 绘制损失图
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.title('LSTM_600000.SH', fontsize='12')
plt.ylabel('loss', fontsize='10')
plt.xlabel('epoch', fontsize='10')
plt.legend()
plt.show()

 完成训练和测试后绘制损失图如下:

2、预测并反转数据(反归一化)

需要将预测结果和测试集数据组合然后进行比例反转(invert the scaling),同时需要将测试集上的预期值也进行比例转换。 

这里为什么进行比例反转(反归一化)呢?(因为我们将原始数据进行了预处理(连同输出值y),此时的误差损失计算是在处理之后的数据上进行的,为了计算在原始比例上的误差需要将数据进行转化。反转时的矩阵大小一定要和原来的大小(shape)完全相同,否则就会报错。)

#模型预测收益率
y_predict = model.predict(test_X)
test_X = test_X.reshape((test_X.shape[0], test_X.shape[2]))

# invert scaling for forecast
#将预测结果按比例反归一化
inv_y_test = concatenate((test_X[:, :6],y_predict), axis=1)
inv_y_test = scaler.inverse_transform(inv_y_test)
inv_y_predict=inv_y_test[:,-1]

# invert scaling for actual
#将真实结果按比例反归一化
test_y = test_y.reshape((len(test_y), 1))
inv_y_train = concatenate((test_X[:, :6],test_y), axis=1)
inv_y_train = scaler.inverse_transform(inv_y_train)
inv_y = inv_y_train[:, -1]
print('反归一化后的预测结果:',inv_y_predict)
print('反归一化后的真实结果:',inv_y)

反归一化后的部分结果如下:

3、绘制模型预测结果图

plt.plot(inv_y,color='red',label='Original')
plt.plot(inv_y_predict,color='green',label='Predict')
plt.xlabel('the number of test data')
plt.ylabel('earn_rate')
plt.title('2016.3—2017.12')
plt.legend()
plt.show()

六、模型评估

回归模型常用评价指标有:均方误差(Mean Squared Error,MAE)、均方根误差(Root Mean Squard Error,RMSE)、平均绝对误差(MAE)、R squared

本实验的LSTM模型主要采用RMSE作为评价标准。主要比较训练集误差损失train_loss和验证集误差损失val_loss和预测集误差损失pre_loss。很显然loss的值越低说明模型拟合的效果越好。

为了测试模型,使用了所有的评估指标,如下:


#回归评价指标
# calculate MSE 均方误差
mse=mean_squared_error(inv_y,inv_y_predict)
# calculate RMSE 均方根误差
rmse = sqrt(mean_squared_error(inv_y, inv_y_predict))
#calculate MAE 平均绝对误差
mae=mean_absolute_error(inv_y,inv_y_predict)
#calculate R square
r_square=r2_score(inv_y,inv_y_predict)
print('均方误差: %.6f' % mse)
print('均方根误差: %.6f' % rmse)
print('平均绝对误差: %.6f' % mae)
print('R_square: %.6f' % r_square)

结果为:

 

 

 

本实验旨在将LSTM应用于股票数据预测,通过使用LSTM对股票收益的预测,学习时间序列数据的处理和转化。使用LSTM来对股票数据的预测具有一定的可行性,但本实验效果不佳。同时可以使用此方法预测股票的价格,每日最高价等,思路相似。如果要形成一个在股票市场比较实用的 LSTM 模型,还需要在 features 选择、模型构建、模型参数选择以及调优等方面进行学习研究。

本博客所述以及代码仅供学习交流。

 

 

 

 

 

 

参考:

1、如何将时间序列转换为Python的监督学习问题

2、Keras中LSTM的多变量时间序列预测

3、空气污染预测

4、用于时间序列预测的LSTM神经网络