Transformer块:注意力然后多层感知机 | AI生成和翻译

Home 2026.06

问题: 在 nanoGPT 的 12 层(或 20 层)堆叠中,每个块内部包含什么?FFN 是否包裹了 MLP,注意力机制是否在 MLP 内部?

回答:

接近了,但嵌套关系有两处错误。我们来修正这个思维模型:

1. MLP 和 FFN 是同一回事。 这是同一个模块的两个名称。”前馈网络”(Feed-Forward Network)是论文中的术语(《Attention Is All You Need》),而”MLP”是 Karpathy 在 nanoGPT 代码中的叫法。它们之间没有包裹关系——它们是同义词。

2. 注意力机制不在 MLP 内部。 注意力机制和 MLP 在块内部是同级关系,按顺序执行。每个块恰好包含两个子层:

Block = LayerNorm → Attention → 残差相加
      → LayerNorm → MLP(FFN) → 残差相加

以下是实际的 nanoGPT 代码(model.py):

class Block(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.ln_1 = LayerNorm(config.n_embd, bias=config.bias)
        self.attn = CausalSelfAttention(config)   # 子层 1
        self.ln_2 = LayerNorm(config.n_embd, bias=config.bias)
        self.mlp = MLP(config)                    # 子层 2(这就是 FFN)

    def forward(self, x):
        x = x + self.attn(self.ln_1(x))   # 先执行注意力
        x = x + self.mlp(self.ln_2(x))    # 再执行 MLP/FFN
        return x

而 MLP 本身——内部没有注意力机制,只是两个线性层中间夹着 GELU 激活函数:

class MLP(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.c_fc   = nn.Linear(config.n_embd, 4 * config.n_embd)  # 扩展 4 倍
        self.gelu   = nn.GELU()
        self.c_proj = nn.Linear(4 * config.n_embd, config.n_embd)  # 投影回原维度

    def forward(self, x):
        return self.c_proj(self.gelu(self.c_fc(x)))

因此,对于 GPT-2 124M(n_layer=12, n_embd=768),每个 token 向量的流动路径为:

x ∈ R^768
  → ln_1 → 注意力(token 之间相互交流)  → + x
  → ln_2 → 768 → 3072 → GELU → 3072 → 768  → + x

× 12 个块,然后经过最终的 LayerNorm → lm_head。

有两个细节值得深入理解:

关于残差流的一个有用直觉:将 x 想象成一个共享的 768 维”总线”,从上到下流动,每个注意力/MLP 子层从中读取数据,计算某些内容,然后将贡献加回总线。没有任何东西包裹任何东西——这是在同一个流上执行的 24 次顺序的”读取-计算-相加”操作(12 次注意力 + 12 次 MLP)。

一个 GPT-2 块的参数检查:注意力 ≈ 4 × 768² ≈ 236 万,MLP ≈ 8 × 768² ≈ 472 万——MLP 占据了每个块约 2/3 的参数。在你 7.6 亿参数的运行中,比例相同(直到你使用 MoE,此时 FFN 正是被复制到专家中的部分——这就是 DeepSeek v4 的关联点)。

参考资料:


Back Donate