DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

自定义 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 对象都能自动保存。