用乐高积木比喻拆解现代GPT工作原理,涵盖RoPE位置编码、RMSNorm、滑动窗口注意力、SwiGLU等核心优化技术,帮助建立直观认知。
一个 token 如何穿越现代 GPT——从入口的 embedding 一路抵达下一个 token 的预测。
每项现代改进实际在做什么:RoPE、RMSNorm、滑动窗口(SSSL)注意力、value embedding、RELU²/SwiGLU、residual lambda,以及带 softcapping 的 untied weights。
为何这些改动让当今的模型比 GPT-2 高效得多——同时没有牺牲质量。
我一直在尝试理解现代 GPT 架构,但由于大量专业术语的存在,概念总是记不住。于是我决定用一个类比来讲一个故事,从而可视化这些技术底层究竟在发生什么——这种可视化能将概念具象化,塑造出坚实的 mental model。写这篇文章不仅是为了自己能深入理解这些概念,这种类比驱动的策略或许也能帮助他人建立坚实的 mental model,同时也能作为我未来的参考资料 😉。
在引入类比之前,先了解原始 GPT-2 架构会很有帮助(即便后文我会指出现代改进对 GPT-2 架构的优化之处),为此我强烈推荐《Build a Large Language Model from Scratch》一书,作者 Sebastian Raschka。这是理解 GPT 如何运作的绝佳资源——核心思想其实很简单:生成下一个 token,但背后的机制足够精密,才让这一切魔法得以发生。虽然类比同样完美适用于旧模型,但先理解遗留架构会让你充分做好准备,并欣赏后续的演进——现代架构的所有改进和效率提升是如何让 GPT 模型变得更加高效和优秀。这就好像 GPT-2 是普通的量产汽车,而现代 GPT 架构是 F1 方程式赛车——因为研究人员发现了精妙的技巧,替换掉了所有让它变慢的零件。归根结底,一切都关乎实现最高可能的效率、从 GPU 中榨取最大性能。
GPT 架构的核心部件是"Transformer Blocks",它赋予了我们定义上下文真值的能力。GPT 模型本质上是一个从"基础真值"到"上下文真值"再到"统一真值"的过程:
基础真值(Base Truth)= 单词独立存在时的静态、冻结含义(主词汇表 blueprint)。
上下文真值(Contextual Truth)= 含义如何根据其邻居而改变(注意力层的握手)。
统一真值(Unified Truth)= 对下一个词的最终、融合后的预测(输出头)。
所以,让我们先窥探一下历史,看看为何实际上需要 transformer,从而尝试理解 transformer。
2017 年之前,序列模型(RNN、LSTM)一次只能处理一个 token,每一步都依赖于前一步的隐藏状态。这导致两个问题:
第 10 步必须等第 9 步完成才能计算,因此训练很慢,无法在真正能发挥并行化优势的 GPU 上扩展。
关于 token 1 的信息必须经历 100 次顺序更新才能影响 token 100,因此信息被稀释,最终消失。
实际上注意力机制早已被发明,作为 RNN 的补丁(在 seq2seq 翻译中),它让 decoder 能直接"回看"encoder 状态(这带来了很大帮助),而不必依赖一个单一的压缩隐藏向量——后者丢失了重要的上下文真值。因此无论我们有 10 个词还是 100 个词,隐藏向量都是相同的固定大小,长句子的信息会丢失。
2017 年的洞察(Vaswani 等人)来自论文《Attention Is All You Need》——完全抛弃了循环结构,只保留注意力。
如果每个 token 在单层中能同时 attend 到所有其他 token,我们就本质上解决了上述两个问题——既没有距离上的退化,整条序列的计算也能并行进行。这种并行化才是真正的解锁所在——它让在互联网规模数据上训练成为可能。
要可视化一个 token 的完整旅程,我们需要从另一个角度看待它——在这里我们将 token 想象成乐高积木。我们将通过一个故事来分析它们的旅程,即"塔"的故事,这些乐高积木可以视为它的访客。
我们"乐高积木"类比中的每个部件(RoPE、滑动窗口注意力、value embedding、untied output weights 等)都是跨多个现代模型使用的技术——LLaMA、Mistral、Qwen 等都会混合搭配这些。nanochat 是一个很好的参考例子,因为它足够小,可以端到端读完,并且在一个地方结合了多个这些部件,这也将是我们故事中主要讨论的内容。
在进入故事之前,让我们分析一下是什么让 GPT 起效。GPT 只使用 transformer 的 decoder 部分,带有 masked self attention——每个 token 只能 attend 到它前面的 token,永远不会 attend 后面的——这是最重要的规则。让我们看下面的图 1 来将这个概念具象化:

图 1. brown(位置 3)回头可以看到"The"、"quick"。它看不到尾部方向的内容——那些词还没有到来。
将这个机制堆叠起来,用一个简单的目标训练它:预测下一个 token!
这个成功的要素本质上是三件事的结合:
一种自监督策略,不再需要昂贵的标注数据,整个互联网现在都成为了训练数据。
一种可以平滑扩展的架构——更多的层、更多的参数、更多的数据可靠地带来更好的性能(scaling laws)。
动态的、上下文依赖的表示——上下文真值意味着 token 的含义会根据它周围的一切被重新计算。
故事介绍——塔(我们的 GPT 模型)
这是一栋多层建筑,我们将在其中可视化访客——即我们的乐高积木——的旅程。让我们认识一下故事中的角色:
The(头,位置 1)
Fox(尾,位置 4)
这个故事的每一章都应该从上到下阅读,以便连贯地理解架构:一个 block(token)如何进入,如何收集上下文,如何查阅存储的知识,如何避免遗忘自己,以及下一个 block 最终如何被选中。
第一章——入口(徽章、平滑化和 twist)
一个裸露的乐高积木到达塔的大门。在门口,门卫给它分配了一个位置徽章,并从他的书(主词汇表)中递给这块积木一条向量条——本质上是一排数字,目前承载着这块积木朴素的、无上下文的身份,即它的基础真值。在这整个旅程中,没有任何东西会改变这块积木本身——它将以与进入塔时相同的原始身份离开;而向量条则会被上下文真值所丰富,并根据最后一块积木的向量条来选择下一块积木——即统一真值。
向量条被附到积木上之后,其粗糙的边缘被磨平,然后积木加入队列。这种平滑化技术在现代架构中也很特别。与它们不同,GPT-2 的 LayerNorm 平滑化因为涉及两步而代价高昂:
重中心化:找出数字的平均值并减去它,使它们围绕零分布。
重缩放:测量它们的分散程度(标准差)并除以它,使分散程度标准化。
简单来说就是让均值变为 0、方差变为 1。
而 RMSNorm(Root Mean Square Normalization)只涉及一步:
重缩放:用数字的均方根去除它们。不需要求均值,不需要减法——原来这才是最优区间,而这正是我们所需的一切,即重缩放步骤。
在区块继续前进之前,定义它在给定序列中的位置至关重要——没有这个信息,模型将无法区分"the cat eats the mice"(猫吃老鼠)和"the mice eat the cat"(老鼠吃猫),它们会被当作一堆积木而无顺序可言。在 GPT-2 中,为了嵌入区块的位置信号,第二条向量条被固定在顶部,但现代架构使用了一种巧妙的技巧——RoPE(Rotary Positional Encoding,旋转位置编码),即根据区块的位置,将向量条上的数字按特定角度在二维配对中扭转,如图 2 所示:

图 2. 该图展示了 d_model = 8 时每个 token 对应 4 个旋钮配对。位置 0("The")不扭转。沿列向下看:快速旋钮(配对 0)从 0° 扫到 172°,明显区分了各位置;慢速旋钮(配对 3)几乎不动。多种速度使得一条向量条能同时编码近端和远端位置。
一个重要的考量点是,RoPE 不直接作用于向量条(这是相对于 GPT-2 的改进),而是作用于从向量条派生出的卡片——即 Q(Question card,问题卡——我在寻找什么)和 K(Key or label card,标签卡——我能提供什么)卡片,当区块进入 Attention 大厅时(在第 2 章中会详细介绍这些大厅)。这些卡片在区块攀升一层时被拆散,并在下一层从一个更丰富的向量条重新派生,而扭转角度保持不变。这一过程会发生在塔的每一层,如图 3 所示。

