如何为Encoder-Decoder模型格式化Ragged Tensor?训练报错解决
解决Seq2Seq模型标签形状不匹配问题
问题核心
训练时触发ValueError: Shapes (None, None) and (None, None, 1220) are incompatible,根源有两点:
- 模型输出为三维张量
(batch_size, seq_len, target_vocab_size),对应categorical_crossentropy要求的one-hot格式 - 预处理后的
target_out是二维整数索引张量(batch_size, seq_len),两者形状不兼容 - 输入层设置了
ragged=True,但预处理中已通过to_tensor()将张量转为密集格式,导致输入输出不匹配
修复步骤
1. 调整损失函数(推荐方案)
无需将标签转为one-hot,直接使用整数索引适配的SparseCategoricalCrossentropy,修改模型编译逻辑:
def create_model(hsize): en_input_layer, enc_out, enc_states = encoder(hsize) de_input_layer, de_preds = decoder(hsize, enc_states) model = Model(inputs=[en_input_layer, de_input_layer], outputs=de_preds) model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics=["acc"]) return model
注:因为输出层用了softmax激活,所以from_logits=False;若移除softmax,建议设为True以提升数值稳定性
2. 修正输入层的ragged参数
预处理阶段已将ragged张量转为密集张量,因此需要去掉输入层的ragged=True设置:
def encoder(hsize, embed_dim=200): # 移除ragged=True en_input_layer = Input(shape=(None,), name='encoder_input_layer') en_embed = Embedding(context_vectorizer.vocabulary_size()+1, output_dim=embed_dim, name='encoder_embedding_layer') en_embed_out = en_embed(en_input_layer) en_gru_1 = GRU(hsize, return_sequences=True, return_state=True, name='encoder_gru_layer_1') en_gru_1_out, en_gru_states = en_gru_1(en_embed_out) return en_input_layer, en_gru_1_out, en_gru_states def decoder(hsize, encoder_states, embed_dim=200): # 移除ragged=True de_input_layer = Input(shape=(None,), name='decoder_input_layer') de_embed = Embedding(target_vectorizer.vocabulary_size()+1, output_dim=embed_dim, name='decode_embedding_layer') de_embed_out = de_embed(de_input_layer) de_gru_1 = GRU(hsize, return_sequences=True, name='decoder_gru_layer_1') de_gru_1_out = de_gru_1(de_embed_out, initial_state=encoder_states) # 修正Dense层维度,匹配Embedding的词汇表大小(包含填充位) de_dense = TimeDistributed(Dense(target_vectorizer.vocabulary_size()+1, activation='softmax'), name='time_distributed_output_layer') de_preds = de_dense(de_gru_1_out) return de_input_layer, de_preds
3. 可选:将标签转为one-hot格式(内存占用较高)
若坚持使用categorical_crossentropy,修改预处理函数对target_out做one-hot编码:
def preprocess_text(context, target): context = context_vectorizer(context).to_tensor() target = target_vectorizer(target) target_in = target[:,:-1].to_tensor() target_out = target[:,1:].to_tensor() # 转换为one-hot格式,维度匹配词汇表(含填充位) target_out = tf.one_hot(target_out, depth=target_vectorizer.vocabulary_size()+1) return (context, target_in), target_out
4. 验证数据管道输出
添加代码确认形状匹配:
for (context_batch, target_in_batch), target_out_batch in train_ds.take(1): print("Context shape:", context_batch.shape) print("Target in shape:", target_in_batch.shape) print("Target out shape:", target_out_batch.shape)
- 用
SparseCategoricalCrossentropy时,target_out_batch.shape应为(batch_size, seq_len) - 用
categorical_crossentropy时,target_out_batch.shape应为(batch_size, seq_len, vocab_size+1)
内容的提问来源于stack exchange,提问作者Krishnang K Dalal
相关产品推荐
相关产品推荐

