看看下面这句话:
我喜欢吃苹果。
你发现了什么?我们人脑可以无意识地自动分词:
我 / 喜欢 / 吃 / 苹果
为了说明这个过程有多无意识,可以再看一句:
中华人民共和国
你可能永远不会意识到这句话里包含"华人"这个词,在我告诉你之前。
也就是说,我们并不是把一串字符里所有可能的词都看见了。我们只会看见某一种切法,而且这件事几乎是在无意识中自动完成的。
从观察开始
于是我开始观察这个过程。
我走在街道上,察看各种招牌。比如我每天路过一个地方,可以看到"夏至酒店"。我似乎从来不会把"至酒"合在一起,记忆里也从来没有这两个字符组合。
看到"中国银行",我不会想到"国银"。
看到"南京大学",我会自然地读成:
南京 / 大学
而不是别的什么。
那么人到底是怎么分词的。
我想到大模型也懂语言,于是我去了解了大模型的分词技术。
当我知道大模型的底层分词技术------------ BPE 的时候,我是惊异的。举例来说:
比如一本 100 万字的书,先阅读一遍,然后找出其中最常出现的 100 个字符组合。
然后再阅读一遍,此时把上一次找出的词看做一个整体,继续找出其中最高频的 100 个组合。
不停重复上述过程,直至抵达预设的词表大小,比如 20 万个词。
也就是说,BPE 要先有一个全集语料,然后从上面统计高频对,再一步步合并,最后得到词表。
这当然是一个很简洁高效的工程方案,但我们也可以立刻判断:这不是人脑的学习路径。
一个婴儿不会先把世界上所有文本看完,再开始学语言。人脑是渐进式学习的。边生活,边记忆,边修正,边遗忘。今天见到一个词,明天再见到一次,后天它开始变得熟悉。没有一个"语料全集"摆在面前供它离线统计。
所以我没有停在 BPE 这里。
让我们继续观察日常语言。
人脑也许是动态分词的
比如这个例子:
南京大
你会觉得,"大"后面好像还没完。
南京大学
你会立刻意识到,这是"大学"。
南京大学生
嗯,这下又变成一个群体了,是"大学生"。
南京大学生源
什么,还有反转。原来不是"大学生",而是"生源"。
这时候我们可以得出一个简单结论:
人脑不是先分完词再理解。人脑是一边接收字符,一边预测,一边切分,一边修正。
尤其在有歧义的时候,举个经典的例子:
南京市长江大桥,在会上做出发言。
只有人会发言,因此前文必须重新切分,那么只能是"南京/市长/江大桥"
也就是说,人脑是动态分词的。
而且它还会做补全预测。
看到"编",你可能立刻想到"编程"。
看到"大",你可能立刻想到"大学"。
看到"果",你可能想到"苹果",也可能想到"果然"。
这些可能性几乎是同时存在的。它们像一组候选,悬浮在那里,等待后面的信息把其中一个确认下来。
这种的动态的灵活性让我好奇。
组合与聚合
于是我们可以再往前想了一步。
如果人脑在做这种候选生成,它到底依赖什么?
我去学习了一些符号学和语言学的知识,发现索绪尔有两个词很有启发性:
- 组合
- 聚合
组合
组合,就是线性的、相邻的关系。谁和谁经常挨着出现。
比如"苹"和"果",显然有极强的组合关系。你见过太多次"苹果",于是"苹"后面几乎必然期待"果"。
这是一种高频相邻形成的粘合力。
聚合
聚合,就是可替换的关系。谁和谁虽然不挨着,但经常出现在相似的位置上。
比如:
- 吃 ___
- 一个 ___
- ___ 很甜
"苹果"和"香蕉"就经常出现在这些相似的位置里。它们能互相替换,所以它们属于同一类。
也就是对于一个类别来说,其中的各个元素的上下文是相似的。
聚合可能有多重情况,比如"苹果"和"香蕉"是一类,也可以和"华为"是另一类。
我的猜想
于是我开始有一个猜想:
人脑可能在利用组合和聚合,先生成若干候选分词,再在语义层完成最终切分。
也就是说,分词也许不是一步完成的,而是一个分层过程:
- 用组合看谁和谁该粘在一起
- 用聚合看这个候选在抽象类别层面是否合法
- 再在上下文语义里决定,到底采用哪一种切法
这很像"乒乓球拍卖完了"这种句子。
你会发现:
- 乒乓球 / 拍卖 / 完了
- 乒乓 / 球拍 / 卖完了
似乎都说得通。
那说明什么?
说明前面的候选生成阶段,可能已经允许了两种切法。真正的难点不在"切",而在后面的语义裁决。
- 乒乓球 / 拍卖 / 完了(为了合理,我们会思考,这可能是某种限定乒乓球拍卖)
- 乒乓 / 球拍 / 卖完了(你会想到,这个更符合日常语境)
到了这里,我们会发现,分词不能只停留在"怎么成词","怎么切词",而是要进入更深层次的语言理解本身。
我一直喜欢先自己想
其实,在我关注分词这个机制之前,一直隐约有个想法:
假如把新华词典交给一段程序,这个程序能否学会词、句子和语义?
这个念头很早就有了,大概是 2021 年。
再往前追溯,就是在 2015 年阅读了吴军的《数学之美》,从中知道了自然语言处理。
后来接触大模型,知道其底层是 Token,即词元,而词元的基础又是通过分词技术得到的,这成为了我开始深入观察思考分词的契机。
但我的习惯一直是这样:
在学习一个技术之前,我倾向于先不看现成的技术思路和实现。
我喜欢先自己想。
这听起来很低效。别人已经在几年、几十年前把东西研究出来了,为什么不直接去读?
因为我很享受这个思索的过程。
如果一上来就看文章和论文,你得到的是结论。但如果先自己思考,你会先得到问题。你会知道自己卡在什么地方,自己真正困惑的是什么。等你最后再去看前人的方案时,你不是在背答案,你是在对照,在验证,在对话。
这种感觉完全不一样。
所以我会让一个问题在脑子里停很久。
有时候想着想着会卡住。卡住的时候我也不感到紧迫。我会去观察生活,去等待,去做其他事,直到某个时机突然灵光一现。
也许是在路上观察到一个现象,也许是看到某篇文本,也许是突然想明白一个点。
那种卡了很久之后,某个点豁然开朗的时刻,感觉会非常棒。
知也无涯。这个世界上有无数技术我可以去学习。如果只是为了掌握它们,当然应该直接看资料,追求效率。但对我来说,这种先自己酝酿、再去验证、再去读前人答案的方式,本身充满了乐趣。
去观察,去学习,去实践
于是我去观察词。
去微信读书找符号学的书。意外发现《中国现代语法》,从中受益匪浅。
去 B 站看语言学的大学课程。原来组合和聚合的概念会变成课堂黑板上的板书。
去理解语素和音位,理解能指和所指,理解过去的语言学家到底是怎么讨论"词"的。
去 Github 搜索和 star 各种语料仓库,就像仓鼠囤积食物那样。
然后我还会去实践。
我觉得,一个想法如果不能动手试一试,它就总还是漂浮着。而能够在现实创作更是一种乐趣。
于是我开始写程序,验证自己的思路。
Node 系统
我的思路最后锚定在一个 Node 系统上。
有一个节点,它叫 Node。
最开始,每个字符就是一个 Node。
程序去遍历一段语料,每遇到一个字符,就记录这个 Node 出现了一次。同时记录它的左邻居和右邻居。也就是说,每个 Node 上,不只是有频次,还有它经常和谁挨在一起。
可以简单理解成这样:
- 它知道自己是谁
- 它知道自己出现了多少次
- 它知道谁常出现在自己左边
- 它知道谁常出现在自己右边
1. 组合机制
接下来就是组合机制。
比如 ABC:
- 遇到
AB,就可以把AB合并成一个新的 Node - 遇到
BC,也可以把BC合并成一个新的 Node
但这两个组合是会竞争的。
如果 BC 的粘合度比 AB 高,那么就把 AB 的频次降低。也就是说,不是所有可能的组合都被平等保留,而是让更强的组合慢慢胜出。
再加上一种阶段性遗忘机制:
隔一段时间,就移除那些低频的组合 Node。
这样整个系统就会持续构建多层组合 Node,也就是自动发现高频组合,而且是渐进式的、动态变化的。
也就是:
边读边长,边长边忘。
直观上,这就像前文所述的人脑过程了。
2. 聚合机制
接着,我们引入聚合机制。
如果多个 Node 的左右邻居分布相似,就可以认为它们相似,把它们聚合为一类。
也就是说,如果:
- "你""我""他""她""它"总是出现在很相似的位置
- "黑""红""黄""蓝"经常出现在类似的上下文里
它们就会被聚到一起。
于是,Node 就有三种可能:
- 它是一个字符
- 它是字符组合成的 Node
- 它是一组相似 Node 聚合成的 Node
关键在于,不管哪一种类型,它们之间都可以继续组合和聚合,持续迭代。
这就变成了一个不断生长的网络。
竞争不是二选一,而是边界会被重新打开
实践过程中,我发现真正有挑战的地方,不是"两个字符能不能组合",而是:
当一个结构已经形成之后,后面的输入来了,它还能不能被改写?
如果不能改写,那这个系统就太固化了。
它只能一路往前拼接,像搭积木一样越搭越高。
可语言不是这样的。
语言里最常发生的事情恰恰是:
前面似乎已经成形了,后面一来,前面的边界突然又松动了。
这一点,我们已经在前面的"南京大学生源"例子中观察到了。
所以我后来在组合机制上,着重探索的是"合起来之后,怎么又能拆开一点"。
已经形成的结构,并不是封死的
通常我们很容易把组合想成这样:
- A 和 B 可以组合成 AB
- AB 再和 C 组合成 ABC
好像结构一旦形成,就会像砖块一样固定下来。
但它更像是这样:
- A 和 B 暂时被看成一个整体
- 但 C 一来,系统会重新看一眼:
- 是不是其实 B 和 C 更该粘在一起?
举个具体例子,"苹果树立了 UX 的典范"
一开始是"<苹果>"
然后是"<<苹果>树>"
这里,"<苹果>"和"<果树>",以及"<苹果><树>"三者之间会同时竞争。
最后是"<<苹果><树立>>",立字的出现,抢走了树,即直接把原本的结构拆解了。
这就意味着,前面已经形成的组合,并不拥有永久的边界权。
新的输入一到来,它的内部边界是可以再次被打开的。
这正是我在代码里努力保留的一种能力:
已形成结构,可以被局部重分析。
即把原来那个组合的右半边重新拿出来,和新来的节点再竞争一次。
竞争发生在边界上,而边界并不稳定
比如说,一个组合节点本身可以拆成左右两部分:
- 左半部分
- 右半部分
当右边又来了新的节点时,真正的竞争不是"整个旧节点"和"新节点"怎么比,
而是:
旧结构内部原有的结合,和右半部分与新节点之间的新结合,谁更强。
也就是说,要比较的是:
- 旧的边界是否还应该保留
- 还是应该把旧结构的右侧切出来,交给新的右邻居
这时候,系统其实是在做一件非常像"重新断词"的事:
- 如果旧边界更强,就维持原状
- 如果新边界更强,就把右半部分从旧结构里抽出来,重新组合
这个过程特别像读一句话时的感觉:
起初你已经顺着某种方式理解了,
但看到后面一个字,你突然觉得不对,
于是前面的边界轻轻松动了一下,
后面的结构接管了它。
它总体上维持了一种平衡,让整体解释始终处于当下的最优解中。
旧组合输了,不只是这一次输了
让我们继续,真正关键的一步还不在"重切"。
而在于:
如果旧组合在这次竞争里输了,它不应该毫发无损地退场。
因为如果系统每次都只是临时比较一下,比较完就算了,那么它并没有真正从经验中学习。
所以我么可以让旧组合在失败时,强度被削弱一点。
这意味着,竞争不只是决定当前怎么切,还会反过来影响过去的经验与记忆。
也就是说:
- 一个组合如果一次次守住自己的边界,它就会越来越稳
- 一个组合如果一次次被后来的结构打败,它就会慢慢失势
于是,"什么看起来像一个自然的词",就不再是词典里预先写好的答案。
它更像是长时间竞争后的优胜者。
有时出场竞争的,甚至不是节点自己
再后来,这件事情还可以更进一步。
因为一个节点不一定只有它当前这一个身份。
它有时还属于某个更高层的父节点,某个抽象类,某个更大的结构。
于是可以让系统在竞争时,不只拿当前节点自己出场。
它还会先看一眼:
这个节点有没有一个更高层的身份,比它自己更适合参与当前竞争?
如果有多个父节点,它还会挑出其中最有竞争力的那个。
这就变成了一个很巧妙的机制:
在同一个位置上,真正参与判断的,不一定是表面上这个节点本身,
而可能是它背后的更高层结构。
这很像我们理解语言时的某种经验。
回顾前面的例子:
"<苹果><树立>"隐含了"主语+谓词"的合法结构。
有时候你看到的不是一个孤立的字词,
而是某个更大的模式突然浮现出来,替代它参与理解,尽管这个过程还是无意识的。
所以竞争也不只发生在平面上。
它会沿着层级向上走。
一个局部的切分问题,有时候会被更高层的结构接管。
这时"分词"变成了动态裁决
到了这里,我意识到,现有代码实现里的"竞争"已经不只是:
- AB 和 BC 谁分数高
而是一整套更活的东西:
- 已形成的结构可以被重新打开
- 旧边界和新边界会围绕一个位置争夺解释权
- 更高层的父节点也可以下来参与这场竞争
- 竞争结果不只影响这次切分,还会反过来改写节点强度
- 于是长期看来,稳定结构会越来越稳,脆弱结构会慢慢消退
换句话说,系统不是先给出一个固定切法,再去做理解。
它是在输入不断到来的过程中,持续地重新安排边界,持续地修正"到底哪里该连在一起"。
这既和现实中的组织变迁相似,也更像前文观察到的人脑过程了。
不是看见一句话,然后把它机械切开。
而是边读,边猜,边连,边改。
前面的东西从来没有完全结束。
后面的输入,总有机会让它重新开始。
在新华词典语料上的实践
我用互联网上下载的词典语料去实践了这个思路。
最终结果基本达到了预期。
它可以自动得到:
- "你们"
- "苹果"
这样的组合词。
也可以得到:
- "你,我,它,他"
- "黑,红,黄,蓝"
- "!,?,~,。"
这样的聚合节点。
这些东西是它自己从频次、邻接和分布相似性里长出来的。
它甚至"学会了"拼音和段落换行,这真是让人振奋。
因为我验证了一套可以自己生长结构的机制,而无需一个预先规定好的词表。
再往前一步:生成
接着我又往前走了一步。
既然已经得到了一套 Node 网络,那能不能让它做一点预测和生成?
于是我让它基于已有的 Node 和邻居分布去生成。
结果是它大多数时生成一些毫无意义的词语组合。偶尔有一些符合语法结构的句子。
比如:
我,水中一个朴素的马
这句话也不是完全胡说,它有一种奇异的、朦胧的合理性,像诗句的开头。
我看到这个结果时,很是惊奇。
一方面,这个系统当然非常原始,和真正的大模型差得太远。没有注意力,没有反向传播,也没有海量参数。
但另一方面,它确实证明了一件事:
只要给系统足够的统计线索,结构是可以自己长出来的。
具象的词可以长出来。
抽象的类可以长出来。
甚至某种原始的生成能力,也可以长出来。
很好,现在只差语义了。
现在,理解什么是语义
我前面一直在做的事情,其实都还停留在结构上。
字符如何组合成词。
词如何在竞争中稳定下来。
相似位置上的词,又如何聚合成一类。
若想回答:
语义到底是什么?
这个问题几乎会把你重新带回语言的源头。
一个词的意义,到底在哪里
我们还是先观察:
我走在路上,看到路边停放的车辆,车上有车牌号。
我看着那一串无意义的符号组合,脑袋不用思考,就会冒出"车牌号"。
它们明明只是一串字母和数字的组合。
我把这串组合发给大模型,它也说这看起来像是一个车牌号。
好在我之前已经对符号学有涉猎,我们可从下面的抽象角度去理解。
在符号学里,一个词可以看成是能指 。
它指向某种东西,而那个被指向的东西,就是所指 。
那么那个所指,也就是这个词的意义。
比如"苹果"这个词。
"苹果"这两个字本身,只是一段声音,一串字符。
关键的地方是,它会把人的意识牵向别的东西。
牵向什么?
牵向那个圆圆的、能吃的水果。
牵向红色。
牵向甜。
牵向脆。
牵向超市、水果摊、果园。
甚至牵向《白雪公主》,牵向牛顿,牵向手机品牌。
这时你会发现,所谓"所指",可能并不是某一个单独的对象。
它更像是聚集在一起的一团东西。
所指也许不是一个点,而是一片模糊的关联
有时,我们会把意义想得很简单。
比如说:
- "苹果"这个词,对应现实中的苹果
- 所以"苹果"的意义,就是那个物
但人实际理解"苹果"的时候,显然不只是调出一个孤零零的水果图像。
它会同时带出许多别的关联:
- 它是什么颜色
- 它是什么味道
- 它能做什么
- 它属于哪一类
- 它和哪些故事连在一起
- 它和哪些别的词经常同时浮现
所以如果要更贴近真实一点,可以把它理解成这样:
一个词的意义,未必是单一对象,而可能是它所牵引出的一整片关联网络。
只是这个网络,并不是清晰的。
它没有明确边界。
有些联系更近,有些更远。
有些是稳定的,比如"苹果"和"水果";
有些是文化性的,比如"苹果"和"白雪公主";
有些甚至是个人的,比如某个人用手机拍摄苹果的记忆。
所以我会更愿意说:
语义,也许就是一个词所能激活的全部关联,形成的那个模糊集合。
这个模糊的集合也不是孤立的,它会在整个语义网络中有机连接。
比如,我们从苹果联想到红色,从红色联想到火焰,从火焰联想到热烈而奔放的情感体验。
这些关联是从哪里来的
那接下来就会遇到另一个问题:
这个"指向"本身,是怎么建立起来的?
我目前能想到的一个朴素的答案,是:
- 时间上的相邻
- 空间上的相邻
也就是说,一个词之所以会和某些东西连起来,可能不是因为世界里先写好了定义,
而是因为它们反复在时间和空间里一起出现。
比如一个孩子听到"苹果"这个词的时候,不是凭空听到的。
可能是:
- 大人指着桌上的那个东西说"苹果"
- 他看见它是红的,圆的
- 他咬下去,觉得甜
- 它经常和"吃""买""水果"这些动作和词一起出现
于是"苹果"这个声音,就和这些经验反复缠绕在一起。
久而久之,这些相邻关系就不再只是偶然相遇,
而会慢慢凝成一种稳定的指向。
所以如果再往前推一步,也许可以说:
意义最早不是定义出来的,而是由反复共同出现的经验,一点一点粘出来的。
词内部的组合,和词向外的扩散
这时你可以想到,我前面做的组合和聚合机制,其实已经隐含了一种最原始的意义形式。
比如"苹果"这个词,内部首先是:
苹 -> 果
这是它作为词的内部结合。
也就是说,它先作为一个稳定单位长出来。
但一旦这个单位长出来之后,它又会继续向外连接别的东西:
- 红色的
- 香甜的
- 是水果
- 树
- 吃
- 白雪公主有一颗
- 手机
可以说,语义至少有两层:
第一层,是内部的结构稳定。
也就是"苹"和"果"为什么会成为"苹果"。
第二层,是这个新形成的单位,会向外和哪些经验、哪些词、哪些场景产生新的连接。
于是"苹果"的意义,不只是"苹+果"。
真正的语义,更像是:
"苹果"作为一个稳定单位形成以后,它向外所牵连出的那一整片东西。
语义也许不是定义,而是关系密度
想到这里时,语义就不再可以理解成一句词典式定义。
词典当然可以告诉你:
苹果:蔷薇科苹果属植物的果实。
但这显然不是人脑里"苹果"的全部。
甚至严格说,它只是对某一部分关系做了符合常识的局部表达。
真正抵达其实质的意义,并不是一句定义,
而是一个节点和别的节点之间,有多少连接,连接到哪里,哪些近,哪些远,哪些强,哪些弱。
换句话说,语义也许不是"这个词等于什么",
而更像:
这个词在一整张经验网络里,处在什么位置。
它连向什么。
它靠近什么。
它可以被什么替换。
它通常和什么共现。
它会把人带去哪些场景,哪些感觉,哪些情绪,哪些故事。
如果这样看,语义就不再是静止的解释。
它更像是一个位置,一个方向,一团关系密度。
语义也许也能长出来
到了这里,我们可以做出一个猜想:
也许语义并不是最后额外加上去的一层东西。
它可能也是长出来的。
或者换个词,它是涌现出来的。
字符通过组合,长出词。
词通过聚合,长出类。
而词和类再通过时间上的相邻、空间上的相邻、场景上的相邻、叙事上的相邻,不断向外连接,最后长出意义。
这样一来,语义就不再是某种神秘的,不可解释的跳跃。
它仍然在"关联"这条路线上,只不过关联的范围更大了,层次更深了,也更模糊了。
如果真是这样,那么"意义"也许并不在某一个节点里面。
它散布在节点与节点之间。
散布在词和感觉之间。
散布在词和场景之间。
散布在词和经验之间。
而一个词之所以有意义,不是因为它内部装着定义。
而是因为它能从外部牵动一大片东西。
语义的与终点
现在回头看,"很好,现在只差语义了"这句话,其实不是快到终点了。
恰恰相反。
走到这里,你会发现,前面那些组合、聚合、竞争、遗忘,可能都只是在铺路。
真正更深的地方是:
一个稳定的词,如何和世界、经验、感觉、故事发生连接。
而所谓"语义",也许并不是一个精确边界清楚的定义体。
它更像是一团模糊但稳定的关联结构。
你碰一下其中一个点,周围一圈东西都会亮起来。
如果真是这样,那么语言的意义,也许不是藏在词典里。
它更像是从无数次相邻、无数次共现、无数次经历里,慢慢长出来的一整片光晕。
这好似人生。
这件事真正吸引我的地方
这个过程中,我发现:
对一个东西最深的理解,往往不来自"看懂了",而来自"我亲手造过一个"。
哪怕这个东西很粗糙,很幼稚,远远不如工业级方案成熟。但当你真的去造,你会遇到那些本来在纸上看不见的问题:
- 阈值怎么设
- 竞争怎么处理
- 遗忘什么时候发生
- 聚合到什么程度才合适
- 组合和聚合如何互相影响
- 如何从零理解语义
这些问题会逼着你不断往本质处想,又进一步遇到重重阻难。
举个例子:我一开始发现词典语料远远不够,于是引入了维基百科中文语料,接着引入了新闻语料。但是我发现我的电脑 CPU 跟不上了,而算法性能优化空间小,因为我要保持其通用性以保证算法的快速迭代。于是我更换了一代电脑。(结果是,我日常玩 PC 游戏更流畅爽快了)
所以到最后,我反而觉得,这整件事最重要的收获,并不是我做出了一个怎样的 Node 系统,如何理解了语义。
而是我越来越确认了自己的思考方式。
我喜欢先看生活里的现象。
我喜欢先不急着看答案,而是让问题在脑子里发酵。
我喜欢去读一些看似绕远路的书,比如符号学、语言学。
我喜欢在卡住的时候等待,而不是强行推进。
我喜欢用代码去验证一个模糊的想法。
我也喜欢把这些想法反复拿去和大模型讨论,不停打磨。
这种方式当然不高效。
但我始终觉得,它很有生命力。
因为它不是为了尽快得到一个标准答案。
它更像是一条向上蜿蜒生长的藤蔓,由于时间跨度大,藤蔓变得错综复杂,将众多因果交织在了一起。
等等,这不正是我们前面所述的语义网络吗?
最后
从一句:
我喜欢吃苹果
一直走到分词、BPE、渐进式学习、组合与聚合、Node 系统,再走到:
我,水中一个朴素的马
这一路看起来,我永远在试着理解:
语言到底是怎么从一堆字符里长出来的。
但额外的,我想阐述的是其背后的原始驱动力:
一个问题如果真的被带进生活,它就不会只在原来的轨道上发展。
你为了它做的选择,会在现实中碰到各种意外后果,而这些后果又会继续通向别的地方。
这些事情在事后能被串成因果。但最迷人的是,你根本想不到它们会彼此相连。
分词和游戏本来毫无关系,但因为我真的沿着这个问题走了一段路,它们就在生活里接上了。
人生和语义竟也可以产生关联,同属于一个隐喻结构。
这种不可能预先设计出来的连接,恰恰是生活最有魅力的地方。