时间序列预测算法——DeepAR

DeepAR概述

DeepAR是亚马逊提出的一种针对大量相关时间序列统一建模的预测算法,该算法采用了深度学习的技术,通过在大量时间序列上训练自回归递归网络模型,可以从相关的时间序列中有效地学习全局模型,并且能够学习复杂的模式,例如季节性、数据随时间的不确定性增长,从而对各条时间序列进行预测。

DeepAR原理和实现过程

时间序列预测算法——DeepAR_第1张图片
上图左边是模型的训练过程,右边是模型的预测过程。

原理

deepar目标是在给定历史history_len时间长度的值及特征的情况下,预测未来future_len时间长度的值。

用zi,t表示第i条序列t时刻的值,xi,t表示第i条t时刻的特征,用表达式来表示就是: P ( z i , t 0 : T ∣ z i , 1 : t 0 − 1 , X i , 1 : T ) P(z_{i,t_0:T}|z_{i,1:t_0-1},X_{i,1:T}) P(zi,t0:Tzi,1:t01,Xi,1:T),其中 [ z i , t 0 , z i , t 0 + 1 , . . . , z i , T ] : = z i , t 0 : T [z_{i,t_0},z_{i,t_0{+1}},...,z_{i,T}] := z_{i,t_0:T} [zi,t0,zi,t0+1,...,zi,T]:=zi,t0:T [ z i , 1 , . . . z i , t 0 − 2 , z i , t 0 − 1 ] : = z i , 1 : t 0 − 1 [z_{i,1},...z_{i,t_0{-2}},z_{i,t_0{-1}}] := z_{i,1:t_0-1} [zi,1,...zi,t02,zi,t01]:=zi,1:t01 [ 1 , t 0 − 1 ] [1,t_0-1] [1,t01]代表历史区间, [ t 0 , T ] [t_0,T] [t0,T]代表预测区间。

基于以上,论文设定模型分布由似然因子的乘积组成:在这里插入图片描述
h i , t h_{i,t} hi,t由神经网络获得,即: h i , t = h ( h i , t − 1 , z i , t − 1 , X i , t , Θ ) h_{i,t}=h(h_{i,t-1},z_{i,t-1},X_{i,t},Θ) hi,t=h(hi,t1,zi,t1,Xi,t,Θ);似然 l ( z i , t ∣ θ ( h i , t , Θ ) ) l(z_{i,t}|θ(h_{i,t},Θ)) l(zi,tθ(hi,t,Θ))是一个fixed分布,其中的参数由神经网络(lstm)的输出 h i , t h_{i,t} hi,t经过函数 θ ( h i , t , Θ ) θ(h_{i,t},Θ) θ(hi,t,Θ)的仿射获得。

论文中提及了关于似然函数的选择, Gaussian likelihood比较适合数值型数据;计数型的数据比较适合negative-binomial likelihood,另外还有Bernoulli likelihood,student_t等,亚马逊在SageMaker中实现的deepar默认选择的似然函数就是student_t。

最后关于模型loss的选择,论文选用对数似然进行梯度优化训练参数。

实现过程

训练时,模型每一时刻的输入包括前一时刻的真实值和当前时刻的特征x,x可以是当前时刻的时间特征;然后经过神经网络的学习,对网络返回的状态hit进行函数仿射,返回当前时刻的预测概率分布。

预测时,每个时刻的输入也包括两部分:前一时刻的预测值和当前时刻的协变量x,即是一种递归式的预测方法;然后利用学习好的网络,进行预测。

基于此,可以通过encode-decode的模式来实现DeepAR,其encode和decode的网络部分可以使用lstm。假设时间序列的时间粒度是分钟级别,可以利用RNN对l历史encode_len分钟长度的时间序列进行encode,返回的隐藏状态输入到decode,预测接下来decode_len分钟长度的时间序列值。

下面是给出算法核心部分的代码实现

训练

首先定义一个lstm,encode和decode都是基于lstm。

def lstm_cell(layer_num,hidden_size,keep_prob):
	stacked_rnn = []
    for i in range(layer_num):
        lstm = tf.contrib.cudnn_rnn.CudnnCompatibleLSTMCell(hidden_size)
        drop = tf.nn.rnn_cell.DropoutWrapper(lstm, output_keep_prob=keep_prob)
        stacked_rnn.append(drop)
    lstm_multi = tf.contrib.rnn.MultiRNNCell(stacked_rnn)
    return lstm_multi
encode
def encoder(encoder_input, encoder_seq_len, keep_prob):
    cells = lstm_cell(layer_num,hidden_size,keep_prob)
    with tf.variable_scope('encoder', reuse=tf.AUTO_REUSE):
        _, encoder_state = tf.nn.dynamic_rnn(cell=cells, inputs=encoder_input,sequence_length=encoder_seq_len,dtype=tf.float32)
    return encoder_state
decode
def decoder(decoder_input, decoder_seq_len, state_in, keep_prob):
	cells = lstm_cell(keep_prob)
    with tf.variable_scope('decoder', reuse=tf.AUTO_REUSE):
    	decoder_outputs, decoder_state = tf.nn.dynamic_rnn(cell=cells,inputs=decoder_input,sequence_length=decoder_seq_len, initial_state=state_in)
    return decoder_outputs, decoder_state
loss

