第三部 · 语言第 7 章 / 14

文字怎样变成数字

一个大语言模型从来没有见过一个「字」。它见到的是 token,处理的是向量。这一章讲这两步转换,它们决定了模型能做什么、不能做什么。

主线 7 分钟 · 深入 6 段 · 实验 2 个
文字流进机器,变成珠子,再散成星星。
文字流进机器,变成珠子,再散成星星。

问一个大语言模型「strawberry 这个词里有几个 r」,很多版本会答错。问它把一句话倒着写,它常常写乱。这些错误让人困惑:一个能写论文、能编程的系统,怎么会数不清三个字母?

答案不在模型的「智力」,而在它的眼睛。模型从来没有见过「strawberry」这个词,它见到的是两三个编号,比如 [496, 675, 15717]。字母 r 对它来说不是一个独立存在的东西。这一章讲文字变成数字的两步:先切成 token,再变成向量。理解这两步,你就理解了大语言模型的一大半奇怪行为。

先说清什么是语言模型

「语言模型」这个词在这门课后半部分会出现几百次,值得先给它一个精确的定义。

语言模型是一个概率分布:给定前面的文字,它对「下一个词是什么」的每一种可能给出一个概率。

「今天天气真」后面,「好」的概率大概是 0.4,「热」0.2,「差」0.1,「香蕉」接近 0。一个模型如果能对任何前文给出准确的概率,它就「懂」这门语言,至少在统计意义上懂。翻译、写作、问答、总结,全都可以化归为「给定这些前文,最可能的续写是什么」。

这个定义有七十年历史。1948 年香农就用字母的统计规律估算过英语的信息量;1990 年代的语音识别和输入法用的 N 元模型,就是数「前 N 个词之后最常出现哪个词」。本站首页那个「只看前一个字」的小玩意,是它最简单的形式。今天的大语言模型仍然是这个定义,变化的只是它能看多长的前文(几十万个词而不是一两个),以及它用什么东西去估计概率(一个万亿参数的神经网络而不是一张计数表)。

第一步:切成 token

模型不能直接处理文字,它需要一个有限的「词表」,把任何输入切成词表里的单元。这些单元叫 token

最朴素的办法是按字(或英文按单词)切。按字切,词表小(几千个汉字),但每个字承载的信息少,「电脑」要拆成两个单元才有意义。按词切,每个单元意义完整,但词表爆炸(英文有几十万个词,还不算变形和新词),而且遇到没见过的词就傻了。

今天几乎所有模型用的是一种折中:字节对编码(BPE)。它从单个字节开始,统计训练语料里哪两个相邻单元最常一起出现,把它们合并成一个新单元,反复几万次。结果是一个几万到几十万条的词表,常见词是一个 token,不常见的词被拆成几个有意义的片段,任何生僻字、表情符号、代码符号都能被拆到字节级别,永远不会「不认识」。

代价是模型看到的世界很奇怪。「strawberry」可能被切成「str」「aw」「berry」,字母 r 分散在三个片段里,模型没有任何直接途径去数它。「2024」可能是一个 token,「2025」可能是「202」「5」两个。中文的情况更微妙:一个常用汉字通常是一个 token,但很多字要占两三个,因为词表主要是在英文语料上建的。

这一步的两个实际后果值得记住。第一,上下文窗口是按 token 算的。一个「十二万八千 token」的窗口,装英文大约九万个词,装中文大约六到九万字,取决于分词器对中文的友好程度。第二,模型的很多「愚蠢」是分词造成的:数字母、倒着写、押韵、拆字游戏,都是在 token 的边界上摔倒的。

用一个小例子走一遍算法。假设语料里有这些词(后面是出现次数):low 5、lower 2、newest 6、widest 3。先把每个词拆成字符,并在词尾加一个标记:

l o w </w>        5
l o w e r </w>    2
n e w e s t </w>  6
w i d e s t </w>  3

统计所有相邻字符对的出现次数:e s 出现 9 次(newest 6 + widest 3),最多,于是合并成 es。再统计:es t 出现 9 次,合并成 est。再来:est </w> 9 次,合并。然后是 l o 7 次,合并成 lolo w 7 次,合并成 low

