news 2026/8/30 11:38:01

学习Transformer前必懂的核心概念:位置编码、注意力与QKV

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
学习Transformer前必懂的核心概念:位置编码、注意力与QKV

学 Transformer 的过程中,真正劝退人的往往不是 Transformer 本身的代码量,而是背后的前置概念没有先理清。很多人一上来就读 MultiHeadAttention 的源码,看到 QKV 三个线性层就发懵,问题通常不出在读代码的耐心上,而是词嵌入、位置编码、注意力机制、缩放点积、掩码这些概念彼此之间还没形成链条。这篇内容适合两类人:一类是刚准备学 Transformer,想先把地基补一补;另一类是已经跑过不少 demo,但被问到“为什么这里要除以根号 d_k”仍然会卡住的人。重点不是说 Transformer 架构有多厉害,而是把进入它之前必须复习的这部分概念,按实际学习顺序重新过一遍。

1. 先搞清楚前置概念到底指什么

1.1 为什么很多人学 Transformer 会卡在半路

我见得比较多的学习路径是这样的:先看一篇博客,把架构图从头到尾扫一遍,看到“多头注意力”觉得大概懂;然后去跑一个 demo 或者直接读源码,结果在reshapetransposemask这些操作上反复打转,最后变成背代码,换一个任务就不会改。

卡住的本质不是代码难,而是概念顺序搞反了。Transformer 不是一个单独模块,它是一组机制的组合。任何一个环节有模糊,后面看 ViT、Swin、GPT 系列、BERT 系列都会受到影响。比如看不懂位置编码,就理解不了为什么要给输入加一个与语义无关的向量;不理解 Q、K、V,就只能在代码层面复制粘贴。前置概念不是考试知识点,它是你排查问题时的地图。模型输出不对,是词表问题、注意力掩码问题还是位置编码没加对,全部依赖这套概念去定位。

1.2 真正需要提前掌握的知识清单

我给自己的内部分层是这样的,按依赖顺序排列:

  1. token 化与词嵌入:知道文本如何变成向量。
  2. 位置编码:知道同一个词在不同位置如何区分。
  3. 注意力机制:知道一个位置如何聚合其他位置的信息。
  4. QKV 与缩放点积注意力:知道注意力分数如何计算,为什么要除以根号 d_k。
  5. 多头注意力:知道为什么一个头不够。
  6. 残差连接、层归一化、前馈层:知道深层网络里的每个模块在稳定什么。
  7. 编码器-解码器结构与掩码:知道训练和推理时为什么有差异。

这七项看起来多,但它们都有同一个主线:输入文本进入模型后,怎么一步步被转换为更有用的表示。后面所有讨论都围绕这条主线。如果某个概念已经很清楚,可以跳过对应小节;如果不清楚,建议按顺序往下读。

2. 从 CNN、RNN 到 Transformer,先看旧方案卡在哪

2.1 CNN 处理特征很强,但建模序列顺序有天然短板

在 Transformer 之前,自然语言处理大量依赖 RNN,而图像任务依赖 CNN。CNN 的核心特点是局部连接与权值共享。一个卷积核只覆盖很小的感受野,通过堆叠层数扩大覆盖范围。它对空间局部特征非常敏感,边缘、纹理、形状都能逐层提取,这也是它长期统治视觉任务的原因。

但把 CNN 用到序列上有几个明显问题。第一,卷积窗口默认只看局部上下文,要覆盖长距离依赖,必须加深网络或者使用大卷积核,计算和参数都会膨胀。第二,卷积操作本身对顺序不敏感,虽然位置不同会有不同的特征位置,但你很难像阅读句子一样显式表达“第 5 个词和第 2 个词之间隔了多远”。第三,序列的长度经常是变长的,文本、语音、时间序列差异很大,CNN 在处理变长输入时往往需要先做 padding、对齐等额外处理。这些限制导致 CNN 不是处理序列建模的首选,尽管现在也有 TCN 这类时间卷积网络在特定场景表现不错,但它在长依赖和顺序显式建模上依然没有 Transformer 自然。

2.2 RNN 循环网络处理序列,却很难逃出串行和长依赖问题

RNN 的设计更贴近序列本身:按时间步一个一个地读输入,每个时间步把当前输入和上一个隐状态合并,更新隐状态。这个隐状态可以理解为模型对“到目前为止看到的内容”的压缩记忆。LSTM、GRU 在此基础上加入门控,缓解梯度消失,在一段时间内是很强的序列建模方案。

