使用tensorflow:LSTM神经网络预测股票(二)

来源:互联网 发布:美工运营对骂gif 编辑:程序博客网 时间:2024/05/16 17:41

与第一版相比的改进点

上一篇博客 直接预测收益,训练过程中通过观察预测值的变动,发现LSTM网络收敛起来非常困难,几百万行数据下来,也没有比较明显的收敛迹象。我分析原因有可能为:一行60个交易日,下一行的60个交易日有59个实际上并没有变,即大部分输入数据并未变动,但是收益变动可能会非常巨大(股票的价格变动),如图所示:
数据生成示意图
收益相对剧烈的变动,给网络的收敛带来了巨大的困难。碍于硬件资源的限制,我没有再尝试更加复杂的网络深度,转而尝试降低收益的波动,具体方法为:
1. 将收益分成3类:收益小于0%,收益小于5%,收益大于等于5%,对应[1, 0, 0]、[0, 1, 0]、[0, 0, 1] 3种输出。
2. 给LSTM网络输出层套上softmax激励函数,将预测收益改为分类问题。

这样做在功能性上牺牲了些,换来了收益波动的减小,网络收敛难度降低。

补充:大家可以通过更改分类和输出层参数,使其降低到二元分类问题,进一步降低收益的波动,例如:小于8%和大于等于8%、处于3%-5%和不处于等。

股票预测相关代码

数据部分

与上一篇 相比,数据部分在输出时将股价替换成3种类型输出,输出的数据矩阵从1列改为了3列。

tensorflow 代码

数据部分

class get_stock_data():    """    获得股票分类数据,用以深度学习。格式为按照时间顺序,由小到大排序的:    [开盘价, 最高价, 最低价, 收盘价,换手率,成交量, 流通市值, 真实收益, 日期]    真实收益定义:下一个交易日开盘买入,再下一个交易日开盘卖出所得到的收益,算法为 100 * (open_next_next / open_next - 1)。    > 如果真实收益的回归预测效果一般,可以再试试分类预测:真实收益>= n 和 <n 等    """    def __init__(self,):        self.file_paths = {}    def get_train_test_data_softmax(self, batch_size, file_path, time_step = 60):        """        将收益变成分类问题。分类定义在classify函数内,分别为:            0: <= 0%            1: (0%, 5%)            2: [5%, 以上)        """        self_variable_name = "data_cur_{0}".format(file_path)        if self_variable_name in self.file_paths:            cursor = self.file_paths[self_variable_name]        else:            self.file_paths[self_variable_name] = cursor = pd.read_csv(file_path, iterator = True)        data_temp = cursor.get_chunk(batch_size)        data_temp['0'] = data_temp['0'].apply(eval)        data_temp['1'] = data_temp['1'].apply(eval)        data_temp["1"] = data_temp["1"].apply(lambda x: x[0])        def classify(y):            if y <= 0:                return [1,0,0]            elif y >= 5:                return [0,0,1]            else:                return [0,1,0]        data_temp["1"] = data_temp["1"].apply(lambda x: classify(x))        return np.array(data_temp["0"].tolist()).reshape(batch_size , time_step * 7), np.array(data_temp["1"].tolist()).reshape(batch_size , 3)

