Transformers 中的 BertJapanese:基于 MeCab/Sudachi 等分词的日语 BERT 分词器全解析
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
本文导读:日语文本没有空格分词,无法直接套用标准 BERT 的 WordPiece 流程。本篇技术指南以官方文档 docs/source/en/model_doc/bert-japanese.md 为主体,系统讲解 Transformers 仓库中
BertJapaneseTokenizer的分层分词架构、四种词级分词器与三种子词级分词器的选型与参数,并给出可一键复现的推理代码、底层源码证据与测试用例验证。读完你既能用一行代码跑通cl-tohoku/bert-base-japanese系列模型,也能按业务需求定制 MeCab/Sudachi/JumanPP 等日文形态素解析方案。
一、BertJapanese 是什么
在日语 BERT 场景中,模型结构本身与标准 BERT完全一致,唯一的关键差异在于分词方式。日语通常不空格分词,因此必须借助形态素解析器(Morphological Analyzer)先将句子切分为“单词”,再喂给子词切分器。文档与源码都反复强调这一点:"This implementation is the same as BERT, except for tokenization method"(模型 API 参考直接沿用 BERT,见 BERT 文档)。
该功能由贡献者 cl-tohoku(东北大学乾研究室发布的cl-tohoku/bert-japanese系列模型作者)于 2020-11-16 引入本仓库。仓库内实现位于两个文件:
- 分词器实现:src/transformers/models/bert_japanese/tokenization_bert_japanese.py
- 单元测试:tests/models/bert_japanese/test_tokenization_bert_japanese.py
文档最初描述了两类分词模型,但从当前源码(以及 测试文件 的用例矩阵)看,可用的组合已扩展到远不止两种:
| 层级 | 可选实现 | 说明 |
|---|---|---|
词级分词(word_tokenizer_type) | basic/mecab/sudachi/jumanpp | 把日文切分为“单词” |
子词级分词(subword_tokenizer_type) | wordpiece/character/sentencepiece | 把单词进一步切分为可入词表的子词 |
下文将逐一说明每种组合如何配置、背后的分词器类如何工作。
二、安装与依赖
文档明确说明:使用 MeCab 词级分词需要额外安装形态素解析相关依赖:
# 从 PyPI 安装时 pip install transformers["ja"] # 从源码安装(clone 仓库后)时 pip install -e .["ja"]这里"ja"extra 的具体内容定义在 setup.py 的extras["ja"]中(第 208-210 行):
extras["ja"] = deps_list("fugashi", "ipadic", "unidic_lite", "unidic", "rhoknp") if PYTHON_MINOR_VERSION < 14: extras["ja"] += deps_list("sudachipy", "sudachidict_core")其中各包对应关系(依赖版本声明见 src/transformers/dependency_versions_table.py):
fugashi(>=1.0):MeCab 的 Python 封装,MecabTokenizer的必需依赖;ipadic(>=1.0.0,<2.0):IPA 词典;unidic_lite(>=1.0.7)与unidic(>=1.0.2):UNIDIC 词典(轻量版 / 完整版,完整版需额外执行python -m unidic download下载,体积较大);rhoknp:Juman++ 的 Python 封装,JumanppTokenizer的必需依赖;sudachipy(>=0.6.6)与sudachidict_core:SudachiPy 分词器及其核心词典,SudachiTokenizer的必需依赖(注意当前 setup.py 将其限定在 Python < 3.14 的环境下安装)。
如果只使用 Character 字符级或 Basic 词级切分(无形态素解析),则无需这些额外依赖。若遗漏安装,源码会抛出明确的提示:例如MecabTokenizer.__init__中捕获ModuleNotFoundError并提示 "You need to install fugashi to use MecabTokenizer"(见 tokenization_bert_japanese.py)。
三、快速上手:两种经典用法
3.1 MeCab + WordPiece(cl-tohoku/bert-base-japanese)
MeCab 与 WordPiece 的组合是cl-tohoku/bert-base-japanese的默认配置。文档给出了如下可直接运行示例(注意原文档中的变量笔误,model.device已修正为bertjapanese.device):
import torch from transformers import AutoModel, AutoTokenizer bertjapanese = AutoModel.from_pretrained("cl-tohoku/bert-base-japanese", device_map="auto") tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese") ## 输入日语文本 line = "吾輩は猫である。" inputs = tokenizer(line, return_tensors="pt").to(bertjapanese.device) print(tokenizer.decode(inputs["input_ids"][0])) # [CLS] 吾輩 は 猫 で ある 。 [SEP] outputs = bertjapanese(**inputs)可以看到,句子先被 MeCab 按词法切分为吾輩 / は / 猫 / で / ある / 。六个“单词”,词与词之间有明确的空格边界。由于吾輩、猫等词整体存在于 BERT 词表中,因此此处 WordPiece 未发生二次切分。
3.2 Character 字符级切分(cl-tohoku/bert-base-japanese-char)
若使用字符级切分模型,则完全不依赖形态素解析器,输出效果是逐字展开:
bertjapanese = AutoModel.from_pretrained("cl-tohoku/bert-base-japanese-char", device_map="auto") tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese-char") ## 输入日语文本 line = "吾輩は猫である。" inputs = tokenizer(line, return_tensors="pt").to(bertjapanese.device) print(tokenizer.decode(inputs["input_ids"][0])) # [CLS] 吾 輩 は 猫 で あ る 。 [SEP] outputs = bertjapanese(**inputs)两种模型在仓库中的注册名均为BertJapaneseTokenizer。在 src/transformers/models/auto/tokenization_auto.py 中通过("bert-japanese", "BertJapaneseTokenizer")映射,因此AutoTokenizer.from_pretrained会根据 checkpoint 的tokenizer_class自动路由到正确实现——这一点由 test_tokenizer_bert_japanese 直接验证(assertIsInstance(tokenizer, BertJapaneseTokenizer))。
四、BertJapaneseTokenizer 构造参数全解
BertJapaneseTokenizer继承自 [PreTrainedTokenizer],构造签名与参数在源码(tokenization_bert_japanese.py)中可查:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
vocab_file | str | 必填 | 每行一个 WordPiece 词条的词表文件(vocab.txt) |
spm_file | str | None | SentencePiece 模型文件(.spm/.model),仅当subword_tokenizer_type="sentencepiece"时必填并校验存在性 |
do_lower_case | bool | False | 是否在 basic/mecab/sudachi 阶段转小写 |
do_word_tokenize | bool | True | 是否执行词级切分;置False则整句作为单个输入 |
do_subword_tokenize | bool | True | 是否执行子词切分;置False则词级结果直接作为 token |
word_tokenizer_type | str | "basic" | 词级分词器,取值["basic", "mecab", "sudachi", "jumanpp"] |
subword_tokenizer_type | str | "wordpiece" | 子词分词器,取值["wordpiece", "character", "sentencepiece"] |
mecab_kwargs | dict | None | 透传给MecabTokenizer的关键字参数 |
sudachi_kwargs | dict | None | 透传给SudachiTokenizer的关键字参数 |
jumanpp_kwargs | dict | None | 透传给JumanppTokenizer的关键字参数 |
never_split/unk_token/sep_token/pad_token/cls_token/mask_token | - | [UNK]/[SEP]/[PAD]/[CLS]/[MASK] | 沿袭标准 BERT 的特殊 token 约定 |
词表相关常量在源码第 33 行定义:VOCAB_FILES_NAMES = {"vocab_file": "vocab.txt", "spm_file": "spiece.model"},即 WordPiece/Character 模式依赖vocab.txt,SentencePiece 模式依赖spiece.model,二者通过subword_tokenizer_type切换使用。
BertJapaneseTokenizer还设定了token_type_ids_pattern="bert_style"、token_type_ids_include_special_tokens=True、special_tokens_pattern="cls_sep",因此它与 BERT 一样:单句编码为[CLS] ... [SEP],句对编码为[CLS] A [SEP] B [SEP],每条 token 都附带 token_type_ids——这与 test_sequence_builders 的断言行为一致。
五、核心流水线:两段式_tokenize
理解BertJapaneseTokenizer的关键是它的两段式切分流水线(源码_tokenize,见 tokenization_bert_japanese.py):
def _tokenize(self, text): if self.do_word_tokenize: tokens = self.word_tokenizer.tokenize(text, never_split=self.all_special_tokens) else: tokens = [text] if self.do_subword_tokenize: split_tokens = [sub_token for token in tokens for sub_token in self.subword_tokenizer.tokenize(token)] else: split_tokens = tokens return split_tokens流水线可概括为:
- 词级切分:由
word_tokenizer把整句切成一串日语“单词”(形态素); - 子词切分:对第 1 步输出的每一个词,再调用
subword_tokenizer做二次切分(如 WordPiece 把词典外长词拆成##前缀子词); - 特殊 token(
[CLS]/[SEP]等)在all_special_tokens的保护下不被切开。
在构造函数中,word_tokenizer_type决定实例化BasicTokenizer/MecabTokenizer/SudachiTokenizer/JumanppTokenizer中的哪一个,subword_tokenizer_type决定实例化WordpieceTokenizer/CharacterTokenizer/SentencepieceTokenizer中的哪一个,非法取值会抛出ValueError。
一个值得注意的工程细节:当词级分词器为mecab/sudachi/jumanpp时,底层持有不可序列化的解析器对象,因此类实现了__getstate__(pickle 时删除word_tokenizer)与__setstate__(反序列化时依据保存的 kwargs 重建解析器),确保模型可被pickle与多进程分发(见 tokenization_bert_japanese.py)。
六、词级分词器(word tokenizer)逐一分析
6.1 BasicTokenizer(basic,默认)
即标准 BERT 的BasicTokenizer。BertJapaneseTokenizer在构造它时强制传入tokenize_chinese_chars=False(见 tokenization_bert_japanese.py)。这是因为对日语启用中文逐字切分会把汉字拆得过碎,破坏后续形态素/子词处理;仓库中的 BasicTokenizer 完整保留了对 CJK 区间(含扩展区)判定的_is_chinese_char逻辑(第 737-759 行),但在此处被显式关闭。它同时完成 Unicode NFKC/NFC 归一化、空白清理、标点拆分与小写化。
6.2 MecabTokenizer(mecab)
通过fugashi封装 MeCab 完成形态素解析(构造逻辑见 tokenization_bert_japanese.py),其mecab_kwargs支持:
| 参数 | 默认值 | 说明 |
|---|---|---|
do_lower_case | False | 是否对非never_splittoken 做小写化 |
normalize_text | True | 切分前是否对文本做 Unicode NFKC 归一化(用于把全角/半角统一,如アップル→アップル、8→8) |
mecab_dic | "unidic_lite" | 使用的词典:"ipadic"(IPA)、"unidic_lite"(轻量 UNIDIC)、"unidic"(完整 UNIDIC,需python -m unidic download),若使用系统词典则传None并改用mecab_option |
mecab_option | None | 直接传给 MeCab 构造器的原始选项字符串 |
设置mecab_dic后,源码会拼出-d "{dic_dir}" -r "{mecabrc}"再附加用户自定义的mecab_option,实现词典路径与配置的自动定位。tokenize对 MeCab 输出的每个词取word.surface作为 token,并按需做小写化。
测试对三种词典均给出预期(见 test_tokenization_bert_japanese.py):同一输入"アップルストアでiPhone8 が 発売された 。",使用ipadic输出アップルストア一词,而使用unidic_lite/unidic输出アップル+ストア两词——这正是不同词典粒度差异的直观体现,实践选型时应保持“训练用词典 = 推理用词典”。
6.3 SudachiTokenizer(sudachi)
基于 SudachiPy 的三粒度形态素解析器(构造见 tokenization_bert_japanese.py),其sudachi_kwargs关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
normalize_text | True | NFKC 归一化开关 |
trim_whitespace | False | 是否剔除并 trim 空白/tab/换行 token |
sudachi_split_mode | "A" | 切分粒度:A(最细)、B(中)、C(不切分) |
sudachi_dict_type | "core" | 词典类型:small/core/full |
sudachi_config_path/sudachi_resource_dir | None | SudachiPy 配置与资源目录 |
sudachi_projection | None | 词投影模式,如surface/normalized/reading/normalized_nouns等(需要 sudachipy>=0.6.8) |
projection是 Sudachi 的特色能力。测试 test_sudachi_tokenizer_projection 展示了投影前后的差异:输入"これはねこです。",在sudachi_split_mode="A"+sudachi_projection="normalized_nouns"下被切分为此れ / は / 猫 / です / 。,其中これ→此れ、ねこ→猫即为词典投影把平假名口语还原为规范表记的结果。
SplitMode 三档差异同样有测试佐证(test_tokenization_bert_japanese.py):对"外国人参政権",A/B/C 三种模式分别输出外国/人/参政/権、外国人/参政権、外国人参政権。将其接入完整分词器时(如测试中的word_tokenizer_type="sudachi", sudachi_kwargs={"sudachi_split_mode": "B"}),会进一步得到带##的 WordPiece 子词外国/##人/参政/##権。
6.4 JumanppTokenizer(jumanpp)
基于rhoknp调用 Juman++(构造见 tokenization_bert_japanese.py)。jumanpp_kwargs支持do_lower_case、normalize_text(默认True)、trim_whitespace(默认False)。其tokenize通过self.juman.apply_to_sentence(text).morphemes逐个取词素文本。测试 test_jumanpp_full_tokenizer_with_jumanpp_kwargs_trim_whitespace 验证了它也能与 WordPiece 正常衔接。Juman++ 由京都大学 Kurohashi 实验室维护,擅长长距离依存与未知语处理,适合作为 MeCab/Sudachi 之外的高精度备选。
七、子词级分词器(subword tokenizer)逐一分析
7.1 WordpieceTokenizer(wordpiece,默认)
标准的 BERT WordPiece 实现(源码见 tokenization_bert_japanese.py),采用最长匹配优先的贪心算法:对每个词从最长子串开始向词表查询,非词首子词一律加##前缀;子串超过max_input_chars_per_word=100或无法切分时输出[UNK]。解码时通过" ".join(tokens).replace(" ##", "")还原(convert_tokens_to_string)。测试 test_wordpiece_tokenizer 展示了核心行为:こんばんは→こん/##ばんは。
7.2 CharacterTokenizer(character)
逐字切分(源码见 tokenization_bert_japanese.py)。先做 NFKC 归一化,再对每个字符查词表:こんにちは→こ/ん/に/ち/は;词表外的字符(如测试中的ほ)映射为[UNK](见 test_character_tokenizer)。由于该模式不需要词级分词器配合形态素解析,是文档所述“第二种经典用法”的底层实现。
7.3 SentencepieceTokenizer(sentencepiece)
当subword_tokenizer_type="sentencepiece"时,构造器要求spm_file指向spiece.model,词表与 id 映射改由 SentencePiece 的sp_model提供(vocab_size、_convert_token_to_id/_convert_id_to_token都会切换到 SP 模型路径)。其实现参考了 Albert 分词器(见类注释及 tokenization_bert_japanese.py),输出采用▁前缀标记词首。注意:此时主切分器仍然先由word_tokenizer(通常配 sudachi/jumanpp)产出词序列,再交给 SentencePiece。这类组合实际存在于社区模型中,例如测试使用的nlp-waseda/roberta-base-japanese-with-auto-jumanpp(Jumanpp 词级 + SentencePiece 子词级),其预期输出如▁国境 ▁の ▁長い ...(见 test_sentencepiece_tokenizer)。
八、解码与词表读写行为
- 解码:WordPiece/Character 模式下,
convert_tokens_to_string以空格拼接 token 并去掉##粘连;SentencePiece 模式下直接调用 SP 模型的decode(见 tokenization_bert_japanese.py)。 - 词表加载:
load_vocab按行读取vocab.txt构造OrderedDict(token → id),并同步维护反向ids_to_tokens(第 38-46、124-125 行)。 - 保存:
save_vocabulary按配置分别写出vocab.txt或 SP 模型的序列化 proto 到spiece.model(第 263-293 行)。 - 词表扩展:
get_vocab在基础词表之上合并added_tokens_encoder中未冲突的新增 token。
九、参数组合速查与选型建议
综合上述实现,可将完整组合归纳如下:
| 词级分词器 | 子词分词器 | 典型搭配场景 |
|---|---|---|
basic | wordpiece | 简单位切分(无形态学信息),仅适合预切好的空格文本 |
mecab | wordpiece/character | 经典cl-tohoku/bert-base-japanese/-char路线,词典选ipadic或unidic_lite |
sudachi | wordpiece/sentencepiece | 需要 split_mode A/B/C 粒度控制或规范化投影 |
jumanpp | wordpiece/sentencepiece | 追求更高精度的形态素切分 |
选型要点:
- 推理必须与训练一致:
do_lower_case、normalize_text、MeCab 的mecab_dic、Sudachi 的sudachi_split_mode/sudachi_dict_type等任何一项不一致,都会改变 token id,导致下游模型结果漂移; - 字符级 vs 词法级:Character 模式实现简单、无需外部词典且对未知词友好,但序列更长、信息粒度更细;词法级模式序列更短、语义单元更完整,但引入外部解析器依赖与词典;
- normalize_text 影响显著:NFKC 会把半角片假名与全角数字统一后再切分。若关闭(
normalize_text=False),测试显示アップル、8会保持原样(见 test_mecab_tokenizer_no_normalize),对依赖原始表记的任务可能更合适; - 切分粒度对比测试可参考仓库测试:同一句
" \tアップルストアでiPhone8 が \n 発売された 。 "在 tests/models/bert_japanese/test_tokenization_bert_japanese.py 中被 ipadic(アップルストア一体)与 unidic_lite(アップル/ストア拆分)解析出不同结果,直接展示了词典差异对下游词表覆盖的影响。
十、延伸阅读
- 模型结构 API(与 BERT 完全一致):BERT 模型文档
- 分词器完整源码:tokenization_bert_japanese.py
- 行为验证用例:test_tokenization_bert_japanese.py
- 日语 extra 依赖声明:setup.py
- AutoTokenizer 路由注册:tokenization_auto.py
- 本文对应的英文原文文档:docs/source/en/model_doc/bert-japanese.md,另有日文、韩文译版
如果你手头的任务恰好是处理无空格书写的日语文本,那么基于BertJapaneseTokenizer的词法级(MeCab/Sudachi/Juman++)+ 子词级(WordPiece/Character/SentencePiece)两段式流水线,就是与预训练权重对齐度最高、也最值得在生产中复用的分词方案。
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考