DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Transformers 批量编码报 tokenizer does not have a padding token

批量编码时缺少 PAD 定义,区分完整 tokenizer 配置、推理中复用 EOS 和新增词表标记;核对 attention mask、模型 embedding 与保存目录。

单条文本可以编码,换成 tokenizer(texts, padding=True, return_tensors="pt") 后,却在模型运行前报错:

ValueError: Asking to pad but the tokenizer does not have a padding token.

这里缺的是 tokenizer 使用的补齐标记。padding=True 只要求补齐序列,不会替模型决定哪个 token 可以代表空位。下面按 Transformers 4.57.1 的接口说明,适用于文本 tokenizer;多模态 processor 的输入规则需要另行确认。

先确认加载了哪个 tokenizer

在产生错误的同一个进程里检查:

print(tokenizer.name_or_path)
print(tokenizer.pad_token, tokenizer.pad_token_id)
print(tokenizer.eos_token, tokenizer.eos_token_id)
print(tokenizer.special_tokens_map)

如果 pad_token 是 None,先核对 tokenizer 是否来自模型对应的仓库或本地目录。模型和 tokenizer 混用,即使后来补了 PAD,也可能得到错误的 token ID。若预训练 tokenizer 本来有 PAD,优先恢复完整配置,别马上向词表添加新标记。

自定义 fast tokenizer 还要区分词表里“有 [PAD] 这个字符串”和“把它注册为 pad_token”。只有前者并不足够。确认 [PAD] 确实是训练时预留的补齐标记后,可以显式指定:

from transformers import PreTrainedTokenizerFast

# tokenizer.json 是自己训练并保存的 tokenizer 文件。
tokenizer = PreTrainedTokenizerFast(
    tokenizer_file="tokenizer.json",
    pad_token="[PAD]",
    unk_token="[UNK]",
)

这个例子要求词表与对应模型匹配,而且 [PAD]、[UNK] 的含义已经确定。旧版 fast tokenizer 曾出现底层 padding 配置没有完整传入包装对象的情况;4.57.1 已有读取底层 padding 设置的逻辑,不能把每次缺失 PAD 都归因于那个旧问题。

推理时复用 EOS,有条件

部分 decoder-only 模型没有独立 PAD。若所用模型的推理约定允许把 EOS 用作补齐,可以按下面的方式处理。这里假定 model 和 tokenizer 已经从同一模型加载,示例只展示修改与调用部分:

if tokenizer.pad_token_id is None:
    if tokenizer.eos_token_id is None:
        raise ValueError("该 tokenizer 没有 EOS,不能用 EOS 作为 PAD")
    tokenizer.pad_token = tokenizer.eos_token

tokenizer.padding_side = "left"
inputs = tokenizer(
    ["Write a short greeting.", "Explain what a tokenizer does."],
    padding=True,
    return_tensors="pt",
).to(model.device)

outputs = model.generate(
    **inputs,
    pad_token_id=tokenizer.pad_token_id,
    max_new_tokens=32,
)

左侧补齐用于这类 decoder-only 模型的批量生成。把 tokenizer 返回的 attention_mask 一起传进去,尤其在 PAD 与 EOS 相同时,不要指望模型单靠 ID 猜出哪些位置是补齐、哪些是正文中的 EOS。设置 pad_token_id 本身也不能修复 tokenizer 的缺失定义:批量编码发生在 generate() 之前。

复用 EOS 的做法不能直接照搬到所有训练任务。某些语言模型 collator 按 PAD 的 ID 将标签设为 -100;PAD 与 EOS 共用 ID 时,真实 EOS 标签也可能被忽略。需要保留 EOS 的训练任务应检查自己的标签和损失掩码。

添加独立 PAD 会改变词表

如果任务确实需要新 PAD,可以在受控的模型准备流程中添加,并同步模型的 embedding 大小:

# 前提:模型支持调整 token embeddings,且需要独立的 PAD。
added = tokenizer.add_special_tokens({"pad_token": "[PAD]"})
if added:
    model.resize_token_embeddings(len(tokenizer))

model.config.pad_token_id = tokenizer.pad_token_id
if model.generation_config is not None:
    model.generation_config.pad_token_id = tokenizer.pad_token_id

新增 token 后只保存 tokenizer、不保存对应模型,会让下次加载的两者不一致。保存到新的目录,保留原 checkpoint;量化模型、适配器或受约束的部署格式,也要先确认支持词表调整。新 embedding 不等于已经完成训练,不能据此保证输出质量。

处理后,用两条 token 长度不同的文本核对编码结果:input_ids 与 attention_mask 应有相同形状,补齐位置的 mask 应为 0。如果 PAD 已正确设置,却在 Trainer 组批时仍报长度不同,继续检查 collator 是否同时处理了输入和标签。