查看本案例完整的数据、代码和报告请登录数据酷客(http://cookdata.cn)案例板块。
本案例适合作为大数据专业TensorFlow深度学习实战课程的配套教学案例。通过本案例,能够达到以下教学效果:
RNN(Recurrent Nerual Network:循环神经网络)是模仿动物神经的一种体现。众所周知,一个普通人在遇到问题大脑思考的时候,是不会从一片虚无开始的,总会根据自己的人生经验,对当前的问题作出反馈。举例来说,假如你是一位文学爱好者,那么“哈姆雷特”你一定能脱口而出。而当我们将这个词反过来,“特—雷—姆—哈”则显得相对拗口。这是因为在看到这个词的一瞬间,念着上一个字已经对下一个字有了预期。
现在我们假设有一组数据需要预测分析,一般的神经网络只能对这组数据中每一个数字独立地预测一个结果。那么如果这些数字有所关联,需要前后数据串联起来分析而非一个一个独立地预测呢?普通的神经网络并不能理解数据之间的关联。那么人类是怎么做到的呢?答案很简单,当人分析下一组数据时,记住当前数据的分析结果,一起分析,RNN就此产生。分析第一个数据后,RNN将分析结果记住,在分析第二个数据时,将旧的记忆调用过来参与分析并且生成新的记忆。如果有更多的有序数据,那么RNN就会一点一点把记忆累积起来一起分析。
如图所示,RNN后一个节点不仅受输入层输入的影响,还会受到上一个节点的影响。
RNN很好地解决了有序数据分析的问题,但是这并不是说RNN就没有缺点了。俄国小说家契诃夫曾经说过,如果在第一幕中出现了一把枪,那么在第三幕中枪一定要响。对于RNN来说,这把枪的记忆就经历了三幕剧中若干个场景,传到了最后枪响的时刻。如果每一次记忆的传递都乘以一个参数,而假设这个参数小于1,那么累乘之后的结果趋向0,这被称为梯度消失;假设这个参数大于1,那么累乘之后的结果会特别大,这被称为梯度爆炸。这两种情况都是导致RNN不能长期记忆的原因。
LSTM就是为了解决这个问题而诞生的。LSTM(Long Short-Term Memory:长短期记忆)是RNN的一种特殊类型。它特殊的地方在于比RNN多了3个控制器:输入控制、遗忘控制、输出控制。LSTM具有一个贯穿整个序列的主干分析部分。每次有新的内容需要输入时,输入控制会对其做出判断;遗忘控制会判断其影响结果具体表现为需要让主干忘记哪些内容;最后输出控制会基于当前主干分析和新的分析,判断如何输出分析结果。
LSTM的第一步决定丢弃什么信息,这由遗忘控制部件所决定,该部件被称为遗忘层。它会读取上一个节点的情况和当前节点的输入来判断主干部分该如何保留信息。接下来LSTM确定需要更新的信息,根据输入的数据创建一个候选值向量。最后对主干部分进行处理,丢弃确定要丢弃的信息,根据候选值更新主干部分信息。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
NumPy支持大量的高维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库,是大量机器学习框架的基础库。
Pandas是基于NumPy的库,提供了高效地操作大型数据集所需的工具,弥补了Python在数据分析和建模方面的空白,使用户不必因为需求而切换到更特定领域的语言,如R。
Matplotlib是一个Python的2D绘图库,只需几行代码即可绘图,如:直方图、条形图、散点图等。
TensorFlow是一个采用数据流图,用于数值计算的库,这次我们用它来搭建LSTM神经网络。
数据来源为爬虫获取,通过对股票信息网站的爬虫来获取所需股票行情,如:基于Python3的数据获取
本案例的数据为平安银行的股票数据(股票代码:sz000001),时间从2017年3月1日到2019年4月19日。它将作为本次案例所使用的数据导入LSTM中,预测收盘价。其中数据量为524行,前500行数据作为训练集,后24行数据作为测试集。
# 读入数据文件
df = pd.read_csv('./input/ts_000001_2017-03-01_2019-04-19.csv', encoding='gbk')
# 展示数据
df.head() # 显示DataFrame的前若干行,默认为5
数据说明:
数据量纲不同,数值大小差别很大。数据中,有的为纯小数如0.98,有的数据动辄上万,两者不具有可比性,因此我们需要引入数据标准化。数据标准化处理主要包括数据同趋化和无量纲化处理。数据标准化的方法有很多种,如:Min-max标准化、Z-score标准化等。
Min-max标准化公式为:新数据=(原始数据-最小值)/(最大值-最小值)
Z-score标准化公式为:新数据=(原始数据-均值)/ 标准差
本案例中采用的标准化方式为Z-score标准化。
# 取出数据中需要的部分,从第二列取至最后一列
data = df.iloc[:,1:].values
可以看到数据集包含了日期、开盘价、最高价、收盘价等数据,除了包含日期的第一列之外,其它数据均对预测有价值,因此从第二列开始取数据。
# 展示数据
df.iloc[:,1:].head()
将数据前5行进行Z-score标准化处理作为演示。之后在模型部分调用的数据是对整体数据进行标准化。
df_demo = df.iloc[:,2:].head()
df_demo = (df_demo - np.mean(df_demo,axis=0))/np.std(df_demo,axis=0)
df_demo
本案例的数据集有15个维度的数据,其中第一列为日期,最后一列为所需预测的标签,将其去除之后还有13个维度的特征。因此本案例的LSTM神经网络使用前一天数据的13个特征作为输入,因此输入层的维度设为13;需要预测出下一天的收盘价,因此输出层的维度设定为1。并且把数据分为训练集和测试集。
#定义常量
rnn_unit=10 #hidden layer units
input_size=13 # 输入层维度
output_size=1 # 输出层维度
lr=0.0006 # 学习率
#输入层、输出层权重、偏置
weights={
'in':tf.Variable(tf.random_normal([input_size,rnn_unit])),
'out':tf.Variable(tf.random_normal([rnn_unit,1]))
}
biases={
'in':tf.Variable(tf.constant(0.1,shape=[rnn_unit,])),
'out':tf.Variable(tf.constant(0.1,shape=[1,]))
}
以上代码为接下来神经网络需要用到的常量,提前定义完毕以便调用。
接下来将数据集分为训练集和测试集。
# 获取训练集
def get_train_data(batch_size,time_step,train_begin,train_end):
batch_index=[]
data_train=data[train_begin:train_end]
normalized_train_data=(data_train-np.mean(data_train,axis=0))/np.std(data_train,axis=0) #标准化
train_x,train_y=[],[] #训练集x和y
for i in range(len(normalized_train_data)-time_step):
if i % batch_size==0:
batch_index.append(i)
x=normalized_train_data[i:i+time_step,:13]
y=normalized_train_data[i:i+time_step,13,np.newaxis]
train_x.append(x.tolist())
train_y.append(y.tolist())
batch_index.append((len(normalized_train_data)-time_step))
return batch_index,train_x,train_y
# 获取测试集
def get_test_data(time_step,test_begin):
data_test=data[test_begin:]
mean, std = np.mean(data_test, axis=0), np.std(data_test, axis=0)
normalized_test_data=(data_test-np.mean(data_test,axis=0))/np.std(data_test,axis=0) #标准化
size=(len(normalized_test_data)+time_step-1)//time_step
test_x,test_y=[],[] #测试集x和y
for i in range(size-1):
x=normalized_test_data[i*time_step:(i+1)*time_step,:13]
y=normalized_test_data[i*time_step:(i+1)*time_step,13]
test_x.append(x.tolist())
test_y.extend(y)
test_x.append((normalized_test_data[(i+1)*time_step:,:13]).tolist())
test_y.extend((normalized_test_data[(i+1)*time_step:,13]).tolist())
return test_x,test_y, mean, std
按行拆分数据集,并将其标准化。同时,用数据集的每一组特征组合为一个列表,用每一组的标签值组合为另一个列表,训练集和测试集通过循环结构填充自己的两个列表,由此完成数据集的拆分。
训练集和测试集在循环阶段的代码虽然看上去不一样,但是思路是一致的。以训练集为例,训练集有500项数据,编号从0到499。每一组数据跨度为time_step
的值,本案例中后续设置time_step
为20,即每一组数据长度为20。以data[i]
表示数据项,那么训练集就是[data[0:20], data[1:21], ……, data[479:499]]
。同理可得测试集。训练集额外有一个batch_index
,每当每一组数据的起始数据项的编号被预设的batch_size
整除,记下该编号形成一个列表。
接下来是构建LSTM神经网络以及训练和测试验证的函数。
# 构建神经网络
def lstm(X):
batch_size=tf.shape(X)[0]
time_step=tf.shape(X)[1]
w_in=weights['in']
b_in=biases['in']
inputdata=tf.reshape(X,[-1,input_size]) #需要将tensor转成2维进行计算,计算后的结果作为隐藏层的输入
input_rnn=tf.matmul(inputdata,w_in)+b_in
input_rnn=tf.reshape(input_rnn,[-1,time_step,rnn_unit]) #将tensor转成3维,作为lstm cell的输入
cell=tf.nn.rnn_cell.BasicLSTMCell(rnn_unit)
init_state=cell.zero_state(batch_size,dtype=tf.float32)
output_rnn,final_states=tf.nn.dynamic_rnn(cell, input_rnn,initial_state=init_state, dtype=tf.float32) #output_rnn是记录lstm每个输出节点的结果,final_states是最后一个cell的结果
output=tf.reshape(output_rnn,[-1,rnn_unit]) #作为输出层的输入
w_out=weights['out']
b_out=biases['out']
pred=tf.matmul(output,w_out)+b_out
return pred,final_states
# 训练模型
def train_lstm(batch_size,time_step,train_begin,train_end,n):
X = tf.placeholder(tf.float32, shape=[None, time_step, input_size])
Y = tf.placeholder(tf.float32, shape=[None, time_step, output_size])
# 获得训练集的数据及其标签和batch_size
batch_index,train_x,train_y=get_train_data(batch_size,time_step,train_begin,train_end)
pred,_ = lstm(X)
# 损失函数
loss = tf.reduce_mean(tf.square(tf.reshape(pred,[-1])-tf.reshape(Y, [-1])))
# 创建Adam优化器,最小化loss
train_op = tf.train.AdamOptimizer(lr).minimize(loss)
# 创建一个Saver,设置需要保留的最近检查点文件最大数为15,用以参数恢复
saver = tf.train.Saver(tf.global_variables(),max_to_keep=15)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# 重复训练n次
for i in range(n):
for step in range(len(batch_index)-1):
_,loss_=sess.run([train_op,loss],feed_dict={X:train_x[batch_index[step]:batch_index[step+1]],Y:train_y[batch_index[step]:batch_index[step+1]]})
# 进度每到10%输出一次情况,并且保存一次参数
if (i+1) % (n/10)==0:
print(i+1,loss_, "保存模型",)
saver.save(sess, './model_stock/stock.model')
# 预测模型
def prediction(time_step,test_begin):
X=tf.placeholder(tf.float32, shape=[None,time_step,input_size])
test_x,test_y, mean, std=get_test_data(time_step, test_begin)
pred,_=lstm(X)
saver=tf.train.Saver(tf.global_variables())
with tf.Session() as sess:
# 参数恢复
module_file = tf.train.latest_checkpoint('./model_stock')
saver.restore(sess, module_file) #取训练集最后一次保存数据
test_predict=[]
# 从测试集中通过循环的方式取出其特征维度,放入模型进行预测,并且记下预测结果
for step in range(len(test_x)-1):
prob=sess.run(pred,feed_dict={X:[test_x[step]]})
predict=prob.reshape((-1))
test_predict.extend(predict)
# 将标准化的数据还原至真实数据值,并且计算测试集偏差
test_y=np.array(test_y)*std[13]+mean[13]
test_predict=np.array(test_predict)*std[13]+mean[13]
# acc为测试集偏差
acc=np.average(np.abs(test_predict-test_y[:len(test_predict)])/test_y[:len(test_predict)])
print(acc)
# 折线图展示
plt.figure(figsize=(20,6))
data_list=['04-22', '04-23', '04-24', '04-25', '04-26',
'04-29', '04-30', '05-01', '05-02', '05-03',
'05-06', '05-07', '05-08', '05-09', '05-10',
'05-11', '05-12', '05-13', '05-14', '05-15']
plt.plot(data_list, test_y[0:20], color='r')
plt.plot(data_list, test_predict[0:20], color='b')
plt.xlabel('date')
plt.ylabel('stock index')
#plt.plot(list(range(len(test_predict))), test_predict, color='b')
#plt.plot(list(range(len(test_y))), test_y, color='r')
plt.show()
首先定义2个占位符,分别表示输入和输出预期。tf.nn.rnn_cell.BasicLSTMCell
定义单个基本的LSTM单元。在LSTM单元中,有2个状态值,一个是当前时间段的输出(同时也是下一个时间段的部分输入),另一个是当前时间段的部分输入(另一部分为上一个时间段的输出)。当state_is_tuple=True
时,state
是由当前时间段的部分输入和上一个时间段的输出构成的元组。接下来初始化状态、设置loss函数和评估函数、设置优化器、构建神经网络。再设置Saver
用于存储/恢复模型训练的情况。最后训练模型再进行预测即可。
# 设置模型训练预测需要的量
batch_size = 60
time_step = 20
train_begin = 0
train_end = 500
test_begin = 500
n = 10000
# 训练模型
with tf.variable_scope('train', reuse=tf.AUTO_REUSE):
train_lstm(batch_size,time_step,train_begin,train_end, n)
训练了10000轮次,接下来使用模型进行预测。
# 模型预测
with tf.variable_scope('train', reuse=tf.AUTO_REUSE):
prediction(time_step,test_begin)
输出的数值为测试集偏差,图中红线为现实历史数据,蓝线为模型预测数据。
可以看到,LSTM模型预测了一个基于当前数据的趋势。为了突出重点,本案例在特征工程、参数调优方面并没有多做介绍,并且考虑到缩短模型训练时间,因此只用了小体量的数据集,适合初学者对于LSTM模型在时间序列问题的探索。值得注意的是,数据的标准化非常重要。必须保证把数据集规范在同一个空间内,否则效果比较差。
本案例首先介绍了RNN、LSTM神经网络,再用Python实现了LSTM对股票收盘价的预测。股价走势预测属于时间序列预测,数据为2017-03-06至2019-04-19的平安银行(sz000001)的收盘价。主要使用的库为TensorFlow,是Python中常见的用于搭建神经网络的库。而其它使用到的库有:多用于科学计算的NumPy库、常见于数据分析任务的Pandas库、负责绘图展示的Matplotlib库。除本案例提到的库之外,还有sklearn库和keras库等机器学习常见的库,都是应该熟练掌握的库。此外,对于股票收盘价走势预测这一任务,本案例仅使用了平安银行的收盘价这一指标,而其它指标理论上也具有参考价值,可以作为本案例的升级版。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。