TensorFlow中多输入样本数不一致的网络训练问题求解
解决TensorFlow中Diet网络训练的数据基数不匹配问题
为什么TensorFlow会有这个检查?
TensorFlow的Model.fit()API设计时默认遵循样本一一对应的训练逻辑:所有输入张量的第一维度(样本数)必须和输出标签的样本数保持一致,这样框架才能明确每个输入样本对应哪个标签,进而计算损失并更新权重。
在你的场景中,主输入data有200个样本,但辅助输入emb_inp只有1个样本,框架无法判断该如何将这两组输入配对(是把这个单样本辅助输入和所有200个主样本配对,还是有其他逻辑?),因此抛出了Data cardinality is ambiguous的错误。
无需修改输入的可行解决方案:自定义训练循环
如果你不想重复辅助输入,最灵活的方式是使用自定义训练循环,手动控制输入的配对逻辑、损失计算和权重更新流程。以下是适配你代码的具体实现:
1. 准备数据集(可选,但推荐)
先把主数据和标签打包成tf.data.Dataset,方便批量训练:
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import numpy as np from sklearn.decomposition import PCA # 保留你原来的训练数据定义 np.random.seed(0) n_samples = 200 #N n_features = 10000 #Nd data = np.random.random((n_samples, n_features)) labels = np.random.permutation([0]*50+[1]*50) pca_data = PCA(n_components=0.95).fit_transform(data.T) extracted_features_count = pca_data.shape[1] #Nf hidden_layer_1 = 100 #Nh1 hidden_layer_2 = 50 #Nh2 # 打包主数据和标签为Dataset,支持批量训练 train_dataset = tf.data.Dataset.from_tensor_slices((data, labels)).batch(32) # 固定辅助输入,保持原形状不变 fixed_aux_input = np.array(list(range(0, n_features))).reshape(1, -1)
2. 定义损失函数、优化器和模型
沿用你原来的模型和自定义损失函数:
class BinryMSECustom(keras.losses.Loss): def __init__(self): super().__init__() def __call__(self, y_true, y_pred): bn = keras.losses.binary_crossentropy(y_true[0], y_pred[0]) ms = keras.losses.mse(y_true[1], y_pred[1]) return tf.reduce_mean(bn) + tf.reduce_mean(ms) # 构建Diet网络模型(和你原来的代码一致) input1 = keras.Input(shape=(n_features,), name='classification_input_NxNd') input2 = keras.Input(shape=(n_features,), name='auxilary_input_NdxN') emb = layers.Embedding(input_dim=n_features, output_dim=extracted_features_count, embeddings_initializer=tf.keras.initializers.Constant(pca_data), trainable=False, name='feature_Embedding_NdxNf')(input2) aux_enc_mlp = layers.Dense(100, activation='tanh', name='aux1_mlp_NdxNh1')(emb) aux_dec_mlp = layers.Dense(100, activation='tanh', name='aux2_mlp_NdxNh1')(emb) x = layers.Dot(axes=1)([input1, aux_enc_mlp]) x_branch = layers.Dense(hidden_layer_1, activation='relu', name='classification_mlp_1_Nh1xNh2')(x) x = layers.Dense(hidden_layer_2, activation='relu', name='classification_mlp_2_Nh2x1')(x_branch) clf_out = layers.Dense(1, activation='sigmoid', name='Output')(x) decode_branch = layers.Dot(axes=-1)([aux_dec_mlp, x_branch]) model = keras.Model(inputs=[input1, input2], outputs=[clf_out, decode_branch]) optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3) loss_fn = BinryMSECustom()
3. 自定义训练循环
手动迭代数据集,每次用固定的辅助输入和当前批次的主数据配对:
epochs = 1 for epoch in range(epochs): print(f"Epoch {epoch+1}/{epochs}") total_loss = 0.0 # 遍历每个批次的主数据和标签 for batch_data, batch_labels in train_dataset: # 固定辅助输入,TensorFlow会自动处理维度广播 with tf.GradientTape() as tape: # 前向传播:传入批次主数据 + 固定辅助输入 clf_pred, decode_pred = model([batch_data, fixed_aux_input], training=True) # 计算损失:标签对应(批次分类标签,批次主数据) loss = loss_fn([batch_labels, batch_data], [clf_pred, decode_pred]) # 计算梯度并更新模型权重 gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) total_loss += loss.numpy() print(f"Total Loss: {total_loss/len(train_dataset)}")
为什么这个方案可行?
自定义训练循环绕过了Model.fit()的样本数检查逻辑,你可以完全控制输入的配对方式:这里我们把固定的单样本辅助输入和每一批主数据进行配对(TensorFlow会自动处理维度广播),同时计算对应的损失并更新模型参数,完美符合Diet网络中辅助网络输入与主网络样本数无关的设计要求。
内容的提问来源于stack exchange,提问作者princethewinner
相关产品推荐
相关产品推荐

