如何用EinsumDense实现时序张量的维度压缩与扩展?
你的核心问题在于当前的einsum公式没有正确实现Horizon维度的映射,且最终层引入了冗余维度导致模型难以学习,以下是针对性的修正方案和解释:
一、先明确符号定义(避免混淆)
统一符号方便理解:
s: Batch Size(你的场景中为1)h_in: 输入的Horizon长度h_out: 目标Horizon长度(即你说的H)f_in: 输入特征数f_out: 目标特征数(即你说的F)
二、FFN层:实现Horizon维度的变换(从(s, h_in, f_in)到(s, h_out, f_in))
你之前用的shf,h->shf公式没有改变Horizon维度——输入和输出的h是同一个维度,相当于只对每个时间步做了无意义的缩放,完全没实现Horizon长度的调整。
正确的公式应该是shf, hh' -> sh'f:
- 输入张量:
(s, h_in, f_in)对应shf - 权重张量:
(h_in, h_out)对应hh'(负责把原时间步映射到目标时间步) - 输出张量:
(s, h_out, f_in)对应sh'f
这个操作本质是对每个特征维度,做时间步的线性投影,把h_in个时间步映射到h_out个,真正实现Horizon长度的变换。
代码示例:
from tensorflow.keras.layers import EinsumDense # FFN层:将输入Horizon转为目标H,保持特征数不变 ffn_layer = EinsumDense( equation='shf, hh_new -> sh_newf', output_shape=(H, f_in), # H是目标Horizon长度,f_in是输入特征数 bias_axes='h_new' # 可选:给每个输出时间步添加偏置,提升拟合能力 )
三、最终输出层:实现特征维度的变换(从(s, h_out, f_in)到(s, h_out, f_out))
你之前用的shf,hfyz->syz公式引入了冗余的y、z维度,导致参数数量爆炸且模型无法聚焦于有效的特征映射,这是效果差的关键原因之一。
根据场景选择两种方案:
方案1:时间步共享特征变换(推荐,参数少、泛化性好)
大多数时序场景下,每个时间步的特征变换逻辑是一致的,用这个方案:
公式:shf, ff' -> shf'
- 输入张量:
(s, h_out, f_in)对应shf - 权重张量:
(f_in, f_out)对应ff'(所有时间步共享这套特征映射权重) - 输出张量:
(s, h_out, f_out)对应shf'
代码示例:
final_layer = EinsumDense( equation='shf, ff_new -> shf_new', output_shape=(H, F), # H是目标Horizon,F是目标特征数 bias_axes='f_new' )
方案2:时间步独立特征变换(仅特殊场景使用)
如果你的时序数据中每个时间步的特征分布差异极大,需要独立的变换逻辑,可以用这个方案:
公式:shf, hff' -> shf'
- 权重张量:
(h_out, f_in, f_out)对应hff'(每个时间步有专属的特征映射权重) - 缺点是参数量会增加
h_out倍,容易过拟合。
四、原有配置效果好的原因
你之前未修改Horizon的配置中,FFN的shf,h->shf(推测实际是shf,f->shf,即对每个时间步的特征做线性变换)和最终层的shf,hz->shz(实际是shf,fz->shz),都是在做稳定的特征维度变换,模型只需要学习特征映射逻辑即可;而错误的einsum公式既没实现Horizon的真正变换,又引入了冗余参数,导致模型学习目标混乱,自然效果差。
完整可运行示例
import tensorflow as tf from tensorflow.keras.layers import EinsumDense, Input, TransformerEncoder # 定义参数 batch_size = 1 input_horizon = 24 input_features = 16 target_H = 48 target_F = 8 # 构建输入 inputs = Input(shape=(input_horizon, input_features)) # Transformer编码器(示例结构) encoder = TransformerEncoder( num_layers=2, d_model=input_features, num_heads=4, dim_feedforward=64 ) x = encoder(inputs) # 输出形状:(batch_size, input_horizon, input_features) # FFN层:变换Horizon到target_H x = EinsumDense( equation='shf, hh_new -> sh_newf', output_shape=(target_H, input_features), bias_axes='h_new' )(x) # 现在形状:(batch_size, target_H, input_features) # 最终输出层:变换特征到target_F outputs = EinsumDense( equation='shf, ff_new -> shf_new', output_shape=(target_H, target_F), bias_axes='f_new' )(x) # 输出形状:(batch_size, target_H, target_F) # 模型构建与查看 model = tf.keras.Model(inputs=inputs, outputs=outputs) model.summary()
内容的提问来源于stack exchange,提问作者SnakeWasTheNameTheyGaveMe

