CUDA学习之路[13]:Attention算子详解

7131 字
36 分钟
CUDA学习之路[13]:Attention算子详解

写在前边#

市面上讲解Transformer架构的博客、视频、书籍实在是太多了,我不敢保证自己真的能够讲的比这些大牛都优秀,只能说尽量把我对于Transformer的全部理解都写在这一个系列博客中。

为什么Transformer火到了现在?#

很多人现在一谈起大模型、多模态等等就自然而然想到了Transformer,但是它最开始真的不是干这个的,从最开始处理NLP,一步一步到后边的大模型,其实中间还有很长一段路要走。

很多教程一上来就开始学技术,位置编码、自注意力、一堆词怼上来直接把人砸晕了。 但是技术本身的发展脉络是阶段性的,我们需要先了解整个Transformer的发展脉络,搞清楚每一个模块都是用来干嘛的。

这一讲完全讲历史,大家也就听一乐,后续几节我再实现具体的代码。

假设,我们现在要把下边这句话翻译成为中文:

The cat that was adopted last year and has been lazy ever since finally yawned.

句子很长,我们只需要看主谓宾:

The cat yawned.

这句话的翻译难点是什么?主语cat和真正的主句谓语yawned之间插入了一段很长的定语从句,这也是我们很多考研、考四六级同学面对长难句时候的困难——难以找到主谓宾。

在Transformer之前,处理自然语言的主流是RNN循环神经网络和LSTM长短期记忆网络。

我当年打美赛,关于股票黄金预测,用的是LSTM神经网络,现在来看,写的真的是很差劲啊【悲】

对于RNN来说,处理句子就像玩传水游戏。它从The开始,把一部分水传给cat,再传给that…一直传到yawned。等传到最后句尾时,句首的信息已经在多次传递中严重衰减(梯度消失)。导致它根本想不起来主语是谁。

RNN传水游戏
RNN传水游戏

为了解决这个问题,LSTM引入了门控机制和细胞状态。通俗来说就是给原本的信息加上了便签,关键的词就进行标记。但是这样子做也有几个问题。首先是,用来标记的“笔记本”是容量有限的,句子太长就溢出了;并且它必须从前到后逐字阅读,很难进行并行计算,训练速度极慢。

直到1027年,Google团队发表了那篇改变历史的论文:《Attention is All you Need》

这个论文题目绝对能进历史前十

详解Transformer的各个模块#

很多人第一次读《Attention is All you Need》,会有一个错觉,这帮人好像一夜之间造出了一个全新的架构,并持续火了十年。但如果你去翻论文的参考文献,会发现 Transformer 的每一个组件都能在之前的某篇论文中找到原型。

Google 团队的真正贡献,不是发明了注意力机制,也不是发明了位置编码,而是把这些机制串在一起,很多人觉得论文的核心是Attention,但在我看来是ALL

它把和Attention无关的机制都祛除掉,只保留Attention核心,这才能说明“Attention is all you need!”

Transformer架构
Transformer架构

自注意力机制#

注意力的idea比大多数人想象的更早,而且起源于计算机视觉,而非自然语言处理

2014年,Google DeepMind 的 Volodymyr Mnih 等人发表了《Recurrent Models of Visual Attention》。 他们在处理图像分类时,让 RNN 模型每次只”看”图像的一个局部区域,然后根据当前看到的内容决定下一次看向哪里。 这就像人眼看东西,你不会一视同仁地扫描整个视野,而是会聚焦在关键区域。

紧接着同一年,Bahdanau等人使用类似attention的机制在机器翻译任务上,将翻译和对齐同时进行。

Attention雏形
Attention雏形

Seq2Seq模型是什么?

Seq2Seq 模型用编码器把整个源语言句子压缩成一个固定长度的向量,再由解码器从这个向量中生成目标语言。问题是:这个是定长向量,是严重的信息瓶颈。 句子一长,向量装不下,翻译质量就崩了。

Bahadanau的解法很符合直觉:不要让编码器把所有信息塞进一个向量里。而是让解码器在生成每一个目标词时,动态地去查看源语言句子的所有隐藏状态,然后自己决定”我现在最应该关注源句子的哪几个位置”。

