Transformer与注意力机制

Transformer原理

灵魂拷问: 你了解 Transformer 吗?

大多数人的第一反应: 我又不训练模型,Transformer 和我有什么关系呢?

那关系可大了:

  • Token是怎么计费的?
  • 上下文窗口为什么都有 128k、200k 的限制?
  • 为什么模型会"忘记"前面的内容?
  • 为什么长对话的质量会越来越差?
  • 为什么Prompt结构化会比大段文字效果要好?

这些全都可以从 Transformer 架构中得到答案

为什么应用也要懂 Transformer

我们问 Transformer,不是要问底层的 推导 QKV 矩阵乘法,而是对大模型的底层逻辑是否了解

用大模型开发,这几个问题一定遇到过

  • Token计费规则是什么: 为什么一次交互就需要消耗几万Token?这和Transformer的计算方式直接相关。
  • 上下文窗口为什么会有限制: GPT-4是128K,Claude Code 是200K,为什么不是无限长?这和 Transformer 的复杂度有关系。
  • 为什么模型会"忘记"之前的内容: 长对话越到后面,模型越容易忘记之前的内容。这和 Transformer 的注意力机制有关。
  • 为什么Prompt结构化会比大段文字要好: 这和多注意力头的机制有关。

所以我们需要: 了解Transformer的核心机制,包括自注意力、多注意力头、位置编码等等。这些理解有助于帮我们在实际开发中更好的管理上下文、优化Prompt、控制Token成本 --- 而不是停留在"调用API"层面。

为什么所有大模型都绕不开 Transformer

为什么 gpt、claude-code、gemini、ollma 都喜欢围绕 Transformer?有没有替代方案?

Transformer之前 RNN 和 CNN的困境

  • **RNN(循环神经网络)**的致命问题:

    • 梯度消失: 序列越长,前面的信息越到后面就越弱。RNN处理长句子就像玩"传声筒"游戏。第一个词传给第二个、第二个传给第三个...... 当第一个传到第一千个时基本没了。这就是 RNN 为什么记不住长距离依赖

      • 举例: 我从小在河边长大......(此处省略500字)......所以我最强的技能是......
      • 由于句子太长,RNN在末尾时已经把开头的河边给忘记了,只能对着空位瞎猜。
    • 串行计算: RNN必须一个Token一个Token的计算,第二个Token必须等待第一个Token计算完,没法并行,训练速度跟不上去。

      • RNN的隐式递归公式: ht=f(Wh⋅ ht−1 +Wx⋅xt+b) h_t = f(W_h \cdot h_{t-1} + W_x \cdot x_t + b) ht=f(Wh⋅ht−1+Wx⋅xt+b),其中 ht h_t ht 依赖 ht−1 h_{t-1} ht−1,形成严格的链式依赖,无法并行。
  • **CNN(卷积神经网络)**的致命问题:

    • 局部感受野 : CNN天然只能看到局部窗口内的信息,缺乏全局观(只见树木不见森林)。想看全局就得堆很多层,堆层数又带来训练难度。

      • 举例: 这个苹果看起来很红,但是它是塑料做的。
      • CNN的思路: 看到 红 -> 像苹果; 看到 圆 -> 更像了; 看到 果柄 -> 确定是苹果! 但是它只是盯着剧不看,完全没有注意到最后那个关键字 塑料。
    • 长距离依赖弱: 两个相隔很远的词之间的关系,CNN很难捕捉到。

Transformer 的解法: 注意力机制

Transformer用注意力机制有以下几个核心优势

  • 并行计算: 所有Token的注意力同时计算,不需要串行等待,GPU尤其擅长这种大规模并行计算。训练速度提升数十倍。
  • 全局视野: 直接捕获长距离依赖,不受序列长度限制。
  • 可扩展性: 架构简洁统一,易于堆叠更深的网络。
