bert-base-chinese 词表解剖:21,128 个词条里都有什么

一句话理解

bert-base-chinese 的词表有 21,128 个词条,其中 7,321 个中文单字、5,545 个英文子词、104 个特殊标记。多字中文词条数为 0——WordPiece 对中文完全退化为逐字切分。


tokenizer.json 是什么?

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、特殊符号

词表的 ID 分布:从特殊标记到各国文字

词表不是"一堆乱序的词条",而是按类型有序排列的。从 tokenizer.json 的实际内容可以看到完整的 ID 分布:

头部:特殊标记(ID 0~103)

ID     0  [PAD]        ← 填充标记
ID   1~99  [unused1]~[unused99]  ← 预留位(未使用)
ID   100  [UNK]        ← 未知标记
ID   101  [CLS]        ← 分类标记(句子开头)
ID   102  [SEP]        ← 分隔标记(句子结尾)
ID   103  [MASK]       ← 掩码标记

ID 104~172:标点符号 + 英文字母 + 基本符号

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~287:各国文字(俄文、阿拉伯文、泰文、藏文、格鲁吉亚文…)

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~532:韩文字母 + 日文假名

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~7991:中文汉字(主体区域)

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 7993~13808:带 ## 前缀的中文字(镜像区域)

与 ID 671~7991 的中文字一一对应,只是加了 ## 前缀。实际分词中几乎不触发(详见下文)。

ID 13809~21127:英文子词 + 日文/符号/emoji 后缀

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 分布总览

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 个汉字,这里还多了一些生僻字和繁体字)。


繁体 vs 简体:模型怎么处理?

答案:繁体和简体是不同的词条,有不同的 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 系统,训练数据全部是简体中文,所以繁体输入可能会影响分类效果。不过实际业务中繁体输入极少,影响可忽略。


多字中文词条:0 个

# 检测词表中是否有中文多字词条
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 词表对比

对比项 bert-base-chinese bert-base-uncased(英文)
词表大小 21,128 30,522
训练语料 中文维基百科 英文维基百科
主体内容 中文单字(7,321 个) 英文子词(~30,000 个)
中文支持 原生支持 基本不支持
"的" 有(单字) 无
"the" 无 有
"playing" 无 有(整词或子词)
多字中文 0 个 不适用

两个模型虽然都叫 "BERT",但词表和权重完全独立训练,针对不同语言优化。


Normalizer:分词的第一步

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

相关笔记