嵌入是查找表,而非函数
Tokenizer 后的第一层
Tokenizer 将整数 ID 交给模型:[256, 1842, 7301, ...]。Transformer 首先将每个 ID 转换为 d_model 维的浮点向量。该向量存在于 d_model 维空间中(ANDREA-120M 使用 768 维)。
嵌入层是一个查找表,而非函数。 想象一个巨大的矩阵:
shape: (V, d_model)
row 0: [e_0_0, e_0_1, ..., e_0_767]
row 1: [e_1_0, e_1_1, ..., e_1_767]
...
row 8448: [e_8448_0, e_8448_1, ..., e_8448_767]
Token ID i 选择第 i 行。直接数组访问,无算术运算,无激活函数。仅为索引操作。
可训练浮点数
该表中的每一项初始均为较小的随机浮点数(通常从按 1/sqrt(d_model) 缩放的正态分布中抽取)。每当批次中出现对应 Token ID 时,反向传播便会更新该行。训练完成后,相似 Token(如 cat、dog、pet)会得到相近的向量;无关 Token(如 cat、Tuesday、xylophone)则在向量空间中相距较远。
ANDREA-120M 令牌嵌入成本
| 数量 | 数值 |
|---|---|
| V | 8,449 |
| d_model | 768 |
| 参数量 | 6,488,832 |
单独的 token 嵌入表中大约有 650 万个参数,约占 ANDREA-120M 总参数的 5.4%。每个词表槽位对应 768 个浮点数。
嵌入表大小计算
点积衡量相似度
向量作为箭头
一个 768 维的向量存在于人类无法想象的空间中,但相同的代数规则适用于任何维度。以下两种关键运算对 Transformer 至关重要:
模长(箭头的长度):
||v|| = sqrt(v_0² + v_1² + ... + v_767²)
点积(两个向量之间的对齐程度):
u · v = u_0 × v_0 + u_1 × v_1 + ... + u_767 × v_767
点积的含义
在任意维度中都成立的两个事实:
- u · v = ||u|| × ||v|| × cos(theta),其中 theta 是它们之间的夹角。
- 指向相同方向的向量会产生较大的正点积。
- 指向相反方向的向量会产生较大的负点积。
- 呈直角的向量点积为零。
点积 = 未归一化的相似度。 训练后的 cat 和 dog 两个 token 嵌入向量会得到较高的点积,因为反向传播将它们推到一起(两者都预测与宠物相关的上下文)。cat 和 Tuesday 的嵌入向量几乎正交,因为它们预测的上下文不同。
为什么 Transformer 关注
Activity 5 (grow_a_language_model_attention) 通过点积构建注意力:查询向量与键向量做点积,得到的分数表明哪些过去 token 对预测下一个 token 更重要。嵌入向量与点积共同承载了 Transformer 内部的所有交互。
预测相似性
ANDREA 使用学习的位置嵌入
一个问题
一个 token 嵌入告诉模型这个位置上是什么词,但它并不告诉模型这个词所处的位置。如果没有位置信息,transformer 会把 the cat sat on a mat 和 mat a on sat cat the 视为完全相同:一组相同的 token,没有顺序信号。
在 transformer 文献中存在三种解决方案:
正弦位置编码(Vaswani 2017)。 一种基于正弦和余弦的固定数学公式。位置 0 得到一个特定的 768 维向量;位置 1 得到另一个;这些向量从不训练,也从不更新。通过公式可以泛化到任意位置。
RoPE(旋转位置编码)。根据位置对查询和键向量进行旋转。被 LLaMA、Qwen 使用。无需额外参数;旋转已内置于注意力机制中。
Learned(可学习位置编码)。一个形状为 (T, d_model) 的独立嵌入表,其中 T 是上下文长度。每一行都通过反向传播进行训练,与 token 嵌入的训练方式相同。
ANDREA 的选择:Learned
ANDREA 继承自 microGPT 的可学习位置编码方法,而 microGPT 继承自 nanoGPT,nanoGPT 又继承自 GPT-2。理由如下:
- 简洁性。 注意力机制中无需特殊数学运算。位置表与词元表外观相同。
- 与自定义 CUDA 兼容。 ANDREA 的 microgpt_cuda.cu 引擎对两次嵌入查找的处理方式完全一致,无需正弦/余弦核函数。
- 适用于固定上下文。 ANDREA 将 T 上限设为 1024。学习表对固定长度序列表现良好。
ANDREA-120M 位置嵌入成本
| 数量 | 值 |
|---|---|
| T (context) | 1,024 |
| d_model | 768 |
| Parameters | 786,432 |
位置嵌入参数量 0.79M。结合词元嵌入:6.49M + 0.79M = 嵌入参数总量 7.27M(ANDREA-120M)。
它们如何结合
在输入序列的每个位置 t:
x_t = token_embedding[token_id_t] + position_embedding[t]
两个 768 维向量,按元素相加。结果 x_t 流入第一个 Transformer 块。模型不再将它们分开;它学会使用这个合并后的信号。
学习式位置嵌入与正弦位置嵌入
嵌入参数的存放位置
完整的 ANDREA-120M 嵌入层
| 组件 | 形状 | 参数量 |
|---|---|---|
| 词元嵌入表 | 8,449 × 768 | 6,488,832 |
| 位置嵌入表 | 1,024 × 768 | 786,432 |
| 总计 | 7,275,264 |
大约 730 万参数。ANDREA-120M 的总参数量:约 1.2 亿。嵌入层仅占 6%。剩余 94% 存在于 Transformer 模块中(注意力 + MLP,详见活动 5-7)。
非绑定嵌入 vs 绑定嵌入
许多 Transformer 设计(包括 GPT-2)将 token embedding 与最终输出投影绑定:同一 V × d_model 矩阵既用于输入,也用于输出(词汇表上的 logits)。绑定可节省 V × d_model 个参数,且通常能提升模型质量。
ANDREA 使用非绑定嵌入:输入 embedding 与输出投影作为两个独立的矩阵进行训练。Activity 7(grow_a_language_model_transformer_block)涵盖了最终层。
到目前为止的前向传播
输入:token ID [256, 1842, 7301, ...](共 1024 个)。每个 ID 查表得到一个 768 维向量。每个位置也查表得到一个 768 维向量。逐元素相加。结果:一个形状为 (1024, 768) 的矩阵 x,包含 token 与位置向量的和。x 流入 transformer block 1。
Activity 5(grow_a_language_model_attention)涵盖 block 1 的操作:带因果掩码与 softmax 的缩放点积注意力。