Skip to content

7.4 OLMo 3:开源对标与 Post-Norm

前几节介绍的 Llama、Qwen3 和 Gemma 3 虽然在不同层面推进了开源大模型的发展,但它们在训练数据、数据配比或训练细节上仍有不同程度的保留。Allen AI 的 OLMo(Open Language Model)系列从一开始就以"完全开源"为核心定位——不仅开放模型权重,还公开训练代码、数据配方和评估工具链。OLMo 3 是该系列的第三代,提供 7B 和 32B 两个规模,在多项基准上对标同规模的 Llama 3.1 和 Qwen 2.5,成为当前最具竞争力的完全开源基座模型之一。

从架构角度看,OLMo 3 与前几节介绍的模型共享了大量现代 LLM 设计范式(RMSNorm、SwiGLU、RoPE、无偏置线性层),但在两个关键位置做出了不同选择:Post-Norm 替代 Pre-Norm7B 模型使用滑动窗口注意力(SWA)+ 多头注意力(MHA)而非分组查询注意力(GQA)。本节将深入剖析这些架构差异背后的设计理由,并通过代码展示 Post-Norm 的实现细节。

OLMo 3 训练数据流水线

图 7-14:OLMo 3 的训练数据体系。完全开源的数据配方包括预训练语料、后训练指令数据和评估工具链。

7.4.1 架构总览:7B 与 32B 的配置对比

OLMo 3 的两个规模共享同一套代码实现,仅在超参数上有所区别。下表列出核心配置:

超参数7B32B说明
vocab_size100,278100,278基于 Tiktoken 的分词器
context_length65,53665,53664K 上下文
emb_dim4,0965,120嵌入维度
n_heads3240查询头数
n_kv_heads328KV 头数
n_layers3264Transformer 层数
hidden_dim11,00827,648FFN 中间维度
head_dim128128每头维度
sliding_window4,0964,096滑动窗口大小
rope_base500,000500,000RoPE 基频
rope_typeYaRNYaRNRoPE 扩展方式

表 7-5:OLMo 3 模型配置。两个规模共享相同的词表、上下文长度和 RoPE 配置。

几个关键数值值得深入分析:

注意力策略的分化。 7B 模型的 n_kv_heads = 32 = n_heads,即标准的多头注意力(MHA),每个查询头拥有独立的 Key 和 Value。32B 模型则采用分组查询注意力(GQA),n_kv_heads = 8,每 5 个查询头共享一组 KV。这一分化策略与 Llama 2 类似——小模型用 MHA 保证充分的表达能力,大模型用 GQA 压缩 KV 缓存以控制推理成本。OLMo 3 7B 用另一种方式弥补 MHA 的效率短板:滑动窗口注意力。

5.4 倍 FFN 扩展。 32B 模型的 hidden_dim = 27,648,与 emb_dim = 5,120 的比值为 27,648/5,120=5.4。这一比例显著高于多数同类模型:Qwen3 32B 的扩展比为 25,600/5,120=5.0,Llama 3 8B 为 14,336/4,096=3.5。这一选择的背景是:OLMo 3 32B 与 Qwen3 32B 共享相同的 emb_dim = 5,120,但由于词表更小(100K vs. 152K),嵌入层参数更少,因此通过增大 FFN 扩展比来填补参数预算,使总参数量对齐到 32B 级别以实现直接对比。相比之下,7B 模型的扩展比为 11,008/4,0962.7,处于 SwiGLU 网络的常规范围。

3:1 混合注意力模式。 OLMo 3 的 layer_types 配置显示,两个规模都采用"3 层滑动窗口 + 1 层全局注意力"的交替模式。以 7B 的 32 层为例:

层  0: sliding    层  1: sliding    层  2: sliding    层  3: full
层  4: sliding    层  5: sliding    层  6: sliding    层  7: full
...(每 4 层重复一次)
层 28: sliding    层 29: sliding    层 30: sliding    层 31: full

32 层中有 24 层滑动窗口、8 层全局注意力,比例为 3:1。与 Gemma 3 的 5:1 相比,OLMo 3 保留了更多的全局注意力层。滑动窗口大小为 4,096(Gemma 3 为 512),也更为保守。这种偏保守的混合策略可能与 OLMo 3 的 Post-Norm 架构有关——Post-Norm 对梯度流动的影响使得全局信息的传播更依赖全局注意力层本身,而非依赖残差连接的直通路径。

