相同目标Token下困惑度计算结果为何不一致?
固定长度模型困惑度计算异常排查
参考Huggingface固定长度模型困惑度(Perplexity)计算文档时,发现异常现象:当使用不同上下文但后续目标Token相同时,按文档说明将上下文对应的target设为-100以忽略其对数似然损失,理论上困惑度应一致,但实际结果不同。测试T5、GPT2、sshleifer/tiny-gpt2等模型均存在该问题,将-100替换为T5的pad token 0后,困惑度结果仍不一致,需排查遗漏的关键点。
实验代码
from transformers import T5Tokenizer, T5ForConditionalGeneration import torch tokenizer = T5Tokenizer.from_pretrained("t5-small") model = T5ForConditionalGeneration.from_pretrained("t5-small") context_1 = 'here is some context_1 and some more stuff' context_2 = 'here is some context and some more stuff and more stuff aspodkaspd' answer_1 = 'this is not the answer' input_ids_wrong = tokenizer(context_1 + answer_1, return_tensors="pt").input_ids input_ids_correct = tokenizer(context_2 + answer_1, return_tensors="pt").input_ids context_1_tokens_length = len(tokenizer(context_1, return_tensors="pt").input_ids[0]) context_2_tokens_length = len(tokenizer(context_2, return_tensors="pt").input_ids[0]) target_ids_wrong = input_ids_wrong.clone() target_ids_correct = input_ids_correct.clone() target_ids_wrong[:, :context_1_tokens_length] = -100 target_ids_correct[:, :context_2_tokens_length] = -100 print('target_ids_wrong', target_ids_wrong) print('target_ids_correct', target_ids_correct) with torch.no_grad(): outputs_wrong = model(input_ids_wrong, labels=target_ids_wrong) outputs_correct = model(input_ids_correct, labels=target_ids_correct) neg_log_likelihood_wrong = outputs_wrong.loss neg_log_likelihood_correct = outputs_correct.loss ppl_wrong = torch.exp(neg_log_likelihood_wrong) ppl_correct = torch.exp(neg_log_likelihood_correct) print('ppl_wrong', ppl_wrong) print('ppl_correct', ppl_correct)
输出结果
target_ids_wrong tensor([[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, 19, 59, 8, 1525, 1]]) target_ids_correct tensor([[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, 19, 59, 8, 1525, 1]]) ppl_wrong tensor(9.0377) ppl_correct tensor(21.1208)
问题根源
你混淆了损失计算的忽略规则和模型的输入依赖逻辑:
- 将labels中上下文位置设为-100,仅作用于损失函数:告诉它跳过这些位置的损失计算,但模型的输入仍然是完整的「上下文+目标Token」序列。
- 自回归/Seq2Seq模型预测目标Token时,是基于前面所有输入Token(包括不同的上下文)进行条件预测的。不同上下文会让模型的隐层状态完全不同,导致对目标Token的预测概率分布产生差异,最终损失和困惑度自然不同。
验证与修正
如果要让两次计算的困惑度一致,需要保证模型预测目标Token时的输入上下文完全相同。例如:
- 仅将目标Token作为模型输入(无上下文)
- 让两个实验的输入上下文完全一致,仅保留目标Token部分相同
补充说明
- 无论使用-100还是pad token 0,核心都是让损失函数忽略对应位置,但无法改变模型对输入序列的依赖关系。
- 困惑度本质是模型对目标Token序列的平均负对数似然的指数,而这个似然是基于前面所有输入Token的条件概率,因此上下文不同,结果必然不同。
内容的提问来源于stack exchange,提问作者Penguin
相关产品推荐
相关产品推荐