但 RNN 有两个硬伤。第一个是串行计算。每一个时间步的隐状态依赖前一个时间步,想并行都没办法。训练一个长文本,时间成本会随着序列长度线性增长,而且很难像 GPU 矩阵运算那样大规模并行。第二个是长距离依赖问题。虽然 LSTM 用了门控,但信息每经过一个时间步都会经过非线性变换和遗忘,距离越远,保留得越不完整。你可以把 RNN 想象成一个人只能一边走一边回忆前面说过的话,走久了前面的细节自然记不住。还有一点,RNN 反向传播沿时间步展开,也容易出现梯度消失或梯度爆炸。解决这些问题的方案并不是在 RNN 上打补丁,而是换一种信息传递方式。

2.3 为什么最后是 Transformer 补上了这个位置

Transformer 的做法是让序列中任意两个位置直接交互。不是一步一步传递信息,而是每个位置同时去“看”所有其他位置,并且通过注意力分数决定看多少。这样有两个直接好处:

一是长距离依赖不再依赖路径深度。无论两个 token 隔多远,注意力计算复杂度都是常数级别的交互。二是所有位置可以同时计算,因为注意力没有串行依赖。你只需要在矩阵层面一次性算出 Q、K、V,然后做乘法。这也是为什么 Transformer 能和 GPU 大规模并行非常契合。

当然,Transformer 也不是没有代价。它把两个问题转嫁了:一个是怎么表示顺序,于是有了位置编码;另一个是计算量确实大,自注意力的复杂度是序列长度的平方,所以后来出现各种稀疏注意力、窗口注意力、线性注意力来优化。理解“为什么最后是 Transformer”,重点不是记年份或论文名,而是理解它在“并行”“长距离”“统一结构”三个点上比 CNN 和 RNN 更适合作为通用骨架。后面对前置概念的理解,都是在解释这三个点怎么落地。

维度CNNRNNTransformer
顺序建模弱,靠窗口和位置特征天然按时间步完全并行,需要位置编码
长距离依赖需要堆叠层数容易衰减任意位置直接交互
并行训练低,串行推进
主要瓶颈局部感受野梯度与串行计算量随序列长度平方增长

3. 进入 Transformer 前,把输入表示和位置信息拆清楚

3.1 token 化与词嵌入:模型看到的不是文字

Transformer 也好,其他神经网络也好,都不能直接吃字符串。它需要把文本切成 token,再映射成向量。token 可以是一个词,也可以是子词,比如“transformer”可以被切成“transform”和“er”这种子词片段。切分方式不影响 Transformer 的核心机制,但会影响词表大小和模型对罕见词的处理。

切完之后,每个 token 会有一个词表中对应的 id,再通过一个词嵌入矩阵转成一个稠密向量。这个向量的维度通常就是 Transformer 里的 d_model,常见值是 512、768 或 1024。对于文本任务,d_model 可以想象成每个 token 用一个 512 维向量表示,这个向量里编码了语义信息。词嵌入是模型最初学习参数的入口,它解决的是“怎么把一个离散符号变成连续向量”。很多人一开始忽略这个东西,直接去看注意力公式,就会在输入那里迷路。

实践中还要注意,不同框架里的输入形状可能有差异。PyTorch 环境下的惯例是(batch_size, seq_len, d_model),而有些代码或论文会写成(seq_len, batch_size, d_model)。看代码时先确认输入张量第 0 维到底是什么,否则后面transposepermute很容易看晕。

3.2 位置编码:为什么 Transformer 必须自己补上“顺序”

RNN 因为是一步一步读的,天然知道时间顺序;第 5 个词和第 2 个词天然有先后。但 Transformer 把所有 token 一起并行处理,注意力计算本身对位置不敏感。如果不加位置信息,把句子里的词随机交换顺序,模型看到的内容几乎一样。这显然不行。

所以原版 Transformer 引入了位置编码,给每个位置生成一个向量,把它加到词嵌入上。最经典的方式是用不同频率的正弦和余弦函数生成位置向量。为什么用正余弦?一个常见解释是它能让模型通过线性关系感知相对位置,同时不需要训练参数,而且能处理比训练时更长的序列。后面很多变体会改成可学习的位置编码,或者更复杂的相对位置编码。具体用哪种不是重点,重点是你得记住:位置编码和词嵌入是相加关系,而不是拼接关系。相加意味着模型把 token 的语义信息和位置信息放在同一个向量空间里,后续注意力网络可以同时使用这两部分信息。

3.3 输入张量形状:从文本到矩阵

把上面的概念落到实际输入,可以简化成这样的过程,下面的形状变化是示意,不是完整可运行代码:

