tf.data.Dataset传入seq2seq模型fit时的格式错误解决请求
构建Seq2Seq模型的数据集格式问题
问题背景
我正在基于词嵌入相关内容构建Seq2Seq模型,输入为context、question、answer的拼接序列,输出为answer序列,采用Keras函数式API搭建,但在数据集格式适配中反复出现错误。
尝试过程及错误
第一次尝试:分数据集传入fit_generator
代码:
train_cont_ds = tf.data.Dataset.from_tensor_slices(train["clean_context"].values) train_q_ds = tf.data.Dataset.from_tensor_slices(train["clean_question"].values) train_a_ds = tf.data.Dataset.from_tensor_slices(train["clean_answer"].values) # 文本向量化步骤省略 c_input = Input(shape=(1,)) q_input = Input(shape=(1,)) a_input = Input(shape=(1,)) c_vec = context_vec_layer(c_input) q_vec = q_vec_layer(q_input) a_vec = a_vec_layer(a_input) c_emb = Embedding(VOCAB_SIZE, GLOVE, weights=[embedding_matrix], trainable=False, mask_zero=True)(c_vec) q_emb = Embedding(VOCAB_SIZE, GLOVE, weights=[embedding_matrix], trainable=False, mask_zero=True)(q_vec) a_emb = Embedding(VOCAB_SIZE, GLOVE, weights=[embedding_matrix], trainable=False, mask_zero=True)(a_vec) c_pool = GlobalAveragePooling1D()(c_emb) q_pool = GlobalAveragePooling1D()(q_emb) a_pool = GlobalAveragePooling1D()(a_emb) concat = concatenate([c_pool,q_pool,a_pool]) Y_pred = Dense(QUES_LEN, activation='relu')(concat) model = Model(inputs = [c_input,q_input,a_input], outputs = Y_pred) model.compile(optimizer='adam', loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=['accuracy']) model.summary() model.fit_generator( [train_cont_ds,train_q_ds,train_a_ds], train_a_ds, validation_data=( [cv_cont_ds,cv_q_ds,cv_a_ds], cv_a_ds), verbose=1,epochs=epochs, callbacks=[tensorboard_callback, cp_callback])
错误:
ValueError: Failed to find data adapter that can handle input: (<class 'list'> containing values of types {"<class 'tensorflow.python.data.ops.dataset_ops.BatchDataset'>"}), <class 'NoneType'>
第二次尝试:封装输入为元组
代码:
train_ds = tf.data.Dataset.from_tensor_slices(((train["clean_context"].values, train["clean_question"].values, train["clean_answer"].values), train["clean_answer"].values)) train_ds = train_ds.batch(batch_size) cv_ds = tf.data.Dataset.from_tensor_slices(((cv["clean_context"].values, cv["clean_question"].values, cv["clean_answer"].values), cv["clean_answer"].values)) cv_ds = train_ds.batch(batch_size) dev_ds = tf.data.Dataset.from_tensor_slices(((dev["clean_context"].values, dev["clean_question"].values, dev["clean_answer"].values), dev["clean_answer"].values)) dev_ds = train_ds.batch(batch_size)
错误:文本向量化层适配失败。
第三次尝试:拼接数据集并转迭代器
代码:
train_cont_ds = tf.data.Dataset.from_tensor_slices(train["clean_context"].values) train_q_ds = tf.data.Dataset.from_tensor_slices(train["clean_question"].values) train_a_ds = tf.data.Dataset.from_tensor_slices(train["clean_answer"].values) train_a_ds = train_a_ds.batch(batch_size) train_ds = train_cont_ds.concatenate(train_q_ds).concatenate(train_a_ds) train_ds = train_ds.batch(batch_size) cv_cont_ds = tf.data.Dataset.from_tensor_slices(cv["clean_context"].values) cv_q_ds = tf.data.Dataset.from_tensor_slices(cv["clean_question"].values) cv_a_ds = tf.data.Dataset.from_tensor_slices(cv["clean_answer"].values) cv_a_ds = cv_a_ds.batch(batch_size) cv_ds = cv_cont_ds.concatenate(cv_q_ds).concatenate(cv_a_ds) cv_ds = cv_ds.batch(batch_size) dev_cont_ds = tf.data.Dataset.from_tensor_slices(dev["clean_context"].values) dev_q_ds = tf.data.Dataset.from_tensor_slices(dev["clean_question"].values) dev_a_ds = tf.data.Dataset.from_tensor_slices(dev["clean_answer"].values) dev_a_ds = dev_a_ds.batch(batch_size) dev_ds= dev_cont_ds.concatenate(dev_q_ds).concatenate(dev_a_ds) dev_ds = train_ds.batch(batch_size) hist = model.fit( [train_ds.as_numpy_iterator()], train_a_ds.as_numpy_iterator(), validation_data=( [cv_ds.as_numpy_iterator()], cv_a_ds.as_numpy_iterator()), verbose=1,epochs=epochs, callbacks=[tensorboard_callback])
错误:
ValueError: Failed to find data adapter that can handle input: (<class 'list'> containing values of types {"<class 'tensorflow.python.data.ops.dataset_ops._NumpyIterator'>"}), <class 'tensorflow.python.data.ops.dataset_ops._NumpyIterator'>
第四次尝试:用生成器封装数据集
代码:
def gen(): for element in zip(train_ds,train_a_ds): yield element ds = tf.data.Dataset.from_generator(gen,output_types=tf.dtypes.float32) def v_gen(): for element in zip(cv_ds,cv_a_ds): yield element v_ds = tf.data.Dataset.from_generator(v_gen,output_types=tf.dtypes.float32) hist = model.fit_generator( ds,validation_data=(v_ds), verbose=1,epochs=epochs, callbacks=[tensorboard_callback])
错误:
ValueError: Target data is missing. Your model was compiled with loss=<keras.losses.BinaryCrossentropy object at 0x00000139D15BA610>, and therefore expects target data to be provided in `fit()`.
正确的数据集格式及解决方案
核心问题
所有错误的根源是输入数据结构与模型Input层不匹配,且tf.data.Dataset没有正确封装输入-标签对。多输入模型要求输入必须是与Input层一一对应的张量集合,标签单独作为目标。
步骤1:正确适配文本向量化层
文本向量化层需要先适配训练数据,确保能将文本转为固定长度的整数序列:
# 定义三个文本向量化层,分别对应context、question、answer的序列长度 CONTEXT_LEN = 200 # 根据你的数据调整 QUESTION_LEN = 50 ANSWER_LEN = 30 context_vec_layer = tf.keras.layers.TextVectorization( max_tokens=VOCAB_SIZE, output_sequence_length=CONTEXT_LEN ) q_vec_layer = tf.keras.layers.TextVectorization( max_tokens=VOCAB_SIZE, output_sequence_length=QUESTION_LEN ) a_vec_layer = tf.keras.layers.TextVectorization( max_tokens=VOCAB_SIZE, output_sequence_length=ANSWER_LEN ) # 适配训练数据 context_vec_layer.adapt(train["clean_context"].values) q_vec_layer.adapt(train["clean_question"].values) a_vec_layer.adapt(train["clean_answer"].values)
步骤2:构建符合要求的tf.data.Dataset
将多个输入封装为元组,标签作为元组的第二部分,同时在Dataset中完成向量化预处理:
# 构建训练集:(输入元组, 标签) train_ds = tf.data.Dataset.from_tensor_slices( ( (train["clean_context"].values, train["clean_question"].values, train["clean_answer"].values), train["clean_answer"].values ) ) # 构建验证集 cv_ds = tf.data.Dataset.from_tensor_slices( ( (cv["clean_context"].values, cv["clean_question"].values, cv["clean_answer"].values), cv["clean_answer"].values ) ) # 定义预处理函数:将文本转为向量 def preprocess(inputs, labels): c_text, q_text, a_text = inputs c_vec = context_vec_layer(c_text) q_vec = q_vec_layer(q_text) a_vec = a_vec_layer(a_text) return (c_vec, q_vec, a_vec), labels # 应用预处理、批量、预取优化 train_ds = train_ds.batch(batch_size).map(preprocess, num_parallel_calls=tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE) cv_ds = cv_ds.batch(batch_size).map(preprocess, num_parallel_calls=tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE)
步骤3:修正模型Input层形状
向量化后的输入是固定长度的序列,所以Input层的shape应该对应序列长度,而非(1,):
c_input = Input(shape=(CONTEXT_LEN,)) q_input = Input(shape=(QUESTION_LEN,)) a_input = Input(shape=(ANSWER_LEN,)) # 后续嵌入、池化、拼接逻辑不变 c_emb = Embedding(VOCAB_SIZE, GLOVE, weights=[embedding_matrix], trainable=False, mask_zero=True)(c_input) q_emb = Embedding(VOCAB_SIZE, GLOVE, weights=[embedding_matrix], trainable=False, mask_zero=True)(q_input) a_emb = Embedding(VOCAB_SIZE, GLOVE, weights=[embedding_matrix], trainable=False, mask_zero=True)(a_input) c_pool = GlobalAveragePooling1D()(c_emb) q_pool = GlobalAveragePooling1D()(q_emb) a_pool = GlobalAveragePooling1D()(a_emb) concat = concatenate([c_pool, q_pool, a_pool]) # 注意:Seq2Seq任务若为生成序列,建议输出改为词汇表大小,用SparseCategoricalCrossentropy损失 Y_pred = Dense(VOCAB_SIZE, activation='softmax')(concat) model = Model(inputs=[c_input, q_input, a_input], outputs=Y_pred) # 修正损失函数 model.compile( optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy'] )
步骤4:正确调用模型训练
此时Dataset格式完全匹配模型输入,直接传入fit即可:
model.fit( train_ds, validation_data=cv_ds, epochs=epochs, callbacks=[tensorboard_callback, cp_callback] )
内容的提问来源于stack exchange,提问作者Manal
相关产品推荐
相关产品推荐

