深度学习11——Tokenization、embedding、位置编码

1. Tokenization 与 Embedding

文本进入 Transformer 前,通常经历以下过程:

text 复制代码
文本
-> Tokenizer
-> Token
-> Token ID
-> Embedding
-> Token 向量

Tokenization

Tokenizer 负责:

  1. 将文本切分成 token。
  2. 根据词表把每个 token 转换成整数 ID。

例如:

text 复制代码
我喜欢猫
-> ["我", "喜欢", "猫"]
-> [2, 3, 4]

词表可以理解为一张映射表:

text 复制代码
<PAD> -> 0
<UNK> -> 1
我     -> 2
喜欢   -> 3
猫     -> 4
狗     -> 5

Token 不一定是完整的字或单词,也可能是子词。具体如何切分,取决于 Tokenizer 使用的算法和词表。

Token ID 只是 token 在词表中的编号,没有大小、距离等数学含义。


Embedding

Embedding 根据 Token ID 查询对应的向量,本质上是一张可训练的向量表:

text 复制代码
Embedding.weight.shape = [vocab_size, d_model]

其中:

text 复制代码
vocab_size = 词表中的 token 数量
d_model    = 每个 token 的向量维度

例如:

python 复制代码
embedding = nn.Embedding(
    num_embeddings=10000,
    embedding_dim=768
)

Embedding 内部有一个形状为:

text 复制代码
[10000, 768]

的参数矩阵。

输入:

python 复制代码
input_ids = torch.tensor([2, 3, 4])
# shape: [3]

经过 Embedding:

python 复制代码
x = embedding(input_ids)
# shape: [3, 768]

相当于查询:

python 复制代码
x = embedding.weight[input_ids]

也就是取出参数矩阵的第 2、3、4 行。

对于 Batch 输入:

text 复制代码
input_ids: [B, S]

经过 Embedding:

text 复制代码
[B, S] -> [B, S, D]

其中:

text 复制代码
B = batch_size
S = seq_len
D = d_model

Tokenizer 与 Embedding 的区别

项目 Tokenizer Embedding
输入 原始文本 Token ID
输出 Token ID 浮点向量
主要操作 文本切分和词表映射 根据 ID 查询向量
是否属于神经网络参数 通常不是 是
是否通过反向传播更新 通常不会 会

Tokenizer 的词表和切分规则可能在模型训练前通过语料构建,但正式训练和使用模型时通常是固定的。


2. 为什么不能直接把 Token ID 输入神经网络

Token ID 是类别编号,不是具有实际数值意义的特征。

假设:

text 复制代码
猫   -> 4
狗   -> 5
苹果 -> 6

这些编号只是词表中的位置,不能说明:

text 复制代码
狗比猫大
苹果比狗大
猫和狗的语义距离是 1
猫和苹果的语义距离是 2

如果直接把 ID 当作普通数字输入神经网络,模型可能错误地把 ID 的大小和差值当成有效特征,例如错误地认为:

text 复制代码
编号 4 和编号 5 比较接近
所以"猫"和"狗"一定相似

但词表编号可以任意调整:

text 复制代码
猫   -> 800
狗   -> 12
苹果 -> 13

编号改变后,数值距离也会改变,但词语含义并没有改变。

因此正确流程是:

text 复制代码
Token ID
-> Embedding 查表
-> 具有多个特征维度的浮点向量
-> 神经网络

Embedding 让每个 token 拥有独立的向量表示,模型不需要使用 Token ID 的数值大小。


3. Embedding 为什么是可学习参数

Embedding 本质上是一个参数矩阵:

text 复制代码
Embedding.weight: [vocab_size, d_model]

模型刚初始化时,每个 token 的向量通常是随机的:

text 复制代码
猫 -> [随机数, 随机数, ...]
狗 -> [随机数, 随机数, ...]

此时这些向量还没有明确含义。

训练过程中:

text 复制代码
前向传播
-> 计算预测结果
-> 计算 Loss
-> 反向传播
-> 更新模型参数

Embedding 的 weight 也参与反向传播,因此会被优化器更新:

