![[从零训练一个200B大语言模型] 6/25:Attention是怎样训练出来的?](/_next/image?url=%2Fblog%2Ftraining-200b-part-6-cover.png&w=3840&q=75&dpl=dpl_6PvZJYM25xw2xGK8pSA7Dt43f6ZF)
[从零训练一个200B大语言模型] 6/25:Attention是怎样训练出来的?
训练材料里有一句话:“窗外下着雨,我拿起了伞。”
把最后的“伞”暂时遮住,模型看到的是“窗外下着雨,我拿起了……”。它会给接下来的内容分配不同的可能性。然后,我们把原文中的答案拿出来比较。这就有了一次训练练习。
可是,答案只是一个“伞”字。它怎样让Attention学会去看前面的“雨”?这篇文章就沿着这一次练习,看看模型里面哪些数字会改变,又是怎样决定改变方向的。
先把一次训练走完
模型接到的文字,先被切成一个个小片段,叫token,再换成数字。数字经过一层层计算,最后变成对下一个token的预测。为了方便讲述,下面用“伞”代表要预测的答案;实际分词时,一个词可能对应多个token。
训练程序知道原文,所以知道这里的答案是什么。它查看模型给正确答案分了多少可能性,再算出一个表示预测差距的数。这个数叫损失,越小表示这次预测越接近训练目标。
接下来,程序沿着刚才的计算往回走,算出哪些数字往哪个方向调整,会帮助减小损失。调整以后,模型再做下一批练习。训练就是不断重复这四步:预测、比较、回传、更新。
图中的蓝色A是Attention,负责从可见的前文取信息;金色B负责继续加工。第四篇讲过它们,第一次读这一篇,只要先记住这两个分工就够了。图里的回传箭头经过A和B,意味着两部分都会学习。
Token:模型处理文字的小片段,可以是一个字、一个词,或词的一部分。
损失:用来衡量预测与训练答案差距的数值。训练通过调整模型,努力让它下降。
Attention到底在学什么?
单看“拿起”,我们只知道这是一个动作。把它放回“窗外下着雨,我拿起了……”,这个动作就有了背景。Attention要学的,就是怎样把有用的背景带到当前位置。
模型里,每个位置都有一串代表当前状态的数字。经过Attention取回信息,再经过B加工,这串数字就会更新。因此,后面层看到的“拿起”,已经可以包含下雨的背景。沿句子向右,可用的前文更多;沿网络往下,每个位置对这些信息的表示也在继续变化。
这里可以借用一叠资料卡来理解。每张卡有供查找的线索,也有可以取回的内容;当前位置则带着一次查找需要。这三项分别叫Q、K、V:
Q:我现在想找什么。
K:我有哪些线索,能与你的需要匹配。
V:匹配以后,我能提供什么信息。
Q和各个K比较,得到不同的匹配程度,再按比例取回、混合对应的V。比如,前面“雨”所在位置可能提供行动背景。这些匹配方式由训练学出来,模型里的实际Q、K、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、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.20000 | 0.00000 | 0.20000 |
| W_Q第2个数 | 0.50000 | −0.20637 | 0.52064 |
| W_K第1个数 | 1.00000 | −0.10318 | 1.01032 |
| W_K第2个数 | 0.00000 | 0.10318 | −0.01032 |
| W_V第1个数 | 1.00000 | −0.27331 | 1.02733 |
| W_V第2个数 | −1.00000 | −0.16577 | −0.98342 |
| W_O | 1.00000 | −0.10754 | 1.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:
| 当前输入位置 | 这个位置允许使用的输入 | 要预测的答案 |
|---|---|---|
| t1 | t1 | t2 |
| t2 | t1、t2 | t3 |
| t3 | t1、t2、t3 | t4 |
| t4 | t1、t2、t3、t4 | t5 |
t1至t5代表分词以后的位置,一个汉字或一个词可能对应一个或多个token。把表换回刚才那句话,意思就是:处理“拿起了”所对应的最后一个位置时,模型可以利用前面的雨,学习预测后面的“伞”。
训练程序知道完整句子,但会给每个位置设一道读取边界。在蓝色A里,t2的Q只能与t1、t2的K比较,取回它们的V;t3、t4虽然也在这一批输入里,对t2仍然不可见。这道边界叫因果掩码。答案放在边界之外,等预测做完,才拿来计算误差。
同一层的各个位置,输入都来自上一层已经得到的状态,因此这些位置可以一起做Q/K/V计算和各自的注意力。t4用到的是这一层输入端的t1至t4,整体进入下一层后,再继续更新。每一层依次向下走;同一层内部,许多位置并行做练习。Transformer论文用掩码和错开一个位置的预测目标实现这种安排。
这种训练方式通常称为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保持不变,因此可以保存下来,供后续步骤直接读取。
缓存省下旧位置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,并在取回信息时参与计算。训练还要对这些运算做反向传播。
这里还要把“一条序列”和“整个项目”分开。假如训练总量固定为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允许读取的位置。
另一条路线先把一段前文整理成较少的数字表示,再选择相关的部分参加注意力。附近文字可以保留详细信息,较远的信息走压缩和选择这条路。训练时,模型需要一起学会怎样整理信息、怎样选择,以及怎样把两条路取回的内容用好。
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 封。
无垃圾邮件,随时取消。