Keras多输入模型训练报错:非符号张量输入问题求解
我想要训练一个Keras模型,其输入为尺寸(20, 300)的向量。但需在每个训练步骤中输入一组对所有样本均固定的向量组。以下是我的尝试代码:
def create_model(num_filters=64, embedding_dim=300, seq_len=20): # input1 Shape (?,20,300) input1 = Input(shape=(seq_len,embedding_dim,), dtype='float32') # Input1 taken from the model input # input2 Shape (5,20,300) input2=get_input2() # Input2: taken from outside the model # CNN Encoding of Input 1 convs = [] filter_sizes = [1,2,3] for fsz in filter_sizes: x = Conv1D(num_filters, fsz, activation='relu',padding='same')(input1) x = MaxPooling1D()(x) convs.append(x) output1 = Concatenate(axis=-1)(convs) output1 = Flatten()(output1) # CNN Encoding of Input 2 convs1 = [] filter_sizes = [1,2,3] for fsz in filter_sizes: x1 = Conv1D(num_filters, fsz, activation='relu',padding='same')(input2) x1 = MaxPooling1D()(x1) convs1.append(x1) output2 = Concatenate(axis=-1)(convs1) output2 = Flatten()(output2)
然而该实现抛出值错误:
ValueError: Layer conv1d_60 was called with an input that isn't a symbolic tensor. Received type: .
请问在Keras中应如何正确实现该需求?
解决方案
这个问题我之前也碰到过,本质是没搞清楚Keras构建计算图时对输入的要求——你直接用get_input2()拿到的是普通张量/数组,不是Keras需要的符号张量,所以无法被Conv1D这类层处理。下面给你两种可行的解决思路,按需选择:
方案1:把固定向量组设为模型的第二个输入
这是最直观的方式,把全局固定的向量组也定义成模型的输入,训练时每次batch都重复传入它就行(因为对所有样本都一样)。
修改后的模型代码:
def create_model(num_filters=64, embedding_dim=300, seq_len=20): # 输入1:每个样本的特征向量,shape (?,20,300) input1 = Input(shape=(seq_len, embedding_dim,), dtype='float32') # 输入2:全局固定的向量组,shape (5,20,300),作为模型的输入 input2 = Input(shape=(5, seq_len, embedding_dim,), dtype='float32') # 编码input1的CNN模块 convs = [] filter_sizes = [1,2,3] for fsz in filter_sizes: x = Conv1D(num_filters, fsz, activation='relu', padding='same')(input1) x = MaxPooling1D()(x) convs.append(x) output1 = Concatenate(axis=-1)(convs) output1 = Flatten()(output1) # 编码input2的CNN模块 convs1 = [] for fsz in filter_sizes: x1 = Conv1D(num_filters, fsz, activation='relu', padding='same')(input2) x1 = MaxPooling1D()(x1) convs1.append(x1) output2 = Concatenate(axis=-1)(convs1) output2 = Flatten()(output2) # 这里可以继续添加后续逻辑,比如合并两个编码结果后做分类/回归 combined = Concatenate(axis=-1)([output1, output2]) final_output = Dense(1, activation='sigmoid')(combined) # 创建包含两个输入的模型 model = Model(inputs=[input1, input2], outputs=final_output) return model
训练时的调用方式:
# 获取全局固定的向量组 fixed_input2 = get_input2() # shape (5,20,300) # 假设你的训练数据是x_train (n_samples,20,300),y_train是对应的标签 model = create_model() model.compile(optimizer='adam', loss='binary_crossentropy') # 训练时,给input2重复生成和batch匹配的副本 model.fit( x=[x_train, np.repeat(fixed_input2[np.newaxis, ...], x_train.shape[0], axis=0)], y=y_train, epochs=10, batch_size=32 )
方案2:把固定向量组作为模型内部的常量
如果不想把它作为外部输入,而是让它成为模型的一部分(不可训练的常量),可以用tf.constant把它转为符号张量,直接嵌入模型:
import tensorflow as tf def create_model(num_filters=64, embedding_dim=300, seq_len=20): # 获取固定向量组并转为符号常量 fixed_input2_data = get_input2() input2 = tf.constant(fixed_input2_data, dtype='float32') # 输入1:样本特征 input1 = Input(shape=(seq_len, embedding_dim,), dtype='float32') # 编码input1的模块 convs = [] filter_sizes = [1,2,3] for fsz in filter_sizes: x = Conv1D(num_filters, fsz, activation='relu', padding='same')(input1) x = MaxPooling1D()(x) convs.append(x) output1 = Concatenate(axis=-1)(convs) output1 = Flatten()(output1) # 编码input2的模块(用常量作为输入) convs1 = [] for fsz in filter_sizes: x1 = Conv1D(num_filters, fsz, activation='relu', padding='same')(input2) x1 = MaxPooling1D()(x1) convs1.append(x1) output2 = Concatenate(axis=-1)(convs1) output2 = Flatten()(output2) # 后续逻辑示例 combined = Concatenate(axis=-1)([output1, output2]) final_output = Dense(1, activation='sigmoid')(combined) model = Model(inputs=input1, outputs=final_output) return model
这种方式的好处是训练时只需要传input1的数据就行,不用管input2,但它只适合向量组完全固定、训练过程中不会变化的场景。
原代码报错的原因
你原代码里input2=get_input2()拿到的是普通的NumPy数组或非符号张量,而Keras的层(比如Conv1D)要求输入必须是符号张量——也就是由Input层创建,或者其他层输出的张量,这样Keras才能构建可微分的计算图,实现反向传播训练。直接传入普通张量会导致Keras无法追踪计算过程,所以抛出了那个错误。
内容的提问来源于stack exchange,提问作者Bushr Haddad