原始 token id: (batch_size, seq_len) 词嵌入之后: (batch_size, seq_len, d_model) 叠加位置编码之后: (batch_size, seq_len, d_model)

到这一步,Transformer 编码器的输入就准备好了。后面所有操作,包括多头注意力和前馈网络,都在这个三维张量上进行。这里有两个容易犯的错:一是忘记加位置编码,导致模型训练和推理结果都明显下降;二是在 batch 维度处理错误,比如在第一个维度上做位置编码,导致同一个位置向量被所有 batch 样本同时使用,不过通常影响是隐性的,不好排查。所以我的习惯是先用一个小 batch 把输入形状打印出来,确认每一步 shape 变化后再进入注意力模块。

4. 注意力机制才是 Transformer 的心脏

4.1 用查字典和搜索来理解注意力

注意力机制本质是“按相关性聚合信息”。你可以把当前要处理的 token 想象成一个查询,它需要从一串候选位置里找出哪些信息值得参考。比如翻译“bank”这个词,如果上下文里有“river”,模型应该更关注“river”,而不是关注很多无关词。

注意力不只是在“找”,还在“加权”。每个候选位置会被分配一个权重,最终输出是所有候选位置的值按权重求和。权重越大,说明那个位置对当前查询越重要。整个过程可以用矩阵乘法一次性完成,不需要循环遍历每个位置,这也是它比 RNN 更适合并行计算的关键。

4.2 Q、K、V 到底从哪里来

Q、K、V 是注意力机制里最劝退的三个字母。其实它们分别对应查询、键和值。可以理解成一个简化版的检索过程:

  • Q(Query):当前 token 的查询向量,表示“我想找什么信息”。
  • K(Key):每个 token 的键向量,表示“我能提供什么索引”。
  • V(Value):每个 token 的值向量,表示“我真正携带的内容”。

计算时,Query 和所有 Key 做点积,得到相关性分数;分数经过 softmax 转成概率分布;再用这个概率分布对 Value 加权求和。在自注意力里,这三个向量都来自同一个输入序列,所以叫 self-attention。

Q、K、V 是怎么来的?不是输入直接分成三份,而是输入向量分别经过三个线性变换矩阵 WQ、WK、WV 得到。这也是为什么源码里会看到三个 Linear 层。WQ、WK、WV 是模型要学习的参数,训练过程中模型会逐渐学会怎样去查询最有用。理解这一点后,再读源码就轻松很多:你看到的不是三个神秘矩阵,而是三个可学习的投影。

4.3 为什么缩放点积要除以根号 d_k

注意力分数的计算是 Q 和 K 的转置做点积,再除以一个缩放因子。缩放因子通常取根号下 d_k,也就是 Q、K 向量的维度。计算过程可以理解为下面这个示意伪代码:

score = Q @ K.transpose(-2, -1) / sqrt(d_k) weight = softmax(score, dim=-1) output = weight @ V

为什么需要缩放?核心原因是控制方差。向量维度越高,点积结果的方差越大,数值会分布在很大范围内,softmax 算出来的概率会非常接近 0 或 1,梯度会变得很小,不好训练。除以根号 d_k 之后,点积的方差被拉回相对稳定的区域,梯度更平滑。这也是面试和学习时最容易问到的一个细节:不是凭空除一下,而是为了让训练更稳定。如果去掉缩放,模型在小维度上可能还能跑,但维度变大后训练会明显吃力。

4.4 多头注意力到底在拆什么

多头注意力是“多个自注意力并行”,但目的不是简单重复。每个头都有自己的 WQ、WK、WV,会从不同子空间学习不同的关注模式。比如一个头可能主要关注语法依赖,另一个头关注指代关系,还有一个头关注距离较远的全局信息。最后把这些头的输出拼接起来,再过一次线性变换。

用“多头”的价值在于:单头注意力只能学一种加权方式,表达力有限。多头相当于让模型从多个角度同时观察序列。你不需要精确解释每个头到底干什么,但应该理解它提升了模型的表达能力,并且没有额外增加计算深度,只是把 d_model 拆成多个子空间并行计算。很多代码里会把 num_heads 设成 8,每个头维度是 d_model / num_heads,比如 512 / 8 = 64。这个数字变化后,整体计算量基本不变,但性能有时会变,因为头的数量影响子空间划分方式。

5. 回到 Transformer 整体结构,把模块角色串起来

5.1 编码器:把输入变成一组上下文表示

原版 Transformer 的编码器由多个相同层堆叠而成,每一层包含两个主要子层:多头自注意力机制和前馈网络。输入序列经过词嵌入和位置编码后,先进入第一层自注意力,得到每个 token 融合了全局上下文的新表示;然后送入前馈网络做非线性变换;子层之间有残差连接和层归一化。

