一句话理解
bert-base-chinese 的词表有 21,128 个词条,其中 7,321 个中文单字、5,545 个英文子词、104 个特殊标记。多字中文词条数为 0——WordPiece 对中文完全退化为逐字切分。
tokenizer.json 是 Hugging Face Tokenizers 库的分词器配置文件,包含:
tokenizer.json
├── version → 格式版本(1.0)
├── added_tokens → 新增的特殊标记(5 个)
├── normalizer → 标准化规则(大小写、中文字加空格等)
├── pre_tokenizer → 预分词规则(按空格/标点切分)
├── post_processor → 后处理规则(加 [CLS]/[SEP])
├── decoder → 解码规则(token → 原文)
└── model
└── vocab → 词表(21,128 个词条,每个词条对应一个 ID)
这个文件就是分词器的全部"知识"——它告诉程序:哪些文字片段是合法的 token,每个 token 对应什么数字编号。
文件路径(本项目):tokenizer.json
用 Python 解析 tokenizer.json,按类型统计:
bert-base-chinese 词表组成
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
词表总大小: 21,128
编号范围: ID 0 ~ ID 21,127(从 0 开始,共 21,128 个)
第一个词条: [PAD] → ID 0 (填充标记)
最后一个词条: ##😎 → ID 21,127 (emoji 后缀)
中文单字(无 ##): 7,321 ← 主体
中文字(带 ## 前缀): 7,321 ← 镜像(理论上存在,实际几乎不触发)
英文子词(无 ##): 3,252 ← 处理英文/数字/符号
英文后缀(## 开头): 2,293 ← 英文后缀(##ing, ##tion 等)
特殊标记 [xxx]: 104 ← [PAD]/[UNK]/[CLS]/[SEP]/[MASK] + 99 个 unused
其他(日文/符号等): 837 ← 日文假名、emoji、特殊符号
词表不是"一堆乱序的词条",而是按类型有序排列的。从 tokenizer.json 的实际内容可以看到完整的 ID 分布:
ID 0 [PAD] ← 填充标记
ID 1~99 [unused1]~[unused99] ← 预留位(未使用)
ID 100 [UNK] ← 未知标记
ID 101 [CLS] ← 分类标记(句子开头)
ID 102 [SEP] ← 分隔标记(句子结尾)
ID 103 [MASK] ← 掩码标记
ID 104 <S> ID 140 ]
ID 105 <T> ID 141 ^
ID 106 ! ID 142 _
ID 107 " ID 143 a
ID 108 # ID 144 b
ID 109 $ ID 145 c
ID 110 % ... ...
ID 111 & ID 166 x
ID 112 ' ID 167 y
ID 113 ( ID 168 z
ID 114 ) ID 169 {
ID 115 * ID 170 |
ID 116 + ID 171 }
ID 117 , ID 172 ~
ID 118 -
ID 119 .
ID 120 /
ID 121 0
ID 122 1
ID 123 2
...
ID 173 £ ID 252 ф ← 俄文字母
ID 174 ¤ ID 253 х
ID 175 ¥ ID 254 ц
ID 176 § ID 255 ч
ID 177 © ID 256 ш
ID 178 « ID 257 ы
ID 179 ® ID 258 ь
... ID 259 я
ID 260 і ID 261 ا ← 阿拉伯文字母
ID 262 ب ID 263 ة
ID 264 ت ID 265 د
...
ID 285 ๑ ← 泰文数字
ID 286 ༄ ← 藏文符号
ID 287 ღ ← 格鲁吉亚字母
ID 288 ᄀ ← 韩文辅音(choseong)
ID 289 ᄁ
ID 290 ᄂ
ID 291 ᄃ
...
ID 528 〖 ← 中文括号
ID 529 〗
ID 530 〜 ← 日文波浪号
ID 531 〝
ID 532 〞
ID 533 ぁ ← 日文平假名
ID 534 あ
ID 535 ぃ
ID 536 い
ID 537 う
ID 538 ぇ
...
ID 670 㗎 ← 粤语方言字(词表中最早的"汉字")
ID 671 一
ID 672 丁
ID 673 七
ID 674 万
ID 675 丈
ID 676 三
ID 677 上
ID 678 下
ID 679 不
ID 680 与
...
ID 7987 龙
ID 7988 龚
ID 7989 龛
ID 7990 龜 ← 繁体(龟的繁体)
ID 7991 龟 ← 简体
ID 7992 ︰ ← 全角冒号(中文区域结束)
共 7,321 个中文单字,覆盖了常用汉字(GB2312 收录 6,763 个)以及部分繁体字、粤语字、日韩汉字。
与 ID 671~7991 的中文字一一对应,只是加了 ## 前缀。实际分词中几乎不触发(详见下文)。
ID 13809~ 英文子词后缀(##ing, ##tion, ##ed 等)
ID 21098 ##u ← 全角字母后缀
ID 21103 ##。 ← 日文句号后缀
ID 21110 ##イ ← 日文片假名后缀
ID 21120 ##゙ ← 日文浊点后缀
ID 21122 ## ̄ ← 全角长音后缀
ID 21123 ##¥ ← 人民币符号后缀
ID 21124 ##👍 ← emoji 后缀
ID 21125 ##🔥
ID 21126 ##😂
ID 21127 ##😎 ← 词表最后一个词条
ID 范围 内容 数量
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0 [PAD] 1
1~99 [unused] 预留位 99
100~103 [UNK]/[CLS]/[SEP]/[MASK] 4
104~172 标点 + 英文字母 + 符号 69
173~287 俄文/阿拉伯文/泰文/藏文等 115
288~532 韩文字母 + 日文假名 245
533~669 日文平假名/片假名/符号 137
670~7992 中文汉字(单字) 7,323
7993~13808 中文汉字(## 前缀镜像) 7,321
13809~21127 英文子词后缀 + 多语言后缀 5,814
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
合计 21,128
bert-base-chinese 虽然叫"中文 BERT",但词表里不只有中文——它收录了俄文、阿拉伯文、泰文、藏文、韩文、日文、格鲁吉亚文等多种文字。
这是因为 bert-base-chinese 的训练语料是中文维基百科,而维基百科包含大量多语言内容(模板、代码、外文引用等)。WordPiece 算法在构建词表时,会把这些语料中频繁出现的字符都收录进来。
不过在实际使用中,非中文/非英文的词条几乎不会被触发。
词表里有 7,321 个带 ## 前缀的中文字(如 ##海、##洋),数量与无 ## 的完全一致——是镜像关系。
但在实际分词中,由于 normalizer 的 handle_chinese_chars 会在每个中文字前后加空格,预分词阶段就把中文字切开了,WordPiece 不会把两个中文字合并,所以 ## 前缀的中文字几乎不会被触发。它们是 WordPiece 训练过程的副产品,存在于词表中但基本不发挥作用。
词表中的中文单字按 ID 排序(前 50 个):
ID 671 一 ID 691 东 ID 711 为
ID 672 丁 ID 692 丝 ID 712 主
ID 673 七 ID 693 丢 ID 713 丼
ID 674 万 ID 694 两 ID 714 丽
ID 675 丈 ID 695 严 ID 715 举
ID 676 三 ID 696 丧 ID 716 丿
ID 677 上 ID 697 个 ID 717 乂
ID 678 下 ID 698 丫 ID 718 乃
ID 679 不 ID 699 中 ID 719 久
ID 680 与 ID 700 丰 ID 720 么
ID 681 丐 ID 701 串 (以下为 ID 721~770 范围)
ID 682 丑 ID 702 临
ID 683 专 ID 703 丸
ID 684 且 ID 704 丹
ID 685 世 ID 705 为
ID 686 丘 ID 706 主
ID 687 丙 ID 707 丽
ID 688 业 ID 708 丶
ID 689 丛 ID 709 丸
ID 690 东 ID 710 丹
共 7,321 个中文单字——基本覆盖了常用汉字(GB2312 收录 6,763 个汉字,这里还多了一些生僻字和繁体字)。
答案:繁体和简体是不同的词条,有不同的 ID。模型不会自动繁简转换。
实测 24 组繁简对,全部在词表中独立存在:
简体 繁体
───────────────────────────────
发 ID=1355 發 ID=4634 ← 不同 ID
国 ID=1744 國 ID=1751 ← 不同 ID
学 ID=2110 學 ID=2119 ← 不同 ID
电 ID=4510 電 ID=7442 ← 不同 ID
车 ID=6756 車 ID=6722 ← 不同 ID
门 ID=7305 門 ID=7271 ← 不同 ID
马 ID=7716 馬 ID=7679 ← 不同 ID
鱼 ID=7824 魚 ID=7797 ← 不同 ID
龙 ID=7987 龍 ID=7983 ← 不同 ID
风 ID=7599 風 ID=7591 ← 不同 ID
气 ID=3698 氣 ID=3706 ← 不同 ID
云 ID=756 雲 ID=7437 ← 不同 ID
语 ID=6427 語 ID=6295 ← 不同 ID
话 ID=6413 話 ID=6282 ← 不同 ID
"头发" → [头 ID=1928, 发 ID=1355]
"頭髮" → [頭 ID=7531, 髮 ID=4634]
→ 完全不同的 token ID 序列
→ 模型走完全不同的推理路径
→ 不会自动识别"头发 = 頭髮"
好处:繁体输入也能正确处理(词表里有) 局限:如果训练数据只有简体,遇到繁体输入时效果可能下降(因为模型没见过这些 ID 组合)
对于我们的 HXXedge 系统,训练数据全部是简体中文,所以繁体输入可能会影响分类效果。不过实际业务中繁体输入极少,影响可忽略。
# 检测词表中是否有中文多字词条
multi_char_cn = [t for t in vocab
if not t.startswith("##")
and not t.startswith("[")
and sum(1 for c in t if '\u4e00' <= c <= '\u9fff') >= 2]
结果:0 个
这从实证角度确认了:bert-base-chinese 的 WordPiece 对中文完全退化为逐字切分。
"海洋监测" → ["海", "洋", "监", "测"] ← 4 个独立 token
不是 → ["海洋", "监测"] ← 不是 2 个子词 token
原因:normalizer 中的 handle_chinese_chars 在每个中文字前后加空格,预分词阶段就把中文字切开了,WordPiece 没有机会合并。
| 对比项 | bert-base-chinese | bert-base-uncased(英文) |
|---|---|---|
| 词表大小 | 21,128 | 30,522 |
| 训练语料 | 中文维基百科 | 英文维基百科 |
| 主体内容 | 中文单字(7,321 个) | 英文子词(~30,000 个) |
| 中文支持 | 原生支持 | 基本不支持 |
| "的" | 有(单字) | 无 |
| "the" | 无 | 有 |
| "playing" | 无 | 有(整词或子词) |
| 多字中文 | 0 个 | 不适用 |
两个模型虽然都叫 "BERT",但词表和权重完全独立训练,针对不同语言优化。
tokenizer.json 中的 normalizer 配置:
normalizer:
├── clean_text → 清理控制字符
├── handle_chinese_chars → 中文字前后加空格(关键!)
├── strip_accents → 去除音标符号
└── lowercase → 英文转小写
handle_chinese_chars 是让中文退化为逐字切分的关键:
输入:"海洋监测"
↓ handle_chinese_chars
" 海 洋 监 测 "
↓ pre_tokenizer(按空格切分)
["海", "洋", "监", "测"]
↓ WordPiece(查词表)
每个字都在词表里 → 直接得到 ID
HXXedge 系统的分词流程:
用户输入 → tokenizer.json 分词 → input_ids → ONNX 模型推理
关键参数:
词表大小:21,128
max_length:64(固定长度)
中文约 60 字以内(64 - [CLS] - [SEP] = 62 个可用位置)
分词器文件位置:
tokenizer.json ← 词表 + 分词规则
tokenizer_config.json ← 分词配置参数
📝 创建:2026-09-14
🔄 标签:BERT、Tokenizer、词表、WordPiece、中文分词、繁简体、tokenizer.json