Tokenization 与模型内部的文本表示
模型处理一句话之前,先把文本变成有限词表中的编号,再查表取出向量。经过网络计算,同一个编号在不同句子里会得到不同的上下文表示。分清这几个阶段,就能解释为什么“一个汉字几个 token”“能否替换 tokenizer”“拿隐藏状态直接做搜索”是三个不同问题。
Token 解决的是怎样编码文本
按整词建词表,会遇到新词、拼写变化和复合词;按单字符编码,序列又可能过长。Sennrich 等人的子词研究用可复用的较小片段表示罕见词。这里的 token 可以是完整单词、词的一部分、标点或字节组合,边界取决于具体 tokenizer。
BPE 从较小单位出发,学习相邻片段的合并规则;WordPiece 使用自己的词表学习与匹配方法;Unigram 为候选切分建立概率模型。这些方法不等于按词典“理解词义”。Hugging Face 的算法说明给出了它们的区别。SentencePiece 是可以训练 BPE 或 Unigram 等模型的工具,能够直接处理原始句子,不要求先用空格切出词;见原论文。
以一个人为规定的教学词表为例,重新加载 可以被编码成 重新、加载,也可以是四个单字。这不是对真实模型切分结果的预测。一个支持完整字节覆盖的 tokenizer 可以把未见过的字符拆成字节片段,但“能够编码”仍不代表模型理解该语言。稀有字符、emoji、混合语言和长数字都可能占用较多 token。上下文长度要用所选模型的 tokenizer 统计,不能把字符数固定除以某个系数。
编号怎样变成向量
设词表大小为 ,隐藏宽度为 ,模型保存一个可训练矩阵 。token ID 为 时,输入向量就是矩阵第 行。编号只是索引;编号 20 不比编号 10“多一倍意思”。
假想词表包含 BOS=0、重新=1、加载=2,输入 [0,1,2] 对应三个向量。取批量大小 2、补齐长度 5、隐藏宽度 4,张量形状依次为:
查表也可理解为 one-hot 向量乘以矩阵,但实现不需要真的分配长度为 的 one-hot。下面的纯 Python 例子仅演示查表,不训练模型:
embedding = [[0.0, 0.1], [0.5, -0.2], [-0.1, 0.8]]
ids = [0, 1, 2, 1]
x = [embedding[i] for i in ids]
assert x[1] == x[3] # 同一个 ID 的初始向量相同
到了上下文层,“银行账户”和“河岸”中的英文 bank 即使恰好对应同一个 ID,也会因可见上下文不同而获得不同表示。网络更新表示所用的计算见注意力机制。
顺序和有效位置也属于输入
只有一组词向量不足以表达“猫追狗”和“狗追猫”的区别。模型还需要顺序信息。原始 Transformer 在输入中加入位置向量;RoPE 则旋转注意力中的查询和键。两者接入位置不同,不能把“位置编码”一律理解为往输入加一个数;结构细节见 Transformer。
为了合成批量,长度分别为 3 和 5 的句子可以补齐到 5。补齐 token 占据张量位置,但通常不应成为被注意力读取的有效内容,也不应成为监督损失中的有效目标。注意力 mask 和损失 mask 负责不同事情;把 padding 从 loss 中排除,并不会自动禁止别的位置读取它。具体库还可能分别处理 causal mask、padding mask 和位置编号,应遵守该模型的输入约定。
聊天模板也会增加角色标记、消息边界及生成起始符。比较两次请求的 token 数时,应统计实际序列化后的输入,而不只是肉眼看到的用户句子。截断会删除实际证据;把重要条件截掉以后,提高生成长度无法补回它。
竖着看任意一列:BERT 把三个等宽向量相加。第一行表示 token,A/B 行标记所属句段,最下面一行表示位置。重复出现的分隔符虽然使用相同的 token 向量,句段向量和位置向量却不同。这是 BERT 的输入构造方式;使用 RoPE 的模型会在别处加入位置信息。
三种常被叫作 embedding 的东西
token embedding 是查表得到的初始向量。上下文隐藏状态 是某一层、某一个位置的输出。检索 embedding 通常是经过指定池化和训练目标得到的句子或文档向量。它们可能拥有相同维度,却承担不同任务。
若把 [batch,sequence,d] 平均成 [batch,d],代码确实产生了一个向量,但搜索质量还取决于训练目标、池化、补齐 mask、归一化以及查询和文档格式。不能因两个向量都长 768 就把它们放进同一个索引。详细的任务比较见 Embedding、Reranker 与分类器。
检查一条真实输入链
在已有本地模型上,可以选择一组公开示例:同义的中英短句、带空格与不带空格的代码、一个生僻字,以及同一句话的纯文本和聊天模板版本。记录 token ID、可读片段、长度、特殊 token 和解码结果。这个练习检查的是输入表示,不是模型能力排名。
重点观察四件事:是否因规范化改变原文,长文本在哪里截断,特殊标记是否重复,以及批量补齐后有效位置是否一致。部分 tokenizer 会规范化文本,所以 decode(encode(text)) 不一定逐字节等于原文。最终必须把模型权重、tokenizer、特殊 token 配置和聊天模板当成匹配的一组;仅替换词表会改变编号含义,原来的嵌入行随即对应错误片段。
理解输入表示后,可以继续读 Encoder、Decoder 与 Encoder–Decoder:它们最大的结构差别之一,就是每个位置能够看到哪些其他位置。
Hugging Face 的 BPE 课程 从词频和相邻片段频率开始,逐步构造 tokenizer。手动跟一轮合并,再看学到的规则如何切分新词,就能分清“学习词表”和“使用已有 tokenizer”这两件事。