自定义 Processor 保存两套 tokenizer 后无法还原,按属性隔离配置
自定义 Processor 的多 tokenizer 保存需要独立属性布局与配置过滤,应采用相容版本并分别验证两套组件的本地往返。
自定义 ProcessorMixin 包含两套 tokenizer,内存里分别可用,save_pretrained() 后再加载却出现配置覆盖、组件类型不对或无法序列化。若两套组件都保存到同一目录的同名文件,最后写入的配置可能遮住前一套;只检查目录中存在 tokenizer_config.json 看不出这种问题。
Transformers 4.57.1 的具体案例使用 tokenizer1 与 tokenizer2。旧保存逻辑对多组件支持有限,后续处理器重构中的统一配置过滤还曾只识别少数硬编码名字。自定义命名的 tokenizer 留在普通处理器配置中,也可能使保存和还原失败。
使用包含通用处理的版本与属性布局
Transformers 5.0.0 的正式代码已按 tokenizer 属性分别保存:主属性 tokenizer 使用根目录,额外属性使用各自的子目录;统一配置也会过滤含 tokenizer 的组件属性。加载时按相同属性名寻找对应目录。
迁移到这一版本需要同时核对项目的其他 v5 兼容要求,不应把整套训练环境无条件升级。针对自定义两 tokenizer 处理器,可以采用清晰的主、次属性名:
from transformers import ProcessorMixin, PreTrainedTokenizerBase
class TwoTokenizers(ProcessorMixin):
def __init__(
self,
tokenizer: PreTrainedTokenizerBase,
decoder_tokenizer: PreTrainedTokenizerBase,
):
super().__init__(
tokenizer=tokenizer,
decoder_tokenizer=decoder_tokenizer,
)
这里的两套 tokenizer 应由项目已有的正确配置构造,不需要为排查下载新模型。属性名称属于保存布局与加载约定的一部分;任意改名为 encoder、decoder,不能假定框架仍会识别其模态。
验收要比较两套组件的往返行为
在本地临时目录保存并通过同一个自定义类加载,避免混入远端缓存和另一份旧配置:
import tempfile
from pathlib import Path
# primary、secondary 是项目原来分别使用的 tokenizer 实例
processor = TwoTokenizers(primary, secondary)
texts = ["tokenizer round-trip", "中文测试"]
with tempfile.TemporaryDirectory() as directory:
processor.save_pretrained(directory)
assert (Path(directory) / "decoder_tokenizer").is_dir()
restored = TwoTokenizers.from_pretrained(directory, local_files_only=True)
for name in ("tokenizer", "decoder_tokenizer"):
before = getattr(processor, name)
after = getattr(restored, name)
for text in texts:
assert before.encode(text) == after.encode(text)
assert before.get_vocab() == after.get_vocab()
assert before.special_tokens_map == after.special_tokens_map
比较应分别绑定同名组件。若两套词表本来不同,增加一条能区分它们的输入;把两套结果都与主 tokenizer 比较,会漏掉次组件被替换的问题。目录结构存在只是入口检查,词表、特殊 token 与编码行为一致才是还原验收。
旧文件若已经被覆盖,升级不能凭空恢复丢失的那套配置。应从仍完整的两个原组件重新导出到新目录,保留旧产物以便追踪,而不是手工将同一份配置复制成两个子目录。
这个方案限定为相容、可由加载入口识别的 tokenizer 与同一自定义类。自定义 tokenizer 类、远端自定义代码、旧布局迁移,以及多个 image processor 等其他组件还需分别核对,不能从两套 tokenizer 的处理推导为任意 Python 对象都能自动保存。


