☰
Transformer零基础入门:注意力机制、多头与编码解码流程拆解
2026/10/22 20:05:50 网站建设 项目流程

我最初看Transformer资料的时候,最大的挫败感来自“每个字都认识,连起来不知道在讲什么”。什么Query、Key、Value,什么多头注意力、位置编码,公式一摆,人就懵了。直到我把那篇经典的《Illustrated Transformer》图解博客反复啃了三遍,脑子里才终于有了一个“画面”:原来注意力机制就是“按需取用”,多头就是“多角度观察”,Transformer的整体流程说白了就是“先理解,再生成”。这篇内容就是按那个思路,把Transformer核心拆成大白话和可复现的细节,哪怕你是零基础小白,也能轻松入门。我建议你收藏起来,遇到不懂的模块随时回来翻。

1. 别被公式吓到:先建立一幅“注意力”的画面

1.1 翻译一句话时,人脑到底在做什么

要理解Transformer,先别急着看矩阵运算,先想一个问题:你在做翻译的时候,大脑是怎么工作的?

比如把英文“I love you”翻译成中文,你看到“love”这个词,不会只盯着“love”本身,你会下意识把“I”和“you”拉进来,“I”是动作发出者,“you”是接受者,这样才知道“love”翻译成“爱”而不是“喜欢”更合适,顺序也不会颠倒。这个“拉进来”的过程,本质就是一种注意力的分配:你在处理当前词时,给句子里的其他每个词分配了不同的关注权重,有的话关联强,有的话关联弱。

Transformer里的“注意力机制”干的就是这件事。它让模型在处理某个词时,可以动态地、按权重地去看句子里的所有其他词。注意是“所有”其他词,不是一个固定窗口,这是它和早期循环神经网络最不一样的地方。早期模型更像“一个字一个字往后读,记住之前的信息”,读到最后,前面的内容可能忘得差不多了;而Transformer是“一句话里每个词都直接和所有其他词对话”,信息不会因为离得太远就流失。这也是它能成为今天几乎所有大模型底座的核心理由。

1.2 图书馆查资料的三步流程

注意力机制听起来抽象,但你可以想象自己在一座图书馆里查资料。

你心里有一个想查的问题,比如“怎么做好番茄炒蛋”。这就是Query,可以理解为“查询”,也就是你最关心的那个信息需求。图书馆里每本书都有书名、关键词,我们把这些索引信息叫作Key,也就是“键”。书里真正的内容,做法、技巧、注意事项,叫作Value,也就是“值”。

你的查资料过程是这样的:你带着Query,走到书架前,把Query和每一本书的Key做对比,“这本是不是讲家常菜的?”“那本是菜谱还是营养学?”对比结果是一个匹配程度分数。匹配度越高的书,你会越多地翻阅它、引用它;匹配度低的书,你扫一眼就跳过。最后,你脑子里形成的“番茄炒蛋做法”其实是所有这些书内容的加权汇总,权重就是刚才算出的匹配度分数。

注意力机制的计算过程一模一样:Query先和每个Key算一个相似度分数,再把这些分数变成权重,然后用权重去加权求和所有Value。谁和当前需求越相关,谁对最终输出的贡献就越大。好消息是,这一切都是模型自己学出来的,不是我们手工指定的规则。

1.3 注意力公式与缩放因子的直觉

理解了画面,现在可以看公式了。自注意力机制的核心公式长这样:

Attention(Q, K, V) = softmax(Q × K的转置 / √d_k) × V

拆开看:

  • Q表示查询矩阵,代表“现在想关注什么”。
  • K表示键矩阵,代表“被用来匹配的标签”。
  • V表示值矩阵,代表“真正内容所在”。
  • Q和K做点积,算出每对词之间的匹配分数。分数越高,说明这两个位置越该互相注意。
  • 除以√d_k,是为了防止分数过大把softmax推向饱和区。
  • softmax把分数变成一组非负且总和为1的权重。
  • 最后用权重和V做加权平均,得到当前词的输出表示。