简单来说就是用一个小型的前馈网络计算相关度分数:

score(hdecoder,hencoderi)=vtanh(W1hdecoder+W2hencoderi)\text{score}(h_{\text{decoder}}, h_{\text{encoder}_i}) = \mathbf{v}^\top \cdot \tanh(\mathbf{W}_1 h_{\text{decoder}} + \mathbf{W}_2 h_{\text{encoder}_i})

这个分数衡量的是当前正在解码的这个词,与源句中第 i 个位置的编码器隐藏状态有多相关。

所有的分数组成了注意力权重,对源句子的隐藏状态做加权平均,得到一个上下文向量。 解码器每一步使用的上下文向量都是动态生成的,不同的目标词关注不同的源词。

这就是加性注意力 的由来,上述 score 函数用了 v^T tanh(Wx + Uy) 这种”加”的形式。

2015年,Luong、Pham、Manning发表了《Effective Approaches to Attention-based Neural Machine Translation》。这篇论文系统的对比了三种注意力得分的计算方式:

方式公式说明
Dot-productscore(ht,hˉs)=htThˉsscore(h_t, h̄_s) = h_t^T h̄_s直接点积
Generalscore(ht,hˉs)=htTWhˉsscore(h_t, h̄_s) = h_t^T W h̄_s加一个可学习矩阵 W,给两个向量对齐的空间
Concat (Additive)score(ht,hˉs)=vTtanh(W[ht;hˉs])score(h_t, h̄_s) = v^T tanh(W[h_t; h̄_s])Bahdanau 的方案,拼接后通过 MLP

并且这篇文章提出来Global Attention和Local Attention两种策略。

发现了吗?Luong 的 Dot-product 方案,就是 Transformer 中 Q·K^T 的直观表示。Google 团队后来加了一个缩放因子 1/√d_k(当然这也是Google的核心贡献)

上述方案都是从“语言A”到“语言B”之间的注意力。

例如I Love you和我爱你,love和爱之间的注意力关系。 每一步解码器用自己的隐藏状态(Query)去和编码器的隐藏(Key/Value)计算注意力分数。 关键在于,Query和Key/Value是来自于两个不同语言句子之间的注意力计算,或者说是编码器-解码器之间的状态。

但是句子内部词和词之间的关系怎么办呢?

2016年,Cheng、Dong 和 Lapata 在《Long Short-Term Memory-Networks for Machine Reading》中提出了 Intra-Attention。 它的思想是:让一个句子内部的每个词关注句子中的所有其他词(包括自己),从而生成上下文感知的表示。

这其实就是自注意力的雏形——句子内部自己对自己做注意力。

同年,Parikh等人提出了Decomposable Attention,即先用注意力对齐两句话中的词,再聚合比较。这篇论文还拿到了当时EMNLP2016的最佳论文奖,证明了单纯的注意力机制+简单的前馈网络就能够在NLP上达到当时的SOTA。

到此为止,Google的切入点呼之欲出。

既然注意力能够跨句子,也能够在句子内部,并且纯注意力+前馈就能够打败复杂的LSTM模型,那为什么不彻底扔掉RNN,只保留注意力呢?

简单来说,Transformer的注意力就是下边这一行公式:

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

之前我们实际上也讲过,这里提问一下大家:

问题一:为什么这里要用到点积?

在若干注意力方案中,点积在大矩阵场景下的计算效率高于加性注意力;加性注意力需要每一对(Q, K)跑一遍MLP,点积注意力只需要一次矩阵乘。

问题二:为什么要除以√d_k

这是原始 Transformer 论文的第一个关键洞察。当 d_k 较大时(比如 64),点积值的方差会达到 d_k(假设 Q 和 K 的每个分量独立,均值为 0,方差为 1)。这意味着 softmax 的输入值范围很大,梯度会落在 softmax 的饱和区(梯度接近 0)。除以 √d_k 将方差控制回 1,使得梯度能正常流动。这是 Transformer 能训练成功的前提之一。

问题三:为什么要用Softmax?

