William 的技术笔记

Transformer 入门:注意力机制是怎么撑起大模型的

2026-10-27 · #Transformer#LLM#深度学习#基础

2017 年,Google 的一篇论文《Attention Is All You Need》提出了一种叫 Transformer 的网络结构。它最初只是为了做机器翻译,但几年之后,GPT、Claude、Llama、Gemini,几乎所有你听说过的大模型,骨架都是它。

前面写过 搭建 Agent 的基础知识 和 AI Infra 工程师,一个讲怎么用模型,一个讲怎么让模型跑起来。这篇往下再挖一层:模型本身到底长什么样。不需要太多数学,但会尽量把每一块“为什么这么设计”讲清楚。

为什么需要 Transformer

在 Transformer 之前,处理文本这种序列数据的主流方法是 RNN(循环神经网络)和它的改进版 LSTM。RNN 的思路很直观:一个词一个词地读,每读一个词就更新一次“记忆”(隐藏状态),读完整句话,记忆里就有了整句的信息。

这个设计有两个根本问题:

  • 难以并行。第 100 个词的计算必须等前 99 个词算完,GPU 有几千个核心也只能干等着;
  • 长距离依赖弱。句首的信息要经过几十上百次传递才能到达句尾,一路上不断被稀释,模型很难记住“很久以前”说过的话。

Transformer 的核心想法是:别按顺序传话了,让每个词直接去看所有其他词。一句话里任意两个词之间的距离都是“一步”,而且所有位置可以同时计算。这个机制就是注意力(Attention)。

全局视角:从文字到下一个词

先看一个现代大模型(GPT 这类“仅解码器”模型)处理一段文字的完整流程:

  1. 词元化(Tokenization):把文字切成一个个 token,并映射成整数 ID;
  2. 嵌入(Embedding):把每个 ID 查表变成一个向量,比如 4096 维;
  3. 加入位置信息:让模型知道每个 token 在第几个位置;
  4. 经过 N 层 Transformer 块:每一层都包含注意力和前馈网络,逐层加工这些向量;
  5. 输出概率:把最后一个位置的向量映射回词表大小,经过 softmax 得到“下一个 token 是什么”的概率分布;
  6. 采样:按概率选出一个 token,拼到输入末尾,回到第 1 步,继续生成。

所以大模型做的事情,说到底就是一遍遍地回答同一个问题:给定前面这些 token,下一个最可能是什么? 下面逐块拆开。

词元化:模型看到的不是字

模型并不直接读汉字或字母,而是读 token。主流做法是 BPE(Byte Pair Encoding)这类子词算法:从单个字节出发,反复把最常一起出现的片段合并成新 token,直到词表达到目标大小(通常几万到二十多万)。

结果是:常见的词是一个 token,罕见的词会被拆成几块。比如英文里 the 是一个 token,Transformer 可能被拆成 Transform + er;中文里常见词可能是一个 token,生僻字可能要好几个字节级 token。

这解释了一些看似奇怪的现象:

  • 模型按 token 计费、按 token 计算上下文长度,而不是按字数;
  • 模型数不清 “strawberry” 里有几个 r,因为它看到的是几个 token,而不是一个个字母;
  • 同样意思的内容,不同语言消耗的 token 数可能差很多。

嵌入:把 token 变成向量

每个 token ID 会通过一张嵌入表(一个 词表大小 × 维度 的矩阵)查出一个向量。这张表是训练出来的,训练之后,意思相近的 token 在向量空间里也会比较接近。

从这里开始,模型内部处理的就全是向量了。每个 token 对应一个向量,一段 1000 个 token 的文本,就是一个 1000 × 4096 的矩阵。后面所有层做的事情,都是在不断改写这个矩阵:每个位置的向量逐层吸收上下文信息,从“这个词本身的意思”变成“这个词在这句话里的意思”。

自注意力:核心中的核心

考虑这句话:“小明把书递给了小红,因为她想看。” 要理解“她”,模型需要把注意力放到“小红”上。自注意力就是让每个 token 自己决定“该关注哪些其他 token、关注多少”的机制。

Q、K、V

对每个 token 的向量,模型用三个可学习的矩阵分别做线性变换,得到三个新向量:

  • Query(查询):我在找什么信息;
  • Key(键):我能提供什么信息的“标签”;
  • Value(值):我实际携带的内容。

