第3章:Transformer基础回顾——自注意力、多头注意力与位置编码

说实话,Transformer刚出来那会儿,我第一反应是“这玩意儿能行?”

毕竟我们做CV的,习惯了卷积那种局部感受野。突然来个全局自注意力,总觉得计算量会爆炸。但后来我在一个文本检测项目里试了试,效果确实惊艳。今天咱们就把Transformer最核心的三个东西掰开揉碎讲清楚。

3.1 自注意力机制:让每个位置都“看见”全局

自注意力(Self-Attention)说白了就是:让序列里的每个元素,都能跟其他所有元素做一次“亲密度”计算。

你想想看,在自然语言里,“他”指的是谁?如果不看上下文,根本不知道。自注意力就是干这个的——它让每个词都能“关注”到句子里的其他词。

具体怎么算的呢?我习惯用这个公式来理解:

Attention(Q, K, V) = softmax(Q × K^T / √d_k) × V

这里Q、K、V都是从输入X通过线性变换得到的。Q是查询,K是键,V是值。嗯,名字有点抽象,我换个说法:

  • Q(查询):当前词想问的问题——“谁跟我有关系?”
  • K(键):其他词的回答——“我跟你关系多大?”
  • V(值):其他词的实际内容——“我携带的信息是什么?”

计算过程分三步:

  1. Q和K做点积,得到注意力分数矩阵
  2. 除以√d_k做缩放,防止内积过大导致softmax梯度消失
  3. softmax归一化后,乘以V得到加权输出

关键点:除以√d_k这一步,我见过不少新手忽略。曾经有个同事在训练时发现loss死活不降,查了半天发现是忘了做缩放。d_k越大,点积结果方差越大,softmax会变得“尖锐”,梯度就没了。

我在项目中遇到过一个问题:当序列长度超过512时,自注意力的计算量会平方级增长。这时候就得考虑稀疏注意力或者窗口注意力了,后面章节会细讲。

3.2 多头注意力:从不同角度理解关系

单头自注意力有个问题——它只能学习一种“关系模式”。

但现实中,词与词之间的关系是多样的。比如“苹果”这个词,可能跟“吃”有动作关系,跟“红色”有属性关系,跟“水果”有类别关系。一个头学不过来。

多头注意力(Multi-Head Attention)就是解决这个问题的:把Q、K、V拆成h份,每份独立做自注意力,最后拼起来。

公式长这样:

MultiHead(Q, K, V) = Concat(head_1, ..., head_h) × W_O
其中 head_i = Attention(Q × W_Q_i, K × W_K_i, V × W_V_i)

我习惯用8个头。为什么是8?其实没有严格规定,但8个头在性能和效果上比较平衡。头数太少,表达能力不够;头数太多,每个头分到的维度太少,反而学不到东西。

头数 每个头维度 特点
4 64 轻量,适合小模型
8 32 最常用,平衡性好
16 16 头数多但维度低,容易欠拟合

我的经验:在轻量Transformer里,我建议把头数控制在4-8之间。头数太多,每个头的维度太低,注意力分布会变得“扁平”,失去区分度。

多头注意力的好处很明显:每个头可以关注不同的位置模式。有的头关注局部语法关系,有的头关注长距离语义依赖。我曾经可视化过8个头的注意力图,发现确实有头专门关注“动词-宾语”关系,很有意思。

3.3 位置编码:给Transformer装上“位置感”

自注意力有个“致命”缺陷:它不关心顺序。

你想想看,“我打你”和“你打我”,在自注意力眼里是一样的——因为它是集合操作,不是序列操作。这怎么行?

位置编码(Positional Encoding)就是给每个位置打上一个“位置标签”,让模型知道词与词的相对位置关系。

最经典的是正弦余弦编码:

PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

为什么用正弦余弦?我个人理解是:它能表示相对位置关系。因为对于任意固定的偏移k,PE(pos+k)可以表示为PE(pos)的线性函数。这意味着模型可以学到“距离为k的两个位置有什么关系”。

不过在实际部署中,我更常用可学习的位置编码(Learned Positional Embedding)。原因很简单:

  • 正弦余弦编码是固定的,不能根据任务调整
  • 可学习编码可以随模型一起训练,更灵活
  • 在轻量模型里,可学习编码参数量也不大

注意:可学习位置编码有个坑——它不支持外推。如果训练时最大长度是512,推理时来了个513长度的序列,位置编码就失效了。我曾经在部署时踩过这个坑,后来改用ALiBi或者RoPE才解决。

现在更流行的是旋转位置编码(RoPE)。它把位置信息编码到Q和K的旋转矩阵里,既支持外推,又能保持相对位置关系。后面讲LLM部署时会重点说。

3.4 三者如何协同工作?

我画了一张图,帮你理清这三个组件的关系:

输入序列 X + 位置编码 多头自注意力 Q、K、V 拆分 → 8个头 → 拼接 上下文表示 核心流程 1. 输入 + 位置编码 2. 生成 Q、K、V 3. 多头自注意力计算 4. 输出上下文表示 图:Transformer核心组件协同流程

流程其实很清晰:

  1. 输入序列先加上位置编码,让模型知道“谁在哪儿”
  2. 然后通过线性变换生成Q、K、V
  3. 多头注意力并行计算,每个头关注不同的关系模式
  4. 最后拼接输出,得到融合了全局信息的上下文表示

一句话总结:位置编码告诉模型“位置关系”,自注意力计算“语义关系”,多头注意力让模型从多个角度理解“关系”。三者缺一不可。

嗯,基础部分就这些。说实话,Transformer的设计真的很优雅——用简单的矩阵运算,实现了复杂的全局依赖建模。后面讲轻量化和卷积融合时,你会发现这些基础组件会以各种变形出现。

可可精品资料库