graph TB subgraph RNN[&#34;RNN:串行传递&#34;] direction LR R1[&#34;Token 1<br/>1.0&#34;] --> R2[&#34;Token 2<br/>0.6&#34;] R2 --> R3[&#34;Token 3<br/>0.2&#34;] R3 --> R4[&#34;Token 4<br/>≈0&#34;] end subgraph RNN_Problem[&#34;RNN 的问题&#34;] P1[&#34;🔴 梯度消失<br/>长距离依赖记不住&#34;] P2[&#34;🔴 必须串行<br/>无法并行,训练慢&#34;] end subgraph Transformer[&#34;Transformer:全局注意力&#34;] T1[&#34;Token 1&#34;] <--> T2[&#34;Token 2&#34;] T1 <--> T3[&#34;Token 3&#34;] T1 <--> T4[&#34;Token 4&#34;] T2 <--> T3 T2 <--> T4 T3 <--> T4 end subgraph Trans_Advantage[&#34;Transformer 的优势&#34;] A1[&#34;🟢 全局建模<br/>一步到位,不看距离&#34;] A2[&#34;🟢 并行计算<br/>GPU同时加速&#34;] end RNN -.-> RNN_Problem Transformer -.-> Trans_Advantage
有替代方案吗

有,但目前没能替代Transformer。

  • Mamba(状态空间模型): 推理速度快、长序列有优势。但生成质量和通用性还比不上Transformer。
  • RWKV: 结合了RNN和Transformer的优势,但生态还未成熟。
  • 混合架构: 部分用Transformer,部分用其他架构。目前仍在探索阶段。

总结

  • Transformer再并行计算与全局建模之间找到了最好的平衡,目前还没有架构在通用性和性能上同时超越它。
  • Transformer之前,RNN有梯度消失与串行计算的问题,CNN有局部视野和梯度爆炸的局限。
  • Transformer的注意力机制让每个Token'能直接与其他Token建立关系,而且可以并行计算,这也是它取代RNN和CNN的核心原因。

Self-Attention: Transformer的灵魂

一句话理解 Self-Attention

Self-Attention 是让每个词去看它与其他所有词的关系,然后根据关系远近决定关注多少

举个例子:

  • 场景一: 我去银行存钱
  • 场景二: 我去银行边上的河边散步

虽然都有银行这个词,但是语义完全不一样。自注意力机制就是根据上下文里其他词的信息,动态调整银行这个词的表示。词的意思是不固定的,是有上下文决定的。

Q、K、V 是什么

Transformer 的注意力机制借鉴了信息检索的思想,将每个词映射到三个不同的向量空间。

  • Q --- Query(查询): 代表 "我想找什么" 当前词想知道自己和谁有关系。当前词的查询意图,用于与其他词的Key进行匹配。
  • K --- Key(键): 代表"我是什么、我有什么"。每个词的特征标识,用于被Query检索。
  • V --- Value(值): 代表"我的内容是什么"。实际传递的信息,根据注意力权重进行加权求和。

这个设计的巧妙之处在于: 相似度计算(Q·K)和信息传递(V)是解耦的。

self-attention的核心逻辑: Q找对象,K判断匹配程度,V提供实际信息

graph TB subgraph QKV[&#34;Q / K / V 三大向量&#34;] direction LR Q[&#34;🔍 <b>Query</b><br/>我想找什么&#34;] K[&#34;🔑 <b>Key</b><br/>我是什么&#34;] V[&#34;💎 <b>Value</b><br/>我的内容&#34;] end subgraph Formula[&#34;Attention 计算公式&#34;] F[&#34;Attention(Q, K, V) = softmax(QKᵀ / √dₖ) · V&#34;] end Q --> Formula K --> Formula V --> Formula

对应用开发的启示

为什么上下文质量决定了输出质量

因为 self-attention 本身就是根据上下文决定关注什么。你给模型的上下文全是噪音,那么注意力就会被分配到不该关注的地方;你给的上下文全是关键信息,注意力就集中到正确的地方。

通过这个说明我们也可以得知了

  • 模糊的Prompt效果差
  • 结构化的Prompt效果好
  • 上下文无关代码太多生成质量差

我的上一篇文章-- Vibe Coding 时代下的自我鞭策也提到了你给的上下文质量决定了Self-Attention的效果,而Self-Attention决定了模型的输出质量

其实我想说明的是,上下文工程还是一门非常值得学习的技能。

Multi-Head Attention: 为什么需要多头注意力

问题先行: Multi-Head Attention 和 Self-Attention 的关系?为什么需要多头注意力?一个头不够吗?

单头注意力的局限性

单头注意力只有一个 QKV 变换,只能捕获一种类型的依赖关系,只能学一种关系模式

但语言的关系是复杂多样的

  • 语法关系 : "他 吃了 一个 苹果" → 动词和宾语的搭配
  • 指代关系 : "小明说 很开心" → "他"指代"小明"
  • 修饰关系 : "非常 漂亮的花" → 副词修饰形容词
  • 语义关系 : "猫坐在 垫子 上" → 空间位置关系
  • 长距离依赖 : "那个 昨天 在公园里跑步的 " → 跨越多个词的主语修饰

一个注意力头很难同时捕捉如此多的关系。

多头的工作机制

多头注意力将输入投影到多个不同的子空间,每个"头"独立计算注意力,最后将所有头的输出拼接起来。

Multi-Head Attention 就是把 QKV 复制多分,每份独立计算注意力,每份学习不同的关系模式。

典型的 Transformer 使用 8 或 16 个注意力头,每个头专注于不同的语言现象。

  • 语法头: 识别主谓宾、定状补等语法关系
  • 语义头: 捕获词义相关性
  • 位置头: 关注相邻词的局部依赖
  • 指代头: 解析代词指向(例如 他 指的是 yyg)
  • 情感头: 识别褒贬色彩和情绪倾向
  • 实体头: 识别人名、地名等命名实体
graph TB subgraph Row1[&#34; &#34;] direction LR H1[&#34;语法头 --- 主谓宾关系&#34;] H2[&#34;语义头 --- 词义关联&#34;] H3[&#34;位置头 --- 距离关系&#34;] H4[&#34;指代头 --- 代词消解&#34;] end subgraph Row2[&#34; &#34;] direction LR H5[&#34;情感头 --- 情绪倾向&#34;] H6[&#34;实体头 --- 命名实体&#34;] H7[&#34;修饰头 --- 定状补&#34;] H8[&#34;全局头 --- 整体语境&#34;] end Row1 --> Result[&#34;8个头从不同角度理解语义 最后拼接融合&#34;] Row2 --> Result

不是说模型被手动设计了这些分工,而是在训练过程中,不同的头自然学会了关注不同的关系模式。

多头的优势

  • 表达能力更强: 不同的头捕获不同类型的依赖关系,避免单一视角的局限。
  • 并行计算: 多个头可以同时计算,不增加计算时间。
  • 鲁棒性更好: 即使某些头学习失败,其他头扔能提供有效信息
数学公式(参考)

MultiHead(Q,K,V)=Concat(head1,...,headh)⋅WO\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) \cdot W^O MultiHead(Q,K,V)=Concat(head1,...,headh)⋅WO

其中headi=Attention(QWiQ,  KWiK,  VWiV) \text{其中} \quad \text{head}_i = \text{Attention}(QW_i^Q, \; KW_i^K, \; VW_i^V) 其中headi=Attention(QWiQ,KWiK,VWiV)

每个头有独立权重矩阵 QKV

笔者是没去钻研,有兴趣的同学可以钻研一下

对应用开发的启示

又回到了之前的灵魂拷问: 为什么Prompt结构化会比大段文字效果要好

原因是: 多头注意力在处理结构化信息时效率更高

举个例子
makefile 复制代码
目标: 增加订单多支付场景
参数: 订单ID、渠道ID
约束: 幂等校验、状态唯一
上下文: ORDER_DETAIL_DTO 如表XX所示

每个注意力头可以快速定位到自己关注的部分 -- 语法头看结构、语义头看关键字、指代头看指代关系。

如果改成一大段文字

复制代码
我需要你帮我完成订单多支付场景,参数有订单ID和渠道ID,要注意幂等校验和状态唯一,orderDetailDTO是这样的。。。。。

信息密度医院,但多头注意力在处理这种格式需要带来额外的计算来提取结构,消耗Token的同时拉低效率。

结构化不是给人看的,是给多头注意力看的

Encoder vs Decoder vs Decoder-Only

灵魂拷问: GPT、BERT、T5 的架构有什么区别?为什么现在大模型都用 Decoder-Only?

位置编码(Positional Encoding)

关键信息: Transformer的自注意力机制本身是和位置无关的 。换句话说: Self-Attention本事诗完全不看顺序的。它把句子看作一个词的集合,而不关心词的顺序。但词序对语义至关重要: "我爱你"和"你爱我"完全是不一样的。

位置编码的必要性

为了让模型感知位置信息,Transformer再输入嵌入中加入位置编码。位置编码是一个与词嵌入维度相同的向量,直接加到词嵌入上。

位置编码就是给每个Token打上一个"位置标签",让模型知道这个词在句子的哪个位置。

正弦余弦位置编码

原始 Transformer 使用固定的正弦余弦函数生成位置编码:

P E(pos,2i) =sin⁡ ( pos100002i/d ) PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d}}\right) PE(pos,2i)=sin(100002i/dpos)

