基于LSTM的股票时间序列预测(附数据集和代码)
LSTM
如果对LSTM原理不懂得⼩伙伴可以看博主下⼀篇博客,因为博主⽔平有限,结合其他⽂章尽量把原理写的清楚些。
数据集
包拯墓⾸先附上数据集
链接:
提取码:6owv
这个数据集是关于股票的,⾥⾯有⽇期,开盘价等信息。
既然是时间序列预测,我们最关⼼的是预测值在时间维度上的⾛势如何,那我们只要最后⼀列volume和第⼀列date这两列就好了。
实战
先是导⼊相关包,⼀些常见的包就不详细说了,我们需要的Sequential,Den, Activation, Dropout,这些可以在博主上⼏期关于keras的实战介绍。mean_squared_error是sklearn⾥⾯⼀个评价模型好坏的指标,相对来说越⼩越好,但也要看数据集值的范围。MinMaxScaler是⼀个归⼀化的包,归⼀化有很多好处,可以让模型算的更快,⼀些求导呀,梯度下降这些的,归⼀化后数据⼩,这些算法⾃然就运⾏快。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import math
dels import Sequential
from keras.layers import Den, Activation, Dropout, LSTM
from sklearn.preprocessing import MinMaxScaler
ics import mean_squared_error
from keras import optimizers
import time
这个是创建变量x和y的,因为lstm时间序列不像别的回归⼀个x,另⼀个值y,lstm的x和y全是⼀组数据产⽣的,也就是它⾃⼰和⾃⼰⽐。有⼀个关键的参数是look_back这个按中⽂直译就是回看,回溯,理解起来也很容易,假如是这个data是[1,2,3,4,5],look_back为1的话. x
[[1]
[2]
[3]] y就是[2 3 4],意思就是⽤前⼀个数据预测后⼀个,这是look_back为1的意思。假如是为8,那前8个数据预测第9个数据。⼩伙伴们可以试试改变这个值,看⼀下结果是否会更好。
注意维度,维度这样设置⼀是归⼀化需要,⼆是输⼊⽹络的要求。
下⾯这个函数功能就是把data,
def creat_datat(datat, look_back=1):
dataX, dataY =[],[]
for i in range(len(datat)-look_back-1):
a = datat[i:(i+look_back)]
dataX.append(a)
dataY.append(datat[i+look_back])
return np.array(dataX), np.array(dataY)
读取⼀下数据,这些参数的意思是,输出ries,ries的序号就是date,这样⽅便下⾯画图,看着也更加直观。因为我们只要⾸尾2列,那就是0,5.
dataframe = pd.read_csv('zgpa_train.csv',
header=0, par_dates=[0],
index_col=0, ucols=[0,5], squeeze=True)
datat = dataframe.values
dataframe.head(10)
这⾥就画⼀下图,之所以弄成ries就是为了⽤pandas画图。
plt.figure(figsize=(12,8))
dataframe.plot()
plt.ylabel('price')
不逞plt.show()
短一些再短一些打一字
这⾥就是归⼀化操作,有⼀个feature_range=(0, 1)这个意思是所有数据标准化到0-1区间内,就是归⼀化的意思,如果(0,100)那就是标准化到0-100这个区间。最后把它转换成列向量
scaler = MinMaxScaler(feature_range=(0,1))
datat = scaler.fit_shape(-1,1))
分割数据集,这个很好理解,⼀会⽤来测试。
train_size =int(len(datat)*0.8)
test_size =len(datat)-train_size
train, test = datat[0: train_size], datat[train_size:len(datat)]
调⽤前⾯函数分别⽣成训练集和测试集的x,y。描写丁香花的诗句
look_back =1
trainX, trainY = creat_datat(train, look_back)
testX, testY = creat_datat(test, look_back)
建⽴模型,这⾥注意下如果是多层lstm的话⼀定要注意前后维度对接,也就是input和output维度,当前层的input维度是上⼀次层的output维度。return_quences=True表⽰返回这个维度给下⾯。
剩下的就是很基础且通⽤的配置了。更详细的在博主另⼀篇关于keras的实战。
model = Sequential()
model.add(LSTM(input_dim=1, output_dim=50, return_quences=True))
#model.add(Dropout(0.2))
model.add(LSTM(input_dim=50, output_dim=100, return_quences=True))
#model.add(Dropout(0.2))
model.add(LSTM(input_dim=100, output_dim=200, return_quences=True))
#model.add(Dropout(0.2))所以单身
model.add(LSTM(300, return_quences=Fal))
model.add(Dropout(0.2))
model.add(Den(100))
model.add(Den(output_dim=1))
model.add(Activation('relu'))
start = time.time()
model.summary()
查看⼀下结构,我们建⽴的⽹络层数不多,参数也不多,还不到100万。
设置了个时间,很快,半分钟都不到就训练完50个epoch。validation_split=0.1表⽰拿出训练集的10%作为验证集,有了验证集能够更好的训练模型,就相当于给模型纠错。 verbo=2表⽰打印出每个epoch的信息,默认是1,1的话就更好看,因为有个进度条和时间。⼩伙伴们不妨试试。这⾥有个history,意思也很容易理解,训练的历史过程,下⾯会阐述⽤处。
history = model.fit(trainX, trainY, batch_size=64, nb_epoch=50,
validation_split=0.1, verbo=2)
print('compilatiom time:', time.time()-start)
这就很简单了,把数据丢进去预测。
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
因为前⾯进⾏了归⼀化,现在的数据都是0-1之间的,这⾥的作⽤就是反归⼀化,让它恢复原来数据范围。
trainPredict = scaler.inver_transform(trainPredict)
trainY = scaler.inver_transform(trainY)
testPredict = scaler.inver_transform(testPredict)
testY = scaler.inver_transform(testY)
打印⼀下评分,写到这博主发现⼩错误,不过懒得改了,下⾯那个是test score。看到我们的score都⾮常⼤,那这是不是说明我们模型很差劲,并不是,这个评分只能参考参考,并不是很绝对。因为我们数据值⼤,数据都是10的8次⽅左右。稍微有⼀点偏差,假如是原来值800000000,预测75000000
0,其实相差不⼤,误差才1/16,但是数值⼤,数值的误差有50000000.这样再平⽅根号⼀系列操作后,有可能会更⼤也有可能变⼩。总之这个指标只是参考参考。博主写到这⾥猜想,如果拿没有反归⼀化的数据进⾏评分那么是不是评分就很⼩呢?⼩伙伴们可以试试。
trainScore = math.sqrt(mean_squared_error(trainY, trainPredict[:,0]))
print('Train Sccore %.2f RMSE'%(trainScore))
testScore = math.sqrt(mean_squared_error(testY, testPredict[:,0]))
print('Train Sccore %.2f RMSE'%(testScore))
接下来的操作是为了画图,⾸先empty_like⽅法表⽰创建⼀个空数组,这个空数组很像datat,为什么呢,因为维度⼀样,但是值还没初始化。然后我们让其全为空后进⾏填值。最后⼀⾏的操作相当于是⼀个100个数值的数值,我填了前⾯70个,因为前⾯70个是我训练集的预测值,后⾯30为空。
trainPredictPlot = np.empty_like(datat)
trainPredictPlot[:]= np.nan
trainPredictPlot = np.reshape(trainPredictPlot,(datat.shape[0],1))
trainPredictPlot[look_back:len(trainPredict)+look_back,:]= trainPredict
那么这⾥同理,⼀个100个数值的数值,前⾯70个为空,那么后⾯30个就由我测试集的预测值来填充。
testPredictPlot = np.empty_like(datat)连续打嗝
testPredictPlot[:]= np.nan
testPredictPlot = np.reshape(testPredictPlot,(datat.shape[0],1))左旋肉碱咖啡
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(datat)-1,:]= testPredict
这就是前⾯history的作⽤了,相当于是记录下来⾥⾯训练过程的⼀些参数变化吧。有⼀个很重要的就是损失,可以看到我们的损失下降很快,最后也收敛到⼀个很⼩的值,还是很不错的。
fig1 = plt.figure(figsize=(12,8))
plt.plot(history.history['loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
飞天侠plt.show()