Repository navigation
Conversation
|
@askliar |
ArthurZucker
left a comment
There was a problem hiding this comment.
Hey! thanks for the PR!
One thing that's super important to stay aligned with transformers philo is that is not somthing we want to ship to old models! We'll be super happy to create new models with modular that do have heterogenous configs per layer, but adding this to llama break llama. GPT OSS does NOT have this, as such it should not support it. Happy to have heterogen_gpt_oss !
This removes code paths, and the if is_heterogeneous etc.
Given this, I think it would mostly be a change in the config to have per_layer_config.
We can find a good way to serialize all args in order to reduce the bloat potentially (meaning all attributes are list, at init time this creates a list of config per-layer) this simplifies code changes in modeling_heterogenous_gpt_oss for example, that would just get per_layer_config[i]
Furthermore I don't understand the skip idea can you give a concrete example? you want to skip an entire layer, but you can just set it to nn.Identity for example? or just re-index them?
|
[For maintainers] Suggested jobs to run (before merge) run-slow: gpt_oss |
`per_layer_config[i]` returned a fresh copy before a model was built from the config, and the layer's live config afterwards, because the layer configs were stored in the build state. Create each layer config on first access and keep it on the heterogeneity spec, so `per_layer_config[i]` always returns the object that layers are built with. The build state keeps only what the skips left behind. Setting or deleting a global attribute now passes the change on to the layer configs that still have the global value, as soon as they exist. Deletions weren't passed on before, so a forward call with `is_causal=False` left the layer configs of a heterogeneous model non-causal for later calls. An override that comes to equal the global value is removed, as when the config is created, so a config behaves the same before and after it's saved and loaded. Also consolidate overlapping heterogeneous config tests.
…s a mask consumer
# Conflicts: # src/transformers/cache_utils.py
# Conflicts: # src/transformers/cache_utils.py # src/transformers/masking_utils.py
…date the “dummy” comment
What does this PR do?
Adds heterogeneous model support - the ability for individual layers to differ from the global config (e.g., different
intermediate_size,num_key_value_heads) and to skip sub-modules entirely (MLP, attention, etc.). This enables models where layers are not uniform, as in pruned, distilled, or NAS-derived architectures.Examples of such models:
These models previously required
trust_remote_code=Trueto modify the classes of the model they are derived from. With this PR, checkpoints derived from the architectures with built-in support (seesupported_models.py) work out of the box, and any other model - including custom models - can opt in with a few lines.This PR adds the generic modeling integration, skip handling, a built-in architecture registry, layer-indexed attention masks, and cache handling for skipped cache writers. It builds on
per_layer_configintroduced in PR #45333, per-layer MTP configuration from PR #48264, and per-layer cache configuration from PR #48178.How it works
per_layer_configdescribes how individual layers differ from the global configuration. For each layer,config.per_layer_config[layer_idx]provides the resolved configuration, including attribute overrides andskipentries. For example:
This PR makes supported model layers consume that resolved configuration during construction.
Modeling (automatic layer patching)
Architecture support is declared with a
HeterogeneousModelingSpec. Built-in specs live insupported_models.py; any other model opts in by setting_heterogeneous_modeling_specon itsPreTrainedModelbase class. A spec targets one layer class. A model that builds its layers from several classes, like Jamba's attention and Mamba layers, sets a tuple with a spec for each.At model init time, the framework monkey-patches the
__init__of each spec'slayer_clsto:layer_idx_resolverconfig.per_layer_config[layer_idx]to the original__init__skipentries, replace the corresponding sub-modules with parameter-free no-op replacementsThe layer-class wrapper remains installed, but it is active only when a matching model is being initialized. A
ContextVarscopes the per-model state, which is restored automatically after model initialization by the wrappers installed through__init_subclass__.config.per_layer_config[i]creates each layer's config on first access and returns the same object from then on, so the layers are built with these objects and keep sharing them. Setting or deleting an attribute on the global config, for example the attention implementation, carries over to layer configs that still have the global value. What the skips leave behind (layers without attention mask consumers or cache writers) is attached to the config only after the root model's initialization succeeds, and initialization fails unless every layer is constructed from one of the specs' layer classes.The model
__init__wrapper is installed on everyPreTrainedModelsubclass through__init_subclass__because any of them, including a user's, can be the root model, and no other hook runs when the root model's__init__returns or raises:post_initruns at the end of each nested model's__init__, such asLlamaModelinsideLlamaForCausalLM, and not at all on failure.The spec names the layer class and its layer-index resolver. Sub-module skipping also requires skip descriptors, and declarations of the layer members that consume the attention mask or write to the cache:
The skip type names ("attention", "mlp") are what configurations reference in their
skiplists. Layer members are named with member keys: a path, such as"self_attn"or"block.attn", or a(path, class)tuple, such as("mixer", NemotronHAttention), which matches only if the member at that path is an instance of the class.(path, class)keys let one skip descriptor cover a member that has a different class in different layers.attention_mask_consumerslists the members that consume the attention mask, andcache_writersmaps the members that write to the cache to the caches they write ("key_value"or"linear_attention"). Both list every such member, including members that no skip replaces, with member keys. A(path, class)key can also name a member that only some layers build, like Bamba'sself_attn, which only its attention layers have.Note: a spec without
skip_descriptorsis sufficient for attribute heterogeneity (varying dimensions across layers).Generic patching is opt-in. It is applied only when the configuration is heterogeneous and the model declares a
HeterogeneousModelingSpec. Models without a spec, or with_heterogeneous_modeling_spec = None, are left unpatched, so architectures that consumeper_layer_configdirectly can handle it in their own modeling code.MTP models
When the MTP configuration is heterogeneous and the main model has a spec, MTP layers are built from their per-layer configs in
mtp_per_layer_config, so per-layer attribute overrides apply to them. MTP layers don't supportskip, and setting it raises an error.Attention masks and caches
When generic patching is applied, the mask factories (causal, bidirectional, sliding-window, and chunked) return an
AttentionMasksByLayerIdxmapping keyed by layer index, and each layer's pre-hook selects its own entry:sliding_window) and cache geometry matchNoneinstead of a maskcreate_masks_for_generate's layer-type-keyed outputWhen a layer's skips replace every writer of one of its caches, its cache entry drops that cache. A layer with no cache left gets a
DummyLayer, which holds nothing, so a static cache allocates no memory for it. A hybrid layer that keeps one of its two caches gets the layer type that holds only that cache, such asfull_attentionfor ahybridlayer that keeps its key/value cache. Positions are read from the first layer with a key/value cache, so the first layer's attention can be skipped.Key changes
src/transformers/integrations/heterogeneity/package - modeling specs and layer-index resolvers, generic layer patching, skip replacements (get_skip_replacement_factory/ReturnEntry), layer-indexed mask construction, cache layer types for skipped cache writers, built-in architecture registry, per-layer configs owned by the config (created once, following global changes), one spec per layer class for models with several layer classes, and configuration validationmodeling_utils.py- applies generic heterogeneous modeling during model initialization and scopes the initialization context by wrapping subclass__init__through__init_subclass__cache_utils.py- drops the caches that skips leave without writers when choosing each layer's cache type, and letsQuantizedCachebuild aDummyLayerfor a layer without any cache. As a result,QuantizedCachealso accepts models with MoE/MLP layers or KV-sharing layers, which it rejected before, because those layers get aDummyLayertoo.modeling_layers.py- builds heterogeneous MTP layers from their per-layer configs, and rejectsskipfor themmasking_utils.py- enables layer-indexed dispatch in the mask factories, and adds alayer_idxargument to the bidirectional onesgeneration/utils.py- handles layer-indexed masks nested in generation's mask dicts, andNoneattentions from skipped layersexporters/utils.py- keeps layer-indexed masks when merging the masks of decode stepsutils/output_capturing.py- recordsNonefor skipped attention layers, soattentions[i]still belongs to layerigeneric_heterogeneous_modeling.md), linked fromheterogeneous_configurations.mdTests
Comprehensive test suite in
tests/heterogeneity/covering 4 architectures (Llama, GptOss, Llama4, NemotronH) with their built-in specs, and Jamba and Bamba with custom specs, for models with several layer classes and for members that only some layers build. It covers structure verification, forward/generate equivalence against manually constructed reference models, model save/load round-trips, cached decoding (including with the first layer's attention skipped), static-cache and assisted generation with skipped attention, cache layer types for skipped cache writers, layer-indexed mask construction and sharing (including no recompiles undertorch.compileas the cache grows), MTP models, built-in and custom specs, layer-index resolution, skip replacements, configuration validation, and edge cases.Concrete example: Llama-3.1-8B-Instruct with 4 layers of attention skipped
Code snippet (click to expand)
Who can review?
@ArthurZucker
@hmellor