几轮之后,词表里多了 lowest</w> 这些片段。此时 lowest 这个语料里从未出现的词,也能被切成 low + est</w> 两个有意义的单元,而不是六个字母。这就是 BPE 的妙处:它在「词表小」和「单元有意义」之间找到了一个由数据决定的平衡点,而且对新词有很强的适应力。

真实模型的词表由几万到二十万次这样的合并得到。合并的顺序就是分词的规则,所以同一个模型的分词是确定的、可复现的,本站的实验用的就是几个真实模型的合并表。

大模型的服务按 token 计费,输入和输出分开算,输出通常贵几倍。理解了分词,就能建立数量感。

一段一百字的中文,在对中文友好的分词器下大约一百二十个 token,在不友好的分词器下可能两百个。一本三十万字的小说,大约四十到六十万个 token,装不进大多数模型的窗口,所以「把整本书发给它总结」在很多产品里其实是先切成几段分别处理。一次带着二十轮历史的对话,每发一句新话,整个历史都要作为输入重新送进去(模型没有记忆,第十章会讲),所以对话越长,每一轮越贵,这是产品限制对话长度或自动摘要历史的原因。

另一个后果是语言之间的不平等。同样一句话,英语用的 token 最少,中文、日文次之,很多小语种要三到五倍。这意味着用同一个模型,非英语用户支付更多、能装进窗口的内容更少,而且模型在训练中见到的非英语 token 也按比例更少。近几年的模型在扩大词表、改善多语言分词,差距在缩小,但没有消失。

当你在为一个班级、一所学校设计 AI 应用时,这些数字会直接变成预算。一个简单的估算法:每次调用的输入 token 数乘以调用次数,再乘以每百万 token 的价格。

英文有天然的分隔符:空格。中文没有。这在分词之前就是一个问题:「南京市长江大桥」可以切成「南京市 / 长江大桥」,也可以切成「南京 / 市长 / 江大桥」。传统的中文信息处理花了几十年在这个问题上,词典、统计模型、序列标注,都是为了先把词切对。

BPE 绕过了这个问题,方法是不再假设「词」存在:它只按字节统计共现,切出来的单元可能是一个字、一个常用词、甚至半个字(一个汉字的 UTF-8 编码是三个字节,如果某个字不常见,它可能被拆成字节碎片)。这让模型「不认识」任何字的情况消失了,代价是模型看到的中文是一种人不熟悉的切法。

具体的数字取决于词表。GPT-2 时代的词表几乎没有为中文优化,一个汉字平均要两到三个 token;2023 年之后的词表普遍扩大到十几万到二十几万,并加入了大量中文语料训练,常用汉字和常见词多数成了单个 token,中文的「token 效率」已接近英文。国产模型的词表在这一点上通常做得更好,因为它们的训练语料里中文占比高。

一个可以在分词实验里验证的小事实:同一句话,用「对中文友好」和「不友好」的词表分别切,token 数可以差一倍。这个差别会直接变成成本和上下文窗口的差别。

上下文窗口的大小在五年里增长了一千倍,它改变了模型能做的事。

年份 典型窗口 大约能装 能做的新事
2019 1K token 一页纸 补全一段话
2020 2K–4K 几页纸 回答一篇短文的问题
2023 32K–128K 一本小册子 读完一份合同、一篇论文
2024 200K–1M 一本书到几本书 整个代码库、整学期的课程材料
2025 之后 1M–2M 一部长篇小说加插图 多轮长任务、大量文档的交叉比对

窗口变大靠三件事:注意力计算的工程优化(第八章说过计算量随长度平方增长,这个问题被各种技巧缓解),位置编码的改进(让模型能处理训练时没见过的长度),以及更多的显存。

但窗口大不等于用得好。研究发现模型对长上下文中间部分的注意力常弱于开头和结尾,「大海捞针」式的测试里,关键信息埋在中间时被漏掉的概率更高。而且窗口不是记忆:每一轮对话,整个窗口都要重新计算,越长越贵、越慢。所以在实践中,「把所有材料都塞进去」往往不如「先检索出相关的几段再放进去」,那就是第十章的检索增强生成。

第二步:变成向量

