Tensorflow LSTM实现多维输入输出预测实践详解

Posted 肖永威

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tensorflow LSTM实现多维输入输出预测实践详解相关的知识,希望对你有一定的参考价值。

摘要:算法模型基于动态多隐层LSTM RNN搭建,损失函数使用cross_entropy损失最大值,输入M维度、输出N维度。代码基于Python3.6.X和Tensorflow1.13.X实现。

1. 前言

对于新零售、金融、供应链、在线教育、银行、证券等行业的产品而言,以数据为驱动的用户行为分析尤为重要。用户行为分析的目的是:推动产品迭代、实现精准营销,提供定制服务,驱动产品决策。

我们以新零售加油站业务场景为例开始,我们在去给车加油时,面临着花钱,还面临让人头疼的堵车和加油排队的等耗费时间的问题。通常,我们大概每周都有可能去一次加油站加油,还可能顺便进便利店买些非油物品,或者还能顺便洗车。

如上图所示的行为事件分析是根据运营关键指标,对用户特定事件加油卡交易数据进行分析。通过按时序追踪或记录用户行为事件,可以快速的了解到事件的趋势走向和加油客户的完成情况。

对于预测股票近期、未来的价格,也是属于时序交易行为分析范畴,我们比较容易获取股票交易数据,也比较明显的对标预测结果。现如今,业界有可参考使用CNN+LSTM算法做行为分析,股票价格预测的案例。由于新零售等业务缺少数据,我们先用股票数据研究算法模型。

2. 时序预测分析,深度学习LSTM算法概述

在深度学习中有适合处理序列数据的神经网络,那就是循环神经网络 RNN,在NLP上应用较多,效果也比较好,而在金融量化分析上也有所应用,特别是在行为分析、股票价格预测上也有较多的应用。我们这里不深入研究算法模型,仅从应用角度介绍关键内容。

2.1. 循环神经网络 RNN概述

循环神经网络(Rerrent Neural Network, RNN )出现于20世纪80年代,是指一个随着时间序列的推移,重复发生循环的简单神经网络结构,它由输入层、隐藏层、输出层组成。

时间序列预测分析就是利用过去一段时间内某事件时间的特征来预测未来一段时间内该事件的特征。这是一类相对比较复杂的预测建模问题,和回归分析模型的预测不同,时间序列模型是依赖于事件发生的先后顺序的,同样大小的值改变顺序后输入模型产生的结果是不同的。

RNN 是在有顺序的数据上进行学习的,为了记住这些数据,RNN 会像人一样产生对先前发生事件的记忆。

2.2. 长短期记忆循环神经网络LSTM概述

普通的 RNN 就像一个老爷爷,有时候比较健忘,为什么会这样呢?普通的RNN,对于输入使用手机软件的信息,要经过很长的路径才能抵达最后一个时间点。然后,我们得到误差,而且在反向传递得到的误差的时候,他在每一步都会乘以一个自己的参数W。如果这个W 是一个小于1 的数,经不断乘以误差,误差传到初始时间点也会是一个接近于零的数,误差相当于就消失了。我们把这个问题叫做梯度消失或者梯度弥散。反之如果W是一个大于1 的数,则到最后变成了无穷大的数,这种情况我们叫做剃度爆炸。这就是普通 RNN 没有办法回忆起久远记忆的原因。

LSTM 就是为了解决这个问题而诞生的,LSTM 和普通 RNN 相比,多出了三个控制器:输入门、输出门、遗忘门。如下图所示为LSTM内部的结果概况。

f t = σ ( W f ⋅ [ h t − 1 , x t ] + b f ) f_t=σ(W_f \\cdot [h_t-1,x_t ]+b_f ) ft=σ(Wf[ht1,xt]+bf)
i t = σ ( W i ⋅ [ h t − 1 , x t ] + b i ) i_t=σ(W_i \\cdot [h_t-1,x_t ]+b_i ) it=σ(Wi[ht1,xt]+bi)
o t = σ ( W o ⋅ [ h t − 1 , x t ] + b o ) o_t=σ(W_o \\cdot [h_t-1,x_t ]+b_o ) ot=σ(Wo[ht1,xt]+bo)
C ~ t = t a n h ( W c ⋅ [ h t − 1 , x t ] + b c ) \\tilde C_t = tanh(W_c \\cdot [h_t-1,x_t ]+b_c) C~t=tanh(Wc[ht1,xt]+bc)
C t = f t × C t − 1 + i t × C ~ t C_t=f_t \\times C_t-1+i_t \\times \\tilde C_t Ct=ft×Ct1+it×C~t
h t = o t × t a n h ( C t ) h_t = o_t \\times tanh(C_t) ht=ot×tanh(Ct)

其中, f t f_t ft是遗忘门, i t i_t it是输入门, o t o_t ot是输出门, C t C_t Ct是神经元状态, h t h_t ht是隐藏层状态值,W和b分别是权重和偏置。

LSTM多出了一个控制全局的记忆,我们在图中表示为主线,相当于剧本中的主线剧情。而普通的 RNN体系就是分线剧情。我们先看输入的分线剧情对于剧终结果十分重要,输入控制就会将这个分线剧情按重要程度写入主线剧情进行分析,对于遗忘方面,如果此时的分线剧情更改了我们对之前剧情的想法,那么遗忘控制就会将之前的某些主线剧情忘记,按比例替换成现在的新剧情。LSTM 就像延缓记忆衰退的良药,可以带来更好的结果。