7.4.2 Post-Norm:与主流的关键分歧

OLMo 3 与当前几乎所有主流 LLM(Llama、Qwen3、Gemma 3、GPT)最显著的架构差异在于归一化层的位置。主流模型普遍采用 Pre-Norm(归一化在子层输入前),OLMo 3 则采用 Post-Norm(归一化在子层输出后、残差加法前)。要理解这一选择的意义,需要回溯 Pre-Norm 与 Post-Norm 的技术渊源。

Post-Norm 的原始定义。 原始 Transformer(Attention Is All You Need,2017)使用的就是 Post-Norm 结构:

xl+1=LayerNorm(xl+SubLayer(xl))

即先计算子层输出,与残差相加后再做归一化。这一设计的问题在于:反向传播时,梯度必须经过 LayerNorm 层才能到达残差路径。随着层数增加,梯度信号被多次 LayerNorm 变换"压缩",导致底层参数的梯度逐渐消失。训练 Post-Norm 深层模型时通常需要精心设计的学习率预热(Warmup),否则容易在初始阶段发散。

Pre-Norm 的兴起。 GPT-2(2019)率先引入 Pre-Norm 结构,将归一化移到子层输入前:

xl+1=xl+SubLayer(LayerNorm(xl))

Pre-Norm 的核心优势在于:残差路径上没有归一化层阻挡,梯度可以从最后一层直通到第一层,不会被中间层衰减。这大幅改善了深层网络的训练稳定性,减少了对学习率预热的依赖。自 GPT-2 以来,Pre-Norm 几乎成为所有 LLM 的标配——Llama 全系列、Qwen3 全系列、Gemma 3 都使用 Pre-Norm。

OLMo 3 的 Post-Norm 变体。 OLMo 3 的 Post-Norm 并非简单回归原始 Transformer 的做法,而是一种改良版本。它在子层输出上施加 RMSNorm,但将归一化放在残差加法之前而非之后:

xl+1=xl+RMSNorm(SubLayer(xl))

这与原始 Post-Norm 的区别在于:原始版本是 Norm(x+f(x)),OLMo 3 是 x+Norm(f(x))。后者保留了残差路径的直通性(归一化不作用于残差分支),同时对子层输出进行范数控制。这种"归一化子层输出 + 干净残差"的组合,既避免了原始 Post-Norm 的梯度消失问题,又对子层输出的尺度进行了约束。

代码实现清晰地展示了这一结构:

python
class TransformerBlock(nn.Module):
    def __init__(self, cfg, attn_type):
        super().__init__()
        self.att = GroupedQueryAttention(...)
        self.ff = FeedForward(cfg)
        # 注意:只有 post-norm,没有 pre-norm
        self.post_attention_layernorm = RMSNorm(cfg["emb_dim"])
        self.post_feedforward_layernorm = RMSNorm(cfg["emb_dim"])

    def forward(self, x, mask_global, mask_local, cos, sin):
        # 注意力子层:无 Pre-Norm,输出经 Post-Norm 后加残差
        shortcut = x
        x_attn = self.att(x, mask, cos, sin)
        x_attn = self.post_attention_layernorm(x_attn)   # Post-Norm
        x = shortcut + x_attn

        # FFN 子层:同样的 Post-Norm 结构
        shortcut = x
        x_ffn = self.ff(x)
        x_ffn = self.post_feedforward_layernorm(x_ffn)    # Post-Norm
        x = shortcut + x_ffn
        return x

对比 Qwen3 和 Llama 的 Pre-Norm 写法:

python
# Pre-Norm(Llama / Qwen3)
shortcut = x
x = self.norm1(x)          # 归一化在子层输入前
x = self.att(x, ...)
x = shortcut + x

# Post-Norm(OLMo 3)
shortcut = x
x = self.att(x, ...)       # 子层直接处理原始输入
x = self.post_norm(x)      # 归一化在子层输出后
x = shortcut + x