那为什么要除以√d_k而不是随便除个数?我用自己的经历说明。Q和K的点积结果会随着向量维度增大而变大。如果向量维度是64,点积结果可能远大于1,softmax输入太大会让输出变得“非黑即白”,也就是概率几乎集中到某一个位置,梯度变得非常平缓,模型几乎学不动。除以√d_k相当于把结果拉回方差接近1的量级,让softmax落在相对敏感的区域,梯度能正常回流。这个细节不少零基础教程会轻轻带过,但实际排查代码时,常常就是因为少了这一除,训练就是不对。

2. 从词到“带位置信息的向量”:输入表示怎么做

2.1 词向量:把词变成一串数字

计算机不能直接处理“love”“you”这些单词,一切要从词嵌入开始,也就是把词转成固定维度的向量。

比如设置一个d_model,也就是模型宽度,在经典Transformer里是512。那么每个词进来,都会对应一个512维的浮点数向量。你可以粗暴地把这个向量理解为“这个词在语义空间中的坐标”。意思相近的词,向量之间的距离往往更近。比如“猫”和“狗”肯定比“猫”和“汽车”在语义上更接近。

在实际操作里,句子先被切分成token,然后查词表,把每个token映射到自己的嵌入向量。假设一句话有4个词,那么输入矩阵的形状就是4×512,每一行是一个词的向量。这个矩阵接下来会进入Transformer的各个层,后面所有计算都基于它。

这里有个初学者常犯的直觉错误:以为嵌入向量是模型直接查表拿到的“真实答案”。其实嵌入层里的向量只是初始化值,训练过程中会不断被更新。最终学到的向量方向才是模型对语义的编码结果,而不是一开始就给定的“标准答案”。

2.2 位置编码:给每个词发一个“座位号”

循环神经网络天然有顺序概念,因为一个字是挨着一个字输入的。但Transformer是并行计算,一句话所有词同时进去,如果不额外告知位置,模型根本不知道“我爱你”和“你爱我”有什么区别。词的集合一样,语义却完全不同。

解决办法就是给每个词的位置也编一个向量,然后直接把位置向量加到词向量上。位置向量是哪里来的?经典论文里用的是三角函数:

PE(pos, 2i) = sin(pos / 10000的(2i/d_model)次方) PE(pos, 2i+1) = cos(pos / 10000的(2i/d_model)次方)

这里的pos是词在句子里的位置,i表示维度下标。

我当时一直想不通,为什么这么一大串三角函数能表示位置。后来换个角度才明白:不同维度的正弦波拥有不同频率,就像钟面上指针转的速度不一样。靠一组高频、低频组合的波形,每个位置都能得到一个独一无二的“指纹”。同时三角函数还自带一个好处:模型可以通过线性变换来学习相对位置关系,这比直接用0到N的自然整数编码要灵活得多,整数值没有上界,而且不同位置之间的关系无法被周期性地建模。

真正动手做模型时,位置编码有两种用法:一种是固定写死,不加训练;另一种是让模型自己学习一套位置嵌入。早期Transformer论文用的是固定三角函数,后来很多大规模模型改用可学习的位置嵌入,效果也没有差太多。关键点在于:无论哪种,都必须记得把位置信息和词向量加起来再送入模型,漏掉这一步,模型连“语序”都感知不到。

2.3 输入层之后的三种张量:Q、K、V从哪来

不少教程直接画三个矩阵W_Q、W_K、W_V,然后说“每个词都乘一下”,但为什么是三个矩阵?这其实是从输入去“投影”出三种不同的信息。

还是以4×512的输入矩阵X为例。我们会初始化三个形状为512×64的可学习权重矩阵:W_Q、W_K、W_V。注意这里的64是d_k,是每个注意头里的维度,不是512。然后直接做矩阵乘法:

  • Q = X × W_Q,得到4×64的查询矩阵
  • K = X × W_K,得到4×64的键矩阵
  • V = X × W_V,得到4×64的值矩阵

