什么是词嵌入?
词嵌入(Word Embedding)是将离散的词汇符号映射到连续向量空间的技术。它将词汇转换为密集的实数向量,使计算机能够进行数学运算并捕捉词汇间的语义关系。
词汇到向量的映射示例
"国王" → [0.2, -0.1, 0.8, 0.4]
"女王" → [0.1, -0.2, 0.7, 0.5]
"男人" → [0.3, 0.1, -0.2, 0.6]
"女人" → [0.0, 0.0, -0.3, 0.7]
嵌入的数学表示
对于词汇表 $V$ 中的词 $w_i$,嵌入函数定义为:
$$E: w_i \rightarrow \mathbf{e_i} \in \mathbb{R}^d$$其中 $d$ 是嵌入维度,$\mathbf{e_i}$ 是词 $w_i$ 的嵌入向量
嵌入矩阵的实现
在神经网络中,词嵌入通常通过查找表(Lookup Table)实现,即嵌入矩阵 $\mathbf{E} \in \mathbb{R}^{|V| \times d}$。
嵌入矩阵示例 (词汇表大小=5, 维度=4)
import torch
import torch.nn as nn
# 创建嵌入层
vocab_size = 10000
embedding_dim = 512
embedding = nn.Embedding(vocab_size, embedding_dim)
# 输入token索引
input_ids = torch.tensor([1, 15, 234, 67]) # batch中的token IDs
embedded = embedding(input_ids) # shape: [4, 512]
print(f"输入shape: {input_ids.shape}")
print(f"嵌入后shape: {embedded.shape}")
位置编码的必要性
Transformer架构没有内在的位置信息概念,因为自注意力机制对输入序列的排列是不变的。位置编码为模型提供了序列中每个位置的信息。
问题示例:
"我爱你" 和 "你爱我" 在没有位置信息的情况下,Self-Attention会产生相同的结果,无法区分词序的重要性。
位置编码的作用
- 序列顺序:告诉模型词汇在序列中的位置
- 相对位置:帮助模型理解词汇间的相对距离
- 语法结构:支持模型学习语言的语法规律
绝对位置编码
可学习位置编码
最简单的位置编码方法是为每个位置分配一个可学习的向量。
其中 $\mathbf{e_i}$ 是词嵌入,$\mathbf{p_i}$ 是位置 $i$ 的位置编码
# 可学习位置编码
max_seq_length = 512
pos_embedding = nn.Embedding(max_seq_length, embedding_dim)
# 生成位置索引
position_ids = torch.arange(seq_length).unsqueeze(0) # [1, seq_len]
position_embeddings = pos_embedding(position_ids) # [1, seq_len, d]
# 添加到词嵌入
final_embeddings = word_embeddings + position_embeddings
正弦位置编码
Transformer原论文提出的正弦位置编码,使用不同频率的正弦和余弦函数。
正弦位置编码的优势
- 确定性:不需要训练,直接计算得出
- 外推性:可以处理训练时未见过的序列长度
- 相对位置:任意两个位置的相对偏移可以表示为线性变换
相对位置编码
相对位置编码关注的是词汇间的相对距离,而不是绝对位置。这种方法在很多任务中表现更好。
相对位置的表示
其中 $\mathbf{r_{i-j}}$ 是位置 $i$ 和 $j$ 之间相对位置的编码
| 编码类型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| 绝对位置 | 简单直观 | 外推性差 | 固定长度序列 |
| 相对位置 | 更好的泛化 | 计算复杂 | 变长序列 |
| RoPE | 兼具两者优势 | 实现复杂 | 长序列任务 |
旋转位置编码 (RoPE)
RoPE(Rotary Position Embedding)是一种新颖的位置编码方法,通过旋转变换来编码相对位置信息。
RoPE 的核心思想
核心原理:将每个位置的嵌入向量在复数平面上进行旋转,旋转角度与位置相关。
对于位置 $m$ 的查询向量 $\mathbf{q}$ 和位置 $n$ 的键向量 $\mathbf{k}$:
$$f_q(\mathbf{x_m}, m) = \mathbf{R_m} \mathbf{W_q} \mathbf{x_m}$$ $$f_k(\mathbf{x_n}, n) = \mathbf{R_n} \mathbf{W_k} \mathbf{x_n}$$其中 $\mathbf{R_m}$ 是旋转矩阵
RoPE 的旋转矩阵
import torch
import torch.nn as nn
def apply_rope(x, position_ids, rope_cache):
"""
应用旋转位置编码
x: [batch_size, seq_len, num_heads, head_dim]
position_ids: [batch_size, seq_len]
"""
seq_len = x.size(1)
head_dim = x.size(-1)
# 生成旋转角度
inv_freq = 1.0 / (10000 ** (torch.arange(0, head_dim, 2) / head_dim))
sinusoid_inp = torch.einsum("i,j->ij", position_ids, inv_freq)
# 计算cos和sin
cos = sinusoid_inp.cos()
sin = sinusoid_inp.sin()
# 应用旋转变换
x1, x2 = x[..., ::2], x[..., 1::2]
rotated = torch.cat([
x1 * cos - x2 * sin,
x1 * sin + x2 * cos
], dim=-1)
return rotated
ALiBi (Attention with Linear Biases)
ALiBi是另一种相对位置编码方法,直接在注意力权重上添加线性偏置。
其中 $m$ 是头部特定的斜率参数
ALiBi 的优势
- 简单高效:计算开销小,实现简单
- 外推能力强:在更长序列上表现良好
- 无需位置嵌入:完全消除了位置嵌入的需要
位置编码的实际应用
不同模型的选择
| 模型 | 位置编码类型 | 最大序列长度 | 特点 |
|---|---|---|---|
| BERT | 可学习绝对位置 | 512 | 固定长度,性能稳定 |
| GPT | 可学习绝对位置 | 1024-8192 | 逐代增加序列长度 |
| T5 | 相对位置偏置 | 不限 | 良好的外推性能 |
| RoFormer | RoPE | 不限 | 中文优化,长序列 |
选择建议
- 短序列任务:可学习绝对位置编码
- 长序列任务:RoPE 或 ALiBi
- 多语言任务:相对位置编码
- 计算资源受限:正弦位置编码或ALiBi
🔧 位置编码可视化
调整参数观察不同位置编码的效果: