你每次在ChatGPT、Claude或Copilot的对话框里敲下的字句,并不是以“单词”的形式直接喂给模型的。它们会先被一种叫作分词器的东西,切成一段一段的小片。这些碎片就是token。有时候一个token是一个完整的词,比如“the”;有时候是半个词,比如“accessibility”可能会被拆成“access”和“ibility”两部分;还有时候,一个逗号或一个空格也可以是一个单独的token。模型自始至终都没有看过你输入的完整句子长什么样,它看到的只是一串代表这些小碎片的数字序列。
这个看似不起眼的技术细节,恰好解释了你在使用大模型时会遇到的很多困惑。为什么长提示要花更多钱?为什么某些词会让模型卡壳?以及那个经典问题——为什么让一个语言模型数出“strawberry”这个单词里有几个字母“r”,对它来说会出奇地困难?答案都藏在token化那一瞬间。
整个过程可以拆成两步来看。第一步是切分文本。拿“accessibility”来说,分词器不会把它当成一个不可分割的整体。虽然它足够常见,有资格作为一个token存在,但在真实世界中,大量文本都会被拆成更小的、可辨认的语块。那些又长、出现频率又低或者技术性更强的词,经常会被分解成两个甚至更多片段。第二步就是把这些片段转换成数字。每个token都在模型训练时用到的固定词汇表里对应着一个数字。“access”可能是5426,“ibility”可能是9821。模型不直接处理字母或语义,它只是处理一串数字,然后去预测统计上最有可能出现的下一个数字是几,预测完之后再把这个数字翻译回文本,显示给你看。这就是为什么大模型在“把一个单词倒过来写”或者“数出strawberry里有多少个r”这类任务上表现挣扎。它不是在逐字母地盯着s-t-r-a-w-b-e-r-r-y看,而是面对着几个不透明的token块,并没有直接通往块内字母的路径。
token化这件事还带来了一些反直觉的边界效应。空格和标点符号也算作token。一个全是短单词和空格组成的句子,最终消耗的token数量,反而可能比一个由更少但更长的单词构成的短句要多。因此,token数并不直接等同于你看见的单词数,更不等于字符数。更值得注意的是,非英文文本在token化时往往表现更差。绝大多数模型主要使用英文数据进行训练,这就导致表达完全相同的意思,用另一种语言写出来,可能会被拆成明显更多的token。
对于只是偶尔用用这些模型的人来说,到这里就够了。但如果你打算在这些模型之上构建产品,那token限制所定义的“上下文窗口”,才是真正影响大局的部分。每一个模型都有一个最大token容量,它一次性能在内存中保有的token数量是固定的。你输入的提示、先前的对话历史,以及模型即将生成的回答,所有这些都在共享着同一个token预算。一旦你超出了这个限制,上下文就会被压缩:要么更早的消息被丢掉,要么输入被截断,要么干脆直接报错。这意味着你精心设计的长提示,可能并没有被模型完整地看到。
热门跟贴