(1)映射到0-1且和为1,天然形成概率分布;(2)可微支持反向传播;(3)指数放大分数之间的差异;

多头注意力机制#

如果只用一个注意力头,那么对于每个位置,模型只能用一种模式去看待和其他位置的关系。但是语言是复杂的,一个词可能与不同的词存在不同的类型关系:语法关系、语义关联、指代等等。

多头注意力类比CNN的多个卷积核,一个卷积层并不会只有一个filter,而是用几百个filter去分别提取边缘、纹理、颜色等不同的特征。

Transformer也是同理:

MultiHead(Q, K, V) = Concat(head_1, ..., head_h) · W^O
head_i = Attention(Q·W_i^Q, K·W_i^K, V·W_i^V)

每个head都拥有自己独立的Q、K、V投影矩阵,因此可以学习到不同类型的注意力方式。 论文中使用8个头,每个头的维度为64,展现出了稳定的效果。

后续研究表明,不同头确实学到了不同的行为模式:

  • 有的头专注相邻位置(局部语法依赖)
  • 有的头专注特定分隔符(如句号、逗号等等标点结构)
  • 有的头专注远距离语义关联(如代词回指其先行词)

这个设计的精妙之处就在于,它参考多卷积核实现了多映射,但是却并没有引入更多的计算原语,就是8个独立的attention+1次拼接投影,硬件实现高效。

关于”多头”的思想原型,可以追溯到2014年的《Show, Attend and Tell: Neural Image Caption Generation》。但真正把多头注意力推广到全架构、彻底替换 RNN 的,是 Transformer。

位置编码#

如果只有Attention机制,完全算不上ALL,那另一个很重要的模块是什么呢?

——位置编码。

知乎:位置编码详解 |

如果说Attention是Transformer的发动机,那位置编码就是它的导航。但是在原论文,位置编码占比不是很高,以至于很多人觉得它只是一个附加的小trick。

直到后来,位置编码的设计直接决定了模型的处理能力,以及能否泛化、甚至扩散到了视觉和多模态等等复杂的任务。

原本RNN天然具有时序感,因为它是逐个处理的,它并不需要位置信息,因为处理顺序本身就是位置信息。每个词的隐藏状态不仅包含了该词的语义,还隐式编码了它前边词的所有信息。

但是自注意力呢?它是完全对称的,它把输入顺序打乱了,换句话说自注意力是一个集合到集合的映射,而非序列到序列的映射,它天然就不感知位置顺序。

假设我们现在有一个极简的Transformer(d_model=4, 单头),输入三个中文词的词嵌入:

输入序列 S: ["我", "爱", "北京"]
词嵌入: x₀=[1,0,1,0], x₁=[0,1,0,1], x₂=[1,1,0,0]

为了简化,假设Q,K,V投影矩阵都是单位矩阵,跳过缩放因子,那么自注意力计算的过程是:

Step 1: 计算注意力分数矩阵(Q·K^T)

我(Q₀) 爱(Q₁) 北京(Q₂)
我(K₀) 2 0 1
爱(K₁) 0 2 1
北京(K₂) 1 1 2

Step 2: Softmax 后得到注意力权重

我 爱 北京
我 0.58 0.08 0.34 ← 这一行决定"我"的输出:主要听自己(58%)和"北京"(34%)
爱 0.08 0.58 0.34 ← 这一行决定"爱"的输出:主要听自己(58%)和"北京"(34%)
北京 0.23 0.23 0.54 ← 这一行决定"北京"的输出:主要听自己(54%)

Step 3: 用权重对 V 做加权平均,得到输出

output[0] = 0.58×[1,0,1,0] + 0.08×[0,1,0,1] + 0.34×[1,1,0,0] = [0.92, 0.42, 0.58, 0.08] ← "我"的输出
output[1] = 0.08×[1,0,1,0] + 0.58×[0,1,0,1] + 0.34×[1,1,0,0] = [0.42, 0.92, 0.08, 0.58] ← "爱"的输出
output[2] = 0.23×[1,0,1,0] + 0.23×[0,1,0,1] + 0.54×[1,1,0,0] = [0.77, 0.77, 0.23, 0.23] ← "北京"的输出