这里的逻辑是:同一个输入词向量,通过乘以不同矩阵,看清三种不同角色。Query代表“我打算索取什么”,Key代表“我能提供什么索引”,Value代表“我实际能给出的内容”。你可以把它想象成一个人同时戴着三副眼镜去看同一个词,每一副眼镜突出了不同侧面。

这里有个维度细节特别容易搞混,我在带新人时几乎每人都会踩一次。X的列数是d_model(512),投影到Q、K、V时,每个头的维度是d_k(64)。也就是说,我们用一个大矩阵512×64去把512维的词向量压到64维的“注意力空间”。后面多头注意力会把8个头的64维结果拼起来,重新拼回512维。理解这个“先压再拼”的流程,整个Transformer的维度变化就通了一半。

3. 核心部件逐个拆:自注意力、多头与残差

3.1 自注意力一次完整计算

现在我们把上面几块组合起来,看一次完整的自注意力计算到底长什么样。还是那个句子,4个词,输入矩阵X是4×512。

第一步,通过三个投影矩阵得到Q、K、V,它们的形状都是4×64。

第二步,把K转置,变成64×4,然后做Q × K的转置。这里的矩阵乘法是4×64乘以64×4,得到4×4的分数矩阵。分数矩阵里第i行第j列的含义是:句子第i个词对第j个词的关注分数。每行代表一个词的注意力分布。

第三步,除以√d_k,也就是√64=8。然后对每一行做softmax。这一步很关键,softmax是按行操作的,每一行的值会被压缩到0到1之间,且加起来等于1。

第四步,用这个4×4的权重矩阵去乘V,V的形状是4×64。结果是4×64的矩阵。这个结果里的第i行,就是第i个词综合了整句话信息后的新表示。

你发现没有,这个过程的输入和输出长度完全一样。正是这种“不管句子多长,每个词都跟所有词交互一次”的特性,让Transformer天然擅长捕捉长距离依赖。但它也有代价,计算复杂度是O(n²)级别的,句子越长,这个两两交互的矩阵会平方级膨胀。这也是后来各种稀疏注意力、线性注意力优化的出发点。

3.2 多头注意力:同一个意思,八个角度看

如果只做一次上面的注意力计算,模型只能在一个维度上判断词与词的关系。但实际语言太复杂了。比如“这只动物没吃到鱼,因为猫在睡觉”,动物指的是谁?如果只看语义相似度,不一定能分辨出指代关系。看到没,“因为”后面的cat才是决定“the animal”所指的关键。

多头注意力就是把计算并行重复好几遍。经典Transformer用8个头,每个头独立拥有一套W_Q、W_K、W_V,只是维度不同。还是4×512的输入,经过8组投影后,得到8组4×64的结果。每一组分别做独立的注意力计算,最后把这8个4×64的结果在最后一维拼起来,又变回4×512。

为什么要多头?我打一个比方。如果一个人带着单一思路读文章,可能会漏掉隐藏的联系。多头注意力相当于同时开了8个视角,有的头更关注语法角色,有的头更关注指代关系,有的头更关注相邻词共现。最后所有的视角融合到一起,信息就全面了。

不过,多头也不意味着头数越多越好。每个头计算都有成本,头数太多,单个头的维度太小,反而学不到什么东西。实际工程里,选择8、12、16都比较常见,关键看模型总宽度和训练数据量是否匹配。很多大规模模型最终其实就是“多头的注意力层堆叠”。

3.3 残差连接与LayerNorm:让深层网络“不散架”

知道了多头注意力还不够,经典Transformer里每个注意力层之后还会紧跟两个固定动作:残差连接和LayerNorm。

残差连接说白了就是“把输入原样加到输出上”。假设输入的向量是x,经过注意力层后的输出是Attention(x),那么真正的输出是x + Attention(x)。这个操作看起来简单,作用巨大。它让梯度在深层网络中有一条“直接回流”的高速路,避免网络层数太深就显得梯度消失、无法训练。你试想一个20层的网络,信息经过这么多次非线性变换可能早就“面目全非”了,有了残差连接,每一层至少保证不会比输入更差。

