2、Transformer核心原理回顾:从Attention到Self-Attention,多头注意力机制详解

好,咱们开始第二讲。在动手部署之前,我习惯先把核心原理捋一遍。尤其是Transformer,很多人把它当黑盒用,但部署的时候一遇到性能瓶颈,就抓瞎了。今天咱们就把它的心脏——注意力机制,彻底拆开看看。

2.1 从最朴素的Attention说起

Attention机制,说白了就是「加权求和」。你想想看,人类看东西的时候,不会把画面里每个像素都同等对待。你会聚焦在关键区域,忽略背景。Attention做的就是这件事。

在NLP里,最早期的Attention是用于机器翻译的。比如翻译"I love you"到中文,模型在生成「爱」这个字的时候,应该更关注英文里的"love",而不是"I"或"you"。这就是一个典型的Encoder-Decoder Attention

它的数学形式很简单:

Attention(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V

这里Q是查询(Query),K是键(Key),V是值(Value)。你可以把Q想象成「我想找什么」,K是「我有什么标签」,V是「我实际的内容」。通过计算Q和K的相似度,得到权重,再用这个权重去加权V。

我的经验: 刚开始学的时候,我总觉得Q、K、V这三个字母很抽象。后来我在一个文本匹配项目里,把Q当成搜索词,K当成文档标题,V当成文档内容,一下子就通了。说白了,这就是一个「检索-加权」的过程。

2.2 Self-Attention:自己跟自己玩

那Self-Attention又是什么?其实就是Q、K、V都来自同一个输入序列。模型在理解一个词的时候,要看看它周围的词是什么。

举个例子,句子「我吃了一个苹果,它很甜」。模型要理解「它」指的是什么,就得看看「它」前面的词。Self-Attention就是干这个的——让每个词都能「关注」到序列里的其他词。

计算步骤我拆解一下:

  1. 生成Q、K、V:输入向量分别乘以三个权重矩阵Wq、Wk、Wv,得到Q、K、V。
  2. 计算相似度:Q乘以K的转置,得到注意力分数矩阵。
  3. 缩放:除以sqrt(d_k),防止内积过大导致softmax梯度消失。
  4. Softmax归一化:得到0到1之间的注意力权重。
  5. 加权求和:权重乘以V,得到最终的输出。
避坑指南: 我曾经在部署一个轻量级模型时,发现推理速度比预期慢很多。排查了半天,发现是Self-Attention的序列长度太长,导致Q*K^T这一步的矩阵乘法变成了性能瓶颈。后来我限制了最大序列长度,速度直接翻倍。所以,部署时一定要关注序列长度对计算量的影响。

2.3 多头注意力机制:让模型「多角度」看问题

单头注意力有个问题——它只能从一个角度去理解关系。比如在句子「他打碎了花瓶,然后把它粘好了」里,「它」既可能指「花瓶」,也可能指「碎片」。单头注意力只能关注到一种可能性。

多头注意力机制(Multi-Head Attention)就是解决这个问题的。它把Q、K、V拆成多个「头」,每个头独立计算注意力,最后再拼起来。

具体做法:

  • 把Q、K、V分别拆成h份(h是头的数量,比如8个)。
  • 每个头独立做一次Self-Attention。
  • 把h个头的输出拼接起来。
  • 再经过一个线性变换,得到最终输出。

数学上可以写成:

MultiHead(Q, K, V) = Concat(head_1, ..., head_h) * Wo
其中 head_i = Attention(Q * Wq_i, K * Wk_i, V * Wv_i)

每个头可以学到不同的关系模式。有的头关注语法关系,有的头关注语义相似度,有的头关注位置距离。

头编号 关注模式 实际例子
头1 相邻词关系 「红色」→「苹果」
头2 远距离依赖 「他」→「打碎」
头3 语法结构 主语→谓语→宾语
头4 指代消解 「它」→「花瓶」
注意: 多头注意力虽然效果好,但计算量是线性增长的。在边缘设备上部署时,我建议先试试4个头,而不是直接用8个。我在一个树莓派项目上做过测试,4个头比8个头快了将近30%,而精度只下降了不到1%。这个取舍很划算。

2.4 位置编码:给模型「空间感」

Self-Attention本身是「无序」的。你想想看,如果把句子里的词打乱顺序,Self-Attention计算出来的结果是一样的。这显然不合理,因为「我打你」和「你打我」意思完全不同。

所以Transformer引入了位置编码(Positional Encoding),给每个词加上位置信息。原始论文用的是正弦和余弦函数:

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

为什么用这个?因为正弦和余弦函数有周期性,模型可以学到相对位置关系。比如位置3和位置5的关系,跟位置103和位置105的关系,在数学上是相似的。

我的建议: 在部署轻量级模型时,如果序列长度是固定的(比如128),可以直接用可学习的位置嵌入(Learnable Positional Embedding),效果差不多,但实现更简单,也更容易量化。

2.5 总结:部署时你需要记住的

好了,原理讲完了。在部署到边缘设备之前,我建议你记住这几点:

  • 计算瓶颈:Self-Attention的计算复杂度是O(n²),n是序列长度。部署时一定要控制n。
  • 内存占用:多头注意力的每个头都需要存储中间结果,头数越多,内存越大。
  • 量化友好性:Softmax这一步对量化不太友好,部署时可以考虑用ReLU或者分段线性函数替代。
  • 剪枝机会:有些注意力头其实是冗余的,可以剪掉。我在一个项目里剪掉了30%的头,精度几乎没掉。

下一讲,咱们会深入Transformer的整体架构,看看Encoder和Decoder是怎么配合的。到时候我会结合一个实际的部署案例,让你看看这些原理在硬件上是怎么落地的。