同一个 token 的 Q 去和所有 token 的 K 做点积,权重由语义相似度决定,与 token 在序列中的位置无关。“我”关注”北京”是因为它们的词向量有分量重合(x₀·x₂=1),而不是因为”北京”在第三个位置。

现在,打乱顺序

输入序列 S': ["北京", "我", "爱"]
词嵌入: x₂=[1,1,0,0], x₀=[1,0,1,0], x₁=[0,1,0,1]

重复上述计算,你会发现打乱后“我”的注意力权重仍然由x₀ 与所有 token 的语义相似度决定,这些 token 的语义没有变(x₀, x₁, x₂ 都没变),所以“我”的注意力输出和打乱前是一模一样的,唯一的区别就是这个输出现在在序列的第二个位置。

无论 token 出现在序列的哪个位置,只要它的词嵌入不变,它的注意力输出就不变。模型天然不感知位置顺序。 这不仅仅是 “The cat sat on the mat” 和 “mat the on sat cat The” 的区别。 对于 Self-Attention 来说,这两个序列中每个词的输出值是完全相同的(只是排列顺序不同)。模型无法区分”狗咬人”和”人咬狗”,这对 NLP 来说是灾难性的。

数学上,这被称为排列等变性

如果对输入序列施加一个排列 π,输出序列也会按相同的 π 重排,但每个位置的输出值不变。形式化:SelfAttention(π(X)) = π(SelfAttention(X))。位置编码的作用就是让输入不再是纯 X,而是 X + PE,从而打破对称性。

在Transformer之前,显式编码也已经出现了。Seq2Seq和GlobalAttention都没有显式的位置编码,而是RNN,位置信息藏在隐藏状态里。

而Memory Networks(2015)是最早的显式位置编码之一。 Facebook AI Research 的 Sukhbaatar 等人在《End-To-End Memory Networks》中,为了让模型理解句子中词的顺序,给每个词加了一个固定的位置编码向量(不需要训练)。这是后来 Transformer 正弦编码的思想先驱。

除了固定编码之外,Gehring 等人用 CNN 替代了 RNN 来做序列建模(这也是 Transformer 扔掉 RNN 思路的重要铺垫)。CNN 和 Self-Attention 一样是不感知位置的。卷积核只看局部窗口,不知道自己在序列中的绝对位置。所以 ConvS2S 给每个位置引入了一个可学习的嵌入向量,加在词嵌入上。

这就是显式位置编码:固定编码 -》 正弦编码 -》可学习编码。

论文中选择了正弦编码:

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

这个公式一开始会觉得莫名其妙,我们来拆解一下:

  • pos 是词在句子中的位置(0, 1, 2, …)
  • i 是维度索引(0 到 d_model/2 - 1)
  • 10000^(2i/d_model)波长(或等价地,决定了频率):i 越大 → 分母的指数越大 → 波长越长 → 频率越低 → 变化越慢

换句话说,小i也就是高频sin/cos,对短距离位置变化敏感;大i对长距离位置变化敏感。

还是不理解

类似于用不同焦距的镜头,拍摄同一组序列。长焦镜头查看相邻词的位置关系;广角镜头查看全局位置结构。模型可以在训练中学会组合这些不同尺度的信号。

为什么选sin/cos呢?

论文给出了数学理由:因为 sin(a+b)cos(a+b) 可以用 sin(a), cos(a), sin(b), cos(b) 的线性组合表示。这意味着理论上 PE(pos+k) 可以被表示为 PE(pos) 的线性函数。模型因此可能学会推断相对位置关系。

但实际上这个”相对位置可通过线性变换得到”的性质,在原始 Transformer 中并没有被有效利用。真正实现相对位置编码的,是后来的 RoPE。

尽管Transformer论文选择了正弦编码,但是工业界很快转向了可学习位置编码:

模型年份位置编码方式最大长度
BERT2018可学习绝对位置嵌入512
GPT-12018可学习绝对位置嵌入512
GPT-22019可学习绝对位置嵌入1024

