如何将Keras多输入自编码器的Embedding输出改为(1000000,20)
解决双输入序列Embedding形状问题(从(1000000,50,20)到(1000000,20))
你的问题核心在于当前模型结构中,每个时间步都生成了一个20维向量,而你需要的是整个序列对应一个20维的全局Embedding。这是因为你在LSTM层中设置了return_sequences=True,导致LSTM输出每个时间步的隐藏状态,后续的全连接层也会对每个时间步单独处理,最终得到的journey_embeddings是序列级别的逐时间步输出。
下面提供两种可行的解决方案,你可以根据自己的需求选择:
方案1:修改LSTM层,只输出最后一个时间步的状态
这种方案让LSTM直接输出序列最后一个时间步的隐藏状态,从根源上得到全局的特征表示,后续的全连接层自然会生成每个样本对应的单个Embedding向量。
修改步骤:
- 把两个LSTM层的
return_sequences=True改为return_sequences=False:
# Input 1: LSTM(修改return_sequences参数) input_1c = LSTM(10, return_sequences=False)(input_1b) # Input 2: LSTM(修改return_sequences参数) input_2c = LSTM(10, return_sequences=False)(input_2b)
- 拼接后的特征形状会变成
(None, 20)(两个LSTM各输出10维,拼接后共20维),后续的Dense层处理后,journey_embeddings的形状就是(None, 20):
# Concatenation layer here x = keras.layers.concatenate([input_1c, input_2c]) x2 = Dense(40, activation='relu')(x) x3 = Dense(20, activation='relu', name="journey_embeddings")(x2)
- 调整自编码器输出分支的结构:因为现在
x3是全局Embedding,需要用RepeatVector把它扩展为序列形状,才能重建每个时间步的输入:
# 重建Input 1的分支(修改后) xl = RepeatVector(max_seq_length)(x3) xf = TimeDistributed(Dense(20, activation='relu'))(xl) xf1 = TimeDistributed(Dense(50, activation='relu'))(xf) xf2 = TimeDistributed(Dense(num_unique_event_symbols, activation='linear'))(xf1) # 重建Input 2的分支(修改后) xd = RepeatVector(max_seq_length)(x3) xd2 = TimeDistributed(Dense(1, activation='linear'))(xd)
方案2:添加全局池化层,聚合时间步特征
如果你希望保留LSTM对整个序列的逐时间步建模能力(比如需要捕捉序列的动态变化),可以在现有x3层之后添加全局池化层,把50个时间步的20维向量聚合为一个20维的全局Embedding。
修改步骤:
- 在
x3之后添加全局平均池化或全局最大池化层,并重命名journey_embeddings到这个池化层:
x3 = Dense(20, activation='relu')(x2) # 添加全局平均池化,将(None,50,20)转换为(None,20) journey_embeddings = GlobalAveragePooling1D(name="journey_embeddings")(x3)
- 调整自编码器输出分支的输入:输出分支需要基于逐时间步的
x3来重建输入,而不是池化后的全局Embedding:
# Re-create the inputs xl = Lambda(lambda x: x, output_shape=lambda s:s)(x3) # 仍使用逐时间步的x3 xf = Flatten()(xl) xf1 = Dense(20, activation='relu')(xf) xf2 = Dense(50, activation='relu')(xf1) xd = Lambda(lambda x: x, output_shape=lambda s:s)(x3) # 同样使用x3 xd2 = TimeDistributed(Dense(1, activation='linear'))(xd)
- 提取Embedding时,直接获取
journey_embeddings层的输出即可,此时形状就是(1000000, 20):
layer_name = 'journey_embeddings' intermediate_layer_model = Model(inputs=model.input, outputs=model.get_layer(layer_name).output) intermediate_output = intermediate_layer_model.predict([X1,X2])
方案选择建议
- 如果你更关注序列的最终状态(比如用户的最终行为),选择方案1,结构更简洁,计算量更小。
- 如果你需要捕捉序列的全局趋势(比如整个时间序列的模式),选择方案2,能保留更多序列动态信息。
内容的提问来源于stack exchange,提问作者Slyron
相关产品推荐
相关产品推荐