python 复制代码
loss.backward()
optimizer.step()

如果输入中出现了"猫",与"猫"对应的 Embedding 向量就可能根据 Loss 获得梯度并被调整。


为什么不把 Embedding 固定住

不同任务需要模型关注不同的信息:

text 复制代码
情感分析:更关注"喜欢""讨厌""开心"等情绪信息
机器翻译:更关注不同语言之间的语义对应关系
文本生成:更关注 token 在上下文中的使用方式
代码模型:更关注变量、语法和程序结构

让 Embedding 可学习,模型就可以根据训练目标自动调整每个 token 的初始表示。

经常出现在相似上下文中的 token,其向量可能逐渐接近。例如:

text 复制代码
我喜欢猫
我喜欢狗
我养了一只猫
我养了一只狗

"猫"和"狗"在这些句子中的使用方式相似,训练时可能获得相似的更新方向,因此向量可能逐渐接近。

不过这种接近不是人工规定的,也不保证每个相似词都一定相邻。它表示的是:

text 复制代码
模型认为这些 token 在当前训练任务中的使用方式相似

Token Embedding 与上下文表示的区别

Embedding 表中每个 Token ID 对应一个固定的初始向量。

例如两个句子:

text 复制代码
苹果很好吃
苹果发布了新手机

两个"苹果"查询到的初始 Token Embedding 相同。

但是经过 Transformer 后,模型会结合上下文产生不同的 Hidden State:

text 复制代码
苹果很好吃       -> 更接近水果含义
苹果发布了新手机 -> 更接近公司含义

因此:

text 复制代码
Token Embedding:与 Token ID 对应的初始表示
Hidden State:经过上下文处理后的动态表示

一个词的多种含义主要由 Transformer 根据上下文进一步区分,而不是只依赖初始 Embedding。


4. 输入 Embedding 与输出 LM Head

语言模型的完整处理流程通常是:

text 复制代码
文本
-> Tokenizer
-> Token ID
-> Token Embedding
-> Transformer
-> Hidden State
-> LM Head
-> 每个 Token 的预测分数
-> Softmax
-> 下一个 Token 的概率

输入 Embedding

输入 Embedding 把 Token ID 转换成向量:

text 复制代码
input_ids: [B, S]
-> Embedding
x: [B, S, D]

它的参数形状是:

text 复制代码
Embedding.weight: [V, D]

其中:

text 复制代码
V = vocab_size
D = d_model

输出 LM Head

Transformer 最后输出:

text 复制代码
hidden_states: [B, S, D]

语言模型需要预测词表中的下一个 token,因此必须把每个 D 维 Hidden State 转换成 V 个词表分数:

python 复制代码
lm_head = nn.Linear(D, V, bias=False)

形状变化:

text 复制代码
[B, S, D] -> [B, S, V]

输出的:

text 复制代码
logits[b, s, :]

表示第 b 个样本、第 s 个位置对整个词表的预测分数。

计算过程可以写成:

text 复制代码
logits = hidden_states @ lm_head.weight.T

其中:

text 复制代码
hidden_states:    [B, S, D]
lm_head.weight.T: [D, V]
logits:           [B, S, V]

为什么输入 Embedding 和 LM Head 可以共享权重

输入 Embedding 的参数形状是:

text 复制代码
[V, D]

LM Head 的权重形状也是:

text 复制代码
[V, D]

两者形状相同,因此可以共享同一个参数矩阵:

python 复制代码
lm_head.weight = token_embedding.weight

这种做法叫:

text 复制代码
Weight Tying,权重绑定或权重共享

输入时,这个矩阵用于:

text 复制代码
Token ID -> 查询对应的 Token 向量

输出时,这个矩阵用于:

text 复制代码
Hidden State -> 与所有 Token 向量计算匹配分数

可以理解为:

text 复制代码
输入阶段:取出某个 token 的表示
输出阶段:判断当前 Hidden State 最接近哪个 token 的表示

