← 返回博客
[从零训练一个200B大语言模型] 6/25:Attention是怎样训练出来的?

[从零训练一个200B大语言模型] 6/25:Attention是怎样训练出来的?

训练材料里有一句话:“窗外下着雨,我拿起了伞。”

把最后的“伞”暂时遮住,模型看到的是“窗外下着雨,我拿起了……”。它会给接下来的内容分配不同的可能性。然后,我们把原文中的答案拿出来比较。这就有了一次训练练习。

可是,答案只是一个“伞”字。它怎样让Attention学会去看前面的“雨”?这篇文章就沿着这一次练习,看看模型里面哪些数字会改变,又是怎样决定改变方向的。

先把一次训练走完

模型接到的文字,先被切成一个个小片段,叫token,再换成数字。数字经过一层层计算,最后变成对下一个token的预测。为了方便讲述,下面用“伞”代表要预测的答案;实际分词时,一个词可能对应多个token。

训练程序知道原文,所以知道这里的答案是什么。它查看模型给正确答案分了多少可能性,再算出一个表示预测差距的数。这个数叫损失,越小表示这次预测越接近训练目标。

接下来,程序沿着刚才的计算往回走,算出哪些数字往哪个方向调整,会帮助减小损失。调整以后,模型再做下一批练习。训练就是不断重复这四步:预测、比较、回传、更新。

训练循环:输入经过蓝色A Attention和金色B加工得到预测;答案用于计算误差,误差沿计算路径返回并更新参数。箭头表示计算或反馈路径,框表示步骤

图中的蓝色A是Attention,负责从可见的前文取信息;金色B负责继续加工。第四篇讲过它们,第一次读这一篇,只要先记住这两个分工就够了。图里的回传箭头经过A和B,意味着两部分都会学习。

Token:模型处理文字的小片段,可以是一个字、一个词,或词的一部分。

损失:用来衡量预测与训练答案差距的数值。训练通过调整模型,努力让它下降。

Attention到底在学什么?

单看“拿起”,我们只知道这是一个动作。把它放回“窗外下着雨,我拿起了……”,这个动作就有了背景。Attention要学的,就是怎样把有用的背景带到当前位置。

模型里,每个位置都有一串代表当前状态的数字。经过Attention取回信息,再经过B加工,这串数字就会更新。因此,后面层看到的“拿起”,已经可以包含下雨的背景。沿句子向右,可用的前文更多;沿网络往下,每个位置对这些信息的表示也在继续变化。

这里可以借用一叠资料卡来理解。每张卡有供查找的线索,也有可以取回的内容;当前位置则带着一次查找需要。这三项分别叫Q、K、V:

Q:我现在想找什么。

K:我有哪些线索,能与你的需要匹配。

V:匹配以后,我能提供什么信息。

Q和各个K比较,得到不同的匹配程度,再按比例取回、混合对应的V。比如,前面“雨”所在位置可能提供行动背景。这些匹配方式由训练学出来,模型里的实际Q、K、V都是数字,里面没有人工写好的“天气”栏目。

蓝色A内,当前状态生成Q,可见状态分别生成K和V。Q与已有K比较后得到总和为100%的混合比例;比例与已有V分别进入加权步骤,再汇总成混合信息。分格条为向量、小格为数字,百分比为示意值

产生Q、K、V的计算,各自有一组可以调整的数字,叫参数。参数决定了模型怎样表达查找需要、怎样提供线索、怎样整理内容。Attention训练的正是这些参数。下一句话进来,同一套参数又能算出新的Q、K、V和匹配比例。Transformer论文给出了这套计算的结构。

参数:模型训练时会调整、保存后供下一次计算使用的数字,也常叫权重。

答案只有“伞”,怎么知道该改哪个数?

我们没有另外告诉模型“请给雨分配60%的注意力”。训练程序得到的是原文的下一个token,以及模型刚才是怎样一步步算出预测的。

把Attention里的一个参数想成一个可调的旋钮。现在问:如果只把这个数调大一点,最后的损失会变大,还是变小?如果会变小,就找到了一个可以尝试的调整方向。其他参数也可以问同样的问题。

真实训练利用每一步运算的关系,直接算出这种影响。假如某个参数改变,会先改变Q;Q改变,会改变匹配比例;比例改变,又会改变取回的信息,最后影响答案的概率。把这条影响关系从最终损失往回接起来,就能算出参数应该怎样调整。