P E(pos,2i+1) =cos⁡ ( pos100002i/d ) PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right) PE(pos,2i+1)=cos(100002i/dpos)

优点

  • 唯一性:每个位置有唯一的编码
  • 相对位置:模型可以学习到相对距离关系
  • 外推性:可以处理比训练时更长的序列

现代位置编码方案

  • 可学习位置编码:BERT、GPT 将位置编码作为可训练参数,而非固定函数。
  • 相对位置编码:T5、DeBERTa 不编码绝对位置,而是编码词之间的相对距离。
  • 旋转位置编码(RoPE):LLaMA、GPT-NeoX 使用的方案,通过旋转 Q 和 K 向量注入位置信息,外推性能更好。
  • ALiBi:通过在注意力分数上加偏置项实现位置感知,无需额外参数。

对应用开发的启示

回到之前的提问: 为什么长上下文后面模型会"忘记"前面的内容?

位置编码有一个隐含的问题:模型在训练时见过的位置范围是有限的。

如果一个模型训练时最长只见过 4096 个 Token 的文本,那它对第 5000 个位置的位置编码就没有学过。虽然可以通过外推(extrapolation)来处理更长的位置,但效果会下降。位置编码需要根据训练时最长的文本长度来调整。

所以:

  • 为什么上下文窗口有硬上限:超出训练时见过的位置范围,位置编码就不可靠了
  • 为什么超长上下文质量会下降:即使模型声称支持 200K 上下文,后半部分的注意力质量也不如前半部分
  • 为什么重要信息要放在 Prompt 开头或结尾:模型对中间位置的信息关注度天然较低,这是所谓的"中间迷失"(Lost in the Middle)问题

