缮经阁的拆字僧
一、字太多了,藏经阁装不下
白马寺的缮经阁,藏了三十万卷经文。从东汉摄摩腾译《四十二章经》算起,六百余年,梵文、巴利文、汉文经卷层层叠叠,光是经目就编了八十本。
可这几年出了个要命的麻烦。
朝廷要重编《大藏经总目》——给三十万卷经书里的每一个字做一个索引卡。哪个字、在哪一卷、第几行。方丈把任务派给了缮经阁。
阁里的僧人们先按老规矩来:把经书里的字一个一个抄到卡片上。抄了三个月,抄出了四百二十万张卡片——光一个"佛"字,就占了整整十二个抽屉。
但问题不在这。问题在于——有些字,一共就出现过两三次,也给它们一人一张卡片,柜子占得满满当当;而"如是我闻"四个字连着出现的次数太多,可阁里没有任何一种办法把它们捆成一个"整件"来检索。
管经目的老僧慧明,在阁里待了五十年。他看了一眼那四百二十万张卡片,说了六个字:
"字不是这么拆的。"
二、常用字留着,生僻字拆开
慧明把沙弥们叫到跟前,在案上铺了一张白纸。
"你们看——'佛'这个字,三十万卷经书里,出现了八十万次。它是个完整的、谁见了都认识的东西。你不能把它拆开——拆成'亻'和'弗'——就没有意义了。"
"但我要是遇到一个极其生僻的字,比如——"他提笔在纸上写了一个"犍"。
沙弥们面面相觑。没人认识。
"这是'犍度'的'犍'。三十万卷经里一共出现了十七次。给它单独立一张卡片,它一个人占一个抽屉。值吗?"
"不值。"慧明自己回答,"可要是把它拆成'牛'和'建'——这两个部件,经书里到处都是。写到'犍'时,就写上'牛'这张卡、再写'建'这张卡——两张卡拼起来。不需要给'犍'单独立卡。"
"再比如'菩萨'二字。经书里从来都是'菩'后头必跟'萨','萨'前头必是'菩'。它俩就该拼成一张卡——'菩萨'。你检索的时候,打'菩萨'一个签就能找到所有。拆成'菩'和'萨'两张卡,反而把一对从来不分家的东西硬拆散了。"
小沙弥举手:"师父,到底哪些字应该留着,哪些字应该拆开?"
慧明从怀里掏出一本翻烂了的册子,封皮上三个字——"字频簿"。
"这本册子我记了五十年。三十万卷经书里,每一个字出现多少次、每一个两字连用的词组出现多少次、每一个三字连用的——全在这儿。"
他翻到其中一页:
佛:803,412次
菩萨:241,667次
涅槃:89,234次
犍度:17次
"规矩简单得很——出现次数多的,就当成一个整件留着。出现次数少的,就拆成更小的件——拆到不能拆为止。能拆成部件就不造新卡。"
他又举了个例子:"你学会了'牛'和'建'怎么写,再去认'犍',是不是一眼就看懂了?拆字的另一个妙处——你不需要认识所有生僻字。你只要认识最基本的那些部件,它们能拼出一切。"
三、既不少拆,也不多拆
沙弥们开始动手了。
头一个月,热情很高。有人把"般若"拆成了"般"和"若"。慧明看见,把卡片摔在地上。
"'般若'在经书里是一个词——它不是'般'的意思加'若'的意思。你拆了,把它的意思拆没了。翻翻频簿:'般若'连用二十八万次——不该拆的,绝对不能拆。"
又有人贪省事,把"阿耨多罗三藐三菩提"捆成了一张卡——十一个字一口吞。慧明又摔了一次卡片。
"十一个字捆一起,你指望它还能在别的经里出现几次?出现次数太少,卡片就是废的。太大了——拆小一点,但别拆太碎。'阿耨多罗'出现了三万次,可以捆。后面再拆。"
小沙弥们慢慢摸着了门道。最妙的是——拆到不能再拆的时候,所有字都落到了同一个"底盘"上:两千三百个最基本部件。这两千三百个部件,覆盖了三十万卷经书里的一切字。没有一个字跑得出去。
到了第三个月,经目编完了。
以前四百二十万张卡片的体系,现在只需要十六万张卡片。每一张卡片的使用频率都很高——没有一张是占着抽屉吃灰的。检索起来飞快,因为要翻的卡片总数一下子少了二十六倍。
最让方丈满意的是:遇到一个新字、一个从来没在经书里出现过的字——比如西域新送来的一部经里写了个从没见过的梵文音译——阁里的沙弥不需要给它新立一张卡。只要用现有的两千三百个部件拼出来就行了。
"天竺的经来得再多,"慧明对沙弥们说,"它们的笔画变不出这两千三百样。"
四、无字不可拆,亦无字不可合
那年秋天,寺里出了一件事。一个云游僧带回来一卷于阗文佛经——既不是汉字也不是梵文,而是一种沙弥们完全没见过的文字。方丈说:"编进去。"
沙弥们一筹莫展。没有频簿,因为于阗文经就这一卷,哪来的"出现次数"?
慧明看了一眼那卷经。文字曲曲弯弯,像画符。
"规矩一样,"他放下茶杯,"把你们当成第一卷汉译《四十二章经》刚到白马寺时的那些译经僧。你面前是一堆你从没见过的笔画,你怎么决定哪些该捆、哪些该拆?"
小沙弥们愣了一夜。第二天早上,最机灵的那个悟了。
"师父——是不是看'频率'?不是在经书里出现的频率,是笔画本身重复的频率?"
"说对了。一个连写笔画在卷子里反复出现——哪怕这是你见过的唯一一卷经——它出现得越多,就越该留下来当整件。出现的次数排下来,从最常连写的开始保留,保到卡的总数你觉得够用——剩下的,全拆了。"
他们把于阗经里所有连写笔画对统计了一遍——哪些笔画总是一起出现、出现多少次。排了个序,取前四千个最常见组合做卡片。四千张卡,把那卷于阗文经编完了。没有一个字漏掉。
慧明翻着新编的目录,点了点头。
"你们终于懂了。拆字的规矩从头到尾就一条——频繁出现的就当整体留着,稀少的就往更小的碎块拆。 这句话放之四海而皆准。你拿到什么文字,用这条规矩一捋,该拆哪儿、该留哪儿,自己就出来了。"
那天晚上,最早的沙弥已经能带新徒弟了。一个新来的小沙弥问:"师兄,师父这套拆字法,到底是给汉字用的,还是给梵文用的,还是给——那种曲曲弯弯的于阗文用的?"
师兄放下手里的频簿。
"跟具体哪个字没关系。跟'频率'有关系。同一个东西反复挨着出现——就别拆。出现得零零散散的——就拆到底。天下的字,都吃这一条。"
技术解读
Byte-Pair Encoding(BPE)是当代大语言模型处理文本的第一道关口。从 2016 年 Sennrich 等人将 BPE 引入神经机器翻译(《Neural Machine Translation of Rare Words with Subword Units》),到 GPT 系列使用的 BPE 变体,再到 SentencePiece(Kudo & Richardson, 2018)将整个分词流程统一为端到端的子词切分——分词算法决定了模型"看到"的文本长什么样。
BPE 的核心思路极其朴素:从字符级别开始,反复将最频繁相邻出现的符号对合并成一个新符号,直到词汇表达到一个预设的大小。这样一来,常见词保持完整,罕见词被拆成有意义的子词——模型既不会因为词表太大而浪费参数,也不会因为词表太小而遇到大量 UNK(未知词)。
核心概念回顾
| 概念 | 通俗解释 |
|---|---|
| 分词(Tokenization) | 将原始文本切分成模型可处理的最小单元(token)的过程 |
| BPE(Byte-Pair Encoding) | 从字符开始,反复合并最频繁相邻符号对的子词分词算法 |
| 词汇表(Vocabulary) | 模型认识的所有 token 的集合,每个 token 对应 embedding 表中的一行 |
| 子词(Subword) | 介于字符和完整词之间的中间粒度——如 "playing" → "play" + "ing" |
| 合并规则(Merge Rules) | BPE 学到的按优先级排列的合并操作序列,从最高频配对开始 |
| UNK / OOV(未知词) | 不在词汇表中的词。子词分词大幅减少了 UNK 的出现——任何词都可以拆到最基本的字符级别 |
| SentencePiece | Google 提出的统一分词框架,将训练和推理的切分逻辑整合为一个端到端模型 |
| 字符覆盖率 | 词表中所有 token 能覆盖多少比例的实际文本。BPE 通过灵活拆解保证了高覆盖率 |
| 词表大小(Vocab Size) | 分词器的总 token 数(如 GPT-2 约 50K,LLaMA 约 32K)。太小→信息密度低;太大→embedding 浪费参数 |
| Byte-level BPE | GPT-2 之后的变体:以字节而非 Unicode 字符为起点,彻底消灭 OOV |
故事中的隐喻对照
| 故事元素 | 映射的技术概念 | 解释 |
|---|---|---|
| 缮经阁(三十万卷经书) | 大规模训练语料 | 需要对其分词的全部原始文本 |
| 四百二十万张卡片(每个字一张) | 字符级分词 | 以单字/字符为单位,词表极小但序列极长、信息密度低 |
| 慧明的"字频簿"(五十年记录) | 语料上的共现频率统计 | BPE 的训练过程就是统计所有相邻 token 对的共现频率 |
| "频繁出现就当整体留着" | BPE 合并规则 | 最高频的 token 对被合并成一个新 token,保留在词表中 |
| "稀少的往更小碎块拆" | 子词分解 | 低频词汇不单独占用词表槽位,而是分解为更小的高频子词 |
| 两千三百个基本部件 | 基础字符 / Byte-level 的字节 | 任何文本最终都能表示为基础字符或字节的序列 |
| "菩萨"不许拆,"般若"不许拆 | 高频词的完整保留 | BPE 确保高频词作为整体 token 存在,不会被拆碎——这不仅节省序列长度,也保留了语义完整性 |
| "阿耨多罗三藐三菩提"十一个字不许捆 | 词表上限约束 | 过长的合并会导致 token 过于专用、泛化性差;词表大小需要平衡覆盖率与参数效率 |
| 十六万张卡片替代四百二十万张 | 词表压缩 | 子词分词让较小词表覆盖较大词表才能处理的文本量 |
| 遇到新字用现有部件拼 | 零样本泛化(零 OOV) | 任何未登录词都能被拆成已知子词——模型永远不会遇到"完全不认识"的东西 |
| 于阗文经(完全陌生的文字) | 对未知语言/书写的泛化 | BPE 可以纯粹基于频率统计应用到任何书写系统上,不依赖语言学知识 |
| 排序取前四千组合 | 预设词表大小 | 在实际训练中,重复合并直到 token 数达到预设上限(如 32K 或 50K) |
为什么这个故事对应 BPE 分词?
"频繁出现就当整体留着"是 BPE 唯一的核心规则。 BPE 迭代式地将最高频的相邻 token 对合并成一个新 token——正如慧明用字频簿决定哪些字符组合要捆在一起。从字符级开始,经过 N 轮合并,最终得到一个固定大小的子词词表。
平衡粒度是分词的根本挑战。 词表太大→每个 token 太专用("阿耨多罗三藐三菩提"),泛化性差且 embedding 矩阵占用过多参数。词表太小→序列太长(每个字符一个 token),计算量和上下文窗口压力剧增。子词分词找到了这个黄金平衡点。
零 OOV 是子词分词的招牌能力。 故事中于阗文经的例子精确对应了 BPE 处理新词的机制——任何未见过的词都可以通过已学会的子词合并规则拆解为已知子词。GPT-2 之后的 byte-level BPE 更进一步:以字节为基础单位,数学上保证了零 OOV。
分词是语言无关的。 BPE 不关心文本是中文、英文还是梵文——它只统计符号的相邻频率。这是它在大语言模型中被广泛采用的关键原因:用同一套逻辑处理多语言语料。
分词是大模型的第一道工序,决定了模型"看"到什么。 所有后续的注意力、表示、生成——都建立在分词器切割出的 token 序列之上。分词器错了,后面全错了。
后记:世上没有不可拆的字,也没有不可合的词。慧明用了五十年悟出的道理,BPE 用一台机器跑一个小时就学会了——但它们的底层逻辑是同一件事:那些总是挨着出现的,就别拆散它们;那些难得见一面的,就别捆在一起。世间万象,分分合合,不过是频率说了算。白马寺缮经阁的灯还亮着,每一卷新来的经书,都在等那位老僧翻开他的字频簿。