这个过程叫反向传播。算出的“某个参数轻微变化,会怎样影响损失”,叫梯度。它让程序能一起处理大量参数,不必逐个转动旋钮、重新运行整个模型。PyTorch的说明展示了这种自动求导过程。

反向传播:沿着已有计算,从最终损失往回计算各个参数的影响。

梯度:一个参数轻微变化时,损失会朝哪个方向、以多快的速度变化。

看一个数字怎样真正改动

为了把刚才的“旋钮”看清楚,我们把计算缩成只有两个输入位置、一组Attention、两个候选答案的小例子,把正确候选叫作“伞”。这个小模型让我们完整算一次参数更新;它的数值用来展示学习过程。

第一次计算,正确候选的概率是56.09%。我们希望它得到更高的概率。Attention中有一个负责生成Q的参数,我们为这个例子选定的初始值是0.5。通过回传,程序算出:把这个参数稍微调大,有助于降低当前损失。

这次算出的梯度约为−0.20637。我们为这个例子选择0.1作为学习率,用它控制每次调整的幅度。按梯度的0.1倍,向降低损失的方向调整,参数就增加约0.02064,从0.5变成0.52064。产生K、V的参数和Attention的输出参数也一起调整。用全部更新后的参数重新计算同一份输入,正确候选的概率变成57.10%。

学习率:控制每次参数调整幅度的数。在这个例子里,调整量等于梯度乘以学习率,再向降低损失的方向更新。

可复算教学例子:一个生成Q的参数从0.5更新到0.52064;其他参数也更新;同一输入重新计算后,正确候选概率从56.09%升至57.10%。每个小格为一个参数数字,条形长度为概率

这就是一次学习留下的变化:保存下来的参数改了,下次查找和加工信息的方式也就跟着改变。Q、K、V和注意力比例,会用新参数重新算。完整初值、更新公式和可运行的小程序,放在下面的“展开这次小计算”里。

整个模型就这样一起学

完整模型有很多层。最后的预测同时受到这些层的影响,反向传播就沿这些计算一路往回走,算出各层参数的梯度。每一层Attention、每一层B,以及把文字换成数字、把最终状态换成预测的部分,都参与训练。

我们刚才只盯着“伞”这一个答案看。真实训练可以把整句话变成多道练习:“窗外下着”后面预测“雨”,“窗外下着雨,我拿起了”后面预测“伞”。每个位置只能读取自己和前文,答案用于预测之后的评分。

同一层的各个位置使用上一层已经得到的状态,因此许多位置可以并行计算;各层则依次往下走。一批材料的预测损失汇总起来,再共同决定这次参数更新。这样,训练程序既能利用GPU并行,又能保持每道练习的读取边界。

练过许多句子以后,参数逐渐学会哪些线索有助于预测、该从哪里取信息、怎样加工取回的内容。我们给的是文本中的答案,模型学的是完成预测的一整套计算方法。每次调整的幅度要合适,某一种联系是否学得好,还要换一批材料来检验。

对我们的200B模型,Attention的训练主线就清楚了:先用参数取信息并做预测,再用预测损失更新这些参数。接下来要选择的是一次给它多长的材料、允许它联系多远的前文。下面保留了并行训练、显存和长文设计的展开说明,便于继续做这一项选择。

展开这次小计算:初值、梯度和重算结果

W_Q、W_K、W_V分别是生成Q、K、V的权重,W_O是混合V以后的输出变换权重。这个小例子的损失取−ln(正确答案的概率),正确概率越高,损失越小。

两个输入状态分别是x1 = [1, 0]与x2 = [0, 1]。只展示第二个位置预测下一个token的损失。输入状态和最终两候选的读出规则固定,Attention的七个参数参与这次更新:

参数更新前梯度更新后
W_Q第1个数0.200000.000000.20000
W_Q第2个数0.50000−0.206370.52064
W_K第1个数1.00000−0.103181.01032
W_K第2个数0.000000.10318−0.01032
W_V第1个数1.00000−0.273311.02733
W_V第2个数−1.00000−0.16577−0.98342
W_O1.00000−0.107541.01075

