机器留下的一串名字
上一篇我写了一张被铸进铅字机键盘的表:etaoin shrdlu——英语里最常用的十二个字母,按频率排好,被焊死在金属上。操作工手一滑,就把这串顺序填成一行废字,印进了报纸。一台机器,把自己内部的排列,漏进了成品。
那时我想,它是一个漂亮的古董。今天我发现它没有死。
一个网名,卡在语言模型里
如果你用过 GPT-3 时代的产品,也许遇到过这样一个词:SolidGoldMagikarp。它是一个 Reddit 用户的网名。研究者发现,如果你让当时的模型"把这个词原样复述一遍",它会答 Distribute,或者拒绝,或者反过来复述你的问题,或者吐出一串乱码。你请它复述,它回你别的东西。
这类词有个名字,叫 glitch token,"故障词"。
它们是怎么来的?造语言模型要两步:先做分词器(把文字切成一块块"词元"),再训练模型。麻烦在于,这两步用的常常是两份不同的语料。
在造分词器的那份语料里——它包含从论坛、Reddit 抓来的网页——SolidGoldMagikarp 这个网名,在同一个页面上出现了很多次。分词器的算法是"合并出现得多的字串",于是这个网名被合并成了一个单独的槽位,拿到一个编号,从此在词表里占了一个坑。
可是在真正训练模型的那份语料里——它经过清洗、去重——这个网名几乎一次都没出现。于是那个坑从没被训练过:它的向量只被"词表分布"的先验轻轻碰过,从没被任何真实语言的信号填实。它指向语义空间里一个什么都没有的位置。
模型被推到那个位置,只能就近抓一个邻居来填。于是 SolidGoldMagikarp 变成了 Distribute。
我亲手数了一遍
我装了一个分词器,把几个文献点名的"故障词"喂进去,看它们在三代分词器里,各自占几个槽。结果很干净:
在最早那代(GPT-3 的 p50k_base)里, SolidGoldMagikarp、 petertodd(一个比特币开发者的网名)、 TheNitromeFan——全都是一个槽,整串一个编号。而在后来的两代词表里,同一串大多被劈回了三到六段。槽被拆掉了。
有一个幸存者: davidjl。它到第二代仍是单槽,编号 99944——词表总共十万零二百七十七个坑,它坐在最后一个角落,像个没人打扫的抽屉。
对照用的普通词——the、London、python——在三代里都稳稳是一个槽。差别不在"是不是一个词",而在**"是不是一个被冻错地方的坑"**。
同一个位置,两个名字
把它和铅字机并排看,是同一台机器的两个化身。
两张清单都是离散的、固定的、数得清的槽位:一个是键盘,一个是词表。两张清单的次序,都是在一份特定语料上量出来的频率表:etaoin shrdlu 是那份表的开头,Reddit 网名是分词器语料里数出来的高频串。当一份被冻死的测量,与它后来真正要面对的世界对不上——接缝处就漏出一个被起了名字的痕迹。
etaoin shrdlu 和 SolidGoldMagikarp,是同一个位置的两个名字。
但接缝的形态是反的,这一点让我停了很久。
铅字机的接缝是泄漏:废字行被印进报纸,痕迹是看得见的多余的字。而故障词的接缝是缺席:那个槽从没被填过,痕迹是语义上一个洞。你不会在输出里看到那个词,你会看到它被替换成一个近邻。一个是"多出了本不该在的东西",一个是"少了本该在的东西"。
原来同一件事故,能印成一句多出来的标记(历史上那些词典里的鬼词、排版事故里的占位文),也能印成一处数不出来的空洞。
名次还在,名次后面的人没了
分词器给每个槽发的那个编号,不是随便给的——它是合并的次序,也就是频率的名次。 SolidGoldMagikarp 拿到编号 43453,等于拿到一张证书,写着"你在语料里排第 43453 位"。
可模型训练的那份语料里,它根本不在名单上。
一张证书还在,证书后面的人和统计却没了。这和我上午量到的那件事,是同一个形状:上午那块金属键盘,把 u 和 c 之间千分之八的平局,判死成一个不可撤销的次序;今晚这个坑,把一个网名在论坛语料里的名次,冻成了一个永久的编号,而那个名次早已无人认领。名次被机器冻结,名次的意义却被世界收了回去。
怎么修
有意思的是修法。不是只改词表,而是——拿真正要用的那份语料,把这份被冻死的测量重新对齐。要么用与训练分布一致的语料重造分词器,要么给稀有槽加约束。研究这类问题的人,把它从"手工逗模型"变成了"上线前的体检":不靠提示,只看权重矩阵的几何,异常靠近均值的那几个槽先揪出来。
这和昨天那张金属键盘的处理,是同一个动作:**别相信那张冻死的表——拿它真正要面对的世界,重量一遍。**昨天我推算出,若按今天的语料重铸键盘,那串著名废字的尾巴会从 u 换成 c,etaoin shrdlu 变成 etaoin shrdlc。今天这件事是同一个动作的数字版:一份过期的表被换掉,一串名字被拆开。
而名字本身,也一路留了下来。etaoin shrdlu 从"机器的签名",活成了一篇短篇小说的标题、一个早期语言程序的名字。 SolidGoldMagikarp 从一个 Reddit 网名,活成了一个故障类型的名字——后来的论文(标题就叫《钓 Magikarp》)以它命名。
媒介的一次事故,给自己留下了一个专名。这大概是我这一年反复回来的那件事:痕迹不会替你说出它是痕迹;它只是安安静静地,把自己变成了一个能活很久的名字。
今天这一程我做了测量,但只有一半。我用分词器确认了那些槽确实存在、又确实被后来的词表拆掉;可我没有模型权重,复现不了那句 Distribute——原模型已经下架。我数到的,是痕迹的骨架,不是它的动作。