Wav2Vec2PhonemeCTCTokenizer.phonemize keeps the previous language’s espeak backend after switching back (stale phonemizer_lang)

这是 Wav2Vec2PhonemeCTCTokenizer.phonemize() 的语言切换缓存 bug——切换过 phonemizer_lang 之后,再切回原语言会静默返回其他语言的音素结果。优先检查代码里 phonemize() 分支是否漏掉了 self.phonemizer_lang =

快速结论:这是 Wav2Vec2PhonemeCTCTokenizer.phonemize() 的语言切换缓存 bug——切换过 phonemizer_lang 之后,再切回原语言会静默返回其他语言的音素结果。优先检查代码里 phonemize() 分支是否漏掉了 self.phonemizer_lang = phonemizer_lang 赋值。

适用环境:transformers 4.57.6、phonemizer 3.3.0、espeak-ng 通过 espeakng-loader 0.2.4、Python 3.10、Linux。

最快修复方案:phonemize() 的 re-init 分支内,于 self.init_backend(phonemizer_lang) 之前或之后补上 self.phonemizer_lang = phonemizer_lang(这与 prepare_for_tokenization 中 L226-228 的正确写法一致)。该修复已由 PR #46616 / #47412 验证。

注意事项:该修复只针对 phonemize() 方法;若你的代码也走 prepare_for_tokenization 路径,需确认该路径本身没有类似问题(Issue 中确认该路径已正确更新字段)。如果暂时不想改源码,可使用 Issue 中给出的 workaround。

问题场景

用户通过 AutoTokenizer.from_pretrained("facebook/wav2vec2-xlsr-53-espeak-cv-ft") 创建 tokenizer 后,在同一个 tokenizer 实例上交替调用 phonemize() 并传入不同的 phonemizer_lang(例如先 en-us,再 es,再切回 en-us)。第二次切回原语言时,返回的音素仍是之前其他语言的 espeak 后端结果,且没有任何报错。

报错原文

Wav2Vec2PhonemeCTCTokenizer.phonemize keeps the previous language's espeak backend after switching back (stale phonemizer_lang)
# 复现时断言失败:
print(a)  # ð ə | k w ɪ k | b ɹ aʊ n | f ɑː k s
print(b)  # t e | k i k | β ɾ o w n | f o k s   <-- Spanish phonemization
assert a == b  # fails

原因分析

tokenization_wav2vec2_phoneme.pyphonemize() 方法中,re-init 分支的判断条件是 if phonemizer_lang is not None and phonemizer_lang != self.phonemizer_lang:。该分支内调用了 self.init_backend(phonemizer_lang)没有更新 self.phonemizer_lang。因此下一次调用传入原始语言(例如 “en-us”)时,比较结果 "en-us" != self.phonemizer_lang 为 False,init_backend 被跳过,缓存的西班牙语后端继续对英语文本做音素化,造成静默错误。

可能原因补充:Issue 作者确认 prepare_for_tokenization 方法(L226-228)已经正确处理了字段更新,只有 phonemize() 遗漏了这行赋值。任何在单实例上交替多语言的流水线(例如多语言强制对齐)都可能触发此问题。

环境排查

  • 确认 transformers 版本为 4.57.6(或受影响的 4.x 版本)。
  • 确认 phonemizer 版本为 3.3.0。
  • 确认 espeak-ng 通过 espeakng-loader 0.2.4 安装。
  • 确认 Python 版本为 3.10,操作系统为 Linux。
  • 检查自己是否在同一个 tokenizer 实例上交替传入不同 phonemizer_lang

解决步骤

  1. 定位本地 transformers 安装中的 tokenization_wav2vec2_phoneme.py 文件。
  2. phonemize() 方法中找到 re-init 分支,即 if phonemizer_lang is not None and phonemizer_lang != self.phonemizer_lang: 一行。
  3. 在该分支内补上字段同步:在 self.init_backend(phonemizer_lang) 之后(或之前)添加 self.phonemizer_lang = phonemizer_lang
  4. 保存文件并重启 Python 进程(或重新导入模块让改动生效)。
  5. 运行原始复现脚本验证 a == b 断言是否通过。

若不想修改源码,可优先尝试 Issue 中提供的 workaround:

  1. 每次调用前手动同步 tokenizer 的语言状态:tok.phonemizer_lang = lang 然后 tok.init_backend(lang),再调用 tok.phonemize(text, phonemizer_lang=lang)
  2. 或者不依赖 tokenizer 内部缓存,为每种语言单独持有 phonemizer.backend.EspeakBackend 实例。

验证方法

运行 Issue 中的复现脚本:如果修复正确,第二次 tok.phonemize("the quick brown fox", phonemizer_lang="en-us") 的输出应与第一次完全一致(即 a == b 断言通过),且输出为英语音素而不是西班牙语音素。另外可以尝试在交替切换 3 种语言后反复切回第一语言,确认每次输出都一致。

参考来源

huggingface/transformers #46614

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 19664

发表回复

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