开始时,当前位置的Q是0 × 0.2 + 1 × 0.5 = 0.5。两个位置的K分别是1和0,V分别是1和−1。Q与K相乘得到两个匹配分数0.5和0。将分数通过softmax换成比例,第一个位置为exp(0.5) ÷ [exp(0.5) + exp(0)] = 0.622459,第二个位置为0.377541。

取回V的混合结果是0.622459 × 1 + 0.377541 × (−1) = 0.244919。再乘W_O,输出仍为0.244919。用这个数和固定的0作为两个候选的分数,再做一次softmax,“伞”的概率是exp(0.244919) ÷ [exp(0.244919) + exp(0)] = 0.560925,损失是0.578167。

我们可以把W_Q第二个数到损失之间的局部变化接起来。它到Q这一段的变化率是1;Q到第一个位置注意力比例是0.622459 × 0.377541;比例到混合结果是两个V之差1 − (−1) = 2;混合结果到输出分数是W_O,即1;输出分数到最终损失是0.560925 − 1。把沿途的这些变化率相乘:

梯度 = 1 × (0.622459 × 0.377541) × 2 × 1 × (0.560925 − 1) ≈ −0.20637

这就是正文中那个梯度的来历。W_Q第1个数的梯度为零,是因为当前输入x2的第1个分量为零,而这个小例子只取第二个位置的损失。

七个参数以学习率0.1同步更新以后,新的Q为0.520637,两个注意力比例约为62.98%和37.02%。新的混合结果是0.282960,乘更新后的W_O得到0.286003,正确候选概率为0.571017,损失为0.560336。注意力比例、Q与V都重新计算,训练后保存的是更新后的参数。

下载并运行这个Python小例子可以重现这些数字。程序还逐项比较解析梯度与微小扰动得到的数值梯度,最大差异约为1.11 × 10⁻¹⁰。真实模型会使用更宽的向量、许多层和更完整的训练目标,沿计算路径求梯度的原理相同。

Softmax:把一组分数转成总和为1的非负比例。在Attention里用于分配取回比例,在输出端用于得到候选token概率。

一整句话怎样并行训练?训练中间结果与KV缓存有什么区别?

各位置怎样分工

先用四个示意token看清楚过程。输入位置依次是t1、t2、t3、t4;训练文本里还有紧接着的t5。每个位置负责预测它后面的那个token:

当前输入位置这个位置允许使用的输入要预测的答案
t1t1t2
t2t1、t2t3
t3t1、t2、t3t4
t4t1、t2、t3、t4t5

t1至t5代表分词以后的位置,一个汉字或一个词可能对应一个或多个token。把表换回刚才那句话,意思就是:处理“拿起了”所对应的最后一个位置时,模型可以利用前面的雨,学习预测后面的“伞”。

训练程序知道完整句子,但会给每个位置设一道读取边界。在蓝色A里,t2的Q只能与t1、t2的K比较,取回它们的V;t3、t4虽然也在这一批输入里,对t2仍然不可见。这道边界叫因果掩码。答案放在边界之外,等预测做完,才拿来计算误差。

同一层的各个位置,输入都来自上一层已经得到的状态,因此这些位置可以一起做Q/K/V计算和各自的注意力。t4用到的是这一层输入端的t1至t4,整体进入下一层后,再继续更新。每一层依次向下走;同一层内部,许多位置并行做练习。Transformer论文用掩码和错开一个位置的预测目标实现这种安排。

训练时输入token和下一token目标错开一位;因果掩码挡住未来,各位置在同一层并行计算,误差用于反向传播和参数更新

这种训练方式通常称为teacher forcing:训练时使用材料中真实的前文。它让我们可以把一句话里的许多预测放在同一次计算中完成。Hugging Face的从零训练示例就把已知文本整理成这样的输入和标签。

因果掩码:限制注意力读取范围,让一个位置只能使用自己和更早位置的信息。

Teacher forcing:训练时给模型提供真实的前文,让它学习预测接下来的内容。

反向传播需要保留什么

这次前向计算中,每一层都要生成自己的Q、K、V。它们参与注意力,和其他中间结果一起形成后面的状态。反向传播还需要其中的一些信息,才能算出参数该往哪个方向调整。