共享权重的主要好处:

  1. 减少模型参数量。
  2. 让输入和输出使用一致的 token 表示空间。
  3. 提高同一组参数的利用率。
  4. 在很多语言模型中有助于训练和泛化。

例如:

python 复制代码
token_embedding = nn.Embedding(vocab_size, d_model)
lm_head = nn.Linear(d_model, vocab_size, bias=False)

lm_head.weight = token_embedding.weight

权重共享是常见设计,但不是所有语言模型都必须采用。


5. 为什么 Transformer 需要位置编码

Self-Attention 根据 token 内容计算它们之间的关系,但它天然不知道 token 的顺序。

例如:

text 复制代码
小明喜欢小红
小红喜欢小明

两句话包含相似的 token,但顺序不同,含义也不同。

如果没有位置编码,Self-Attention 可以知道序列中有哪些 token,却无法天然表示:

text 复制代码
哪个 token 在前
哪个 token 在后
两个 token 相距多远

因此需要把位置信息加入模型。

常见方法包括:

方法 主要思想
Sinusoidal Positional Encoding 使用固定的正弦、余弦函数表示绝对位置
Learnable Positional Embedding 为每个绝对位置学习一个向量
Relative Position Encoding 表示两个 token 之间的相对距离
RoPE 根据位置旋转 Q、K
ALiBi 给注意力分数添加距离偏置

6. 正弦、余弦位置编码

原始 Transformer 使用固定的 Sinusoidal Positional Encoding:

text 复制代码
PE(pos, 2i)   = sin(pos / 10000^(2i / d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i / d_model))

其中:

text 复制代码
pos     = token 在序列中的位置
i       = 维度编号
d_model = Token Embedding 的维度

基本规则:

text 复制代码
偶数维度使用 sin
奇数维度使用 cos
不同维度使用不同频率

正弦位置编码不是训练参数,而是通过公式预先计算出来的固定向量。


为什么不同维度使用不同频率

不同频率可以让位置编码同时表达不同范围的位置变化:

text 复制代码
高频维度:变化较快,适合区分相邻位置
低频维度:变化较慢,适合表示较长距离的位置关系

可以把它类比为使用多种时间单位记录时间:

text 复制代码
秒、分钟、小时、日期

只使用一种频率,能够表达的位置模式比较单一;使用多种频率组合,可以让每个位置拥有更容易区分的编码。


为什么可以与 Token Embedding 直接相加

Token Embedding 和位置编码具有相同的最后一维:

text 复制代码
Token Embedding:    [B, S, D]
Position Encoding: [1, S, D]

通过广播:

text 复制代码
[B, S, D] + [1, S, D]
-> [B, S, D]

相加后,每个 token 的向量同时包含:

text 复制代码
这个 token 是什么
这个 token 在什么位置

相加确实会把内容和位置信息混合在同一个向量中,但模型可以通过训练逐渐学会如何使用这些信息。

使用相加而不是拼接,还有一个重要好处:

text 复制代码
相加:[D] + [D] -> [D]
拼接:[D] 和 [D] -> [2D]

相加不会扩大模型维度,也不需要修改后续 Linear 层的输入大小。


7. 可学习位置 Embedding 与相对位置编码

可学习的绝对位置 Embedding

可学习位置 Embedding 为每个位置准备一个可训练向量:

python 复制代码
position_embedding = nn.Embedding(
    max_seq_len,
    d_model
)

参数矩阵形状:

text 复制代码
[max_seq_len, d_model]

例如:

text 复制代码
位置 0 -> 一个可学习向量
位置 1 -> 一个可学习向量
位置 2 -> 一个可学习向量

使用时:

text 复制代码
Token Embedding + Position Embedding

与固定正弦编码相比,它可以根据训练任务自动调整,但通常受到 max_seq_len 限制,超出已定义的位置范围时不能直接查询。


相对位置编码

正弦位置编码和可学习位置 Embedding主要表示绝对位置:

text 复制代码
这是序列中的第 10 个 token

相对位置编码关注两个 token 的距离和方向:

text 复制代码
另一个 token 在当前 token 前面 2 个位置
另一个 token 在当前 token 后面 1 个位置