但可学习位置编码有一个致命的局限:它是一个固定大小的查找表。如果你训练时的最大长度是 512,推理时来了一个 1024 长度的句子,位置 513~1024 根本没有被训练过。它们的嵌入向量是随机初始化的(或者根本不支持查表),模型在这些位置上的行为不可预测。这就是长度外推问题

在 BERT 和 GPT 的时代,这个问题并不突出,因为当时的”长文本”也就是 512~1024 个 token。但当大模型时代到来,人们对 4K、8K、32K、128K 乃至 1M 上下文的追求,让这个问题变成了核心瓶颈。

到此为止,我们就已经了解了位置编码的主要功能,但是有关位置编码还远远没有结束。实际上,模型并不需要真正知道某一个确定的位置是什么,而是词A和词B距离有多远。 换句话说,绝对位置,或许并没有那么重要,相对距离才是核心。

很多同学上来就是RoPE,但是我一定要讲清楚,为什么有RoPE

2018年,Shaw等人在《Self-Attention with Relative Position Representations》中,作者修改了 Transformer 的注意力计算,不再给每个 token 加一个绝对位置向量,而是在计算 attention score 时,根据两个 token 之间的相对距离 j - i 来查找一个可学习的关系向量,加到 key 上:

eij=(xiWQ)(xjWK+ajiK)Tdk e_{ij} = \frac{(x_i W^Q)(x_j W^K + a_{j-i}^K)^T}{\sqrt{d_k}}

其中 ajiKa_{j-i}^K 是一个只依赖于相对距离的可学习向量。这有一个截断上限 K(比如 K=16),超过 K 的距离共享同一个向量,因为假设太远的词之间的直接关系不重要。

19年,Dai 等人在《Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context》中,提出了一套更彻底的相对位置编码方案,具体内容大家可以查看论文。简单来说就是把注意力机制分为了四项:

  • 内容 - 内容
  • 内容 - 位置
  • 全局内容偏置
  • 全局位置偏置

这套设计让 Transformer-XL 能够在段落级别实现递归。上一段的隐藏状态可以被当前段使用,从而实现对超长文本的建模。

20年,Raffel等人在T5中提出了一个更加高效的相对距离方案。 21年,Ofir等人更是提出了ALiBi,简单来说是给每一个头一个独特的斜率:

softmax(qiKTdk+m[(i1),(i2),,2,1,0])\operatorname{softmax}\left( \frac{q_i K^T}{\sqrt{d_k}} + m \cdot \left[-(i-1), -(i-2), \ldots, -2, -1, 0\right] \right)

ALiBi 的惊人之处在于外推能力,用 512 长度训练的模型,可以直接推理 2000+ 长度的序列,困惑度不升反降。

ALiBi 曾经被 BLOOM(BigScience 多语言大模型)和 MPT(MosaicML)系列采用,证明了极度简单的方案也可以非常有效。但它的局限也显而易见:线性衰减过于刚硬,不能表达更复杂的距离-重要性关系(比如”前文第 5 句的主语”可能比”前文第 3 句的从句”更重要,这不是简单的距离函数能描述的)。

截止到现在,针对位置编码已经有了系统性的演进,但究竟是谁来终结比赛的呢?

——RoPE(Rotary Position Embedding)

RoPE
RoPE

2021 年,苏剑林(Jianlin Su)在中文博客”科学空间”上连载了一系列文章,探讨如何用旋转矩阵来编码位置。这个想法后来以论文《RoFormer: Enhanced Transformer with Rotary Position Embedding》正式发表,成为了 LLaMA、Mistral、Qwen、Gemma、DeepSeek 等几乎所有现代大模型的标配。

核心思想是RoPE并不把位置编码直接加到Token向量上,而是把位置编码乘进去,也就是做旋转。

qm=RΘ,m(WQxm)(用位置 m 的旋转矩阵旋转 query)q_m = R_{\Theta,m} \cdot (W^Q x_m) \quad \text{(用位置 } m \text{ 的旋转矩阵旋转 query)}kn=RΘ,n(WKxn)(用位置 n 的旋转矩阵旋转 key)k_n = R_{\Theta,n} \cdot (W^K x_n) \quad \text{(用位置 } n \text{ 的旋转矩阵旋转 key)}

