图片来源:/read/cv328714/
文章内容分为以下8个部分:
1 问题描述2 模块准备3 数据准备 3.1 获取数据3.2 缺失值处理3.3 拆分训练集与测试集3.4 数据归一化3.5 重构数据4. 模型建立5. 模型拟合6. 模型预测7. 预测结果可视化8. 本文的缺陷
本视频也有对应的视频(6min):
/video/BV1dk4y117nz
本教程的完整代码:
/kisaragiRY/Bilibili-Stock-Prediction
并且作为补充,想了解LSTM具体原理的同学可以看一下colah的博文:
英文原版:
http://colah.github.io/posts/-08-Understanding-LSTMs/
中文翻译版:
/p/95d5c461924c
让我们正式开始吧!
1 问题描述
在这篇文章中,我们将应用一个叫Long short-term memory (LSTM)的循环神经网络(Recurrent Neural Network, RNN),对bilibili(NASDAQ: BILI)从上市-3-28到-8-18的股票数据进行分析及预测,并详细介绍其Python代码的实现。
想要预测精确的股票价格是不可能的,神经网络算法仅能帮助探索一下股票的大致走势,所以
注意!!:此篇文章内容不建议用于股票投资决策使用,所有结果仅作为参考!!
2 模块准备
如果没有某个模块,如pandas_datareader,可以在命令窗口输入以下代码来下载
pip install pandas_datareader
几乎所有Python的模块或包的下载方式都是这样
pip install [模块的名字]
接下来引入本篇文章要用上的所有模块
#引入相关模块#读取数据用的模块from pandas_datareader import data as pdr#数据预处理用的模块import mathfrom sklearn.preprocessing import MinMaxScalerimport numpy as np#建立模型用的模块from keras.models import Sequentialfrom keras.layers import Dense, Dropout, LSTM, TimeDistributed#作图用的模块import missingno as msnoimport matplotlib.pyplot as pltimport seaborn as snssns.set(color_codes='Ture')
3 数据准备
3.1 获取数据
#根据股票代码BILI,从雅虎财经上获取从-3-28到-8-18的股票数据df=pdr.get_data_yahoo('BILI',start='-03-28',end='-08-18')#查看数据df
输出:
数据有603行,6列。即样本量(samples)是603,特征数(features)是6,分别是当日最高价(High),最低价(Low),开盘价(Open),收盘价(Close),成交量(Volume),调整后的收盘价(Adj Close)。
本篇文章只对收盘价这一列数据进行拟合及预测。
3.2 缺失值处理
接下来我们要查看一下缺失值,若存在缺失值,我们则需要做一些相应处理,根据情况看是删除还是填充
#可视化缺失值msno.matrix(df)
输出:
由上图可以看出,此数据是完整数据,没有缺失值,可以继续接下来的步骤
3.3 拆分训练集与测试集
我们将取前80%的数据作为训练集来训练模型,后20%的数据作为测试集来测试模型的预测能力
#将dataframe按索引降序排列data = df.sort_index(ascending=True, axis=0)#只用Close这一列,即只使用股票的收盘价来进行拟合和预测dataset=data[['Close']].values#取80%的数据作为训练集training_data_len=math.ceil(len(dataset)*.8)train_data=dataset[0:training_data_len,:]#取剩下的数据作为测试集#在做预测时,余下的数据的第一个数据,需要前60天的数据来预测,因此这倒溯了60天test_data = dataset[training_data_len-60: , : ]
3.4 数据归一化
这一步,会将数据映射到[0,1]区间,目的是为了提升模型的收敛速度,提升模型的精度。
这里采用的方法叫min-max标准化(Min-max normalization)或0-1标准化(0-1 normalization)
xnorm=x−min(x)max(x)−min(x)x_{norm}=\frac{x-min(x)}{max(x)-min(x)} xnorm=max(x)−min(x)x−min(x)
#特征缩放scaler=MinMaxScaler(feature_range=(0,1))scaled_train=scaler.fit_transform(train_data)scaled_test=scaler.fit_transform(test_data)
3.5 重构数据
这一节的目的是使数据能符合算法的输入要求,分为三步
将数据集拆分成x和y两部分,用x来预测y,将数据转变成时间序列和有监督学习问题
为了图像说明简便,假如我们以3天为一个timestep,即用前3天的数据(橙色大框,绿色大框,x)来预测第4天的数据(橙色小框,绿色小框,y)。在此博客中,实际的timesteps取的是60,即用前60天数据预测第61天数据。 将list类型数据转变成array数据,以便第三步使用将二维数据转成三维数据。LSTM的输入需要一个三维数组(samples, timesteps, feature)分别指样本量,时间步数,特征数目。
x_train和x_test会作为LSTM的输入数据,而此时它俩是二维数据(samples, timsteps=60),我们需要给这俩数据再添一维feature,因为我们只取了收盘价来分析,所以此处的feature=1。
#训练集的重构#1 分离x和yx_train=[]y_train=[]for i in range(60,len(scaled_train)):x_train.append(scaled_train[i-60:i,0])y_train.append(scaled_train[i,0])#2 将list类型数据转变成array数据x_train,y_train=np.array(x_train),np.array(y_train)#3 将二维数据变成三维数据x_train=np.reshape(x_train,(x_train.shape[0],x_train.shape[1],1))#测试集的重构#1 分离x和yx_test = []y_test = dataset[training_data_len: , : ] for i in range(60,len(scaled_test)):x_test.append(scaled_test[i-60:i,0])#2 将list类型数据转变成array数据x_test = np.array(x_test)#3 将二维数据变成三维数据x_test = np.reshape(x_test, (x_test.shape[0],x_test.shape[1],1))
4. 模型建立
初始化神经网络:Sequential()
LSTM层:LSTM()
一般两层LSTM就能比较好的拟合数据了,多层的LSTM会提升模型的拟合度,但同时也增加了模型的复杂度和训练的难度,有兴趣的读者也可以试试多层LSTM效果。
参数units=50的意思是该层有50个LSTM神经元,并且这一层输出的是一个50维的向量
参数input_shape需要我们输入一个二维数组,包含timesteps和features,即时间步数和特征数目
参数return_sequences是用来设定是否返回含有timesteps这一维的数组
True返回的是一个三维数组(batch size,timesteps,number of units),分别指批数目,时间步数,神经元数目,False返回的是一个二维数组(batch size, number of units)
防止过拟合的dropout层:Dropout()
函数Dropout(0.2)意思是上一层输出的数据中,随机删除20%的数据
全连接的神经网络层:Dense()
也叫Densely connected layer,意思是这一层的每个节点与上一层的所有节点相连,参数unites=1的意思是这一层有一个神经元。
神经网络编译:compile()
用Adam来优化,以MSE(均方误差)为损失函数
#构建模型#初始化模型model = Sequential()#LSTM层model.add(LSTM(units=50, return_sequences=True,input_shape=(x_train.shape[1],1)))#Dropout层model.add(Dropout(.2))#LSTM层model.add(LSTM(units=50, return_sequences=False))#Dropout层model.add(Dropout(.2))#全连接层model.add(Dense(units=1))#模型编译pile(optimizer='adam', loss='mean_squared_error')
整体神经网络的结构如图
#模型结构model.summary()
输出:(能看到每层输出的数据结构以及参数个数)
5. 模型拟合
用训练集数据x_train,y_train来训练上一步建立好的模型,本文不涉及调整参数的步骤,在这笔者只是对参数batch_size和epochs随机给了两个值25和10
batch_size:将完整的数据分为多次输入,一次输入的样本大小叫做batch_sizeepochs:完整的数据通过一次神经网络,称为一次epoch
而其实这两个值对模型拟合的好坏影响很大,笔者会在以后的文章中再仔细探讨一下如何调整参数。
#拟合模型model.fit(x_train, y_train, batch_size=25, epochs=10)
6. 模型预测
用训练集训练好模型后,用测试集来做预测;
又因之前对x_test做过归一化处理,这里需要用inverse_transform()这个函数来将它复原
#预测predictions = model.predict(x_test) #还原 特征缩放predictions = scaler.inverse_transform(predictions)
7. 预测结果可视化
#创建用来画图的dataframetrain = data[:training_data_len]valid = data[training_data_len:]#给valid添加新的一列,把预测值predictions赋值给新的一列valid['Predictions'] = predictions#开始作图#图像大小plt.figure(figsize=(16,8))#图像标题plt.title('Model')#x轴plt.xlabel('Date', fontsize=18)#y轴plt.ylabel('Close Price USD ($)', fontsize=18)#画训练集的折线图plt.plot(train['Close'])#分别画出真实值和预测值的折线图plt.plot(valid[['Close', 'Predictions']])#展示图例plt.legend(['Train', 'Val', 'Predictions'], loc='lower right')plt.show()
输出:
由图可以看出算法LSTM的确能较为精确的预测出股票的浮动趋势