编码器和解码器

Transformer 的完整架构由编码器(Encoder)解码器(Decoder)两部分组成,分别负责理解输入生成输出

生态常见架构对比

维度 Encoder-Only(仅编码器) Decoder-Only(仅解码器) Encoder-Decoder(编码器-解码器)
代表模型 BERT GPT、Claude、LLaMa T5、BART
注意力方式 双向注意力 单向注意力 Encoder双向+Decoder单向
能看到什么 整个输入 只看前面的Token Encoder看全部,Decoder看后面
擅长什么 理解、分类、抽取 生成、对话、推理 翻译、摘要、转换
生成能力 最强
复制代码
GPT为什么只用解码器

GPT系列模型采用 自回归生成 方式,逐个预测下一个词。仅解码器架构天然适合这种生成任务,且结构简洁,易于扩展到千亿参数规模。

编码器 -- Encoder

编码器由多层(通常是6-12层)相同的结构堆叠而成,每层包含两个子层:

  • 多头自注意力层: 捕获输入序列内部的依赖关系
  • 前馈神经网络: 对每个位置独立进行非线形变换

每个子层后面都有残差连接层归一化,确保深层网络的训练稳定性。

解码器 -- Decoder

解码器也有多层堆叠,但每层有3个子层。

  • 掩码多头自注意力: 只能看到当前位置之前的词,防止"作弊"。
  • 交叉注意力: 连接编码器和解码器,让解码器关注输入序列
  • 前馈神经网络: 与编码器相同。