图 3. 向量条沿虚线脊笔直向上,从不扭转,只增加音符。在每个注意力大厅,它生成新的问题卡/标签卡,获得位置扭转,然后比较,最后丢弃。大厅 2 的扭转角度与大厅 1 相同。
队列继续攀爬高塔。每一层都有一个消息大厅,区块在这里分享笔记,只遵循一条不变规则:只向后看,看向队列的头部。
一个区块将其问题卡与前面区块的标签卡进行比较,以决定该听谁的话,然后将被选中的内容复制到其向量条中,用上下文丰富它,并向"上下文真相"更近一步。
在长序列中读取每个前面的区块代价高昂,假设我们的窗口大小是 3(在生产模型中可能是 1024 及以上),大多数大厅都是"短"的:一个区块可能只读取最近的几个邻居,即当前区块及其后面的 2 个。
SSSL 具体代表滑动窗口注意力,每四个大厅有一个市政大厅("长"),在那里限制解除,区块可以一直看到队列的头部。三短一长——S.S.S.L 节奏——在整叠中重复。
简言之,我们试图兼得两者之长。短大厅通过滑动窗口拾取上下文,无需回看全部(这也可以描述为"接力"),而市政大厅则给予它们机会一路回到头部交换笔记,丰富它们的向量条——同时在整个过程中保持架构的高效。
接力可以简单描述如下:处于短大厅的区块只能看到后面两个座位(因为我们这里将窗口大小设为 3),但头部的消息仍然能到达;像传桶一样向前传递。一旦近处的区块吸收了"The",更远的区块在下一层读取该区块,并二手继承它。窗口从不扩大,但传递者增加了。深度本质上被广度所替代。这个概念如图 4 所示:

图 4. 追踪单词"The"向上经过四层。箭头显示每个新区块如何在其 2 个座位的窗口内从传递者那里获取消息;每条箭头长度相同。前沿在每个短层前进两个座位,然后市政大厅将其同时传递给所有人。
我之前提到过,在每个大厅,区块从其向量条派生两张卡片,即 Q 和 K,然后根据计算出的角度应用扭转,实际上区块实际生成三张卡片,即 Q(问题)、K(标签)和 V(Value 值),其中 V 卡是特殊的,扭转不会应用于这张卡片,它基本上可以被认为是改变手的实际内容包裹。
经过十几层的重写和丰富向量条后,原始事实"这个区块 literally 就是单词 fox"会逐渐淡出,因此在交替的大厅,区块会被递给一个背包——这是一个根据其单词从第二个账本中查找的数据包;它原始的、最初的身份,不受任何楼层的影响。
现在有趣的部分来了:这个身份应该注入多少也是由模型学习的,一个音量旋钮(sigmoid 门)决定有多少被注入到值包裹中。
从计算角度来看,这个操作代价可以忽略不计,因为它只是一个查找,但我们获得了几乎零计算量的巨大召回提升!
要可视化这个概念,请看下面的图 5:

图 5. 从 fox 的向量条派生三张卡片;Q 和 K 被扭转,V 没有。一个背包(原始"fox"的键查找)通过门控计量并混入 V 中。背包只出现在交替的大厅。
在每一层,区块通过消息大厅后,会独自进入一个私人房间(前馈层)。其他区块不允许在这里加入。房间的墙壁可以被认为是概念探测器的巨大交换台,这些是模型在训练中学习到的("这是动物吗?","下一个可能是动词吗?")。
区块举起它的向量条,房间里的每个交换台对它的匹配程度进行评分。
RELU² 可以被认为是这个房间中的布线,它遵循两条规则:
任何评分不为正的交换台被彻底杀死至零。
任何被触发的交换台的音量被"平方",因此自信的匹配变得更突出,而胆怯的匹配则缩小。
在 GPT-2 中,GELU 的软布线让微弱的低语通过,这需要一些计算密集型操作,因为即使一个概念只有最低的匹配概率也会通过,意味着更多的指标操作。另一方面,RELU² 对负值是一个硬开关,这意味着许多交换台将完全关闭(零),而匹配的则被放大。这些零使查找变得廉价,因此激活也比 GELU 更便宜。
这些激活可以从下面的图 6 中为 fox 的区块可视化:

图 6. 上图:一个输入处的交换台。负分为关闭;正分在 score² 处发光。下图:激活曲线——RELU² 在零左侧完全平直,然后是二次方,因此弱匹配被抑制,强者占主导。
最后,点亮的交换台将它们存储的事实注入区块的向量条,使其在事实层面也更丰富。
消息大厅混合来自邻居的上下文;私人房间独立处理每个区块(来自模型权重的知识)。这发生在每一层,向量条在两个轴上都变得更丰富。
一些现代架构也使用 SwiGLU 激活,这可以带来更高质量,但代价是更多计算,因为它引入了更多参数。重要的是,我们也要在这里简要讨论 SwiGLU,因为它是大模型最喜爱的选择之一。
SwiGLU 改变了布线本身,它添加了 RELU² 没有的第二个控制,要理解 SwiGLU,让我们将其与 RELU² 进行比较:
RELU²——一条路径,里面有一个交换台。探测器对向量条评分,然后该分数通过交换台。

图 7. 一条路径——分数决定自己的音量。
每个神经元不再从词条上计算一个数字,而是计算两个独立的数字:
门控数会经过一个 S 形软压缩函数(称为 SILU/Swish,是 sigmoid 的平滑表亲),被转换成一个软音量旋钮。然后两个数字相乘,得到最终信号:
Final output = content × gate-knob
也就是说,一个神经元可以计算出很强的内容,但自己的门控可以把它调低,反之亦然。最终输出本质上是两个信号之间协商的结果。

图 8. 两条路径——内容与门控旋钮分别计算后相乘。
让我们用比喻来重新表述这个比较:RELU² 的交换台每个开关配一个操作员,仅凭匹配结果决定开/关和音量;而 SwiGLU 的交换台给每个开关配两个操作员,一个提议内容,另一个掌控旋钮;他们的输出在事实注入词条之前先相乘。同一间房、同一目的(检索事实),但每个开关有了更具表达力的控制机制。
在入口门处,模型悄悄制作一份原始词条的主副本(我们可以称之为 x₀),并将它滑入一个永远不会变化的区块口袋中。
这包含了该区块在零层时的状态,即其位置相关的上下文无关含义。那么为什么我们需要它呢?附加的词条在每一层都会得到丰富——它会被覆盖很多次,原始身份最终会在深度处变成耳语——因此拥有来自零层的原始词条状态有助于该区块在各层之间保持这种身份。
这里可能会产生一个常见混淆:我在第 4 章中提到,在交替的层中会给区块一个背包,其中包含它的原始身份;但每个背包携带的东西是有区别的:
背包:词的原始身份,局部于注意力,从表中查询而来(顺便说一句,它不是主词汇表,而是一个专门为此目的而存在的另一个表,用于在深度处注入身份,也称为值嵌入),由区块查询——"这个槽位就是 fox"。它是一个新鲜的向量,在查询时生成,不是区块自带的东西。
口袋副本:区块的完整入口词条——它在门口被交付的完整向量,包括其位置和上下文无关的含义。它是区块自己的原始状态,被封存并向上携带。
所以真正的区别是:背包是"我是什么词",口袋副本是"我出发时完整的词条是什么",一个是字典查询,另一个是此特定区块在零层的快照。
在每一层都有一个混音台,上面有两个也是每层学习的旋钮:
向上携带的词条是两者的混合。GPT-2 中普通的跳跃连接只回溯一层,而这里的跳跃连接一直回溯到零层,每一层都是如此。

图 9. 原始状态在 12 层中存活多少。简单加法让它衰减趋近于零;重新混合 x₀ 使其保持在由 λ₀(此处 = 0.3)设定的平台上,一直深入塔中(曲线为示意性)。
此时,该区块的词条已经包含了上下文真相和事实知识,而且由于 fox 在末尾,它的词条将是命名下一个区块的那个。
Fox 将它的词条交给阅卷官,由他对词汇表中的每个词打分。
解耦权重(Untied weights):GPT-2 在出口处(反向运行)复用了门房的簿记本以节省空间。nanochat 给阅卷官一本单独的书,只为预测而训练。
为什么我们有两本书?因为它们服务不同的目的:将词转换为词条和将词条读回为词是两回事,阅卷官的书将允许模型做出更锐利的猜测,但代价是额外的参数,这是我们为质量必须做出的权衡。
Logit 软上限(Logit softcapping):在分数变成投票之前,每个分数都要经过一个"不要喊叫"