相关文章推荐
大鼻子的奔马  ·  Android Studio - ...·  2 月前    · 
潇洒的牙膏  ·  postgresql c ...·  1 年前    · 
小胡子的火锅  ·  python - ...·  1 年前    · 

为什么要在时间序列预测中使用seq2seq以及seq2seq为什么可以支持不定长的输入和输出

2 年前 · 来自专栏 图算法-时序建模-迁移学习

为什么使用seq2seq:

1、seq2seq的架构相对于常规的cnn或者rnn处理序列问题的效果在实际的应用中效果更好;

2、seq2seq的框架非常灵活可以支持不定长的输入和输出;

3、seq2seq中的很多技巧可以进一步提升模型的效果,例如attention机制、teacher forcing等;

4、seq2seq考虑了输出的标签之间的序列依赖性,这是常规的RNN或cnn无法做到的,常规的RNN和CNN只能以输出向量的形式解决多步预测问题,这意味着它们无法考虑到输出标签之间的序列依赖性;

例如常规的LSTM我们要解决n步预测问题,则输出ht一般会接 Dense层,这个dense层的units的数量设置为n,此时实际上就是一个简单的全连接层将hidden state映射为一个n维的向量,从而满足问题的数据的形式,这个时候n维向量输出(即n个待预测的时间步)之间是完全独立,不互相影响的;

而seq2seq的结构则可以考虑到输出序列之间的序列依赖性;


那么seq2seq为什么可以支持不定长的输入和输出呢?

这一点看似复杂,其实在实现上非常的简单:

from keras.layers import Input, LSTM, Dense
# Define an input sequence and process it.
encoder_inputs = Input(shape=(None, num_encoder_tokens))
encoder = LSTM(latent_dim, return_state=True)
encoder_outputs, state_h, state_c = encoder(encoder_inputs)
# We discard `encoder_outputs` and only keep the states.
encoder_states = [state_h, state_c]
# Set up the decoder, using `encoder_states` as initial state.
decoder_inputs = Input(shape=(None, num_decoder_tokens))
# We set up our decoder to return full output sequences,
# and to return internal states as well. We don't use the 
# return states in the training model, but we will use them in inference.
decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)
decoder_outputs, _, _ = decoder_lstm(decoder_inputs,
                                     initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)
# Define the model that will turn
# `encoder_input_data` & `decoder_input_data` into `decoder_target_data`
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

核心的地方就在这里:

encoder_inputs = Input(shape=(None, num_encoder_tokens)) 
decoder_inputs = Input(shape=(None, num_decoder_tokens)) 

核心就是把timestemp的大小设置为None,这样LSTM就可以支持不定长的输入了,这个时候我们应该把思路打开,不要局限于过去的固定长度输入的LSTM上。

以上图为例,以前我们处理时间序列问题,尤其是使用传统的机器学习算法,基本上必须做的就是划分时间窗,选定使用多长的历史数据来预测未来多长的数据,比如说像上图这样,假设我们使用过去的3天的数据预测未来两天的数据,以单变量为例(多变量原理一样的,只是多变量的简单推广而已)

[1,2,3][4,5]

[2,3,4][5,6]

[3,4,5][6,7]

.....

则网络设计的使用input一般设置为 Input(shape=(3,1)),即一个样本有3个时间切片,每个时间切片下有一个特征,输出的维度则为2;

但是当我们把3设置为None的时候,此时input层就具有了接收不定长输入的能力了,简单来说就是你fit的时候,你的输入可以是

[1,2,3]也可以是 [-2,-1,0,1,2,3]。。。。。etc,长度可以随意指定,其实很好理解:

这是一个LSTM cell,我们其实可以把这个LSTMcell延续无数次,第一个LSTM cell接受了x1的输入,输出了h1和c1进入了下一个LSTM cell,因为LSTM本身是在时间上的扩展,其物理结构使用时同一个LSTM cell,即在不同的时刻 这个LSTM cell始终是同一个物理结构,参数共享了,因此无论我们的输入是多长,只要输入的feature满足input(shape=(None,feature))中的feature的大小的设定,程序运行都是莫得问题的;