举例使用高斯似然,对decode的输出状态进行仿射得到计算高斯似然的参数,从而计算loss进行梯度优化。

def create_gaussian_parameters(decoder_outputs, decoder_scale_factor, decoder_len,hidden_size):
    """
    对decode的输出进行仿射,得出计算高斯似然的参数。
    decoder_scale_factor:是归一化因子
    """
    batch_size = tf.shape(decoder_outputs)[0]
    with tf.variable_scope('gaussian_parameters', reuse=tf.AUTO_REUSE):
        W_mu = tf.get_variable(shape=[hidden_size, 1], dtype=tf.float32,name="W_mu")
        W_sigma = tf.get_variable(shape=[hidden_size, 1], dtype=tf.float32,name="W_sigma")
		b_mu = tf.get_variable(shape=[1], dtype=tf.float32, name="b_mu")
        b_sigma = tf.get_variable(shape=[1], dtype=tf.float32, name="b_sigma")
		W_mu_expand = tf.tile(tf.expand_dims(W_mu, 0), [batch_size, 1, 1])
        W_sigma_expand = tf.tile(tf.expand_dims(W_sigma, 0), [batch_size, 1, 1])
		b_mu_expand = tf.tile(tf.expand_dims(b_mu, 0), [batch_size, 1])
        b_sigma_expand = tf.tile(tf.expand_dims(b_sigma, 0), [batch_size, 1])
		mu = tf.reshape(tf.matmul(decoder_outputs, W_mu_expand), [batch_size, -1]) + b_mu_expand
        sigma = tf.log(1 + tf.math.exp(tf.reshape(tf.matmul(decoder_outputs, W_sigma_expand), [batch_size, -1]) + b_sigma_expand)) + 1e-6
		mu_scale = tf.math.multiply(mu, tf.reshape(decoder_scale_factor, [-1,decoder_len]))
        sigma_scale = tf.math.multiply(sigma, tf.reshape(decoder_scale_factor, [-1,decoder_len]))
        return mu_scale, sigma_scale

def gaussian_likelihood(y_true, mu, sigma):
    return tf.math.reduce_mean(0.5 * tf.math.log(tf.math.square(sigma)) + 0.5 * tf.math.divide(tf.math.square(y_true - mu),tf.math.square(sigma))) + 1e-6

预测

encode

预测时候的encode和训练时候一样。

decode
def decoder_inference(encoder_state, decoder_x_input_scale, decoder_len,decoder_feature, decoder_scale_factor, keep_prob):
    """
    推理的时候,需要将当前时刻的预测值传递给下一个时刻的输入。训练的时候,因为数据都已知,所以直接做一个shift的平移。
    """
    mu_ta = tf.TensorArray(dtype=tf.float32, size=decoder_len)
    sigma_alpha_ta = tf.TensorArray(dtype=tf.float32, size=decoder_len)
    def cond_fn(time, prev_state, prev_output, decoder_scale_factor, mu_ta, sigma_alpha_ta):
        return time < decoder_len
	def loop_fn(time, prev_state, prev_output, decoder_scale_factor, mu_ta, sigma_alpha_ta):
        batch_size = tf.shape(prev_output)[0]
        features = tf.slice(decoder_feature, [0, time, 0], [-1, 1, -1])
        next_input = tf.concat([prev_output, features], -1)
        decoder_seq_len = tf.ones([batch_size])
        decoder_outputs, decoder_state = decoder(next_input, decoder_seq_len, prev_state, keep_prob)
        mu, sigma_alpha = create_gaussian_parameters(decoder_outputs, decoder_scale_factor, 1)
        mu_ta = mu_ta.write(time, mu)
        sigma_alpha_ta = sigma_alpha_ta.write(time, sigma_alpha)
        sample_data = mu
        sample_data_scale = tf.math.divide(tf.reshape(sample_data, [batch_size, 1, 1]), decoder_scale_factor)
        return time + 1, decoder_state, sample_data_scale, decoder_scale_factor, mu_ta, sigma_alpha_ta
    loop_init = [tf.constant(0, dtype=tf.int32),
                 encoder_state,
                 decoder_x_input_scale,
                 decoder_scale_factor,
                 mu_ta,
                 sigma_alpha_ta]
    _, _, _, _, mu_output, sigma_alpha_output = tf.while_loop(cond_fn, loop_fn, loop_init)
	mu_result = tf.reshape(tf.transpose(mu_output.stack(), perm=[1, 0, 2]),[-1, decoder_len])
    sigma_alpha_result = tf.reshape(tf.transpose(sigma_alpha_output.stack(), perm=[1, 0, 2]),[-1, decoder_len])
	return mu_result, sigma_alpha_result

算法的优缺点

优点

1.对实数和计数分别设计了不同的loss;

2.数据预处理方面使用归一化的变换和预测使用weighted sampling。

缺点

1.没有attention机制,对较长的时间序列可能会出现记忆丢失的问题,无法捕获长周期、季节等信息。但在输入部分可以加入attention机制,比如用同期的数据作为一个特征。

其他

1.预测多条时间序列时,论文中提到可以对每条时间序序列进行category的编码,训练时进行embedding的学习;

2.可以提取每条时间序列的时间特征,作为feature输入到模型。

论文

论文连接

你可能感兴趣的:(时间序列预测算法,深度学习,算法,tensorflow)