Encoder-Only:BERT 的路线

BERT 用双向注意力,每个 Token 可以看到前面和后面所有的 Token。

好处:理解能力强,做分类、实体识别、语义相似度这些任务效果很好。

坏处:不能用来生成。因为生成必须是"看前面的词,预测下一个词",双向注意力等于偷看了答案。

Encoder-Decoder:T5 的路线

Encoder 用双向注意力理解输入,Decoder 用单向注意力生成输出。

典型的"理解了再写"模式,适合翻译、摘要这类输入和输出明确分离的任务。

Decoder-Only:GPT 的路线

只用单向注意力,每个 Token 只能看到前面的 Token,预测下一个 Token。

这就是自回归生成:看前面的词,预测下一个词,再看前面的词(包括刚预测的),再预测下一个......一步步生成下去。

sequenceDiagram autonumber title: Decoder-Only 自回归生成:一步步预测下一个 Token participant Input as 输入 participant Model as 模型 participant Output as 输出 Note over Input,Output: Step 1: 预测下一个 Input->>Model: &#34;猫&#34; Model-->>Output: &#34;吃&#34; (预测下一个Token) Note over Input,Output: Step 2: 看前面的,预测下一个 Input->>Model: &#34;猫 吃&#34; Model-->>Output: &#34;鱼&#34; (基于前文预测) Note over Input,Output: Step 3: 继续预测 Input->>Model: &#34;猫 吃 鱼&#34; Model-->>Output: &#34;。&#34; (预测句尾) Note over Input,Output: 最终输出: 猫 吃 鱼 。

自回归生成的三个特征:

  • 串行生成: 第N个Token必须等前N-1个生成完,所以生成比理解慢得多
  • 本质是续写: 你给Prompt,模型接着往下写。对话、代码生成、问答都是续写
  • 末尾影响最大: 最后的Token离生成位置最近,所以Prompt末尾指令最关键

为什么现在的大模型都用 Decoder-Only

Scaling效果最好

同样的参数量和数据量,Decoder-Only在扩大规模时收益最大。GPT系列从 1.7亿参数调整到1.8亿参数,效果持续提升。这不是偶然 --- 而是因为Decoder-Only的架构更简单统一,规模越大优势越明显。

生成和理解都能做

虽然Decoder-Only天然是生成式的,但通过Prompt设计,它也能做到理解任务。反之,Encoder- Only就做不了生成。一专多项 > 只能做一样。

训练更高效

Decoder-Only 每个位置的预测目标都是下一个Token,训练目标统一。Encoder-Only需要同时训练理解和生成两个部分,协调成本更高。

对应用开发的启示

  • 为什么 Prompt 的最后一句特别重要:模型是接着你最后一句话往下写的,最后一句话的方向决定了生成方向
  • 为什么 Few-shot 有效:给几个示例,模型就会"续写"出类似格式的内容
  • 为什么 System Prompt 要放在最前面:最先出现的内容对整个生成过程都有影响,System Prompt 在开头相当于给"续写"定了基调

Transformer的局限性

局限一: O(n²) 计算复杂度

Self-Attention 的计算量和序列长度的平方成正比。序列长度翻一倍,计算量翻四倍。

序列长度 注意力计算量
1K Token 100 万次
2K Token 400 万次
4K Token 1600 万次
128K Token 163 亿

这也解释了以下几个问题

  • Token计费: 序列越长成本越高,不只是线形增长,是平方级增长
  • 上下文窗口不能无限大: 200K上下文的注意力计算已经是40亿级别,硬件扛不住更大了
  • 长对话越来越慢: 对话越长,每次新生成都要对全部历史做注意力计算