两种方案的本质区别在于子层看到的输入是否经过归一化。Pre-Norm 让子层处理归一化后的输入,确保输入尺度稳定;Post-Norm 让子层处理原始残差流,输出后再控制尺度。OLMo 团队在 OLMo 2 论文中报告,Post-Norm 变体在大规模训练中展现出更好的训练稳定性——这看似反直觉,但有两个可能的解释:

  1. 子层输出的范数约束。 Post-Norm 直接作用于子层输出,确保每一层贡献给残差流的增量不会过大。在 Pre-Norm 中,子层输出的范数不受直接约束,随着训练进行可能逐渐增大,导致残差流的尺度在深层膨胀。
  2. 与 QKNorm 的协同。 OLMo 3 在注意力层内部还对 Q 和 K 应用了 RMSNorm(QKNorm)。在 Post-Norm 架构中,注意力的输入是原始残差流(未经归一化),QKNorm 承担了稳定注意力 logits 的职责;而子层外部的 Post-Norm 则控制输出范数。这种"内外双重归一化"形成了互补的稳定机制。

三种归一化方案对比。 下表总结了原始 Post-Norm、Pre-Norm 和 OLMo 3 Post-Norm 的核心差异:

RoPE 旋转位置编码原理

图 7-15:RoPE 旋转位置编码的可视化。OLMo 3 使用 YaRN 扩展方案,对不同频率分量采用差异化插值,平滑地将上下文扩展至 64K。

特性原始 Post-NormPre-Norm(主流)OLMo 3 Post-Norm
公式Norm(x+f(x))x+f(Norm(x))x+Norm(f(x))
残差路径被 Norm 阻断完全直通完全直通
子层输入原始残差流归一化后的输入原始残差流
子层输出控制间接(与残差混合后归一化)无直接控制直接归一化
梯度流动深层梯度消失梯度直通,稳定性好梯度直通 + 输出约束
代表模型原始 TransformerLlama、Qwen3、Gemma 3OLMo 2/3

OLMo 3 的方案可以视为 Pre-Norm 和原始 Post-Norm 的折中:它保留了 Pre-Norm 的梯度直通优势,同时引入了对子层输出的显式范数控制。这也与 Gemma 3 的"Pre-Norm + Post-Norm 三明治归一化"(§7.3)形成了有趣的对比——Gemma 3 在两端都放置了归一化层(共 4 个 RMSNorm/层),而 OLMo 3 仅保留输出端的归一化(2 个 RMSNorm/层),参数更少但同样实现了输出范数控制。

7.4.3 QKNorm 的实现差异

OLMo 3 和 Qwen3 都使用了 QKNorm,但实现方式有所不同。Qwen3 的 QKNorm 是逐头归一化:先将 Q/K reshape 为 (batch, num_heads, seq_len, head_dim),再对每个头的 head_dim 维度独立做 RMSNorm,归一化参数维度为 head_dim = 128

OLMo 3 的 QKNorm 则是全投影归一化:在 reshape 之前,对整个投影输出做 RMSNorm。Q 的归一化参数维度为 num_heads * head_dim(7B 为 4096),K 的归一化参数维度为 num_kv_heads * head_dim(7B 为 4096,32B 为 1024):

python
class GroupedQueryAttention(nn.Module):
    def __init__(self, d_in, num_heads, num_kv_groups, head_dim):
        super().__init__()
        # OLMo 3:对整个投影输出做 RMSNorm
        self.q_norm = RMSNorm(num_heads * head_dim)      # 7B: 4096, 32B: 5120
        self.k_norm = RMSNorm(num_kv_groups * head_dim)   # 7B: 4096, 32B: 1024

    def forward(self, x, mask, cos, sin):
        queries = self.W_query(x)
        keys = self.W_key(x)
        values = self.W_value(x)

        # 先归一化,再 reshape
        queries = self.q_norm(queries)
        keys = self.k_norm(keys)

        # reshape 为多头格式
        queries = queries.view(b, num_tokens, self.num_heads, self.head_dim).transpose(1, 2)
        keys = keys.view(b, num_tokens, self.num_kv_groups, self.head_dim).transpose(1, 2)
        ...

全投影归一化允许不同头之间的交互——归一化的均方根是跨所有头计算的,因此某个头的异常大值会被其他头"稀释"。逐头归一化则让每个头完全独立。两种方式的性能差异在实践中较小,但全投影归一化的参数略多(尤其在 MHA 配置下,Q 和 K 的归一化参数量等于 emb_dim)。

7.4.4 YaRN RoPE 扩展