tf代码

    def stock_lstm_softmax(self,):    """    使用LSTM处理股票数据    分类预测    """    #获取当前python文件运行在哪个目录下并去掉最后的文件名,如:F:/deeplearning/main.py --> F:/deeplearning    #在linux下同样起作用    basic_path = os.path.dirname(os.path.abspath(__file__))    #定义存储模型的文件路径,当前为运行的python文件路径下,文件名为stock_rnn.ckpt    model_save_path = r"F:\\123\\save\\stock_rnn_save.ckpt"#os.path.join(basic_path, "stock_rnn.ckpt")    #定义训练集的文件路径,当前为运行的python文件路径下,文件名为train_data.csv    train_csv_path = os.path.join(basic_path, "train_data.csv")    #定义测试集的文件路径,当前为运行的python文件路径下,文件名为test_data.csv    test_csv_path = os.path.join(basic_path, "test_data.csv")    #学习率    learning_rate = 0.001    #喂数据给LSTM的原始数据有几行,即:一次希望LSTM能“看到”多少个交易日的数据    origin_data_row = 60    #喂给LSTM的原始数据有几列,即:日线数据有几个元素    origin_data_col = 7    #LSTM网络有几层    layer_num = 5    #LSTM网络,每层有几个神经元    cell_num = 200    #最后输出的数据维度,即:要预测几个数据,该处需要处理分类问题,按照自己设定的类型数量设定    output_num = 3    #每次给LSTM网络喂多少行经过处理的股票数据。该参数依据自己显卡和网络大小动态调整,越大 一次处理的就越多,越能占用更多的计算资源    batch_size = tf.placeholder(tf.int32, [])    #输入层、输出层权重、偏置。    #通过这两对参数,LSTM层能够匹配输入和输出的数据    W = {     'in':tf.Variable(tf.truncated_normal([origin_data_col, cell_num], stddev = 1), dtype = tf.float32),     'out':tf.Variable(tf.truncated_normal([cell_num, output_num], stddev = 1), dtype = tf.float32)     }    bias = {    'in':tf.Variable(tf.constant(0.1, shape=[cell_num,]), dtype = tf.float32),    'out':tf.Variable(tf.constant(0.1, shape=[output_num,]), dtype = tf.float32)    }    #告诉LSTM网络,即将要喂的数据是几行几列    #None的意思就是喂数据时,行数不确定交给tf自动匹配    #我们喂得数据行数其实就是batch_size,但是因为None这个位置tf只接受数字变量,而batch_size是placeholder定义的Tensor变量,表示我们在喂数据的时候才会告诉tf具体的值是多少    input_x = tf.placeholder(tf.float32, [None, origin_data_col * origin_data_row])    input_y = tf.placeholder(tf.float32, [None, output_num])    #处理过拟合问题。该值在其起作用的层上,给该层每一个神经元添加一个“开关”,“开关”打开的概率是keep_prob定义的值,一旦开关被关了,这个神经元的输出将被“阻断”。这样做可以平衡各个神经元起作用的重要性,杜绝某一个神经元“一家独大”,各种大佬都证明这种方法可以有效减弱过拟合的风险。    keep_prob = tf.placeholder(tf.float32, [])    #通过reshape将输入的input_x转化成2维,-1表示函数自己判断该是多少行,列必须是origin_data_col    #转化成2维 是因为即将要做矩阵乘法,矩阵一般都是2维的(反正我没见过3维的)    input_x_after_reshape_2 = tf.reshape(input_x, [-1, origin_data_col])    #当前计算的这一行,就是输入层。输入层的激活函数是relu,并且施加一个“开关”,其打开的概率为keep_prob    #input_rnn即是输入层的输出,也是下一层--LSTM层的输入    input_rnn = tf.nn.dropout(tf.nn.relu_layer(input_x_after_reshape_2, W['in'], bias['in']), keep_prob)    #通过reshape将输入的input_rnn转化成3维    #转化成3维,是因为即将要进入LSTM层,接收3个维度的数据。粗糙点说,即LSTM接受:batch_size个,origin_data_row行cell_num列的矩阵,这里写-1的原因与input_x写None一致    input_rnn = tf.reshape(input_rnn, [-1, origin_data_row, cell_num])    #定义一个带着“开关”的LSTM单层,一般管它叫细胞    def lstm_cell():        cell = rnn.LSTMCell(cell_num, reuse=tf.get_variable_scope().reuse)        return rnn.DropoutWrapper(cell, output_keep_prob=keep_prob)    #这一行就是tensorflow定义多层LSTM网络的代码    lstm_layers = tf.contrib.rnn.MultiRNNCell([lstm_cell() for _ in range(layer_num)], state_is_tuple = True)    #初始化LSTM网络    init_state = lstm_layers.zero_state(batch_size, dtype = tf.float32)    #使用dynamic_rnn函数,告知tf构建多层LSTM网络,并定义该层的输出    outputs, state = tf.nn.dynamic_rnn(lstm_layers, inputs = input_rnn, initial_state = init_state, time_major = False)    h_state = state[-1][1]    #该行代码表示了输出层    #将LSTM层的输出,输入到输出层(输出层带softmax激活函数),输出为各个分类的概率    #假设有3个分类,那么输出举例为:[0.001, 0.992, 0.007],表示第1种分类概率千分之1,第二种99.2%, 第三种千分之7    y_pre = tf.nn.softmax(tf.matmul(h_state, W['out']) + bias['out'])    #损失函数,用作指导tf    #loss定义为交叉熵损失函数,softmax输出层大多都使用的这个损失函数。关于该损失函数详情可以百度下    loss = -tf.reduce_mean(input_y * tf.log(y_pre))    #告诉tf,它需要做的事情就是就是尽可能将loss减小    #learning_rate是减小的这个过程中的参数。如果将我们的目标比喻为“从北向南走路走到菜市场”,我理解的是    #learning_rate越大,我们走的每一步就迈的越大。初看似乎步子越大越好,但是其实并不能保证每一步都是向南走    #的,有可能因为训练数据的原因,导致我们朝西走了一大步。或者我们马上就要到菜市场了,但是一大步走过去,给    #走过了。。。综上,这个learning_rate(学习率参数)的取值,无法给出一个比较普适的,还是需要根据实际情况去    #尝试和调整。0.001的取值是tf给的默认值    #上述例子是个人理解用尽可能通俗易懂地语言表达。如有错误,欢迎指正    train_op = tf.train.AdamOptimizer(learning_rate).minimize(loss)    #这块定义了一个新的值,用作展示训练的效果    #它的定义为:预测对的 / 总预测数,例如:0.55表示预测正确了55%    correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(input_y, 1))    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))    #用以保存参数的函数(跑完下次再跑,就可以直接读取上次跑的结果而不必从头开始)    saver = tf.train.Saver(tf.global_variables())    #tf要求必须如此定义一个init变量,用以在初始化运行(也就是没有保存模型)时加载各个变量    init = tf.global_variables_initializer()    #获取数据(这是我们自己定义的类)    data_get = get_stock_data()    #设置tf按需使用GPU资源,而不是有多少就占用多少    config = tf.ConfigProto()    config.gpu_options.allow_growth = True    #使用with,保证执行完后正常关闭tf    with tf.Session(config = config) as sess:        try:            #定义了存储模型的文件路径,即:当前运行的python文件路径下,文件名为stock_rnn.ckpt            saver.restore(sess, model_save_path)            print ("成功加载模型参数")        except:            #如果是第一次运行,通过init告知tf加载并初始化变量            print ("未加载模型参数,文件被删除或者第一次运行")            sess.run(init)        #给batch_size赋值        _batch_size = 200        for i in range(20000):            try:                #读取训练集数据                train_x, train_y = data_get.get_train_test_data_softmax(batch_size = _batch_size, file_path = train_csv_path)            except StopIteration:                print ("训练集均已训练完毕")                train_accuracy = sess.run(accuracy, feed_dict={                    input_x: train_x, input_y: train_y, keep_prob: 1.0, batch_size: _batch_size})                print ("step: {0}, training_accuracy: {1}".format(i + 1, train_accuracy))                saver.save(sess, model_save_path)                print("保存模型\n")                break            if (i + 1) % 20 == 0:                train_accuracy = sess.run(accuracy, feed_dict={                    input_x:train_x, input_y: train_y, keep_prob: 1.0, batch_size: _batch_size})                print ("step: {0}, training_accuracy: {1}".format(i + 1, train_accuracy))                saver.save(sess, model_save_path)                print("保存模型\n")            #按照给定的参数训练一次LSTM神经网络            sess.run(train_op, feed_dict={input_x: train_x, input_y: train_y, keep_prob: 0.6, batch_size: _batch_size})        #计算测试数据的准确率        #读取测试集数据        test_size = 100        test_x, test_y = data_get.get_train_test_data_softmax(batch_size = _batch_size, file_path = test_csv_path)        print ("test accuracy {0}".format(sess.run(accuracy, feed_dict={            input_x: test_x, input_y: test_y, keep_prob: 1.0, batch_size:_batch_size})))

交流及完整数据

欢迎加入qq群: 643033887 一起探索量化分析股票的方法。

示例数据(训练集10W,测试集1W)可在这里中获得。

更加详尽的训练集数据(2006年至2017年10月,文件名:stock_data_for_dl_1.01.rar 解压缩后约42个G)可以在qq群里下载获得。

阅读全文
1 0
原创粉丝点击