LayerNorm做的事情是对每一条样本的每一个位置做标准化。具体来说,它会把某个位置向量的所有维度变成均值为0、方差为1的分布,然后再缩放和平移一层。这样做的好处是,每一层的数值范围都相对稳定,不至于因为网络加深而越来越爆炸。

顺序也有讲究。Transformer里采用的是“先归一化再加残差”的做法,不过在具体实现中,有些代码库会把顺序反过来,叫“Post-Norm”和“Pre-Norm”。早期论文用的是Post-Norm,也就是“注意力后加残差,再LayerNorm”;后面很多大规模模型改用Pre-Norm,出现在每层处理之前做归一化。两种各有优劣,初学者不需要纠结,只要知道这是一个工程调优的选择就好。

4. 整个Transformer是怎么拼起来的:Encoder与Decoder

4.1 Encoder:句子编码成特征

单个注意力层讲完了,现在看整个模型。

经典Transformer由两部分组成:Encoder,编码器;Decoder,解码器。Encoder的职责是“读句子,理解成特征”。

每一个Encoder层包含两个主要部分:一个是多头自注意力层,另一个是前馈神经网络层。前馈神经网络就是两个全连接层加一个激活函数,它对每个位置独立处理,可以理解成对注意力提取到的信息做一次“深度加工”。这两部分各自都带了残差连接和LayerNorm。

经典模型把Encoder层堆叠6次。每一层都在不断“提炼”信息:第一层可能关注基本的词义和搭配,高层就能捕捉更复杂的语义关系。输入句子越复杂、越歧义,堆叠深层的优势就越明显。这就是为什么“深”对Transformer这么重要。

一个关键现象是, Encoder内部的自注意力不遮掩任何位置,所有词互相可见。因为理解句子不需要做防偷看,所有信息都可以光明正大地参考。

4.2 Decoder:带“防偷看”的生成器

Decoder的任务是逐词生成输出。它比Encoder多了一些限制。

Decode的基础结构和Encoder很像:也有多头自注意力层、前馈神经网络层,也有各自的残差连接和LayerNorm。但有两个关键不同。

第一个是掩码自注意力,也就是Masked Self-Attention。生成翻译时,模型必须一个一个词地出结果。比如翻译“我爱你”,第一个词生成“我”的时候,它不应该提前看到“你”和“爱”。所以Decoder里的自注意力会用一个上三角掩码把“未来位置”的信息屏蔽掉。计算注意力分数时,未来位置的分数会被设置为负无穷,经过softmax后权重就变成0。这就是“防偷看”机制。

第二个是交叉注意力,也就是Cross-Attention。Decoder不只是自己埋头生成,它还需要参考Encoder对原句的理解。交叉注意力的Query来自Decoder当前层的输出,而Key和Value来自Encoder最后一层的输出。你可以把Encoder看成“已经读完全文的记忆库”,Decoder每生成一个词,都会去这个记忆库里检索最相关的信息。这一步就是机器翻译里“忠实原文”的保证。

整个生成过程也叫自回归。模型先输入一个起始符,预测第一个词,把第一个词当输入预测第二个词,如此反复,直到预测出结束符为止。

4.3 一个极简示例帮助理解

用“I love you”翻译成“我爱你”走一遍完整流程,你马上就有感觉。

Encoder读到“I love you”三个词的嵌入向量,加上位置编码,经过6层Encoder后,每个词都变成了一个融合了整句信息的上下文向量。这里模型已经隐约知道“love”的发出者是“I”,对象是“you”。

Decoder收到一个起始符,输出第一个词的预测时,先用掩码自注意力看当前序列里只有起始符自己,然后通过交叉注意力去Encoder的输出中问“这个句子的核心动词是什么?动作的发出者是谁?”得到线索后,预测出“我”。

生成第二个词时,Decoder输入变成了“起始符+我”,掩码自注意力允许它在这两个位置之间做注意力,交叉注意力再去原句里重点检查“动作是什么”,于是预测出“爱”。以此类推。整个过程非常像一个“边查笔记边说话”的人:笔记是Encoder的理解,说话内容是Decoder的产出。

