Transformers documentation

逐层配置

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

逐层配置

某些检查点是非同质的(并非各层结构一致)。例如,某一层的 MLP 规模较小,另一层的键值头数量较少,或者特定层采用了不同的布局,这意味着单一的全局配置无法准确描述整个模型堆栈。

在编写或检查配置时,以及当模型代码将使用这些差异时,请使用 PreTrainedConfig 上的 per_layer_config 来记录这些差异。每个条目仅存储与全局配置不同的部分,其余部分则继承自全局配置。

异构配置是一项强大的功能。如果异构布局成为一种常见或主流架构,我们将努力在架构实现中对其进行显式建模,而不是依赖 per_layer_config。当存在显式架构时,请优先选择该架构。

以下模型属于异构检查点。堆栈中的层并非均匀一致。它们采用专用架构,使用 block_configs 和自身的 model_type,而非在标准架构上使用 per_layer_config。

模型源自
nvidia/Llama-3_3-Nemotron-Super-49B-v1_5meta-llama/Llama-3.3-70B-Instruct
nvidia/Llama-3_1-Nemotron-Ultra-253B-v1meta-llama/Llama-3.1-405B-Instruct
nvidia/gpt-oss-puzzle-88Bopenai/gpt-oss-120b
nvidia/NVIDIA-Nemotron-Labs-3-Puzzle-75B-A9B-BF16nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16

定义逐层覆盖

将 per_layer_config 作为从层索引到属性覆盖的映射传递给 LlamaConfig。层索引从零开始。只需指定与全局配置不同的属性。

per_layer_config 负责记录并解析配置值。它本身并不会改变模型所创建的模块,也不会影响这些模块的运行方式。要应用大小覆盖或 skip,需要模型代码在构建或运行每一层时读取解析后的逐层配置。当前的 LlamaModel 使用全局配置来构建每一个 LlamaDecoderLayer,因此下文中的 skip 条目仍然是配置值,并不会在 from_pretrained 之后移除相应的模块。

以下示例记录了四个层的覆盖设置:第 5 层使用较小的 MLP,第 11 层使用更少的键值头,而第 23 层和第 27 层则记录了支持该架构的 skip 值。

from transformers import LlamaConfig


config = LlamaConfig(
    hidden_size=4096,
    intermediate_size=14336,
    num_hidden_layers=32,
    num_attention_heads=32,
    num_key_value_heads=8,
    per_layer_config={
        # Use a smaller MLP in one layer.
        5: {"intermediate_size": 8192},

        # Use fewer key-value heads in another layer.
        11: {"num_key_value_heads": 4},

        # Record a request to skip the MLP in architectures that support it.
        23: {"skip": ["mlp"]},

        # Record a request to skip attention in architectures that support it.
        27: {"skip": ["attention"]},
    },
)

架构可以跳过的子模块(例如 "mlp" 和 "attention")是按架构定义的。skip 接受一个列表,因此一个层可以记录多个子模块覆盖。

访问 config.per_layer_config[layer_idx] 会返回一个已解析的层配置。该已解析配置将全局配置与该层的覆盖项合并在一起。

# Layer 0 does not define overrides, so it inherits the global values.
config.per_layer_config[0].intermediate_size
# 14336

config.per_layer_config[0].num_key_value_heads
# 8

# Layer 5 overrides the MLP intermediate size.
config.per_layer_config[5].intermediate_size
# 8192

# Layer 11 overrides the number of key-value heads.
config.per_layer_config[11].num_key_value_heads
# 4

# Layer 23 records an MLP skip.
config.per_layer_config[23].skip
# ["mlp"]

# Layer 27 records an attention skip.
config.per_layer_config[27].skip
# ["attention"]

使用 per_layer_config 的配置与其他配置一样,支持相同的 save_pretrained() 和 from_pretrained() 往返流程。

每种架构都在其代码中定义了它在层级别上会消费哪些属性。per_layer_config 提供了记录这些层级别差异并将其与全局配置进行解析的机制。

全局属性访问

具有逐层覆盖的属性没有单一的模型级值。num_key_value_heads 在大多数层中可能为 8,而在特定层中为 4。在层上下文之外读取 config.num_key_value_heads 会产生歧义。

默认情况下,该访问会引发 AmbiguousGlobalPerLayerAttributeError,并引导你查看 config.per_layer_config[layer_idx]。该属性在全局配置中仍然存在,因此这并非 AttributeError。在没有层索引的情况下读取该全局值仍然是错误的。如果代码基于全局的 num_key_value_heads 来构建键值缓存,则会错误地计算错误层的尺寸。

仅当您确实需要全局回退机制且能够处理异构配置时,才设置 allow_global_per_layer_attribute_access=True。此时将允许全局访问,并会发出一次警告。

config = LlamaConfig(
    hidden_size=4096,
    intermediate_size=14336,
    num_hidden_layers=32,
    num_attention_heads=32,
    num_key_value_heads=8,
    allow_global_per_layer_attribute_access=True,
    per_layer_config={
        11: {"num_key_value_heads": 4},
    },
)

config.num_key_value_heads
# 8
# Emits a one-time warning because num_key_value_heads has a per-layer override.

序列化

per_layer_config 默认采用稀疏序列化方式,未进行覆盖的层会被省略。与全局值相同的被覆盖属性同样会被省略。

from transformers import LlamaConfig


config = LlamaConfig(
    hidden_size=4096,
    intermediate_size=14336,
    num_hidden_layers=4,
    num_attention_heads=32,
    num_key_value_heads=8,
    per_layer_config={
        0: {"num_key_value_heads": 8},
        2: {"num_key_value_heads": 4},
    },
)

config.to_dict()["per_layer_config"]
# {"2": {"num_key_value_heads": 4}}

设置 serialize_explicit_per_layer_config=True 以在 per_layer_config 中为每个层包含所表示的属性。当某些值仍与全局配置一致时,这使得检查层布局变得更加容易。

explicit_config = LlamaConfig(
    hidden_size=4096,
    intermediate_size=14336,
    num_hidden_layers=4,
    num_attention_heads=32,
    num_key_value_heads=8,
    serialize_explicit_per_layer_config=True,
    per_layer_config={
        0: {"num_key_value_heads": 8},
        2: {"num_key_value_heads": 4},
    },
)

serialized_per_layer_config = explicit_config.to_dict()["per_layer_config"]

serialized_per_layer_config
# {
#     "0": {"num_key_value_heads": 8},
#     "1": {"num_key_value_heads": 8},
#     "2": {"num_key_value_heads": 4},
#     "3": {"num_key_value_heads": 8},
# }

使用稀疏序列化以获得紧凑的配置,并在需要完整的逐层布局以提升可读性或便于工具处理时,使用显式序列化。

Update on GitHub