OLMo 3 使用 YaRN(Yet another RoPE extensioN)方案扩展上下文长度至 64K。YaRN 的核心思想是对 RoPE 的不同频率分量采用差异化的插值策略:高频分量(短距离位置信息)保持不变(外推),低频分量(长距离位置信息)进行线性插值,中间频段则在两者之间平滑过渡:

θi={θi高频(外推)θi/s低频(插值)blend(θi,θi/s)中间频段

其中 s 为缩放因子(OLMo 3 中 rope_factor = 8.0)。高低频的分界由 beta_fast = 32.0beta_slow = 1.0 控制。此外,YaRN 还引入了注意力因子 attention_factor = 1.208,用于微调旋转后的 cos/sin 值的幅度。

与 Llama 3.1 的 RoPE 频率缩放(§7.1)相比,YaRN 的差异化插值更加精细:Llama 3.1 将频率分量简单地分为"保持原样"和"按固定因子缩放"两类,YaRN 则在两类之间增加了平滑的过渡带,减少了频率边界处的不连续性。

7.4.5 7B 与 32B 模型对比总结

维度OLMo 3 7BOLMo 3 32B
注意力类型MHA(n_kv_heads = n_heads = 32)GQA(n_kv_heads = 8,n_heads = 40)
混合注意力3:1(24 层 SWA + 8 层 Full)3:1(48 层 SWA + 16 层 Full)
FFN 扩展比2.7x(11,008 / 4,096)5.4x(27,648 / 5,120)
归一化Post-Norm(2 个 RMSNorm/层)Post-Norm(2 个 RMSNorm/层)
QKNorm全投影归一化(维度 4,096)全投影归一化(Q: 5,120,K: 1,024)
RoPEYaRN(θ=500K,factor = 8)YaRN(θ=500K,factor = 8)
总层数3264

7B 到 32B 的缩放路径体现了两条策略:

  1. 用 GQA 换效率。 7B 使用 MHA 保证每个头的独立性和表达能力;32B 在头数增至 40 的同时,将 KV 头压缩到 8,使 KV 缓存大小仅为 MHA 的 8/40=20%。这对 64 层、64K 上下文的大模型来说是必要的内存优化。
  2. 用 FFN 扩展比补参数。 32B 的 5.4 倍扩展比在同类模型中属于极高水平。更大的中间维度意味着前馈网络的容量更大——每一层可以在中间表示空间中编码更多的知识模式。这一选择也使得 FFN 在总参数中的占比更高,反映了一种"重 FFN、轻注意力参数"的设计倾向。

OLMo 3 训练曲线

图 7-16:OLMo 3 的训练过程。Post-Norm 的引入使得训练更加稳定,尤其在模型规模扩大时表现出更好的收敛特性。

本节小结

OLMo 3 是目前最具竞争力的完全开源基座模型,其架构设计在共享现代 LLM 通用范式的基础上做出了几个有辨识度的选择:

  • Post-Norm 架构:归一化放在子层输出后、残差加法前(x+Norm(f(x))),既保留了残差路径的梯度直通性,又对子层输出施加了显式的范数控制。这与 Pre-Norm(x+f(Norm(x)))形成互补——前者控制输出尺度,后者控制输入尺度。OLMo 团队报告该方案在大规模训练中稳定性更优。
  • 注意力策略分化:7B 用 MHA + SWA 以局部注意力的低成本弥补 MHA 的效率短板;32B 切换到 GQA 以压缩 KV 缓存,应对更深的网络和更长的上下文。两个规模都采用 3:1 的 SWA/Full 混合比例。
  • 5.4 倍 FFN 扩展:32B 模型通过异常大的 FFN 中间维度来填补因小词表而节省的参数预算,使总参数量对齐到 32B 级别。
  • YaRN RoPE:对不同频率分量采用差异化插值(外推 / 混合 / 插值),比 Llama 3.1 的分段缩放方案更平滑地扩展上下文到 64K。

从更宏观的视角看,OLMo 3 的 Post-Norm 选择提示了一个值得关注的趋势:归一化层的位置并非只有 Pre-Norm 一种正确答案。随着 QKNorm、Post-Norm、三明治归一化等技术的出现,归一化策略正在从"一刀切"走向"按需分配"——在不同的子层、不同的位置使用不同的归一化方案,以实现训练稳定性、表达能力和计算效率的最优平衡。