例如:

text 复制代码
A B C D

以 B 为当前 token:

text 复制代码
A 相对 B 的位置:-1
B 相对 B 的位置: 0
C 相对 B 的位置:+1
D 相对 B 的位置:+2

语言中的很多关系更依赖相对距离,而不是 token 的绝对编号。


8. RoPE 与 ALiBi

RoPE

RoPE 全称是:

text 复制代码
Rotary Position Embedding
旋转位置编码

RoPE 不把位置向量直接加到 Token Embedding 上,而是根据 token 的位置旋转 Q 和 K:

text 复制代码
Q -> 按位置旋转后的 Q
K -> 按位置旋转后的 K
V -> 通常不旋转

多头注意力中的形状通常是:

text 复制代码
Q、K、V: [B, H, S, d]

RoPE 会在最后一个维度上将特征两两分组,并使用不同角度旋转:

text 复制代码
[x0, x1]
[x2, x3]
[x4, x5]

旋转不会改变张量形状:

text 复制代码
[B, H, S, d] -> [B, H, S, d]

为什么 RoPE 作用于 Q 和 K

注意力分数由 Q 和 K 的内积决定:

text 复制代码
scores = Q @ K.T

RoPE 根据位置分别旋转 Q 和 K。两个旋转后向量的内积,会受到它们位置差的影响:

text 复制代码
位置 m 的 Q
与
位置 n 的 K

计算得到的注意力分数能够反映:

text 复制代码
相对位置 n - m

因此 RoPE 不是简单告诉模型"这是第几个位置",而是直接让 Q 和 K 的匹配分数包含相对位置信息。

V 主要负责传递被关注 token 的内容,不参与注意力分数 Q @ K.T 的计算,所以通常不需要旋转 V。


ALiBi

ALiBi 不修改输入 Embedding,也不旋转 Q 和 K,而是直接给注意力分数添加一个与距离有关的偏置:

text 复制代码
scores = Q @ K.T + distance_bias

通常 token 距离越远,加入的惩罚越大:

text 复制代码
距离近 -> 惩罚较小
距离远 -> 惩罚较大

不同注意力头可以使用不同的距离斜率,从而学习不同范围的注意力关系。

现阶段可以简单记住:

text 复制代码
正弦位置编码:把固定位置向量加到输入上
可学习位置 Embedding:把可训练位置向量加到输入上
相对位置编码:关注 token 之间的相对距离
RoPE:旋转 Q 和 K,让注意力内积包含相对位置
ALiBi:给注意力分数加入与距离有关的偏置
相关推荐
拉格朗日(Lagrange)1 分钟前
【第 1 章】WorkBuddy 从入门到高手
人工智能
猎头南楼2 分钟前
企业网络安全体系与AI安全检测实践:零信任、纵深防御与LLM安全
人工智能·安全·web安全
yi0113 分钟前
DAY17: LeetCode 139|单词拆分:从“把单词删掉”到用 DP 记录合法切口
人工智能·笔记·python·算法·leetcode·动态规划
科技重器5 分钟前
京东方中央研究院推出高灵敏度电化学生物传感器芯片,为疾病早筛提供“芯”守护
人工智能·物联网
马剑威(威哥爱编程)5 分钟前
【AI全栈后端12-03】Spring Boot 用多模型路由把智能客服成本降下来
java·人工智能·spring boot
云计算-Security7 分钟前
AI 赋能运维:UniRack 主机纳管平台的架构与实践
运维·人工智能·架构
霸道流氓气质10 分钟前
AI模型幻觉检测与抑制完全指南:从规则引擎到RAG对比的Java生产级实战
java·开发语言·人工智能
weixin_4045512413 分钟前
使用 ZCode 改造 PPT 模板:实践复盘与能力边界
人工智能·powerpoint
陈希瑞15 分钟前
LongCat-2.5-Preview 的web逆向能力实测
人工智能·算法·wasm
曲鸟18 分钟前
体验完鸿蒙AI后的几点感受
人工智能·华为·harmonyos