TensorFlow:如何正确合并多个已拼接的数据集以适配多输入Keras模型?
首先,咱们来定位你遇到的错误根源:TypeError: Inputs to a layer should be tensors. Got: <tensorflow.python.data.ops.dataset_ops._NestedVariant object>。这个问题出在window函数的处理上——window返回的是一个「数据集的数据集」(Dataset of Datasets),你虽然用了interleave,但每个元素依然是未展开的Dataset对象,而不是Keras需要的张量。另外,你分开处理三种类型的数据集再zip的方式,也容易出现样本不匹配的问题。
下面是修正后的完整方案,我会标注关键修改点:
步骤1:修正windowed数据集的处理逻辑
我们需要把每个window转换成一个张量,用flat_map配合batch来实现——因为你的window size是5,drop_remainder=True,所以每个window刚好有5个元素,batch后会变成形状为(5, 2)的张量(对应2个特征)。
步骤2:调整数据集合并顺序
先为每个DataFrame单独生成(输入组,目标)的数据集,确保单个DataFrame的windowed、static、target样本一一对应,再把这些数据集合并在一起,这样能避免样本错位。
修正后的完整代码
import pandas as pd import numpy as np import tensorflow as tf # Create dataframes with 4 features and target dataframe1 = pd.DataFrame(np.random.randn(1000, 5), columns=["feature1", "feature2", "feature3", "feature4", "target"]) dataframe2 = pd.DataFrame(np.random.randn(800, 5), columns=["feature1", "feature2", "feature3", "feature4", "target"]) # 修正:处理windowed数据集时,将每个window转为张量 def get_dataset_windowed(df: pd.DataFrame, features): dataset = tf.data.Dataset.from_tensor_slices(df.loc[:, features].to_numpy()) # window返回Dataset of Datasets,用flat_map+batch把每个window转成(5, 2)的张量 dataset = dataset.window(5, shift=1, stride=1, drop_remainder=True).flat_map(lambda window: window.batch(5)) return dataset def get_dataset(df: pd.DataFrame, features): # 注意:window处理后,有效样本数是len(df)-4,所以这里要和windowed数据集对齐 dataset = tf.data.Dataset.from_tensor_slices(df.loc[:, features].iloc[4:].to_numpy()) return dataset # 修正:先为每个DataFrame生成完整的(输入,目标)数据集,再合并 def create_full_dataset_for_df(df): windowed_ds = get_dataset_windowed(df, ["feature3", "feature4"]) static_ds = get_dataset(df, ["feature1", "feature2"]) target_ds = get_dataset(df, ["target"]) # 为单个DataFrame zip输入和目标 return tf.data.Dataset.zip( ( { "short_term_ts_input": windowed_ds, "static_input": static_ds, }, { "output": target_ds, } ) ) # 合并两个DataFrame对应的数据集 full_dataset = create_full_dataset_for_df(dataframe1).concatenate(create_full_dataset_for_df(dataframe2)) # 打乱+分批 full_dataset = full_dataset.shuffle(buffer_size=1024).batch(128) # 模型部分保持不变(你的原代码是对的,补充了损失函数) short_term_ts_input = tf.keras.Input(shape=(5, 2), name="short_term_ts_input") static_input = tf.keras.Input(shape=(2,), name="static_input") short_term_ts_features = tf.keras.layers.LSTM(32, return_sequences=False)(short_term_ts_input) short_term_ts_features = tf.keras.layers.Dense(8)(short_term_ts_features) static_features = tf.keras.layers.Dense(16)(static_input) x_concat = tf.keras.layers.concatenate([short_term_ts_features, static_features]) x_concat = tf.keras.layers.Dense(32)(x_concat) output = tf.keras.layers.Dense(1)(x_concat) model = tf.keras.Model(inputs=[short_term_ts_input, static_input], outputs=[output]) model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss="mse") # 补充损失函数,适配回归任务 model.fit(full_dataset, epochs=5)
关键修改点解释
windowed数据集的展开:
原代码中get_dataset_windowed返回的是Dataset of Datasets,我们添加了.flat_map(lambda window: window.batch(5)),把每个window里的5个元素拼成一个形状为(5, 2)的张量,这样每个元素就变成了Keras可以接收的张量。数据集合并顺序:
先为每个DataFrame单独生成对应的(输入组,目标)数据集,确保windowed、static、target的样本完全对齐,再用concatenate合并两个数据集。这种方式比分开处理三类数据集再zip更可靠,避免了不同DataFrame样本数差异导致的错位问题。补充损失函数:原代码
model.compile没指定损失函数,训练时会报错,所以我加了loss="mse"(你可以根据任务类型调整为分类损失等)。
这样修改后,full_dataset中的每个元素都是(字典形式的输入张量,字典形式的目标张量),完全符合Keras模型的输入要求,就不会再出现NestedVariant的错误了。
内容的提问来源于stack exchange,提问作者Perceval Desforges

