如何在TensorFlow中不使用Embedding实现Seq2Seq用于时序预测?
嘿,我之前做时间序列Seq2Seq预测的时候也踩过这个坑——官方教程全是神经机器翻译的Embedding例子,完全不贴合数值序列的场景。其实不用Embedding太合理了,因为时间序列是连续数值型数据,不像NMT里的离散词需要转成向量,直接把原始序列喂给模型就行!下面给你一步步讲清楚怎么实现,以及TrainingHelper的参数设置:
在NMT任务里,Embedding是为了把离散的单词(比如"hello")转换成模型能处理的连续向量,但时间序列的每个时间步都是数值(比如温度、股价),本身就是连续向量了,所以完全不需要额外的Embedding层,直接把输入序列作为张量传入模型即可。
你提到的decoder_emb_inp、decoder_lengths这两个核心参数,在时间序列场景下这么设置:
decoder_emb_inp:这个参数直接传入decoder的训练输入张量就行!这里的输入是训练时用的「teacher forcing」数据——通常是目标序列的移位版本。举个例子:如果你的输入序列是前8个点(t1-t8),要预测接下来3个点(t9-t11),那decoder的输入可以是
t8 + t9 + t10(也就是输入序列的最后一个点,加上目标序列的前2个点),形状为(batch_size, target_seq_len, feature_dim),其中feature_dim是每个时间步的特征数(单变量预测就是1,多变量就是对应维度)。decoder_lengths:这个是每个样本的decoder序列长度。因为时间序列预测一般用固定长度的序列,所以直接生成一个形状为
(batch_size,)的张量,每个元素都是你的目标序列长度就行。比如目标序列长度是3,就用tf.fill([batch_size], 3)来生成。如果是变长序列,就根据每个样本的实际长度设置,但时间序列里很少用到。其他参数:
time_major默认设为False就好(我们常用的是batch-major格式,也就是batch_size在第一维度),sample_weight不需要的话留空就行。
下面是一个极简的单变量时间序列Seq2Seq实现,用TrainingHelper来做训练阶段的teacher forcing:
import tensorflow as tf # 超参数设置 batch_size = 32 input_seq_len = 8 # 输入是前8个点 target_seq_len = 3 # 要预测接下来3个点 feature_dim = 1 # 单变量时间序列 hidden_units = 64 # LSTM隐藏层单元数 # ---------------------- 编码器部分 ---------------------- encoder_cell = tf.keras.layers.LSTMCell(hidden_units) encoder_rnn = tf.keras.layers.RNN(encoder_cell, return_state=True) # 模拟输入数据(实际训练时换成你的真实数据) encoder_inputs = tf.random.normal([batch_size, input_seq_len, feature_dim]) # 得到编码器的输出和最终状态(状态要传给解码器做初始状态) _, encoder_h, encoder_c = encoder_rnn(encoder_inputs) encoder_states = [encoder_h, encoder_c] # ---------------------- 解码器训练阶段 ---------------------- # 构造teacher forcing的decoder输入:输入序列最后一个点 + 目标序列前target_seq_len-1个点 # 实际训练时,target_seq[:-1]是真实的目标序列前n-1个点 target_seq = tf.random.normal([batch_size, target_seq_len, feature_dim]) decoder_train_inputs = tf.concat([encoder_inputs[:, -1:, :], target_seq[:, :-1, :]], axis=1) # 设置TrainingHelper helper = tf.compat.v1.contrib.seq2seq.TrainingHelper( inputs=decoder_train_inputs, sequence_length=tf.fill([batch_size], target_seq_len), time_major=False ) # 解码器cell decoder_cell = tf.keras.layers.LSTMCell(hidden_units) # 用BasicDecoder配合TrainingHelper做训练 decoder = tf.compat.v1.contrib.seq2seq.BasicDecoder( cell=decoder_cell, helper=helper, initial_state=encoder_states ) # 运行解码器得到输出 decoder_outputs, _, _ = tf.compat.v1.contrib.seq2seq.dynamic_decode(decoder) # 接Dense层得到最终预测值 predictions = tf.keras.layers.Dense(feature_dim)(decoder_outputs.rnn_output) # 计算损失(用MSE适合回归任务) loss = tf.reduce_mean(tf.square(predictions - target_seq))
- 编码器的最终状态一定要传给解码器做初始状态,这是Seq2Seq模型能捕捉输入序列信息的核心。
- Teacher forcing的输入要和目标序列长度一致,这样模型才能一步步学习从输入到输出的映射。
- 如果是多变量时间序列,只需要把
feature_dim改成对应的特征数就行,其他代码逻辑完全一致。
内容的提问来源于stack exchange,提问作者dnovai