有了 token 的编号,还不能直接算。编号只是名字,「496」和「497」之间没有任何意义上的关系。模型需要的是一种能计算的表示:相似的词,数字也相似。

办法是给每个 token 分配一个向量,比如一串 4096 个数字,叫它的嵌入(embedding),或者词向量。关键在于,这些数字不是人定的,是训练出来的。训练时,模型被要求预测下一个词,为了预测得好,它不得不把「用法相似的词」放到相近的向量上,因为它们后面跟的东西相似。「猫」和「狗」的向量会靠近,因为它们前后出现的词高度重叠;「猫」和「宪法」会离得很远。

意义的几何:相近的聚在一起,关系是一个方向。
意义的几何:相近的聚在一起,关系是一个方向。

这就把语言变成了几何。在这个几千维的空间里,距离对应意义的相似,方向对应意义的关系。2013 年的一项著名发现是,这个空间里可以做算术:

$$\vec{\text{国王}} - \vec{\text{男人}} + \vec{\text{女人}} \approx \vec{\text{女王}}$$

「男人到国王」这个方向,和「女人到女王」这个方向几乎一样。类似地,「北京 − 中国 + 法国 ≈ 巴黎」。没有人教过模型什么是首都、什么是性别,这些关系是它为了预测下一个词而自己发现的结构。第六章讲的「表示学习」,在这里换了一种输入而已。

两个向量是否「相似」,最常用的度量是它们夹角的余弦:方向完全一致时为 1,垂直时为 0,相反时为 −1。用夹角而不是距离,是因为向量的长度往往和词频有关,而我们关心的是方向。

$$\cos(\theta) = \frac{\vec a \cdot \vec b}{|\vec a|\,|\vec b|}$$

这个简单的度量支撑了今天大量的应用。语义搜索:把文档和查询都变成向量,返回余弦最大的文档,于是搜「怎么让孩子爱上阅读」能找到一篇标题是「培养阅读习惯的五个方法」的文章,尽管没有一个词相同。推荐:把用户的历史行为和商品都变成向量,推荐方向接近的。检索增强生成(第十章会讲):先用向量找出相关资料,再让模型基于资料回答。手机相册搜图:把图片和文字变到同一个向量空间,第六章开头那个问题的完整答案就在这里。

理解了「意义即几何」,你也就理解了它的局限:向量只反映训练语料里的共现,语料里的偏见会被忠实地编码进去。早期的词向量里,「医生」离「男人」比离「女人」近,「护士」则相反。这不是模型的观点,是语料的统计;但它会通过模型影响下游的每一个应用。

既然文本、图片、用户都可以变成向量,「找最相似的」就成了一个基础操作。一个知识库有一百万段文字,每段一个几千维的向量,用户提问时要在一百万个向量里找出最接近的十个。逐个算余弦相似度太慢,于是有了专门的数据结构和系统,通常叫向量数据库。

它们的核心是「近似最近邻」算法:不保证找到绝对最近的,但能在毫秒内找到足够近的。常见做法是把向量空间分区、建成图或树,查询时只在少数分区里比较。这是一个纯工程问题,但它决定了第十章检索增强生成的响应速度,也决定了相册搜图、语义搜索、推荐系统能否实时。

对使用者的含义:当有人说「我们把 AI 接到了自己的知识库上」,背后大概率是「把文档切成段,每段算一个向量,存进向量数据库,提问时先检索再让模型回答」。这条流水线的质量取决于三个环节:文档怎样切段(太长会稀释,太短会失去上下文),用什么模型算向量(决定「相似」的含义),以及检索出来的段落怎样喂给模型。出问题时,先查这三处,再怀疑模型。

这两步决定了什么

把这一章合起来看:一段文字进入模型,先被分词器切成 token 序列,再被嵌入层变成向量序列。此后模型内部处理的全部是向量,直到最后一步再把向量变回 token 的概率。

今天天气真好 一句话 ① 分词器切成 token,查编号 今天 天气 210388776311509 ② 嵌入层:每个编号换成一串数字(向量) [0.12, −0.8, 0.33, …] [0.09, −0.7, 0.41, …] [−0.5, 0.2, 0.05, …] [−0.4, 0.3, 0.1, …] 「今天」和「天气」的向量很像:它们前后常出现同样的词 几千维,人读不懂,但可以算距离、算方向 这之后模型内部处理的全是向量,直到最后一步再变回 token 的概率。
两步:切成 token 并查编号,再把编号换成向量。