仔细看,点积如下:

qmTkn=(RΘ,mq)T(RΘ,nk)=qTRΘ,mTRΘ,nk=qTRΘ,nmkq_m^T k_n = (R_{\Theta,m} q)^T (R_{\Theta,n} k) = q^T R_{\Theta,m}^T R_{\Theta,n} k = q^T R_{\Theta,n-m} k

我们惊奇的发现,Q和K的点积只以来于它们的相对位置n-m,而不是各自的绝对位置。这就是RoPE的核心性质:通过在绝对位置上做旋转,自动或者相对位置编码的效果

后续我会详细解释RoPE的模块

这就是为什么RoPE中使用torch.polar

对比方案RoPE 的优势
vs 正弦编码RoPE 天然编码相对位置,而非只是绝对位置
vs 可学习绝对编码RoPE 零参数,理论上有无限长外推潜力
vs Shaw 相对编码RoPE 不需要 O(K) 的可学习参数;外推无截断
vs T5 biasRoPE 的位置信号随内容变化(乘法交互),而非固定的加法偏置
vs ALiBiRoPE 提供振荡衰减而非刚性线性衰减,更灵活

你以为到这里就结束了吗?站在2026年的当下,位置编码的历史还远远没有结束。

尽管RoPE比之前的方案有了好得多的外推能力,但是当你真的把推理长度拉到训练长度的100倍时,RoPE仍然会崩。

2023年,随着LLaMA的爆火,研究者们又是一顿猛干:

Position Interpolation (PI, 2023.06)。 把长序列的位置索引按比例压回训练范围内。比如一个模型训练时用了 2048 个位置,现在要处理 4096 个位置,就把实际位置 pos 替换为 pos × 2048/4096 = pos/2。效果出奇地好,证明了 RoPE 的编码空间是连续的,只需要在更密的采样点上插值即可。

NTK-aware Scaling (2023.08)。 但 PI 有一个问题:把所有频率都按相同比例压缩,导致高频维度的分辨率下降。NTK-aware 方案的改进很巧妙。不压缩位置索引,而是调低 RoPE 的频率基数(从 10000 调到一个更大的值,如 1000000)。这相当于把低频维度”推开”以覆盖更长的范围,同时保持高频维度不变(对邻近位置的敏感度不变)。“NTK”的名字来自 Neural Tangent Kernel 理论中的频带扩展思想。

YaRN (2023.10)。 Peng 等人结合了 PI 和 NTK 的优点,并引入了一个温度参数来精细控制注意力分布的”锐度”。YaRN成为了当时最流行的长上下文微调方案。

后续有关位置编码仍然由新的发现,但是由于笔者能力受限,给大家姑且抛砖引玉。当我们走出NLP,进入视觉、进入多模态,发现故事才刚刚开始。

从论文发表的速度和数量可以看出来,基本2-3个月一篇,需要极其强的科研实力。

UP有话说

直到现在,位置编码仍然是一个活跃的话题,位置编码看似只是 Transformer 的一个小组件,但它决定了模型能看多远、看多宽、以及能否将一种模态中的空间经验迁移到另一种模态。在接下来的章节中,我们将用 CUDA 和 Triton 来实现其中最重要的几种位置编码 kernel,特别是 RoPE 及其长上下文扩展。

残差连接#

借鉴ResNet,保证模型的深度,不做过多赘述。

层归一化LN#

参考之前的文章Norm系列。

前馈神经网络#

FFN(x) = max(0, x·W_1 + b_1)·W_2 + b_2 (原论文用 ReLU)
FFN(x) = GELU(x·W_1 + b_1)·W_2 + b_2 (现代 LLM 的常见选择)

看起来似乎只是两个线性变换中间夹一个非线性激活,但是为什么必须要有前馈呢?

实际上,注意力层做的是信息路由,让Token A看到Token B的信息,并且聚合上下文。 FFN做的则是信息加工,赋予模型思考的能力。

