使用from_pretrained加载自定义微调EncoderDecoderModel异常问题
BERT EncoderDecoder模型加载警告及输出异常问题排查
问题说明
在Google Colab中使用Hugging Face Transformers库构建BERT-based EncoderDecoder模型,经Seq2SeqTrainer微调后保存至Google Drive。在另一Colab加载模型时,反复出现以下警告:
The following encoder weights were not tied to the decoder ['bert/pooler']
同时模型存在异常行为:首次输入输出看似可用(但与微调时结果有差异),再次输入前一次的输出时,模型会返回完全相同的内容(如输入“apple”得“banana”,再输入“banana”仍输出“banana”)。
问题分析与解决
1. bert/pooler未绑定警告的本质
该警告属于正常现象,无需过度担心:
- BERT的
pooler层用于提取句子的整体表征,但在EncoderDecoder架构中,解码器仅依赖encoder的最后一层隐藏状态,并不需要pooler层的输出。 - 当设置
tie_encoder_decoder=True时,框架会尝试绑定编码器和解码器的可共享权重,但pooler层不属于共享范畴,因此触发警告。 - 若要消除警告,可在加载模型时显式设置
tie_encoder_decoder=False,或者直接忽略该提示(不影响模型功能)。
2. 输出重复异常的原因及解决
该异常与pooler警告无关,主要由以下几点导致:
(1)生成配置不合理
- 代码中设置
no_repeat_ngram_size=0,这会允许模型生成完全重复的内容,极易陷入循环输出。建议将其设置为2或3,限制重复n元组的生成:model.config.no_repeat_ngram_size = 2 min_length=10的设置可能强制模型为短输入(如“banana”)凑够长度,导致重复生成。可根据任务需求调整该值,或设置为1取消强制长度限制。
(2)模型保存与加载的一致性问题
- 当前代码中直接调用
model.save_pretrained,但Seq2SeqTrainer训练后会将最优模型保存至output_dir下的checkpoint-xxx子文件夹中。如果未加载训练后的checkpoint就保存,实际保存的是初始未训练模型,这会导致加载后的输出与微调结果不符。 - 正确的保存方式应为训练后调用
trainer.save_model("/folder/path"),或者加载指定checkpoint后再保存:# 训练完成后保存最优模型 trainer.save_model("/folder/path") # 或者加载某一checkpoint后保存 model = EncoderDecoderModel.from_pretrained("./checkpoint-100") model.save_pretrained("/folder/path") - 加载模型时,应直接读取保存的配置,避免手动重复设置参数,确保训练与推理时的配置一致:
model = EncoderDecoderModel.from_pretrained("/folder/path") # 如需调整参数,基于加载后的配置修改 model.config.num_beams = 4
(3)生成时的参数对齐
调用model.generate()时,需确保生成参数(如max_length、num_beams、length_penalty)与训练时predict_with_generate=True所用参数一致,否则会导致输出差异。
修正后示例代码
训练与保存阶段
from transformers import EncoderDecoderModel, Seq2SeqTrainingArguments, Seq2SeqTrainer # 初始化模型 model = EncoderDecoderModel.from_encoder_decoder_pretrained( "bert-base-uncased", "bert-base-uncased", tie_encoder_decoder=True ) # 合理设置生成配置 model.config.max_length = 512 model.config.min_length = 1 model.config.no_repeat_ngram_size = 2 model.config.early_stopping = True model.config.length_penalty = 2.0 model.config.num_beams = 4 # 训练参数 training_args = Seq2SeqTrainingArguments( predict_with_generate=True, fp16=True, output_dir="./", logging_steps=2, save_steps=10, per_device_train_batch_size=8 ) # 初始化训练器并训练 trainer = Seq2SeqTrainer( model=model, tokenizer=tokenizer, args=training_args, train_dataset=train ) trainer.train() # 保存训练后的模型 trainer.save_model("/content/drive/MyDrive/finetuned_bert_seq2seq")
加载与推理阶段
from transformers import EncoderDecoderModel, AutoTokenizer # 加载模型与tokenizer model = EncoderDecoderModel.from_pretrained("/content/drive/MyDrive/finetuned_bert_seq2seq") tokenizer = AutoTokenizer.from_pretrained("/content/drive/MyDrive/finetuned_bert_seq2seq") # 推理示例 input_text = "apple" inputs = tokenizer(input_text, return_tensors="pt", padding=True, truncation=True) outputs = model.generate(**inputs) print(tokenizer.decode(outputs[0], skip_special_tokens=True))
内容的提问来源于stack exchange,提问作者thewaterbuffalo
相关产品推荐
相关产品推荐