编码器的输出可以理解为“每个位置都携带了整句话信息”的表示序列。它的形状仍然是 (batch_size, seq_len, d_model)。对理解前置概念来说,编码器只需要记住一点:它不生成文本,只负责把输入转换成更丰富的上下文特征。BERT 类模型基本就是用编码器结构,所以它天生适合做分类、匹配这类理解任务。

5.2 解码器:生成任务里为什么多了一个交叉注意力

解码器比编码器多了一个子层,整个结构是:带掩码的多头自注意力、多头交叉注意力、前馈网络。前两个注意力都是多头注意力,但作用不同。掩码自注意力负责处理“已经生成的部分”,训练时防止当前 token 看到未来 token。交叉注意力负责把解码器当前状态和编码器输出的上下文表示进行交互,这是翻译任务中“根据源语句生成目标语句”的关键。

理解交叉注意力,重点是 Q 来自解码器,K、V 来自编码器。也就是说,解码器当前生成的每个位置,会去源语句里检索自己最应该关注哪些信息。这个设计让编码器和解码器形成分工:编码器理解源句,解码器逐步生成目标句。如果你以后学 Seq2Seq、机器翻译、T5、BART,会发现交叉注意力是理解整套生成框架的重要线索。

5.3 残差连接、层归一化和前馈层都不是摆设

很多初学者看 Transformer 架构图,会忽略长条虚线和每个子层后面的 Norm。这些模块看似只有稳定作用,实际上没有它们,深层网络很难训练。

残差连接解决的是深层退化问题。它让输入 x 和子层输出加在一起,这样一来梯度可以直接穿过子层反馈到更早的层。层归一化负责把数据分布拉回稳定范围。Transformer 里用的通常是 LayerNorm 而不是 BatchNorm,因为序列长度和 batch size 变化较大时,LayerNorm 对每个样本单独归一化,更稳定,也更适合 NLP 任务。前馈网络是两层全连接加激活函数,作用是给每个位置做非线性变换,增强模型的表达能力。自注意力主要负责信息聚合,前馈网络负责对聚合后的信息做进一步加工,两者缺一不可。

5.4 掩码的作用与自回归生成

解码器里的掩码是容易忽略的前置概念。它分成两种:padding 掩码和因果掩码。padding 掩码用于忽略输入里无效的填充位置;因果掩码用于确保解码器在预测第 i 个 token 时,不能看到第 i 个之后的内容。为什么不能看?因为解码器生成是自回归的:先有第 1 个 token,才有第 2 个 token。如果训练时允许看未来,模型在推理时就会懵,因为推理时根本没有未来。

实现上,常见方式是把未来位置对应的注意力分数设成非常大的负数,这样 softmax 之后权重变成 0。这也是源码里容易看晕的地方:mask 矩阵的形状、加的位置、为什么有的 mask 是二维有的变四维。我的建议是先理解目的,再去跟代码。只要记得“训练时不让模型作弊,推理时按顺序生成”,大部分掩码代码都能对应上。

6. 怎么检验自己到底有没有掌握

6.1 用自己的话讲清楚:不看公式能不能答出三句话

前置概念学得怎么样,有个笨办法很有效:不看任何资料,用三句话回答“自注意力是什么”。如果你能说出类似“每个位置通过查询向量去和所有键向量算相关度,再用相关度加权求和对应的值向量”,那说明你对自注意力已经有一个清晰的骨架。如果只能说“就是 QKV”,还不够。

同样的方法可以用来检验位置编码、多头注意力、掩码。每个概念都用三句话解释,讲不清楚就是还有模糊点。不要觉得这是浪费时间,很多卡在源码层的人都能跑通代码,但被问“为什么这里要除以根号 d_k”就说不出来。能用自然语言讲清楚,往往比背公式更实在。

6.2 手撕 Transformer 前,先画图再写代码

很多热词都会提到“手撕 Transformer”。我的建议是先画图再写代码。画图不需要很精致,但要把下面几条链路画出来:

  • 输入:文本 -> token -> 词嵌入 -> 加位置编码 -> (batch_size, seq_len, d_model)。
  • 自注意力:输入 -> Q/K/V 线性变换 -> 点积 -> 缩放 -> softmax -> 加权求和。
  • 多头:拆分成多头并行计算 -> 拼接 -> 线性变换。
  • 编码器层:自注意力 -> 残差和 LayerNorm -> 前馈 -> 残差和 LayerNorm。
  • 解码器层:掩码自注意力 -> 交叉注意力 -> 前馈 -> 残差和 LayerNorm。