所以: 控制上下文长度不只是生气,是在控制计算复杂度

局限二: 位置编码的外推问题

前文也提到过,模型对训练数据外的位置编码不可靠。即使做了长度外推优化,超长上下文的质量也会大打折扣。

现有市场的环节方案:

  • RoPE(旋转位置编码): GPT-4、LLaMa 都在用,外推能力比正弦定理要好
  • YaRN/NTK-Aware: 通过调整频率来扩展位置编码的有效范围
  • 滑动窗口注意力: 不做全局注意力,只在局部窗口内计算,牺牲全局信息来换取更长的有效长度

但这些都是缓解,不是根治。

局限三: 中间迷失(Lost in the Middle)

上文提到过,Transformer对中间部分的信息关注度明显低于开头和结尾。

无论模型多大、上下文多长,这个现象都存在。原因复杂,但和注意力分配机制有关 -- 开头信息因为位置靠前,对所有后续的Token都有影响;结尾信息因为距离生成最近,也天然获得更多关注。中间信息两边都不靠,容易被忽略。

所以: 关键信息别放在Prompt中间,放开头或结尾

局限四: 生成是串行的

Decoder-Only 模型生成Token是一个一个来的,第N个Token必须等待第N-1个Token生成完。这种自回归特性决定了生成速度有上限。

生成比理解慢的多,需要大量输出的场景要考虑流式输出。

高频问题汇总

为什么 Transformer 能取代 RNN

  • 全局建模: 每个Token直接和所有Token建立联系,不需要和 RNN 一样传声筒
  • 并行计算: 所有Token的注意力可以同时计算,不像 RNN 必须串行。训练速度快了几个数量级

自注意力机制(Self-Attention)的QKV各自是什么

  • Q: Query,查询,当前词在查找什么
  • K: Key,键,每个词提供什么
  • V: Value,值,每个词的实际内容

通俗的说: Q找对象、K表示匹不匹配、V提供实际内容

为什么需要 Multi-Head Attention

单头注意力只能学一种关系模式,但语言的多重关系--语义、语法、指代。多头能让不同的头关注不同类型的关注,最后综合判断。就像从多个角度看同一件事比一个角度看更加全面。

为什么现在大模型都用 Decoder-Only?

  • Scaling 效果最好------参数量和数据量越大,效果提升越稳定;
  • 生成和理解都能做------虽然天然是生成式,但通过 Prompt 也能做理解任务,而 Encoder-Only 做不了生成;
  • 训练更高效------目标统一,就是预测下一个 Token。

总结

  • Self-Attention 告诉你上下文质量为什么重要
  • 多头注意力告诉你结构化 Prompt 为什么有效
  • 位置编码告诉你长上下文为什么质量下降
  • O(n²) 复杂度告诉你 Token 成本为什么这么高。
相关推荐
橘子星42 分钟前
树与二叉树:从概念到 JavaScript 实现
前端·javascript·面试
AiClaw42 分钟前
AIClaw 的 Skills 机制:先注入索引,再按需读取完整说明
前端
YHHLAI43 分钟前
HTML5 Canvas 从入门到实战:画布绘图 · 帧动画 · 小游戏 · 数据可视化
前端·信息可视化·html5
码流怪侠43 分钟前
【GitHub】 Headroom 深度解析:AI Agent 上下文压缩层的完整技术拆解
人工智能·github·agent
qq_4112624244 分钟前
ESP32-S3 AI相机硬件组成与通信配置说明
人工智能·数码相机
前端 贾公子1 小时前
npx skills:AI Agent Skill 的 npm,50+ 工具统一的 Skill 管理工具
前端
闲人小吴1 小时前
Loop Engineering:当杠杆点从「写 Prompt」移到「设计循环」
人工智能
Yobeeo1 小时前
记忆与存档——Checkpointer 与状态持久化 — LangGraph 实战——构建跨平台爆款图文 Agent 第3篇
人工智能
触底反弹1 小时前
面试官问"Ajax原理",我从XHR讲到async/await,他直接懵了!
前端·面试·架构