简单来说大概过程是这样,我们保持同一个LSTM的cell,假设第一次输入是:

[1,2,3],[9,10]

则x1=1,输入LSTMcell中得到h1和c1进入下一个时间步,然后x2=2,接受h1和c1经过LSTMcell得到h2和c2.。。。一直到x3对应输出h3,c3,然后h3可以直接作为输入层接一个dense,units=2,对应多步输出 [9,10],然后模型fit一下就可以了,此时是一个简单的LSTM结构(是的,LSTM的特性本身也可以直接支持不定长输入而不一定需要seq2seq的框架,只不过seq2seq更强大)得到了Model1

那么假设第二次输入为[1,2,3,4,5,6,7],[9,10],则和上述的步骤一样,我们可以直接用Model1去fit这种输入,即使它的长度已经发生了变换,运算的逻辑和上面一样,最终得到h9和c9,h9作为输出balabal。。。。最终训练,Model1更新得到了Model2;

预测的时候也是一样,我们可以predict不定长的数据,也就是你输入的数据长度无论怎么变,去predict都可以得到一个最终的输出结果,只不过你在长度为10的数据上训练的模型去predict一个长度为1000的数据可能没有什么意义就是了;

这里我们就把seq2seq encoder可以支持不定长输入的问题讲完了,核心原因在于LSTM的灵活性问题,当然使用CNN也可以实现不定长的输入,只不过逻辑上和LSTM有一些区别,这个后续讲wavenet的时候再展开来说,wavenet本身是一个典型的基于CNN的seq2seq结构,关于基于cnn的seq2seq结构我发现网络上介绍其在时间序列的应用很少,wavenet算是一个典型代表了,实现也是非常的简单;


然后是关于decoder支持不定长的问题,这个需要注意的地方就是,sinple seq2seq的局限性大一些,从代码实现上看只能支持不定长输入无法支持不定长输出


上图是一个sinple seq2seq的例子

m = Sequential()
m.add(GRU(256, input_shape=(None, 15)))
m.add(RepeatVector(2)) ##核心
m.add(GRU(128, return_sequences=True)) ## 使用timedistributed来包装
m.add(TimeDistributed(Dense(1)))
m.add(Flatten())

这里的核心一个是上面说过的input的部分的shape的timesteps部分设置为None,

但是simple seq2seq仅仅在encoder的context,也就是

这个部分的ht做简单的repeatvector操作,这里repeat的次数=我们要预测未来的多少个时间步,

也就是说,decoder的输入在simple seq2seq里是一毛一样的,因此显著与这里的:

m.add(RepeatVector(2)) ##核心

是固定的2,我们的输出output的数据的size只能固定成2步;

不过对于teacher forcing的训练方式来说则没有这个问题:

teacher forcing的训练过程如上图,训练过程的output对应的是真实的标签,使用基于LSTM的teacher forcing的时候需要注意(这个地方特意强调了是LSTM的teacher forcing,因为wavenet中的CNN的teacher forcing的数据准备工作还不一样,不过会更简单一些),我们的decoder部分的数据分两部分,decoder input和decoder output,二者仅仅相差一个时间步;

模型训练完毕之后,推断会麻烦一些,因为t时刻的数据我们是已知的,所以t时刻的数据要作为decoder部分的输入初始化,预测得到t+1之后将预测结果作为input继续输入,其实就是下面这个 图:

因为本身时间序列数据并不是离散的,没有所谓的topk的概念,因此无法使用beam search的方法来进行优化,所以在时间序列问题中teacher forcing的输出最终还是要迭代输出,即预测结果作为输入进入下一个时间步的LSTM输出;

teacher forcing的训练机制使得我们可以支持不定长的输出,比如你想输出未来的20个时间步,写一个for 循环就可以,每一次都把上一个时间步的预测输出作为下一个时间步的输入就可以了,代码实现上大概长这个样子:

horizon=2
output = list()
for t in range(horizon):
    # 预测下一个时间步的标签
    yhat, h,c = decoder_model.predict([dec_input] + states_value)