在上一篇博文中,我们介绍了长短时记忆网络的基本概念,在这一节中,我们将以长短时记忆网络(LSTM)为例,讲解深度学习算法在股票价预测中的应用。
我们要分析的数据如下所示:
各列依次为:股票代码、日期、开盘价、收盘价、最低、最高、交易量、金额、涨跌幅、第二天的最高价。
我们首先读入训练样本集,代码如下所示:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY, date2num
from mpl_finance import candlestick_ohlc
from pylab import mpl
from datetime import datetime
class SdpsDs(object):
ds_file = 'dataset_2.csv'
def __init__(self):
self.cls_name = 'SdpsDs'
@staticmethod
def initialize_data():
'''
从原始数据文件中读出股票数据
'''
with open(SdpsDs.ds_file) as fd:
df = pd.read_csv(fd) #读入股票数据
data = df.iloc[:,2:10].values #取第3-10列
return data
@staticmethod
def get_train_ds(batch_size=60,time_step=20,train_begin=0,train_end=5800):
'''
获取训练样本集
'''
data = SdpsDs.initialize_data()
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=[],[] #训练集
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,:7]
y=normalized_train_data[i:i+time_step,7,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
第30行:从CSV文件中读出第3至11列
第31行:定义迷你批次索引号数组,batch_index[2]=1008表示第3个迷你批次由第1008条记录开始;
第32行:定义0~5800条为训练样本集数据;
第33行:对数据进行标准化,公式为:
x
ˉ
=
x
−
μ
σ
\bar{x}=\frac{x-\mu}{\sigma}
xˉ=σx−μ
即标准化后的值为原始值减去均值,再除以标准差;
第34行:用train_x保存训练数据集训练样本,用train_y保存训练数据集标签,即正确结果;
第35行:将训练样本集的记录,每一行(代表一天的数据),time_step条记录为一个迷你批次;
第36、37行:当读入time_step行数据形成一个迷你批次后,迷你批次序号加1,并将当前记录号记录到batch_index数组中;
第38行:第1~7列为训练样本;
第39行:第8列为第二天的最高价,也是我们要预测的正确结果;
第40行:形成训练数据集样本集;
第41行:形成训练数据集标签集(正确结果集);
第42行:在迷你批次索引号数组中添加最后一个迷你批次起始索引号。
测试样本集的处理与训练样本集的方法类似,如下所示:
@staticmethod
def get_test_ds(time_step=20,test_begin=5800):
'''
获取训练样本集
'''
data = SdpsDs.initialize_data()
data_test=data[test_begin:]
mean=np.mean(data_test,axis=0)
std=np.std(data_test,axis=0)
normalized_test_data=(data_test-mean)/std #标准化
size=(len(normalized_test_data)+time_step-1)//time_step #有size个sample
test_x,test_y=[],[]
for i in range(size-1):
x=normalized_test_data[i*time_step:(i+1)*time_step,:7]
y=normalized_test_data[i*time_step:(i+1)*time_step,7]
test_x.append(x.tolist())
test_y.extend(y)
test_x.append((normalized_test_data[(i+1)*time_step:,:7]).tolist())
test_y.extend((normalized_test_data[(i+1)*time_step:,7]).tolist())
return mean,std,test_x,test_y
接下来我们需要设计长短时记忆网络(LSTM),我们首先定义一些需要用到的变量,如下所示:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY, date2num
from mpl_finance import candlestick_ohlc
from pylab import mpl
from datetime import datetime
import tensorflow as tf
from sdps_ds import SdpsDs
class Lstm(object):
rnn_unit = 10 # 隐层神经元的个数
lstm_layers = 2 # 隐层层数
input_size = 7 # 输入层神经元个数
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,]))
}
keep_prob = tf.placeholder(tf.float32, name='keep_prob')
接下来我们看怎样定义在长短时记忆网络(LSTM)中使用的基本组件Cell,如下所示:
@staticmethod
def create_lstm(X):
print('初始化LSTM网络')
batch_size = tf.shape(X)[0]
time_step = tf.shape(X)[1]
w_in = Lstm.weights['in']
b_in = Lstm.biases['in']
input = tf.reshape(X, [-1, Lstm.input_size]) #需要将tensor转成2维进行计算,计算后的结果作为隐藏层的输入
input_rnn = tf.matmul(input, w_in) + b_in
input_rnn = tf.reshape(input_rnn, [-1, time_step, Lstm.rnn_unit]) #将tensor转成3维,作为lstm cell的输入
cell = tf.nn.rnn_cell.MultiRNNCell([Lstm.create_lstm_cell() for i in range(Lstm.lstm_layers)])
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 = tf.reshape(output_rnn, [-1, Lstm.rnn_unit])
w_out = Lstm.weights['out']
b_out = Lstm.biases['out']
pred = tf.matmul(output, w_out) + b_out
return pred, final_states
@staticmethod
def create_lstm_cell():
#basicLstm单元
basicLstm = tf.nn.rnn_cell.BasicLSTMCell(Lstm.rnn_unit)
# dropout
drop = tf.nn.rnn_cell.DropoutWrapper(basicLstm, output_keep_prob=Lstm.keep_prob)
return basicLstm
第2行:create_lstm函数中的参数X为Design Matrix,其每一行为一个样本,列代表样本的特征;
第4行:在这里一次性读入整个训练样本集,第0维代表迷你批次数量;
第5行:在每个迷你批次中,有time_step条记录,每一条记录代表长短时记忆网络(LSTM)中的一个时刻;
第6行:初始化输入层到隐藏层的连接权值矩阵w_in;
第7行:初始化隐藏层神经元偏置值b_in
第8行:将原始输入变为最后一维为7(输入变量特征数)的二维数组,例如我们假设迷你大小缺省为60,每个训练样本包括20行记录,则原始输入信号为
R
60
×
20
×
7
R^{60 \times 20 \times 7}
R60×20×7,经过本步之后,input为
R
1200
×
7
R^{1200 \times 7}
R1200×7;
第9行:求出隐藏层的输出input_rnn,input为
R
1200
×
7
R^{1200 \times 7}
R1200×7,w_in为
R
7
×
10
R^{7 \times 10}
R7×10,b_in为
R
10
R^{10}
R10,最后input_rnn的维度为
R
1200
×
10
R^{1200 \times 10}
R1200×10;
第10行:将input_rnn变为
R
60
×
20
×
10
R^{60 \times 20 \times 10}
R60×20×10,其中60为缺省的迷你批次大小,20为一个训练样本中时刻数,10为隐藏层神经元个数;
第11行:生成两层由LSTM Cell组成的隐藏层,输入信号维度为10;
第12行:生成在
t
=
0
t=0
t=0时刻隐藏层的初始状态,init_state为一个元组,由两个子元组构成,分别对应第11行生成的LSTM Cell,每个LSTM Cell的状态为:
c
0
∈
R
60
×
10
\boldsymbol{c}_0 \in R^{60 \times 10}
c0∈R60×10,
h
0
∈
R
60
×
10
\boldsymbol{h}_0 \in R^{60 \times 10}
h0∈R60×10;
第13行:定义隐藏层的输出为网络输出
o
u
p
u
t
r
n
n
∈
R
60
×
20
×
10
ouput_rnn \in R^{60 \times 20 \times 10}
ouputrnn∈R60×20×10;
第14行:将输出改为
o
u
t
p
u
t
∈
R
1200
×
10
output \in R^{1200 \times 10}
output∈R1200×10;
第15行:定义隐藏层到输出层的连接权值矩阵w_out;
第16行:定义输出层偏置值b_out;
第17行:求出输出层的输出;
下面我们来看长短时网络(LSTM)训练代码,如下所示:
@staticmethod
def train(batch_size=60, time_step=20, train_begin=2000, train_end=5800):
X = tf.placeholder(tf.float32, shape=[None, time_step, Lstm.input_size])
Y = tf.placeholder(tf.float32, shape=[None, time_step, Lstm.output_size])
batch_index,train_x,train_y = SdpsDs.get_train_ds(batch_size, time_step, train_begin, train_end)
with tf.variable_scope("sec_lstm"):
pred,_=Lstm.create_lstm(X)
loss = tf.reduce_mean(tf.square(tf.reshape(pred, [-1])-tf.reshape(Y, [-1])))
train_op = tf.train.AdamOptimizer(Lstm.lr).minimize(loss)
saver = tf.train.Saver(tf.global_variables(), max_to_keep=15)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(10):
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]], Lstm.keep_prob:0.5})
print("Number of iterations:",i," loss:",loss_)
print("保存模型参数: ",saver.save(sess,'./model_save2/modle.ckpt'))
print("模型训练结束")
第3行:定义训练样本集输入信号的设计矩阵
X
∈
R
60
×
20
×
7
X \in R^{60 \times 20 \times 7}
X∈R60×20×7;
第4行:定义训练样本集输出正确结果
Y
∈
R
60
×
20
×
1
Y \in R^{60 \times 20 \times 1}
Y∈R60×20×1;
第5行:求出批次开始位置的索引号数组,训练样本集输入信号和训练样本集输出信号;
第6、7行:定义长短时记忆网络(LSTM);
第8行:设置代价函数为最小平方误差函数;
第9行:采用Adam优化算法;
第10行:为保存模型参数进行设置;
第12行:启动Tensorflow会话;
第13行:初始化变量;
第14~17行:对每个迷你批次进行训练,一共训练10次,通常情况下,训练遍数越多效果越好;
第18行:保存模型参数;
当模型训练好之后,我们需要在测试样本集上进行测试,看看我们的模型性能如何,代码如下所示:
def predict(time_step=20):
X = tf.placeholder(tf.float32, shape=[None, time_step, Lstm.input_size])
mean,std,test_x,test_y = SdpsDs.get_test_ds(time_step)
with tf.variable_scope("sec_lstm", reuse=tf.AUTO_REUSE):
pred, _ = Lstm.create_lstm(X)
saver = tf.train.Saver(tf.global_variables())
with tf.Session() as sess:
#参数恢复
module_file = tf.train.latest_checkpoint('model_save2')
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]], Lstm.keep_prob:1})
predict = prob.reshape((-1))
test_predict.extend(predict)
test_y = np.array(test_y)*std[7]+mean[7]
test_predict = np.array(test_predict)*std[7]+mean[7]
acc = np.average(np.abs(test_predict-test_y[:len(test_predict)])/test_y[:len(test_predict)]) #偏差程度
print("测试样本集精度:",acc)
#以折线图表示结果
plt.figure()
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行:生成测试样本集输入信号
X
∈
R
个
数
×
20
×
7
X \in R^{个数 \times 20 \times 7}
X∈R个数×20×7,因为我们将所有测试样本作为一个迷你批次,我们针对每个时间点预测下一个时间点的最高价;
第3行:获取测试样本集均值、方差、测试样本集输入信号和测试样本集正确结果;
第4、5行:创建长短时记忆网络(LSTM)模型;
第6行:为恢复模型参数做准备;
第7行:启动TensorFlow会话;
第9行:找到最新的模型参数文件;
第10行:从最新模型文件中恢复网络参数;
第11行:定义存储模型预测结果的列表变量;
第12~15行:第测试样本集上每条记录进行循环,求出网络输出值,并将其添加到测试样本集预测结果列表中;
第16行:恢复测试样本集正确结果的真实值,因为我们在准备数据时做过预处理:
x
~
=
x
−
μ
σ
\tilde{x}=\frac{x - \mu}{\sigma}
x~=σx−μ;
第17行:恢复测试本样集预测值的真实值,因为我们在准备数据时做过预处理:
x
~
=
x
−
μ
σ
\tilde{x}=\frac{x - \mu}{\sigma}
x~=σx−μ;
第18、19行:计算并打印在测试样本集上的参数;
第20~24行:计算预测值和真实值曲线,预测值用蓝色,真实值用红色。
运行上面的程序,并产生如下所示的结果:
如图所示,我们所训练的模型,除了在开始位置有问题之外,大部分时间与正确结果(红色曲线)相关较小,而我们这个模型只训练了几分钟而已,如果加大训练时间,可以取得更好的结果。
截止目前为止,我们已经有了一个基本可用的模型,可以用于实际应用了。由于股市对一般人来讲,没有自动交易接口,只能用历史数据,比较枯燥乏味,不容易引起兴趣,我们将从下一节开始,以火币网比特币交易为例,向大家详细讲解类似长短时记忆网络(LSTM)怎样应用到实际金融业务中去。