Transformers 的 generate 结果为什么包含输入提示词
decoder-only 的 generate 返回输入加新增 token。按实际 input_ids 张量宽度裁剪,区分左补齐批次、结构化返回、pipeline 与 encoder-decoder。
把 model.generate() 的返回值整段解码后,结果从输入提示词开始,后面才接模型生成的回答。对使用 input_ids 的常规 decoder-only 文本生成,这通常是返回序列的正常结构,不足以证明模型在重复回答。
下面按 Transformers 4.57.1 的 PyTorch 接口说明。先确认 model.config.is_encoder_decoder:decoder-only 与 T5 这类 encoder-decoder 模型的返回结构不同,不能共用同一段裁剪逻辑。
在解码之前裁掉输入 token
假定 model 和 tokenizer 已匹配加载,下面的示例适用于普通文本、单条输入和 decoder-only 模型:
if model.config.is_encoder_decoder:
raise ValueError("此示例仅适用于 decoder-only 模型")
inputs = tokenizer("Explain what a tokenizer does.", return_tensors="pt")
inputs = inputs.to(model.device)
input_width = inputs["input_ids"].shape[1]
outputs = model.generate(**inputs, max_new_tokens=64)
new_ids = outputs[:, input_width:]
answers = tokenizer.batch_decode(new_ids, skip_special_tokens=True)
print(answers[0])
input_width 是 token 数,取自实际提交给模型的张量。它不是 Python 字符串长度,也不是 UTF-8 字节数。先解码再用 text[len(prompt):] 截字符串,可能受到特殊标记跳过、空格清理和分词还原的影响。先切 token 再解码,可以避开这些差异。
如果开启了结构化返回,要先取 sequences,再按同样的宽度切:
result = model.generate(
**inputs,
max_new_tokens=64,
return_dict_in_generate=True,
)
answers = tokenizer.batch_decode(
result.sequences[:, input_width:],
skip_special_tokens=True,
)
max_new_tokens 限制新生成的 token 数,不包括这里裁掉的输入。它不会改变返回序列保留输入的规则。
批量左补齐后,边界是张量宽度
普通 decoder-only 模型批量生成时,一般使用左侧补齐,并把 attention_mask 一起传入。先确认 tokenizer 已有适合模型的 PAD;缺失时参考 没有 padding token 的处理分支。
tokenizer.padding_side = "left"
inputs = tokenizer(
["Say hello.", "Explain tokenization in one sentence."],
padding=True,
return_tensors="pt",
).to(model.device)
input_width = inputs["input_ids"].shape[1]
outputs = model.generate(**inputs, max_new_tokens=48)
answers = tokenizer.batch_decode(
outputs[:, input_width:],
skip_special_tokens=True,
)
这时每行的原始文本长度可以不同,但提交的 input_ids 已补齐为相同宽度。新增 token 从这个统一宽度之后开始。不要逐行用 attention_mask.sum() 当裁剪起点:它只统计非补齐 token,左侧 PAD 仍占据返回序列的位置,按这个较小的数字切会留下部分输入。
多条候选输出也保留各自的输入前缀。启用 num_return_sequences 后,还需按该参数对应的输出顺序整理回答与输入之间的映射;不要默认一行输入始终只返回一行结果。
pipeline 有自己的返回选项
如果使用的是 pipeline("text-generation"),字符串输入可以在调用时要求只返回新增文本:
# generator 是已创建好的 text-generation pipeline。
result = generator(
"Explain what a tokenizer does.",
max_new_tokens=64,
return_full_text=False,
)
print(result[0]["generated_text"])
return_full_text 是这个 pipeline 的后处理选项,不是直接传给任意模型 generate() 的通用参数。对话消息输入的返回值可能是消息列表,也不能照字符串结果机械拼接或截取。
如果裁掉输入之后,新增部分仍连续重复同一句话,才需要继续排查模型选择、对话模板和生成参数。encoder-decoder 输出、仅传 inputs_embeds 的调用、多模态模型和流式输出都需要各自核对边界,不能直接套用本文的 input_ids.shape[1]。