这些用于训练的中间结果叫激活。把它们留在显存里,反向传播时就能直接用;也可以只留下部分结果,等反向传播需要时,再把相应的一段计算重做一次。这就是激活重计算,用更多计算换取较少的显存占用。PyTorch的说明展示了这两种安排。

逐token生成的KV缓存解决的是另一项工作:模型已经算过前面的文字,接着生成时把旧K、V拿来继续用。我们这里的常规预训练,一次就处理整段已知材料,通常不使用这种不断追加的生成缓存。Hugging Face的缓存文档也把它放在推理阶段。训练更新了参数以后,旧参数算出的K、V不能直接当成新参数的结果继续用。

因此,安排训练显存时,要核算模型参数、梯度、优化器状态、激活以及运行需要的临时空间。生成服务里的“一个请求要留多大的KV缓存”,属于模型上线后的另一张账。生成合成训练数据、强化学习中让模型先生成回答时,也可能使用KV缓存;那一步承担的是生成工作。

激活:模型处理当前输入时产生的中间结果,其中一些要用于反向传播。

激活重计算:先少保存一部分中间结果,在反向传播需要时重新算出它们,以减少显存占用。

KV缓存:逐步生成时保存已经算出的K、V,供后续生成步骤复用的数据。

上线生成时,KV缓存怎样工作

模型上线生成回答时,先处理完整提示词,这一步叫prefill。随后每次接出一个新token,再处理这个位置、预测后续token,这一步叫decode。输入前缀和模型参数保持相同时,因果注意力让旧位置的K、V保持不变,因此可以保存下来,供后续步骤直接读取。

仅推理:先处理输入并保存每层KV,再计算新位置的KV并追加,后续生成复用旧KV

缓存省下旧位置K、V的重复生成。新位置仍要计算自己的Q、K、V,用新Q与可见的K匹配,再取回对应的V。前文越长,读取这些信息仍有开销。

这是一笔推理存储账。用40层、每层8组K/V、每组K和V各128个数、每个数2字节的教学配置,一个位置的KV需要:

40 × 8 × 128 × 2份(K和V)× 2字节 = 163,840字节 = 160 KiB

一个请求保留8,192个位置就是1.25 GiB;32,768个位置是5 GiB;131,072个位置是20 GiB。这些数字只计算该请求的KV,不包括模型权重或其他运行数据,也不代表训练显存。200B模型的实际配置确定后,再按其层数和K/V结构重新计算上线预算。

Prefill:生成服务先处理已经给出的完整输入,为后续输出准备状态的阶段。

Decode:处理新生成的token,继续预测后续token的阶段。

KiB与GiB:存储单位。1 KiB等于1,024字节,1 GiB等于1,024的三次方字节。

让200B模型学习更长的前文:计算成本、2026年架构与实验计划

训练材料长一倍,要多做多少工作?

先只看蓝色A中的全范围因果注意力。第一个位置可以看1处,第二个看2处,第三个看3处。一直加到第N个,就是:

1 + 2 + … + N = N × (N + 1) ÷ 2

8个位置共有36对可用关系,16个位置有136对。随着N变大,一条序列的长度翻倍,这部分关系的数量接近四倍。每一对关系都需要比较Q和K,并在取回信息时参与计算。训练还要对这些运算做反向传播。

训练中8个位置有36对可见关系,16个位置有136对;固定总token数时,两条8位置序列有72对关系,一条16位置序列有136对

这里还要把“一条序列”和“整个项目”分开。假如训练总量固定为4T token,把序列长度翻倍,序列条数就大约减半。以小图为例,两条8位置的序列一共16个token,关系数是36加36,等于72;换成一条16位置的序列,同样16个token,关系数变成136。前一种安排中,两条序列之间没有注意力连接;后一种则多了跨过原来边界的联系。

于是,在固定总token数量、每条序列内部使用全范围注意力的口径下,长度翻倍会让这部分注意力关系总量接近翻倍。模型里的其他计算、GPU并行效率和数据搬运也在影响用时,所以训练时间要用完整任务实测。第五篇里“少做计算”和“更快完成”的区别,在这里仍然适用。

显存也要实测。序列变长,一条序列会产生更多中间状态。实现方式则决定了哪些结果需要同时留在显存。比如FlashAttention把注意力分块计算,避免把整张注意力分数矩阵完整地存到GPU显存中,同时保持精确注意力的计算结果。图中的三角形画的是允许发生的关系,可以用来数工作量;实际运行不必把它原样存成一张巨大的表。