可以类比成查资料:每个 token 拿着自己的 Query,去和所有 token 的 Key 比对,匹配度越高,就从那个 token 的 Value 里取越多的内容。

计算过程

写成公式是这样:

Attention(Q, K, V) = softmax(Q·Kᵀ / √d) · V

拆开就是三步:

  1. 打分:用 Q 和每个 K 做点积,点积越大说明越相关;
  2. 归一化:除以 √d(d 是向量维度,防止数值过大导致 softmax 过于尖锐),再做 softmax,得到一组加起来为 1 的权重;
  3. 加权求和:用这组权重对所有 V 做加权平均,得到这个位置的新向量。

所有位置的计算可以写成矩阵乘法一次完成,这正是 GPU 最擅长的事情,也是 Transformer 能扩展到这么大规模的关键原因之一。

因果掩码

生成文本时,模型在预测第 t 个 token 时不能偷看后面的内容。做法是在 softmax 之前,把“未来位置”的分数设成负无穷,这样它们的权重就是 0。这叫因果掩码(Causal Mask),GPT 这类模型的每一层注意力都带着它。

多头注意力:从多个角度看

一组 Q、K、V 只能学到一种“关注方式”。但语言里的关系是多种多样的:语法上的主谓关系、指代关系、相邻词之间的搭配、远距离的主题呼应……

多头注意力(Multi-Head Attention) 的做法是:把向量切成若干份(比如 32 个头),每个头有自己独立的 Q、K、V 矩阵,各自做注意力计算,最后再把结果拼起来、做一次线性变换融合。

研究者分析训练好的模型时,确实能找到一些“专门干某件事”的头:有的头总是关注前一个 token,有的头负责把代词连到它指代的名词上,有的头负责复制前文出现过的模式。不过大部分头的功能并没有这么清晰可解释。

现代大模型还常用一些变体来节省推理开销,比如 MQA / GQA(多个查询头共享同一组 K、V),能显著减少推理时需要缓存的数据量,后面讲推理时会提到为什么这很重要。

位置编码:告诉模型顺序

注意力机制本身有一个特点:它对顺序不敏感。把句子里的词打乱,每个词和其他词的点积分数不会变。但“狗咬人”和“人咬狗”显然不是一个意思。

所以需要额外注入位置信息:

  • 原始论文用的是固定的正弦/余弦函数,生成一个位置向量,直接加到嵌入上;
  • 可学习的位置嵌入(早期 GPT、BERT):为每个位置训练一个向量,缺点是无法超出训练时的最大长度;
  • RoPE(旋转位置编码):今天大多数开源大模型的选择。它不是把位置加到向量上,而是根据位置把 Q 和 K 向量“旋转”一个角度,使得两个 token 的注意力分数自然地依赖于它们的相对距离。RoPE 也更方便做上下文长度的外推和扩展。

前馈网络、残差和归一化

一个完整的 Transformer 块,除了注意力,还有几个同样重要的部件:

x = x + Attention(Norm(x))
x = x + FFN(Norm(x))

前馈网络(FFN):两层(或三层)全连接网络,先把向量放大到 4 倍左右的维度,经过非线性激活(现在常用 SwiGLU),再压回原维度。注意力负责“在 token 之间搬运信息”,FFN 负责“在每个 token 内部加工信息”。一般认为,模型记住的大量事实性知识主要存在 FFN 的参数里,它也占了模型参数量的大头。

残差连接:注意每一步都是 x = x + ...,而不是 x = ...。每一层只是在原来的基础上“加一点修改”,而不是推倒重来。这让信息和梯度可以畅通无阻地穿过上百层,是深层网络能训练起来的关键。可以把它想象成一条贯穿所有层的“主干道”,每一层都往上面写一点东西。

层归一化(LayerNorm / RMSNorm):把每个向量的数值规模拉回稳定范围,防止训练过程中数值爆炸或消失。现代模型普遍把归一化放在子层之前(Pre-Norm),训练更稳定。

把这样的块堆叠几十层(7B 模型大约 32 层,更大的模型可以到 100 层以上),就是一个完整的 Transformer。

三种架构

原始的 Transformer 由编码器和解码器两部分组成,后来演化出三个分支:

架构 注意力方式 代表模型 擅长
仅编码器 双向,能看到整句 BERT 理解类任务:分类、检索、向量化
编码器-解码器 编码器双向,解码器因果 T5、原始 Transformer 输入到输出的转换:翻译、摘要
仅解码器 因果,只看前文 GPT、Claude、Llama 生成,以及几乎一切