5. 实操环节:用代码与手推把流程验证一遍

5.1 几十行代码模拟一次自注意力

看图读了十遍,不如跑一次代码来得快。下面这个例子用Python和numpy模拟一个极简版的自注意力计算,验证维度变化,也方便你对照公式理解。

import numpy as np def softmax(x, axis=-1): # 防止指数溢出:减去每行最大值再求exp e_x = np.exp(x - np.max(x, axis=axis, keepdims=True)) return e_x / np.sum(e_x, axis=axis, keepdims=True) def attention(Q, K, V): d_k = Q.shape[-1] scores = Q @ K.T / np.sqrt(d_k) weights = softmax(scores) return weights @ V np.random.seed(42) d_model = 8 # 模型宽度 d_k = 4 # 单头维度 seq_len = 4 # 句子长度 # 随机造一个“句子”:4个词,每个词8维 X = np.random.randn(seq_len, d_model) # 三个可学习投影矩阵 W_Q = np.random.randn(d_model, d_k) W_K = np.random.randn(d_model, d_k) W_V = np.random.randn(d_model, d_k) Q = X @ W_Q K = X @ W_K V = X @ W_V out = attention(Q, K, V) print("Q shape:", Q.shape) print("K shape:", K.shape) print("V shape:", V.shape) print("输出 shape:", out.shape)

这段代码里,输入X是4×8,投影后得到4×4的Q、K、V。attentions中首先计算Q@K.T,得到4×4的注意力分数矩阵,除以√4也就是2,softmax后得到权重,再和V做矩阵乘法,最后输出还是4×4。你如果打印中间结果,会看到每一行的权重之和都等于1,这就是“每个词对其他词的关注度分配”。

实际工程代码里,Q、K、V的维度通常保持相同,以便处理方便。但代码验证时,你完全可以把d_k和d_v故意设置成不同值。再回到库里查资料那个比喻,查询的维度和内容维度不必须一致。只是经典实现里用了相同值,减少复杂度。

5.2 怎么把图解中“盒子和箭头”画成自己的推导图

当初那篇经典图解对我帮助最大的一点是:它画出了每一步张量形状的变化。你完全可以自己复刻这种图,不需要任何绘图工具,纸笔就够。

我的练习方法是:把一句话的输入尺寸写在最左边,比如“4×512”,然后每过一个模块就用箭头标出新尺寸。第一层词嵌入:4×512。加位置编码:还是4×512。经过3组投影:Q、K、V各是4×64。Q乘以K的转置:4×4。除以√d_k并softmax:还是4×4。乘以V:4×64。8个头的输出拼接:4×512。残差连接加LayerNorm:还是4×512。前馈网络,里面有中间扩大的维度,比如2048,输出又回到512。

等你把这条链画完,看一眼依然是一张整齐的“盒子箭头图”,Transformer在你心里就再也不是零散公式,而是一台有固定流水线的机器。任何维度问题,只要你不确定,就重新在纸上走一遍尺寸,基本不会错。

5.3 新手常见问题与排查

我在学习初期踩过不少坑,也帮不少人排查过类似问题,把最高频的几个整理成一张表:

常见问题可能原因排查方法
代码里维度报错混淆了d_model和d_k,投影后形状对不上从后往前检查每个张量的最后一维,确认投影矩阵形状
注意力分数特别大,softmax后权重几乎非0即1忘记除以√d_k检查有没有做缩放,scaled dot-product attention里缩放不是可选项
模型训练时梯度不稳定,Loss忽高忽低缺少LayerNorm或残差结构有问题检查每个子层后是否做了Add & Norm,顺序是否一致
训练和推理时结果差别很大Decoder掩码在训练时可能漏加或被忽略确认训练时是否使用teacher forcing,预测时是否自回归逐步生成
输入顺序颠倒后结果几乎没变化位置编码缺失或没有被正确加到输入上检查词向量和位置向量是否确实做了相加操作

