第 02 章 · 基础
文本怎样进入模型:Token 与向量
从分词、词表和 Embedding 出发,看懂文字如何变成神经网络可计算的数字。
学习准备与本章目标
- 开始前
- 知道模型会预测下一个 Token
- 学完后
- 解释 Tokenizer 的作用 · 看懂 Embedding 矩阵 · 理解序列位置为何需要额外编码
本章路线图
模型不能直接计算文字。文本必须经过标准化、切分、编号和向量查表;位置编码再告诉模型各 Token 的顺序。
原始文本 → 规范化 → Tokenizer → Token ID
→ Embedding → 位置信息 → [B, T, D] 隐藏状态
学完后,应能根据一段文本估计 Token 成本,解释词表与序列长度的权衡,并检查模型与 Tokenizer 是否匹配。
Token 为什么不是字或单词
如果只按完整单词建词表,生僻词、拼写变化和新词会造成大量未知项;如果只按字符或字节切分,词表虽小,序列却会变长。子词 Tokenizer 在两者之间取平衡:常见片段保留为一个 Token,罕见内容拆成更小单元。
英文 unbelievable 可能被拆成 un、believ、able;中文词组可能是一项,也可能按单字或 UTF-8 字节片段拆开。结果由具体词表决定,不存在“一个汉字固定等于一个 Token”的通用规则。
Tokenizer 的目标不是得到语言学上最漂亮的分词,而是构造适合模型训练的有限符号集合。
BPE、WordPiece 与 Unigram
| 方法 | 核心直觉 | 常见特点 |
|---|---|---|
| BPE | 反复合并高频相邻单元 | 实现直观,GPT 系模型常见 |
| WordPiece | 选择能改善语言模型似然的合并 | BERT 系模型常见 |
| Unigram | 从较大候选词表中逐步删减 | 可保留多种切分概率,SentencePiece 常用 |
SentencePiece 可以直接在原始 Unicode 文本上训练,不要求先按空格切词,适合多语言场景。实际工程中,算法名字不如最终词表、规范化规则和特殊 Token 配置重要。
从文本到 Token ID
一条可靠管线通常包含:
- Unicode 规范化和必要的空白处理。
- 预切分或字节级编码。
- 按词表规则拆成 Token。
- 映射为整数 ID。
- 加入 BOS、EOS、角色、分隔符等特殊 Token。
整数 ID 本身没有大小语义。ID 9000 不比 ID 12 更重要,它们只是词表中的行号。模型训练和部署必须绑定同一份 Tokenizer 文件、特殊 Token 表与聊天模板;只替换权重而沿用错误词表,会让每个 ID 指向错误内容。
聊天模板也是输入协议
对话接口中的 system、user、assistant 最终也会被序列化成普通 Token。一个简化模板可能类似:
<|system|>你是学习助手<|end|>
<|user|>解释梯度下降<|end|>
<|assistant|>
不同模型使用不同控制符和换行规则。推理时缺少 generation prompt,模型可能继续用户文本;训练时把用户部分也计入 loss,可能让模型学着生成问题。模板属于模型协议,而不是可随意替换的展示格式。
Padding、Truncation 与 Attention Mask
同一个 batch 中序列长度不同,需要 padding 到共同长度。attention_mask 标记哪些位置是真实 Token,哪些只是补齐。训练时还要用 ignore_index 排除 padding 标签,否则模型会被迫学习输出 PAD。
截断决定超长文本保留哪一段。只留开头可能丢掉问题,只留末尾可能丢掉系统规则。RAG 和长对话应显式安排系统提示、历史、检索文档和当前问题的 Token 预算。
encoded = tokenizer(
texts,
padding=True,
truncation=True,
max_length=2048,
return_tensors="pt",
)
print(encoded.input_ids.shape) # [B, T]
print(encoded.attention_mask.shape) # [B, T]
Embedding 是可学习的查找表
设词表大小为 、隐藏维为 ,Token Embedding 矩阵为:
输入 ID 形状 [B, T],查表后得到 [B, T, D]。这一步等价于用 one-hot 向量乘矩阵,但查表更高效。训练会更新被访问行的表示,使模型能够利用上下文关系。
Embedding 的“相似”不是固定字典含义。一个 Token 的向量只有放入后续网络与上下文中才发挥作用;同一个词在不同句子里的最终隐藏状态会不同。静态 Embedding 是入口,Transformer 输出的才是上下文化表示。
许多语言模型让输入 Embedding 与输出投影共享权重,称为 weight tying。这样可减少约 V × D 参数,并让输入、输出空间保持联系,但具体架构仍需查看模型配置。
位置为什么必须单独编码
自注意力若不加入位置信息,对输入排列本身没有顺序感。模型需要区分“猫追狗”和“狗追猫”,也要知道当前 Token 与前文相距多远。
- 学习式绝对位置:每个位置有可训练向量,简单但最大长度通常固定。
- 正弦位置编码:用不同频率的 sin/cos 表示位置,无需学习。
- RoPE:按位置旋转 Query 和 Key,使点积自然包含相对距离信息。
- ALiBi:在注意力分数中加入与距离相关的线性偏置。
RoPE 不是把位置向量加到 Token 上,而是对注意力空间中的 Q/K 成对维度做旋转。长上下文扩展会改变位置频率分布,不能只把配置里的最大长度调大;还要验证短上下文能力、远距离检索和真实任务效果。
词表设计影响成本与公平性
上下文按 Token 计数,不按字数。代码、数字、少数语言、罕见符号可能被切得更碎,从而增加序列长度、推理费用和注意力计算。词表过大又会扩大 Embedding 与输出层,并提高 Softmax 成本。
构建多语言 Tokenizer 时要检查各语言平均字符/Token 比率、数字与代码的切分、Unicode 规范化、特殊 Token 冲突,以及训练数据对目标语言的覆盖。
一个最小形状实验
import torch
B, T, V, D = 2, 5, 1000, 64
input_ids = torch.randint(0, V, (B, T))
token_embedding = torch.nn.Embedding(V, D)
position_embedding = torch.nn.Embedding(128, D)
positions = torch.arange(T).unsqueeze(0)
x = token_embedding(input_ids) + position_embedding(positions)
assert x.shape == (B, T, D)
真实 Decoder-only 模型可能使用 RoPE,因此不一定执行位置向量相加,但 Transformer 入口最终仍要得到 [B, T, D] 的表示。
常见错误与排查
- 出现乱码或输出完全失常:核对模型权重、Tokenizer 和聊天模板版本。
- batch 结果受 padding 影响:检查
attention_mask、padding 方向和position_ids。 - loss 异常偏低:确认 padding、提示部分和答案部分的标签遮罩。
- 长文本效果骤降:记录真实 Token 数、截断位置与各段预算。
- 新增特殊 Token 后无效:通常还需扩展 Embedding,并训练这些新行。
章末检查
- 为什么 ID 的数值大小没有语义,而 Embedding 有可学习含义?
- 词表更大和序列更短之间有什么代价交换?
- 聊天模板错误为什么会让同一模型表现完全不同?
- 输入
[B, T]经过 Embedding 后形状为什么是[B, T, D]? - RoPE 与绝对位置向量的注入位置有何不同?
动手练习:任选三段中文、英文和代码,分别统计字符数与 Token 数,写出差异原因,并记录特殊 Token 后的实际上下文长度。
延伸资料
本章进阶内容
原理专题与代码实践
完成主教材后,按顺序阅读原理专题、完成代码实践,并用掌握标准复查本章内容。
- 01
理解字符串为何不能直接进入模型
- 02
比较 BPE、WordPiece、位置编码与表示学习
- 03
练习 Tensor、索引、广播和矩阵乘法
- 04
手算一次分词—查表—形状变化
本章术语
六个关键词
- Tokenizer
- 把文本按固定规则转换为 Token 与整数 ID 的组件。
- 词表
- Token 与 ID 的固定映射集合。
- Embedding
- 把离散 ID 映射为连续高维向量的可学习查找表。
- Padding
- 为组成等长 batch 而补入的占位 Token。
- Attention Mask
- 标出真实、填充或不可见位置的遮罩。
- RoPE
- 通过旋转 Q/K 向量注入相对位置信息的方法。
章节记录