全范围因果注意力:每个位置可以读取自己和全部更早的位置。

序列长度:一次训练样本包含的token数量。固定总token数量时,改变长度也会改变样本条数。

FlashAttention:通过分块计算和安排数据搬运,降低注意力计算显存开销的一类实现方法。

想读得更长,可以怎样设计蓝色A?

全范围注意力给每个位置最多的直接联系,代价也随长度增加。我们可以调整蓝色A,让模型在保留关键关系的同时,减少每一步需要处理的内容。

一个办法是让某些层细看附近,另一些层负责更远的联系。第四篇的Meta Muse Glimmer就是2026年的例子:它按“三层局部、一层全局”重复安排,局部窗口为2,048个位置。对应到我们的总图,变化的是每层蓝色A允许读取的位置。

另一条路线先把一段前文整理成较少的数字表示,再选择相关的部分参加注意力。附近文字可以保留详细信息,较远的信息走压缩和选择这条路。训练时,模型需要一起学会怎样整理信息、怎样选择,以及怎样把两条路取回的内容用好。

蓝色A使用附近的详细表示与较远前文的压缩表示;箭头表示信息流,分格条是向量、小格是数,外框区分信息路径

DeepSeek在2026年9月公开的V4.1-Flash报告把局部注意力与压缩稀疏注意力结合起来,10月17日的Flash-1017更新沿用这一架构。报告分别讨论训练中的注意力共享实现与上线后的缓存管理。这里先取其中的架构选择:哪些前文保留详细表示,哪些先压缩,再怎样选出值得参与的部分。

窗口越小,单层直接看见的附近范围越小;压缩得越多,留下的表示就要更集中;选择的位置越少,选得是否准确就越重要。因此,训练这类结构时,要给它能学到远距离关系的材料,再检查模型能否找到长文里的日期、关联分散的证据、辨别前后修订。长度和架构一起决定了它有机会学什么。

滑动窗口:把一个位置的直接读取范围限制在附近一段输入中的注意力安排。

稀疏注意力:从可用位置或表示里选择一部分参与注意力计算。

压缩表示:用更少的数据保留输入信息,供后续计算使用。

我们的200B模型,应该怎样定这一项?

先列出8K、32K和128K token三档实验长度,使用相同分词器和可比的数据来源与质量。每一档先安排相同的累计训练token数量,长度更长时相应减少序列条数;再调整每批序列数和梯度累积,让每次参数更新处理的总token数量保持一致。这让我们能比较:花同样的训练材料预算,更长的可见范围带来了什么。

这三档是实验选项。正式选择还要看我们准备让模型处理什么材料:日常对话、完整会议记录,还是跨许多页的技术文档。训练序列里要有对应的长距离关系,评估材料也要有足够长的前后依赖,模型才有学习和展示这种能力的机会。

架构从全范围注意力开始,再比较局部与全局交替、压缩与选择读取的候选。每次实验记录训练损失、长文任务表现、每秒处理的训练token数和峰值显存。吞吐包括前向、反向和参数更新,使用相同的GPU配置;把激活重计算、每批序列数等运行设置一起留在记录里。这样,效果、时间和机器需求才对应同一个方案。

如果决定分阶段增加训练长度,就把每阶段的长度、token数量和数据配比列清楚,再按各阶段实测吞吐分别计算用时,更新第二篇的训练排期和第三篇的预算。模型支持多大的输入窗口,以及训练中实际练习了多长的材料,都要写进最终配置。

回到这篇文章的主线:Attention用参数生成Q、K、V,取回信息并参与预测;预测损失沿计算路径往回传,帮助程序更新这些参数。反复练习以后,模型逐渐学会怎样寻找和使用前文。接下来,再把候选结构和材料长度放进训练实验,让效果与实测成本一起决定200B模型的具体配置。

梯度累积:先处理几个小批次,累积梯度后再更新一次参数,用来安排每次更新的总训练量。

训练吞吐:包含前向、反向和参数更新的训练过程中,单位时间处理的token数量。

峰值显存:一次运行中GPU显存占用达到的最高值。

新文章,直接发到你的邮箱。

AI、工程与实验,每月 1–2 封。

无垃圾邮件,随时取消。