能画出来,再去看代码,你会发现源码里每个变量都在对应这条链路。如果画不出来,先不要急着复现代码,否则容易变成背代码。手撕的检验标准不是“能跑”,而是“改坏了之后能不能通过日志和形状变化定位问题”。

6.3 从 ViT 到 Swin Transformer,前置概念怎么迁移

学完前置概念,再去看常见变体会顺很多。Vision Transformer 把图片分成固定大小的 patch,每个 patch 展开并线性投影成一个向量,当作文本里的 token,再加上位置编码,后面就直接套用 Transformer 编码器。这里的核心迁移是把“句子中的词”换成“图片中的 patch”,注意力机制完全不变。

Swin Transformer 则针对高分辨率图像引入窗口注意力,把自注意力限制在每个窗口内,再通过移位窗口实现跨窗口信息交互。它的目的很简单:降低计算复杂度。你看懂底层注意力,再关注窗口划分和 padding 处理即可。所谓 Transformer 改进,大部分改的是位置编码方式、注意力计算方式、归一化位置、掩码策略和 token 化方式,底层的 QKV 机制没有本质变化。所以前置概念扎实了,后面那些“涨点”方法才有讨论价值。同理,Transformer 在时间序列预测、股票走势建模等场景里也很常见,但用的仍然是同一套编码器或编码器-解码器骨架,输入从文本换成数值窗口,前置概念完全通用。

6.4 进入进阶前先过的四道检查清单

最后给一个检查清单,每一条都很直接:

  1. 能不能画出单层 Transformer 编码器的结构,并说清每个子层解决什么问题。
  2. 能不能说明 Q、K、V 的来源和注意力分数计算流程,包含缩放原因。
  3. 能不能说出为什么解码器训练时要加掩码,推理时又怎么处理。
  4. 能不能把一个具体任务拆成 tokenization、embedding、位置编码、encoder/decoder、输出映射五个环节。

四条都能用自然语言讲清楚,再进入源码阅读、模型改进、分布式训练这些进阶方向会更稳。如果有一条卡住,倒回去看对应章节。很多人觉得 Transformer 难学,其实不是架构概念太多,而是前置概念没有形成闭环。概念一旦串起来,后面的代码和变体都会顺畅不少。

回到开头那句话:学 Transformer,最值得花的功夫其实在它前面。词嵌入、位置编码、注意力、多头、掩码,这些东西单个看都不难,但它们组合起来就构成了现代大规模语言模型、视觉模型和跨模态模型的共同基础。先把这条链路走通,再去看各种新架构,就不会出现“每个词都认识但连起来不知道在讲什么”的情况。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 11:37:28

Tutti VM:多智能体协作的环境隔离与实战指南

多智能体是今年绕不开的技术热词,但很多人一开始尝试多智能体项目,就被环境问题卡住了:多个 Agent 之间怎么隔离依赖、怎么管理不同版本的 Python 和模型配置、多个智能体同时跑的时候怎么避免互相干扰。这些问题的答案,往往不在 …

作者头像 李华
网站建设 2026/8/30 11:36:59

10步玩转Guava RangeMap:对称加密 vs 非对称加密,你选对了吗?

🔥关注墨瑾轩,带你探索编程的奥秘!🚀 🔥超萌技术攻略,轻松晋级编程高手🚀 🔥技术宝库已备好,就等你来挖掘🚀 🔥订阅墨瑾轩,智趣学习不孤单🚀 🔥即刻启航,编程之旅更有趣🚀 导读: 年龄权限、金额折扣这类区间映射需求,用传统 Map 只能手动遍历,代码…

作者头像 李华
网站建设 2026/8/30 11:35:57

使用CMake与VS2019编译DCMTK 3.6.8 SDK:医学影像处理开发环境搭建指南

简介:本资源是面向医学影像软件开发者与DICOM技术实践者的DCMTK SDK编译成品包,专为解决VS2019环境下DCMTK3.6.8版本在x64平台编译门槛高、配置复杂等实际问题而提供。资源包含完整编译产出的debug与release双模式SDK,涵盖头文件(…

作者头像 李华
网站建设 2026/8/30 11:33:30

具身智能落地:从四朵云的半步到树莓派小车实战

具身智能这四个字,在 2025 年的国内科技圈几乎是“焊死”在热搜上的。与此相伴的,是各大云计算厂商接连发布的机器人模型、具身智能平台、机器人开发套件。光看发布会,你会觉得机器人时代已经近在眼前;但如果把这些方案真正拆开看…

作者头像 李华