对于LSTM算法模型,输出states是个tuple,分别代表 C t C_t Ct h t h_t ht,其中 h t h_t ht与outputs中对应的最后一个时刻(即最后一个cell)的输出相等。

3. LSTM算法模型预测股票价格实践

3.1. 基于Tensorflow1.13.X构建LSTM回归模型

我们以类(class MultiLSTM)的方式定义LSTM RNN的主体结构,此RNN由3个组成部分 ( input_layer, cell, output_layer):

(1)定义输入层:

        with tf.name_scope('inputs'):
            self.xs = tf.placeholder(tf.float32, [None, n_steps, input_size], name='xs')
            self.ys = tf.placeholder(tf.float32, [None, n_steps, output_size], name='ys')
            self.batch_size = tf.placeholder(tf.int32, [], name='batch_size')
            #节点不被dropout的概率
            self.keep_prob = tf.placeholder(tf.float32, name='keep_prob')

其中,输入层另外包括batch_size(训练数据批次大小)和keep_prob(避免过拟合,不被删掉节点的概率)。

(2)定义隐藏层:

构建多隐层神经网络。

    # 定义多层LSTM    
    def add_multi_cell(self):
        cell_list = tf.contrib.rnn.BasicLSTMCell(self.cell_size, forget_bias=1.0, state_is_tuple=True) 

        with tf.name_scope('dropout'):
            if self.is_training:
                # 添加dropout.为了防止过拟合,在它的隐层添加了 dropout 正则
                cell_list = tf.contrib.rnn.DropoutWrapper(cell_list, output_keep_prob=self.keep_prob)
                tf.summary.scalar('dropout_keep_probability', self.keep_prob)
        
        lstm_cell = [cell_list for _ in range(self.num_layers)]
        lstm_cell = tf.contrib.rnn.MultiRNNCell(lstm_cell, state_is_tuple=True) #遗漏过?, state_is_tuple=True

        with tf.name_scope('initial_state'):
            self.cell_init_state = lstm_cell.zero_state(self.batch_size, dtype=tf.float32)
        self.cell_outputs, self.cell_final_state = tf.nn.dynamic_rnn(
            lstm_cell, self.l_in_y, initial_state=self.cell_init_state, time_major=False)

在RNN中进行dropout时,对于RNN的部分不进行dropout,仅在同一个t时刻中,多层cell之间传递信息的时候进行dropout。

tf.nn.dynamic_rnn(cell, inputs)中的 time_major 参数会针对不同 inputs 格式有不同的值:

  • 如果 inputs 为 (batches, steps, inputs)则对应为 time_major=False; 如果 inputs
  • 为 (steps, batches, inputs) 则对应为time_major=True;

(3)定义输出层:

输出层实现不带激活函数的全连接,也就是线性的 y i = w i x i + b i y_i=w_ix_i+b_i yi=wixi+bi

    # 定义输出全连接层
    def add_output_layer(self):    
        l_out_x = tf.reshape(self.cell_outputs, [-1, self.cell_size], name='2_2D')        
        Ws_out = self._weight_variable([self.cell_size, self.output_size])
        bs_out = self._bias_variable([self.output_size, ])
        # shape = (batch * steps, output_size)
        with tf.name_scope('Wx_plus_b'):
            self.pred = tf.matmul(l_out_x, Ws_out) + bs_out

(4)定义损失函数:

使用tf.contrib.legacy_seq2seq.sequence_loss_by_example定义损失函数,由于是多输出模型,取均值将影响结果,我采用取最大值方案。

    def compute_cost(self):
        losses = tf.contrib.legacy_seq2seq.sequence_loss_by_example(
            [tf.reshape(self.pred, [-1], name='reshape_pred')], 
            [tf.reshape(self.ys, [-1], name='reshape_target')],       
            [tf.ones([self.batch_size * self.n_steps*self.output_size], dtype=tf.float32)], 
            average_across_timesteps=True,
            softmax_loss_function=self.ms_error,
            name='losses'
        )
      
        with tf.name_scope('average_cost'):
            # 取最大损失值
            self.cost = tf.reduce_max(losses, name='average_cost')
            '''
            self.cost = tf.div(
                tf.reduce_sum(losses, name='losses_sum'),
                self.batch_size_,
                name='average_cost')
            '''
            tf.summary.scalar('cost', self.cost)
        print('self.cost shape is '.format(self.cost.shape))

(5)搭建LSTM网络常见问题及注意事项:

  1. 多对多是 RNN 中最经典的结构,其输入、输出都是等长的序列数据;
  2. 在Tensorflow1.x的中,batch_size应设置为不定值,方便测试和模型应用时,不必再构造batch_size;
  3. 在做losse时,例如均方差过程中,注意数据个数为batch_sizen_stepsoutput_size;
  4. 由于数据集大小所限,当timestep过大,而训练集长度较小时,训练出来的模型进行预测会出现结果波动不大,呈一条直线的情况,建议缩短timestep。

至此,多层LSTM模型构建完成,整合代码如下

class MultiLSTM(object):
    def __init__(self, n_steps, input_size, output_size, cell_size, batch_size,num_layers,is_training):
        self.n_steps = n_steps
        self以上是关于Tensorflow LSTM实现多维输入输出预测实践详解的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow LSTM实现多维输入输出预测实践详解

Keras LSTM实现多维输入输出时序预测实践详解

TensorFlow搭建LSTM实现时间序列预测(负荷预测)

TensorFlow搭建双向LSTM实现时间序列预测(负荷预测)

Keras 中的 LSTM 序列预测只输出输入的最后一步

TensorFlow LSTM 预测相同的值