ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【Bug已解决】XLMRobertaTokenizer.__init__ passes dict to Unigram(vocab=...) expecting a Sequence 解决方案

2026/8/5 2:20:43 拓冰建站 浏览量
【Bug已解决】XLMRobertaTokenizer.__init__ passes dict to Unigram(vocab=...) expecting a Sequence 解决方案 【Bug已解决】XLMRobertaTokenizer.initpasses dict to Unigram(vocab...) expecting a Sequence 解决方案一、现象长什么样初始化XLMRobertaTokenizer或其相关 tokenizer时报TypeError: Unigram.__init__() got a dict for vocab..., expected a Sequence (list)或AssertionError: vocab must be a list of strings, got dict最迷惑的是你只是正常AutoTokenizer.from_pretrained(xlm-roberta-base)就想加载却炸在 tokenizer 的__init__里——说明问题不在你的代码而在XLMRobertaTokenizer.__init__把参数传给底层的UnigramSentencePiece 的 unigram 实现时类型传错了。本质XLMRobertaTokenizer底层用 SentencePiece 的Unigram它的vocab参数期望一个Sequence即 list/tuple of token 字符串。但XLMRobertaTokenizer.__init__在构造时把vocab当成了一个dict比如{token: id}形式的映射或把sp_model之外额外传的 vocab 字典传给了Unigram(vocab...)。Unigram 只接受 list于是TypeError。二、背景SentencePiece 的Unigram模型在transformers里的接口大致是Unigram(vocablist_of_tokens, ...)vocab应该是一个token 字符串的列表有序索引即 id。这是Sequence语义。而XLMRobertaTokenizer的__init__在某些情况下尤其是自定义构造、或从一个vocabdict 初始化而非从spm模型文件加载会写出# 错误写法 Unigram(vocabself.vocab, ...) # self.vocab 是 dict {token: id}这里self.vocab是{token: id}字典从vocab.json或某些转换路径得到但Unigram要的是 list。于是类型错配。常见触发场景你手动XLMRobertaTokenizer(vocabsome_dict)构造而非from_pretrained转换脚本把vocab.jsondict直接喂给 tokenizer 构造某些版本里__init__的 vocab 处理逻辑把 list 和 dict 搞混。下面用可运行代码复现Unigram 收到 dict 而非 list 报错。三、根因根因一句话XLMRobertaTokenizer.__init__把vocab以 dict{token: id}形式传给了底层Unigram(vocab...)而Unigram的vocab参数期望Sequencelist of token 字符串类型错配导致 TypeError。三个具体失配vocab 类型错dict 传给要 list 的Unigram。dict→list 缺失__init__没把{token:id}转成有序 token 列表。构造路径混淆from_pretrained走 spm 文件正常手动vocabdict构造才触发。四、最小可运行复现用纯 Python 模拟Unigram 期望 list收到 dict 报错from dataclasses import dataclass from typing import Sequence, Dict, List, Union class Unigram: def __init__(self, vocab: Sequence[str]): if not isinstance(vocab, (list, tuple)): raise TypeError( fUnigram vocab 期望 Sequence(list)收到 {type(vocab).__name__} ) self.vocab list(vocab) def xlm_roberta_init(vocab: Union[Dict, List]): 模拟 XLMRobertaTokenizer.__init__直接把 vocab 传给 Unigram。 return Unigram(vocabvocab) def main(): vocab_dict {s: 0, pad: 1, a: 2} # dict 形式 try: xlm_roberta_init(vocab_dict) except TypeError as e: print(复现到报错:, e) # 修复dict - 按 id 排序的 token 列表 vocab_list [t for t, _ in sorted(vocab_dict.items(), keylambda kv: kv[1])] u xlm_roberta_init(vocab_list) print(修复后 vocab 列表:, u.vocab) if __name__ __main__: main()运行会先打印复现到报错: Unigram vocab 期望 Sequence(list)收到 dict再打印修复后的列表——正是 dict 误传的本质。五、解决方案第一层最小直接修复最立竿见影的修复在传给Unigram(vocab...)之前把 dict 形式的 vocab 转换成按 id 排序的 token 字符串列表dict 的 key 是 token、value 是 id转 list 时必须按 id 排序以保证索引id。**from typing import Dict, List, Union def vocab_dict_to_list(vocab: Union[Dict[str, int], List[str]]) - List[str]: 修复dict {token: id} - 按 id 升序的 token 列表。 if isinstance(vocab, dict): return [t for t, _ in sorted(vocab.items(), keylambda kv: kv[1])] return list(vocab) def build_unigram(vocab): from dataclasses import dataclass dataclass class Unigram: vocab: list return Unigram(vocabvocab_dict_to_list(vocab)) def main(): vocab {s: 0, a: 2, pad: 1} u build_unigram(vocab) print(正确 vocab 列表(索引id):, u.vocab) # [s, pad, a] if __name__ __main__: main()第一层修复让Unigram收到的永远是正确的 listTypeError 消失且索引与 id 对齐。六、解决方案第二层结构性改进把vocab 归一化为 Unigram 期望的 list收口成一个VocabNormalizer在XLMRobertaTokenizer.__init__构造Unigram前统一处理dict→按 id 排序 listlist→原样非法类型报错。from dataclasses import dataclass from typing import Dict, List, Union dataclass class VocabNormalizer: def to_unigram_vocab(self, vocab: Union[Dict[str, int], List[str]]) - List[str]: if isinstance(vocab, dict): # 按 id 升序保证 list 索引 token id return [t for t, _ in sorted(vocab.items(), keylambda kv: kv[1])] if isinstance(vocab, (list, tuple)): return list(vocab) raise TypeError(fvocab 必须是 dict 或 list收到 {type(vocab)}) def main(): n VocabNormalizer() for raw in ({s: 0, a: 2}, [s, a]): print(归一化:, n.to_unigram_vocab(raw)) if __name__ __main__: main()第二层的关键是VocabNormalizer把vocab 转 list固化并严格校验类型后续任何Unigram(vocab...)调用前都过它杜绝 dict 误传。七、解决方案第三层断言 / CI 守护加 pytest 守护(1) dict vocab 被转成按 id 排序的 list(2) list vocab 原样保留(3) 非法类型必须被拒(4) 转换后list[index]对应原 dict 的 id。import pytest def to_list(vocab): if isinstance(vocab, dict): return [t for t, _ in sorted(vocab.items(), keylambda kv: kv[1])] if isinstance(vocab, (list, tuple)): return list(vocab) raise TypeError(bad type) def test_dict_sorted_by_id(): out to_list({s: 0, a: 2, pad: 1}) assert out [s, pad, a] def test_list_passthrough(): assert to_list([x, y]) [x, y] def test_invalid_rejected(): with pytest.raises(TypeError): to_list(123) def test_index_matches_id(): d {s: 0, a: 2, pad: 1} out to_list(d) assert out[0] s and out[2] a if __name__ __main__: pytest.main([__file__, -q])CI 里test_dict_sorted_by_idtest_index_matches_id通过就能保证 vocab dict 转 list 后索引与 id 对齐杜绝Unigram收到 dict 的回归。八、排查清单XLMRobertaTokenizer.__init__报 vocab 类型错误时按此顺序查确认报错在 tokenizer 构造stack 指向Unigram(vocab...)收到 dict。检查你如何构造是from_pretrained通常走 spm 文件正常还是手动vocabdict构造触发。第一层修复手动构造时把{token:id}dict 转成按 id 排序的 token 列表再传。确认索引id转换后list[i]必须对应原 dict 里 idi 的 token否则词表全错。用 VocabNormalizer 兜底构造Unigram前统一归一化。优先 from_pretrained若可用直接加载 spm 模型文件绕开 vocab dict 构造路径。升级 transformers部分版本已修正该__init__的 vocab 处理。九、小结XLMRobertaTokenizer.__init__把 dict 传给Unigram(vocab...)期望 Sequence 而报错根因不在 SentencePiece 坏而在**XLMRobertaTokenizer底层用Unigram其vocab要 list of token 字符串但__init__在手动构造时把{token: id}字典直接传了进去类型错配导致 TypeError**。正常from_pretrained走 spm 文件不受影响手动vocabdict构造才触发。修复三层第一层构造Unigram前把 dict 转成按 id 排序的 token 列表保证 list 索引token id第二层用VocabNormalizer把dict→list、list→原样、非法报错固化第三层用 pytest 断言dict 按 id 排序转 list、索引与 id 对齐。记住Unigram的 vocab 是 list 不是 dict手动构造时把{token:id}按 id 排成列表索引才对得上。