今天的大语言模型几乎都是仅解码器结构。原因不复杂:结构最简单,训练目标(预测下一个 token)可以直接用海量无标注文本,而且实践证明,只要规模够大,生成式模型也能把理解类任务做得很好。

训练:预测下一个 token

大模型的训练目标朴素得惊人:给定前文,预测下一个 token,用交叉熵衡量预测的概率分布和真实答案的差距,然后反向传播更新参数。

得益于因果掩码,一段 4000 token 的文本可以一次性产生 4000 个训练样本,所有位置并行计算。这种高效率,加上互联网规模的数据,是 Transformer 能“大力出奇迹”的基础。

一个现代对话模型通常经历几个阶段:

  1. 预训练:在数万亿 token 的文本上做下一个 token 预测,学会语言、知识和基本推理;
  2. 监督微调(SFT):用高质量的“指令-回答”数据,教模型按对话的方式回应;
  3. 强化学习:通过人类偏好反馈(RLHF)或可验证的奖励(比如代码能否通过测试、数学题答案是否正确),进一步提升有用性、推理能力和安全性。

研究者还发现了规模定律(Scaling Laws):模型的损失随参数量、数据量和计算量的增长,呈现相当平滑、可预测的下降。这让“把模型做大”从赌博变成了可以规划的工程。

推理:KV Cache 与它的代价

生成文本时,模型是一个 token 一个 token 往外吐的。如果每生成一个新 token 都把整个前文重新算一遍,计算量会随长度平方增长。

KV Cache 解决了这个问题:前面 token 的 K 和 V 向量算过一次就存起来,生成新 token 时只需要计算新 token 自己的 Q、K、V,再和缓存里的 K、V 做注意力。

于是推理分成两个阶段:

  • Prefill(预填充):一次性处理整个输入提示,并行度高,主要受算力限制;
  • Decode(解码):逐个生成 token,每步计算量小,但要读取全部模型参数和 KV Cache,主要受显存带宽限制。

KV Cache 的大小随上下文长度和并发数线性增长,长上下文场景下甚至可能比模型本身还占显存。前面提到的 GQA、PagedAttention、量化等技术,很大程度上都是为了对付它。这部分在 AI Infra 工程师 里有更多展开。

局限与演进

Transformer 并不完美,几个公认的问题:

  • 注意力的平方复杂度:序列长度翻倍,注意力的计算量变成四倍。FlashAttention 通过精心设计显存访问大幅提高了实际速度,但没有改变平方的本质;
  • 上下文有限:窗口再大也有上限,而且模型对超长上下文中间部分的利用往往不如开头和结尾;
  • 推理成本高:每生成一个 token 都要动用全部参数。

围绕这些问题,近年的主要演进方向有:

  • 混合专家(MoE):把 FFN 换成多个“专家”,每个 token 只激活其中几个。总参数量可以很大,但每个 token 的计算量不变,是目前很多顶尖模型的选择;
  • 长上下文技术:RoPE 外推、稀疏注意力、滑动窗口注意力等;
  • 替代架构:Mamba 等状态空间模型、线性注意力,试图以线性复杂度处理序列,也出现了把它们和注意力层混合使用的模型。

到目前为止,这些改进大多是在 Transformer 的框架里修修补补,而不是取代它。九年过去,“注意力 + 前馈 + 残差”这个基本结构依然是主流。

小结

  1. Transformer 用注意力替代了循环,任意两个位置一步可达,所有位置并行计算;
  2. 自注意力 = 用 Q 和 K 打分,按分数加权 V,让每个 token 根据上下文改写自己的表示;
  3. 多头注意力从多个角度建模关系,位置编码(如 RoPE)补上顺序信息;
  4. 一个块 = 注意力 + 前馈网络,靠残差和归一化堆到几十上百层;
  5. 大模型是仅解码器的 Transformer,训练目标只是预测下一个 token,能力来自规模;
  6. 推理靠 KV Cache 加速,而 KV Cache 本身又成了长上下文的主要瓶颈。

理解了这些,再去看那些关于上下文长度、token 计费、推理速度、模型“幻觉”的讨论,会发现很多现象背后都能找到结构上的原因。模型的能力还在快速变化,但它底层的这套机制,值得花一个下午弄明白。