CodeLlama tokenizer strips one leading space on encode→decode round-trip (regression vs v4)

用户在使用 transformers v5 加载所有 CodeLlama 系列模型( codellama/CodeLlama-7b-hf 、 codellama/CodeLlama-7b-Instruct-hf 、 codellama/CodeLlama-7b-Python-hf 、 codella

CodeLlama tokenizer strips one leading space on encode→decode round-trip (regression vs v4)

CodeLlama tokenizer strips one leading space on encode→decode round-trip (regression vs v4)

快速结论:该问题在 transformers v5 迁移后出现,原因是解码器中增加了一个无条件的 decoders.Strip(content=" ", left=1) 步骤,导致所有以空格开头的文本在 encode→decode 往返后丢失一个前导空格。优先排查 tokenizer 的 _decode 方法是否已应用修复补丁,或回退到 transformers v4 系列。

问题场景

用户在使用 transformers v5 加载所有 CodeLlama 系列模型(codellama/CodeLlama-7b-hfcodellama/CodeLlama-7b-Instruct-hfcodellama/CodeLlama-7b-Python-hfcodellama/CodeLlama-13b-hfcodellama/CodeLlama-34b-hfcodellama/CodeLlama-70b-hf)时,执行 encodedecode 往返操作,发现前导空格(leading space)被无声地剥离一个。该问题在 transformers v4 中不存在。

报错原文

BUG: '   leading spaces' -> '  leading spaces'
BUG: '    indented_line' -> '   indented_line'

原因分析

该问题的根本原因是 tokenization_code_llama.py 中解码器序列(第163-164行)包含了一个无条件的 decoders.Strip(content=" ", left=1) 步骤:

self._tokenizer.decoder = decoders.Sequence(
    [decoders.Replace("▁", " "), decoders.ByteFallback(), decoders.Fuse(), decoders.Strip(content=" ", left=1)]
)

这个 Strip 步骤原本是为了移除 SentencePiece 规范化器引入的合成 前缀(类似于 Prepend ▁Replace " " → ▁ 的流程)。但是,在解码管道中,合成前缀和用户提供的前导空格在 " " 替换后变得无法区分,导致每次解码时无条件剥离一个前导空格。在 v4 的 CodeLlamaTokenizerFast 中,这种区分是存在的,但 v5 迁移移除了 Fast 类,合并了新的解码逻辑。

进一步分析发现:对于恰好一个前导空格的情况(如 " hello"),即使没有 Strip 也无法正确还原,因为 "hello"" hello" 编码后会得到完全相同的 token ID 列表(均为 [22172, 3186]),这是算法固有的歧义。实际需要修复的是多前导空格场景(如两个以上空格),此时会产生纯 token。

环境排查

  • transformers 版本:v5.x(问题在 v5.10.2 上复现);v4.x 无此问题
  • tokenizers 版本:0.22.2
  • Python 版本:3.11.2(其他版本可能也受影响)
  • 模型路径:所有 CodeLlama 系列 checkpoints(CodeLlama-7b-hfCodeLlama-13b-hfCodeLlama-34b-hfCodeLlama-70b-hf 等均受影响)
  • PyTorch:不影响(仅 tokenizer 操作)

解决步骤

  1. 确认问题表现:使用以下代码验证本地环境是否受影响:
  2. from transformers import AutoTokenizer
    tok = AutoTokenizer.from_pretrained("codellama/CodeLlama-7b-hf")
    for s in ["   leading spaces", "    indented_line"]:
        decoded = tok.decode(tok.encode(s, add_special_tokens=False))
        print(f"{'OK' if decoded == s else 'BUG'}: {s!r} -> {decoded!r}")
  3. 方案一(推荐,可优先尝试):升级 transformers 到包含修复版本(打开 Issue #46491 的修复 PR #46574)。修复方式为:
    – 移除 decoders.Strip(content=" ", left=1)
    – 在 CodeLlamaTokenizer 类中复写 _decode 方法,实现条件性 strip:仅当首个真实 token(非特殊 token)的文本内容完全由 字符组成时,才移除前导空格
  4. 方案二:如无法升级,临时回退到 transformers v4 系列(如 v4.55.4)。注意回退可能需要调整其他依赖。
  5. 方案三(自行修补):手动编辑本地 tokenization_code_llama.py 文件,移除 Strip 并添加条件判断。具体实现可参考 PR #46574 中的改动。

验证方法

修复后运行上述验证脚本,应输出:

OK: '   leading spaces' -> '   leading spaces'
OK: '    indented_line' -> '    indented_line'

同时验证单前导空格场景的合理行为:decode(encode(" hello")) 返回 "hello"(这是算法歧义下的合理选择,与 v4 行为一致)。

参考来源

huggingface/transformers #46491

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 13839

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注