05. LLaMA3 Block Tutorial代码笔记
2026/6/25 14:00:30 网站建设 项目流程

背景

这个教程实现了LLaMA 风格的 Transformer 解码器层所需的两个核心模块:

  • LlamaMLP:使用 SwiGLU 激活函数的前馈网络。
  • LlamaDecoderLayer:组合注意力与 MLP,并采用Pre-Norm 残差结构(归一化放在子层之前)。

同时上方提供了两个占位类DummyRMSNormDummyAttention,模拟 RMSNorm 和多头注意力(简化版),便于整体流程演示。


TODO 1:定义 SwiGLU 所需的三个线性层(无偏置)

self.gate_proj=nn.Linear(hidden_size,intermediate_size,bias=False)self.up_proj=nn.Linear(hidden_size,intermediate_size,bias=False)self.down_proj=nn.Linear(intermediate_size,hidden_size,bias=False)

SwiGLU 是什么?
SwiGLU 是一种激活函数,结合了门控线性单元(GLU)和 Swish/SiLU 激活。其公式为:
SwiGLU(x)=(SiLU(x⋅Wgate)) ⊙ (x⋅Wup) \text{SwiGLU}(x) = \big( \text{SiLU}(x \cdot W_{\text{gate}}) \big) \;\odot\; \big( x \cdot W_{\text{up}} \big)SwiGLU(x)=(SiLU(xWgate))(xWup)
然后再通过一个输出投影矩阵Wdown W_{\text{down}}Wdown将维度映射回hidden_size
在 LLaMA 模型中,这三个投影都是无偏置的。

补全解释:

  • gate_proj:将输入从hidden_size映射到intermediate_size,然后通过 SiLU 激活,充当“门控”信号(0 到 1 之间的软开关)。
  • up_proj:同样将输入从hidden_size映射到intermediate_size,提供“数值”信号。
  • down_proj:将门控与数值的逐元素乘积结果从intermediate_size再映射回hidden_size,恢复原始维度,以便加上残差连接。

三个线性层都没有偏置(bias=False),严格遵循 LLaMA 设计。


TODO 2:实现 SwiGLU 的前向传播

returnself.down_proj(F.silu(self.gate_proj(x))*self.up_proj(x))

分步拆解:

  1. self.gate_proj(x):计算门控部分的线性投影,结果形状为[batch, seq, intermediate_size]
  2. F.silu(...):对门控结果施加 SiLU(也叫 Swish)激活函数:siLU(u)=u⋅σ(u)\text{siLU}(u) = u \cdot \sigma(u)siLU(u)=uσ(u)。这使门控值变为平滑的非线性,范围约为(-0.278, ∞),在正值区间提供类似 ReLU 的激活,在负值区间有轻微负值,有利于梯度流动。
  3. self.up_proj(x):计算数值部分的线性投影,形状同样为[batch, seq, intermediate_size]
  4. *:将激活后的门控信号与数值信号进行逐元素相乘。这就是 GLU 的核心:门控控制了哪些信息可以通过。
  5. self.down_proj(...):将乘积结果投影回hidden_size,输出形状恢复为[batch, seq, hidden_size]

这种结构比传统 ReLU/GeLU 的 MLP 多了一组门控分支,能更好地筛选信息,已被 LLaMA 等模型采用。


TODO 3:实现 LLaMA 的 Pre-Norm 残差连接

# --- Attention Block ---residual=hidden_states h=self.input_layernorm(hidden_states)h=self.self_attn(h)h=residual+h# --- MLP Block ---residual=h out=self.post_attention_layernorm(h)out=self.mlp(out)out=residual+outreturnout

LLaMA 的 Pre-Norm 结构:

  • 与原始 Transformer(Post-Norm,在子层之后做 LayerNorm)不同,LLaMA 使用Pre-Norm,即在注意力或 MLP 之前进行归一化。
  • 这样做的好处是训练更稳定,梯度更容易流动,是当前大语言模型的主流选择。

代码详解:

  1. 注意力块(Attention Block)

    • residual = hidden_states:保存残差连接的分支(即输入本身)。
    • h = self.input_layernorm(hidden_states):先对输入做 RMSNorm 归一化。
    • h = self.self_attn(h):归一化后的张量送入注意力模块(这里用DummyAttention模拟,实际会有 RoPE、GQA 等)。
    • h = residual + h:将注意力输出与残差相加,实现恒等映射的旁路。
  2. MLP 块(MLP Block)

    • residual = h:保存注意力块输出作为新的残差。
    • out = self.post_attention_layernorm(h):再次进行 RMSNorm 归一化(在 MLP 之前)。
    • out = self.mlp(out):通过 SwiGLU MLP 层。
    • out = residual + out:加上残差,输出最终结果。

结构顺序对应典型的 LLaMA 解码器层

输入 → RMSNorm → 注意力 → + 残差 → RMSNorm → MLP → + 残差 → 输出

这种 Pre-Norm + 残差的组合,既保证了深层网络的训练稳定性,又让每个子层都有一条原始的“高速通道”传递信息。


总结

  • TODO 1 & 2实现了 LLaMA 的 SwiGLU MLP:用三个无偏置线性层,前向时用 SiLU 门控乘以上投影,再下投影。
  • TODO 3实现了 Pre-Norm 的残差解码器层:分别对注意力和 MLP 做“归一化→运算→加残差”,完整串联起一层的计算流。

这些组件组合起来,就是现代大语言模型(如 LLaMA)最基本的构成单元。

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

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

立即咨询