另外给大家一个排错经验:不要凭肉眼去盯Loss曲线猜问题,先在很小的数据集上跑过拟合测试。如果你用几十条样本都无法让Loss降到很低,那大概率是模型结构问题或维度问题,而不是数据问题。这个小技巧帮我省下的调试时间,至少抵得上自己瞎试三天。

6. 学完这些之后:这些概念的实际用处

6.1 从Transformer衍生出的方向

Transformer核心机制,尤其是自注意力,已经不只是机器翻译的专利。现在几乎所有主流大规模预训练模型,无论生成文本、总结摘要、对话系统还是代码生成,底层都离不开这类结构。

有一种理解是,Transformer提供了一种“通用特征抽取器”。它不限定输入形式:你可以把文本转成token序列,也可以把图像切成patch序列,甚至可以把音频波形切成片段序列。只要是“序列到序列”的逻辑,Transformer都能套进去。这个特点催生了各种跨模态的方向:比如用同一套注意力机制同时处理文字和图像输入,或者在文本基础上生成图片。很多当初看来很复杂的视觉任务,最终用的核心反而就是“图像切片后当成词来处理”的思路。

这个范式的迁移能力,直接导致了一个结果:预训练加微调成了主流工作模式。先用大规模语料让模型学通用知识,再用小规模标注数据做下游任务适配。Transformer之所以适合这种模式,因为它有很强的共性表达能力和良好的可扩展性,堆层数、加数据就能持续提升效果。

6.2 为什么“多头”是工程和效果的权衡

很多人刚学会多头注意力时容易陷入一个误区:多头越多越好。实际上,多头的核心价值在于“用更多子空间去建模不同类型的关系”,但每个头的维度是d_model除以头数。头数越多,每个头的维度越小,能表达的信息就越有限,甚至会变得冗余。

在实际模型配置里,d_model=512,h=8,每个头维度就是64。如果你把头数改成16,每个头只有32维,信息表达能力下降,效果不一定提升,反而计算成本上去了。这个“先降维再拼接”的套路,是工程里典型的高效实现技巧。所以理解多头注意力不只看个数,还要看每个头“容量”是否够用。

6.3 进阶路线:从这张图走向更多源码复现

如果你已经能完整手推一遍上面的流程,下一步就不应该只停留在看图和回顾公式了。我的建议是两条路并行。

第一条路,去找一份极简的Transformer实现,最好是单文件、纯矩阵运算那种,逐行读并打上自己的注释。不要只抄代码,要能回答“这一行对应的是公式里的哪一部分”。尤其是mask、padding mask和attention mask的区别,光靠概念记忆很容易忘,动手写一遍才记得牢。

第二条路,尝试在小数据集上训练一个翻译或者文本分类模型。不需要用大规模语料,只需验证流程能跑通。配置好一个微型Transformer,用几百上千条样本,观察Loss是否下降,生成是否有初步语义。这个过程会帮你看清“配置参数”“训练过程”“推理阶段”三者的工程差异。我自己的经验是,一旦跑通这个mini全流程,再回头看各种大模型的技术报告,就不再是“看天书”了。

刚开始接触Transformer时,我一直觉得那些图很复杂,直到自己动手把每个张量形状标出来,再把示例代码跑通,才对“注意力”有了真正踏实的理解。如果让我给你一个最朴素的学习顺序建议,那就是:先看经典图解建立画面,再手推一遍维度变化,最后用几十行代码验证自己的理解。三件事做完,Transformer核心就算真正入门了。之后再遇到那些复杂变体,只需要在这张图的基础上做加减法,整个体系就不会再散。

最后分享一个小习惯,我至今都在用:每次读到一篇模型论文,第一件事不是看理论分析,而是先找模型结构图,把输入张量的起始形状标出来,顺着数据流往下走。哪一步维度变了、哪里多了一个分支、哪里加了mask,全部高亮出来。这个方法对我理解模型特别有效,你可以试试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询