这两步是模型的「感官」,它们的特性直接决定了模型的能力边界。分词决定了它对字母、数字、标点的「视力」,也决定了它的窗口能装多少内容、每种语言的成本是多少。嵌入决定了它对意义的「感觉」,也决定了语料里的偏见怎样进入模型。当你下次看到一个大模型犯了一个小学生都不会犯的错误,先想想:这个错误,是不是发生在它的眼睛上,而不是脑子里?

下一章进入脑子:把向量序列送进去之后,Transformer 做了什么。

自测先自己想,再点开答案
  1. 用「语言模型是一个概率分布」这个定义解释:为什么翻译可以看作「预测下一个词」?

    答案

    把原文放在前面,加上「翻译成英文:」,模型对后续文字的概率分布中,最可能的续写就是译文。任何「给定输入产生输出」的语言任务都可以化归为在特定前文之后预测最可能的续写。

  2. 为什么大模型经常数不清一个单词里有几个字母?

    答案

    因为模型看到的不是字母而是 token,一个单词往往被切成几个片段,字母分散在片段里,模型没有直接的途径去逐个访问它们。这是分词造成的「视力」问题,不是推理能力问题。

  3. 同一个上下文窗口,为什么装的中文字数通常少于英文词数?

    答案

    窗口按 token 计,而主流分词器的词表主要在英文语料上构建,常见英文词多为一个 token,很多汉字却要占两三个。对中文不够友好的分词器,会让同样长度的中文消耗更多 token。

  4. 「国王 − 男人 + 女人 ≈ 女王」说明词向量空间有什么性质?这个性质是谁教给模型的?

    答案

    说明空间里的方向对应意义关系(性别、地位),关系可以用向量差表示并在不同词对之间迁移。没有人教它,这是模型为了预测下一个词、在训练语料的共现统计里自己发现的结构。

本章术语

语言模型Language model

一个概率分布:给定前面的文字,对「下一个词是什么」的每种可能给出概率。翻译、写作、问答都可化归为「最可能的续写」。七十年来定义未变,变的是能看多长的前文和用什么估计概率。

TokenToken

模型处理文字的基本单元,由分词器从词表中切出:常见词一个 token,生僻词拆成几个片段,任何字符最终都能拆到字节。模型从未见过「字」,只见过 token 的编号,这解释了它数字母、倒写等任务上的失误。

分词Tokenization

把文本切成 token 序列的过程,是模型的「眼睛」。它决定了模型对字母、数字的视力,也决定了各种语言的成本:主流词表在英文语料上构建,很多汉字要占两三个 token。

BPEByte-Pair Encoding

字节对编码。从单个字节开始,反复把语料中最常相邻出现的两个单元合并成新单元,进行几万到几十万次,得到词表。它在「词表小」与「单元有意义」之间取得由数据决定的平衡,对新词适应力强,是今天几乎所有大模型的分词方法。

上下文窗口Context window

模型一次能看到的 token 数量上限,从几千到上百万。超出部分模型看不见;窗口内的中间部分也常比开头结尾受到更少注意。窗口不是记忆,对话结束后模型什么都不记得。

词向量Word vector

每个 token 对应的一串数字(几百到几千维),由训练得到:用法相似的词向量靠近,意义关系对应空间中的方向,于是「国王 − 男人 + 女人 ≈ 女王」。它把语言变成了几何,是文字的表示学习。

嵌入Embedding

把离散对象(token、图片、用户、商品)映射到连续向量空间的操作或结果。词向量是文字的嵌入。语义搜索、推荐、检索增强生成、图文互搜都建立在「把不同东西嵌入同一空间再比较」之上。

向量相似度Vector similarity

衡量两个向量是否「相近」的方法,最常用的是夹角余弦(方向一致为 1,垂直为 0)。支撑语义搜索、推荐与检索增强生成。它反映的是训练语料中的共现,语料的偏见会被忠实编码。