F03|文字为什么要先变成Token编号
从具体对象与小例子开始,理解计算过程与适用条件。
本章目录
文字、字节、Token、编号是四件事
为什么不能把“一个汉字”当成“一次生成”?
Token是分词规则选出来的片段,编号是它在词表中的地址。一个Token可以跨越多个字符,也可能只覆盖字符编码的一部分。教学中把“学习”合成一个Token,只是明确的演示规则,不能当成所有模型的实际分词。
输入与适用条件:本图使用人为词表:PAD0、我1、爱2、学习3、你4、!5;不演示真实BPE训练。
1 / 6 · 原文字
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
先确定待编码原文,两句之间不能直接接起来当作同一人的上下文。
本步状态由图形与说明共同给出。默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
弄清计数单位,可避免上下文预算、输出长度和界面刷新混为一谈。
怎样实现
用目标模型配套分词器编码和解码,并分开观察字符数、字节数、Token数。
可能的代价与误解
更换分词器后沿用旧编号,会读到错误的词表项。
如何判断是否做对
对实际输入做一次往返编解码,并按所用分词器规则处理特殊Token。
字符、字节和Token先分开
你看到的“爱”是一个字符。计算机保存文字需要编码规则;例如UTF-8会把它表示为若干字节,字节是8个二进制位的存储单位。一个字符不必只占一个字节。
Token是分词器处理的片段单位。Tokenizer(分词器)是把文本按约定规则变成Token编号序列、以及把编号解回文本的工具。Token可能覆盖一个词、一部分词、标点、空格,甚至只是一个字符的部分字节,因此不等于字数。
模型内部处理数字而不是屏幕字形。分词器先回答“它是词表中的哪一项”,后面的Embedding再回答“用哪一排数表示这一项”。这两个步骤不能合成一句含糊的“转成向量”。
任何整数最终都会以0、1比特的形式存进电脑,例如十进制编号2可以按8位无符号整数保存为00000010。这说的是机器如何存储整数,不是语言模型怎样表示“爱”的含义。
Token编号2表示查词表第2号项目。后面模型可以用这个编号到Embedding参数表中取出一排包含小数与负数的浮点数。它不等于把2编译成一串0和1。F05会把二进制、One-hot和Embedding三者逐一放在一起比较。
一张教学词表和两条输入
为方便手算,定义以下小词表。<PAD>表示稍后填空位的特殊项。
| 编号 | 片段 |
|---|---|
| 0 | <PAD> |
| 1 | 我 |
| 2 | 爱 |
| 3 | 学习 |
| 4 | 你 |
| 5 | ! |
假设教学分词规则把“我爱学习”切成“我、爱、学习”,就得到[1,2,3];把“你爱”切成“你、爱”,得到[4,2]。编号大小没有语义大小:4不比1聪明,也不能通过4−1=3推导“你减我等于学习”。
词表有6项,用符号N=6表示。后文也会见到论文用V表示词表大小;Attention里的V又表示Value矩阵。本书在基础例子中用N,避免你把两者混淆。
BPE的“学习词表”和“编码输入”不是同一阶段
BPE是一类常见的子词构建方法。先从基础符号出发,在训练语料中反复统计相邻符号组合,选择组合并合并。例如语料中a b经常相邻,就可以增加ab这一项,再重新统计下一轮组合。这一阶段产生词表和合并规则。
使用已经准备好的分词器时,不会每收到一句话都重新统计整个世界的文本。它按固定规则把新输入拆成已有片段,并查出编号。实际实现还有预切分、特殊标记和可能的规范化步骤,不能把它理解成随意切词。
为什么代码很难按字符数估算Token?因为user_id和随机串x7b9q2的可用子词组合不同;换行、缩进和空格也可能影响切分。实际预算要使用对应模型的计数规则,教学词表只是为解释机制。
聊天角色怎样成为输入的一部分
聊天原来是带角色的消息,比如“用户问什么”“助手答什么”。聊天模板负责把这些角色与正文组织成模型训练时认识的形式。模板可能加入表示角色开始、消息结束的控制标记,这些标记也可能占Token。
普通字符串里写“system:”不自动等于真正的系统角色;模型接口如何组织消息,决定服务怎样处理它。不能把人类看见的聊天气泡逐字拼接,就声称复现了商业服务最终输入。
编码后还没发生什么
拿到[1,2,3]时,只是找到了编号;还没有得到每个位置的上下文表示,没有计算下一Token概率,也没有读任何外部文件。分词是表示转换,不是“理解已完成”。
反向解码也要注意边界:有的处理会折叠大小写或规范化文本;有的Token单独解码不是完整字符。代码任务中要区分“意思相同”和“原始字节相同”。
把“我爱学习”编码,再原样解码
输入与条件。固定教学词表:我→1,爱→2,学习→3,你→4,PAD→0;本例明确把“学习”视为一个Token。它不代表某家模型的真实分词。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1切片段 | “我爱学习”切成“我/爱/学习” | 四个汉字、三个Token,字符数与Token数不同。 |
| 2查编号 | 依次查词表 | 得到 [1,2,3],还没有查Embedding。 |
| 3编码另一句 | “你爱”切成“你/爱” | 得到 [4,2];两句话里的“爱”编号相同。 |
| 4反向查词表 | [1,2,3] 对应“我/爱/学习” | 按本例拼接规则恢复“我爱学习”。 |
| 5看字节层 | “我”的UTF-8字节是十六进制 E6 88 91 | 这是字符存储,不是Token编号1,也不是Embedding的特征。 |
把过程连起来。先给分词规则,再给词表编号,才能知道列表究竟代表哪句话。不要从整数大小推测文字含义。
边界与反例。真实分词器还涉及空格、字节、特殊Token及规范化;本例只验证这张明确词表的编码链。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
BPE合并时,为什么不是总选左边那一对
输入与条件。输入字符序列[a,b,c]。假定已学习到两条合并规则:b+c优先级0,a+b优先级1;数字更小优先。词表里a编号1、bc编号5。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1列候选 | 当前相邻对为(a,b)和(b,c) | 分别查到优先级1和0。 |
| 2选择规则 | 先合并优先级0的(b,c) | 序列变成[a,bc],不能同时再用原来的(a,b),因为b已经被合入bc。 |
| 3重新找相邻对 | 现在只有(a,bc) | 假设没有该合并规则,停止。 |
| 4查最终编号 | a→1,bc→5 | 输出[1,5],不是[编号ab,编号c]。 |
把过程连起来。BPE推理使用已经学到的规则顺序;原始相邻位置不等于合并优先级。每次合并后候选邻接关系会改变。
边界与反例。字符是教学基本单位,实际byte-level BPE可能以字节为起点,还需遵循特殊Token和预分词规则。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
课后自检
“学习”在教学词表占一个Token,现实所有模型也一样吗?
查看参考答案收起参考答案
Token编号5和编号1的距离是4,能说明语义距离吗?
查看参考答案收起参考答案
延伸阅读:相关系统问题
01|输入究竟是什么:从用户的一句话到模型张量10|模型输出怎样变成真实工具调用:协议与执行边界
13|每一轮Prompt怎样组装:上下文是一条数据管线
15|检索离线链路:文档怎样变成可查询索引
机制依据:[BPE][UNIGRAM][CHAT]。本节数值与情景除另注外均为原创教学示例;来源不代替中文讲解。