更具体地说:FFN 的隐藏维度通常是 d_ff = 4 × d_model(比如 d_model=512 时,d_ff=2048)。这意味着每个 token 的表示先被扩展到 4 倍维度(给模型足够大的空间做非线性映射),再压缩回原始维度。可以类比为把一本书展开成一张巨大的表格,勾画关键信息,再折叠回去。

这也是为什么 LLM 的知识主要存储在 FFN 层中,编辑 FFN 的权重可以直接修改模型的知识。

一个标准的 Transformer block:

组件参数量 (d_model=d, d_ff=4d)
QKV 投影4 × d × d = 4d²
Output 投影d × d = d²
FFN W₁ + W₂2 × (d × 4d) = 8d²
总计~13d²,其中 FFN 占 ~62%

FFN 占了大部分参数,但 FLOPs 却与 Attention 相近(因为 Attention 是 O(n²·d),FFN 是 O(n·d²))。这是一个”参数大、计算小”的组件,非常适合存放大量知识。

为什么是这些组件?#

如果把Transformer想象成为一个信息处理组件,需要在模型经历三层加工:

  • 我应该和谁交流?
  • 我获得了什么新的知识?
  • 哪些知识需要保留,哪些需要更新?

这三个交替堆叠,形成了Transformer处理信息的节奏:全局交流、独立思考、选择性更新等等。

Transformer后的10年#

Transformer是一篇17年的工作,但是大模型真正涌现基本是在23年左右。

这里我总结了一个简单的时间线,没那么详细,但是基本重要的模型都在其中:

架构层面#

年份改进论文核心变化
2018BERTDevlin et al.双向 Transformer + 掩码语言模型预训练;开创 Pre-training + Fine-tuning 范式
2018GPTRadford et al.单向(因果)Transformer + 生成式预训练;去除 Encoder 只留 Decoder
2020GPT-3Brown et al.175B 参数, 上下文学习(In-Context Learning), 证明了 Scaling Law
2023LLaMATouvron et al.开源高效 LLM 标杆;RMSNorm + SwiGLU + RoPE 成为新标配
2024Mamba-2 / SSDDao & Gu状态空间模型与线性注意力的统一;O(L) 替代方案的里程碑

注意力层面#

年份改进核心变化
2019Sparse Attention (Sparse Transformer)只计算部分 token 对的注意力,O(L√L) or O(L log L)
2021RoPE (Rotary Position Embedding)用旋转矩阵编码相对位置,成为 LLaMA 系标配
2022FlashAttentionIO-aware 精确注意力:tiling + online softmax,1.5-3× 加速
2023FlashAttention-2改进的并行策略 + 更少的非 matmul FLOPs,2× 加速
2023GQA (Grouped Query Attention)多 Q head 共享少量 KV head,大幅减少 KV cache
2023Flash-Decoding针对 long-context 推理的 decoding 优化
2024FlashAttention-3H100 Hopper 专用:WGMMA + TMA + FP8

激活函数#

年份改进核心变化
2016GELUGaussian Error Linear Unit, BERT/GPT 标配
2020SwiGLUSwish-gated Linear Unit: SiLU(x·W_gate) ⊙ (x·W_up)

归一化层面#

年份改进核心变化
2019RMSNorm去除去均值操作,LLaMA 标配
2022DeepNorm改进残差初始化,支持 1000 层 Transformer

总结#

写到这里,可以看到激活没有关于技术的细节,有的更多的是脉络,可能大家会觉得信息量很大,但是你并不需要记住每一个细节,真正需要理解的就是两个模块:

  • 为什么需要注意力?
  • 为什么要这么设计?

如果有任何问题,欢迎进群讨论或者B站评论区讨论。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
CUDA学习之路[13]:Attention算子详解
https://dlog.com.cn/posts/cuda13/attention/
作者
杜子源
发布于
2026-06-25
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
杜子源
都是风景,幸会
公告
请狠狠地打赏我,打赏一次,爆更一篇!!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
29
分类
8
标签
11
总字数
93,152
运行时长
0
最后活动
0 天前

目录