公司动态
从一条直线到大模型输出一个token(五):Transformer Block 全景与层归一化
从一条直线到大模型输出一个token五Transformer Block 全景与层归一化建议先看从一条直线到大模型输出一个token四位置编码 RoPE上一篇末尾留了个悬念「苹果」在我吃了一个苹果里是水果在苹果发布新手机里是公司——同一个 token含义要靠旁边的词决定。从这一篇开始我们正式走进 Transformer 大厦看词和词怎么发生关系。从一条直线到大模型输出一个token五Transformer Block 全景与层归一化从一条直线到大模型输出一个token五Transformer Block 全景与层归一化1. 先看全景一个 Block 长什么样1.1 Block 是大模型的标准层1.2 数据怎么流过 Block1.3 N 个 Block 堆叠成完整模型2. 层归一化 LayerNorm2.1 它解决什么问题数值会爆炸2.2 定义一行的体检2.3 贯穿案例「西安」行的完整手算2.4 完整矩阵全部 6 行的 LayerNorm 结果3. LayerNorm 与 BatchNorm两种归一化的对比小结下一篇预告1. 先看全景一个 Block 长什么样1.1 Block 是大模型的标准层把大模型想成一栋楼Block 就是标准楼层——整栋楼就是把同一个 Block 重复盖 N 遍。LLaMA-3-8B 盖了 32 层Qwen2.5-7B 盖了 28 层DeepSeek-V3 盖了 61 层。一个 Block 内部只有两种核心部件结构如图 5-1 所示多头自注意力负责词和词之间交换信息第 6、7 篇拆解前馈网络 FFN负责每个词自己做深度加工第 8 篇拆解外加两个配角层归一化LayerNorm本篇主角和残差连接第 8 篇讲。图5-1 一个 Block 的完整流程Pre-Norm 结构1.2 数据怎么流过 Block矩阵进矩阵出形状始终是 6×4096。这六步是严格串行的——第②步注意力的输出是第③步归一化的输入一步都不能跳、不能换序图 5-1 的垂直流程就是这个意思① 层归一化把矩阵的数值拉回稳定区间② 多头自注意力每个词环顾四周吸收其他词的信息加残差注意力加工的结果加上未经加工的原始输入快车道直通③ 再层归一化再拉一次④ 前馈网络 FFN每个词独立深度加工再加残差又一条快车道注意图 5-1 中绕在主流程外侧的两条蓝色旁路——这是残差连接不管中间的加工多复杂原始信息永远保留一条高速通道。它的作用第 8 篇细讲。1.3 N 个 Block 堆叠成完整模型嵌入层 RoPE 之后矩阵依次流过 Block 1、Block 2……Block N最后进输出层如图 5-2 所示。图5-2 N 个 Block 堆叠矩阵 6×4096 从头流到尾逐层加深语义矩阵的形状从头到尾不变变的是数字里编码的语义浅层 Block 加工语法中层加工语义深层加工到任务级别哪些层负责什么第 9 篇展开。顺带一提结构细节图 5-1 画的是 Pre-Norm先归一化再进子层——2017 年原始论文是 Post-Norm先算子层再归一化但深层模型训练时 Post-Norm 容易梯度不稳现代主流大模型LLaMA 系及之后全部改用 Pre-Norm。这也是原始论文 ≠ 现代实现的一个典型例子。这里展开说下梯度不稳。训练时误差信号要从输出层往输入层逐层回传反向传播每经过一层梯度都要乘一次该层的系数Post-Norm归一化在残差相加之后等于主干上每一层都插了一个除以 σ的关卡。梯度回传时连乘几十次这种系数要么越乘越小梯度消失浅层学不到东西要么突然放大梯度爆炸训练发散——32 层以上就很难训Pre-Norm归一化挪到子层入口主干变成干净的直通加法线x f ( L N ( x ) ) x f(\mathrm{LN}(x))xf(LN(x))。梯度沿主干回传时不受归一化干扰多深都稳一句话Post-Norm 把关卡设在主干上梯度每层都被过安检Pre-Norm 把关卡挪进支路主干一路绿灯。这就是现代大模型全部选 Pre-Norm 的原因。2. 层归一化 LayerNorm2.1 它解决什么问题数值会爆炸Block 里的每一步都是矩阵运算数字会一层层被放大。做个实验设每层让数值扩大 1.5 倍再加一点偏移真实模型里的放大系数远不止 1.5不加归一化连续过 10 层结果如图 5-3 所示。图5-3 为什么必须归一化无 LayerNorm 时数值指数爆炸初始 1.0 的数值10 层后变成 114.3——放大了 114 倍。真实模型 60 层起步、数值跨度更大不控制的后果是前向传播数值溢出变成∞ \infty∞或N a N \mathrm{NaN}NaN浮点数表示不了这么大的数反向传播梯度爆炸模型根本训不动。LayerNorm 的作用就是每层入口都做一次数值体检不管进来的是 1 还是 114都拉回均值 0、方差 1 的标准状态。红色爆炸曲线 vs 绿色稳定曲线就是有没有归一化的天壤之别。2.2 定义一行的体检LayerNorm 只对矩阵的每一行独立做「今天」这一行的 4096 个数算一个均值一个方差「西安」那一行自己算自己的行与行互不干扰。LayerNorm ( x ) x − μ σ ⊙ γ β \text{LayerNorm}(\mathbf{x}) \frac{\mathbf{x} - \mu}{\sigma} \odot \boldsymbol{\gamma} \boldsymbol{\beta}LayerNorm(x)σx−μ⊙γβx \mathbf{x}x某一行向量一个 token 的 4096 维μ \muμ、σ \sigmaσ这一行自己的均值和标准差γ \boldsymbol{\gamma}γ、β \boldsymbol{\beta}β可学习的缩放和平移参数下面专门解释γ 和 β 到底是什么掰开说它们是两个长度 4096 的数字列表和输入向量一样长不是单个数。第 1 维有第 1 维的γ 1 , β 1 \gamma_1, \beta_1γ1,β1第 2 维有第 2 维的γ 2 , β 2 \gamma_2, \beta_2γ2,β2……每个维度配自己的一对。一个 LayerNorm 层总共 2×4096 8192 个参数。可学习的意思这 8192 个数字不是人定的是训练出来的。训练开始前初始化为γ \gammaγ全 1、β \betaβ全 0等于先不干预训练过程中每次预测错了误差会反传回来告诉每个γ i \gamma_iγi、β i \beta_iβi该调大还是调小——和嵌入表的 5.3 亿参数一样靠梯度下降一点点学。γ 管拉多宽缩放乘法系数。γ i 1 \gamma_i 1γi1把第 i 维放大 1 11压缩。比如归一化后某维的值是 1.2乘γ 1.5 \gamma1.5γ1.5变成 1.8更突出乘γ 0.5 \gamma0.5γ0.5变成 0.6更淡化。β 管往哪挪平移加法偏移。β i 0 \beta_i 0βi0整体右移 0 00左移。比如 1.2 加β 0.3 \beta0.3β0.3变成 1.5。为什么需要它们第 3 步的减 μ 除 σ把每行都强制拉成均值 0、方差 1——数值是稳了但也把维度之间的差异抹平了。可实际上不是每个维度同等重要语义空间里是不是地名这个维度可能很关键“语气强弱可能次要。γ 和 β 就是给模型留的后悔药”标准化之后再让模型自己决定每个维度恢复多大的幅度、往哪个方向偏。用「西安」行的真实数字走一遍γ [ 1.2 , 0.8 , 1.0 , 1.1 ] \gamma [1.2, 0.8, 1.0, 1.1]γ[1.2,0.8,1.0,1.1]β [ 0.1 , − 0.1 , 0.05 , 0.2 ] \beta [0.1, -0.1, 0.05, 0.2]β[0.1,−0.1,0.05,0.2]维度归一化后第 3 步结果γ缩放β平移输出d₁-1.289×1.20.1-1.446d₂0.704×0.8-0.10.463d₃1.207×1.00.051.257d₄-0.622×1.10.2-0.485表5-1 γ缩放与β平移的逐维效果「西安」行d₁ 被放大并微移-1.289 → -1.446d₂ 被压缩0.704 → 0.463——同一行里不同维度受到的待遇各不相同全由训练学出来的 γ、β 决定。用代码描述大模型实现细节跳过不影响理解classLayerNorm:def__init__(self,d_model4096):self.gammanp.ones(d_model)# 初始化为 1self.betanp.zeros(d_model)# 初始化为 0defforward(self,x):# x: (6, 4096)mux.mean(axis-1,keepdimsTrue)# 每行各自的均值sigmax.std(axis-1,keepdimsTrue)# 每行各自的标准差return(x-mu)/sigma*self.gammaself.beta2.3 贯穿案例「西安」行的完整手算拿 RoPE 后的「西安」向量[ − 1.079 , 0.004 , 0.277 , − 0.717 ] [-1.079,\ 0.004,\ 0.277,\ -0.717][−1.079,0.004,0.277,−0.717]四步手算如图 5-4 所示。图5-4 LayerNorm 手算「西安」行的完整四步第 1 步取「西安」这一行[ − 1.079 , 0.004 , 0.277 , − 0.717 ] [-1.079,\ 0.004,\ 0.277,\ -0.717][−1.079,0.004,0.277,−0.717]。第 2 步算这行的均值和标准差μ − 1.079 0.004 0.277 − 0.717 4 − 0.379 \mu \frac{-1.079 0.004 0.277 - 0.717}{4} -0.379μ4−1.0790.0040.277−0.717−0.379σ 2 ( − 0.700 ) 2 ( 0.383 ) 2 ( 0.656 ) 2 ( − 0.338 ) 2 4 0.295 , σ 0.543 \sigma^2 \frac{(-0.700)^2 (0.383)^2 (0.656)^2 (-0.338)^2}{4} 0.295, \quad \sigma 0.543σ24(−0.700)2(0.383)2(0.656)2(−0.338)20.295,σ0.543第 3 步每个数减 μ、除以 σx ^ 1 − 1.079 − ( − 0.379 ) 0.543 − 1.289 \hat{x}_1 \frac{-1.079 - (-0.379)}{0.543} -1.289x^10.543−1.079−(−0.379)−1.289四个数依次得到[ − 1.289 , 0.704 , 1.207 , − 0.622 ] [-1.289,\ 0.704,\ 1.207,\ -0.622][−1.289,0.704,1.207,−0.622]——现在这行的均值是 0、方差是 1。第 4 步乘可学习的γ \gammaγ、加可学习的β \betaβγ \gammaγ、β \betaβ的完整解释见 2.2 节和表 5-1本例取γ [ 1.2 , 0.8 , 1.0 , 1.1 ] \gamma [1.2, 0.8, 1.0, 1.1]γ[1.2,0.8,1.0,1.1]β [ 0.1 , − 0.1 , 0.05 , 0.2 ] \beta [0.1, -0.1, 0.05, 0.2]β[0.1,−0.1,0.05,0.2]1.2 × ( − 1.289 ) 0.1 − 1.446 1.2 \times (-1.289) 0.1 -1.4461.2×(−1.289)0.1−1.446最终输出[ − 1.446 , 0.463 , 1.257 , − 0.485 ] [-1.446,\ 0.463,\ 1.257,\ -0.485][−1.446,0.463,1.257,−0.485]——正是表 5-1 最右列。每个数都可手算验证。2.4 完整矩阵全部 6 行的 LayerNorm 结果「西安」只是其中一行。整张矩阵每一行独立做一遍同样的四步各算各的 μ 和 σ结果如下γ、β 取同一组演示值X L N [ 0.565 − 1.356 1.226 0.208 − 1.446 0.463 1.257 − 0.485 − 1.218 0.026 1.599 − 0.469 0.740 − 1.353 − 0.038 1.433 0.133 − 0.539 − 1.025 1.955 1.265 0.170 0.417 − 1.643 ] \mathbf{X}_{LN} \begin{bmatrix} 0.565 -1.356 1.226 0.208 \\ -1.446 0.463 1.257 -0.485 \\ -1.218 0.026 1.599 -0.469 \\ 0.740 -1.353 -0.038 1.433 \\ 0.133 -0.539 -1.025 1.955 \\ 1.265 0.170 0.417 -1.643 \end{bmatrix}XLN0.565−1.446−1.2180.7400.1331.265−1.3560.4630.026−1.353−0.5390.1701.2261.2571.599−0.038−1.0250.4170.208−0.485−0.4691.4331.955−1.643各行自己的 μ 和 σ体现每行独立tokenμ这行的均值σ这行的标准差今天0.0450.710西安-0.3790.543的0.4100.346天气-0.1470.715怎么样-0.4040.5050.1280.692表5-2 六行各自的均值与标准差每行独立统计注意「西安」行的[ − 1.446 , 0.463 , 1.257 , − 0.485 ] [-1.446,\ 0.463,\ 1.257,\ -0.485][−1.446,0.463,1.257,−0.485]正是上节手算的结果——第 2 行。这就是矩阵进 Block 前的第一次数值体检形状不变还是 6×4096但每一行都被拉回了各自的稳定区间。3. LayerNorm 与 BatchNorm两种归一化的对比有机器学习基础的读者会问归一化不是有现成的 BatchNorm 吗图像领域用得好好的为什么 NLP 要另起炉灶先把两者的定义摆出来。归一化的对象是一个数据组区别在于怎么分组BatchNorm批量归一化2015 年提出CV 领域标配把同一个批次里、不同样本的同一个维度分成一组。比如一个批次 32 张图片把 32 张图的红色通道第一行像素拿在一起算这 32 个数的均值方差来归一化——跨样本、按维度LayerNorm层归一化2016 年提出把同一个样本自己的全部维度分成一组。比如「西安」这个 token 的 4096 维算它自己 4096 个数的均值方差——单样本、按层内全部维度放进我们的矩阵里看矩阵一行是一个 token、一列是一个维度分组方式如图 5-5 所示。图5-5 为什么 NLP 选 LayerNorm 而不是 BatchNorm矩阵的每一行是一个 token每一列是一个维度BatchNorm 按列归一化把「今天」「西安」「的」「天气」的第 1 维拿在一起比——跨 token 统计。问题来了句子有长有短这次 6 个 token下次 600 个每批的统计量乱跳更致命的是生成场景是逐 token 出结果的根本没有一批句子可比LayerNorm 按行归一化每个 token 自己的 4096 维内部统计——句长变化不影响来一个 token 归一化一个完全稳定表5-3 LayerNorm vs BatchNorm 对比维度BatchNormLayerNorm归一化方向按列跨样本同一维度按行单个样本全部维度依赖批大小强依赖batch 太小统计不稳完全无关变长序列每次统计量不同不稳每行独立天然适配逐 token 生成无法用没有完整批次完美支持适用场景CV固定尺寸图像NLP / 大模型变长文本一句话总结文本是变长的、生成是逐个的只有按行独立的 LayerNorm 能同时扛住这两点。小结这一篇我们拿下了 Transformer 大厦的结构图和第一块砖Block 标准楼层层归一化 → 多头自注意力 → 残差 → 层归一化 → FFN → 残差Pre-Norm 是现代主流N 个 Block 堆叠矩阵形状 6×4096 从头流到尾语义逐层加深LayerNorm 解决数值爆炸不归一化 10 层放大 114 倍模型根本训不动LayerNorm 按行独立减 μ 除 σ 再乘 γ 加 β「西安」行四步手算全程可验证不用 BatchNorm 的原因文本变长 逐 token 生成只有按行归一化扛得住下一篇预告Block 结构里注意力排第一个位置——它是词和词交换信息的唯一通道也是整个 Transformer 名字里 Attention 的由来。下一篇拆注意力的第一步Q、K、V 三剑客。「西安」想知道自己是城市名它得先发出一张查询单Q我要找地名相关信息其他词得亮出自己的名片K我有什么信息和干货V我的具体内容。三张单子怎么从同一个矩阵变出来这是第 6 篇的故事。系列目录从一条直线到高维空间分词与 token 表隐藏层与嵌入位置编码 RoPETransformer Block 全景与层归一化当前篇QKV 三剑客注意力权重与多头机制残差连接与前馈网络输出矩阵与多层堆叠首 token 诞生与 KV-Cache版权声明本文为博主原创文章遵循 CC 4.0 BY-SA 版权协议转载请附上原文出处链接和本声明。