Generalize dense-layer MLP hooks across interleaved MoE architectures - #1666
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
MoEBridgesubclass carrying fallback-list aliases. The same mapping exists in sixteen other adapters, so this generalizes the fix intoMoEBridgeitself and retires that subclass.MoEBridge.set_original_componentnow dispatches per layer at bind time: when a layer's module actually exposes the adapter-declared dense projections, that instance adopts the gated-MLP alias set (hook_pre,hook_pre_linear,hook_post) plusW_gate/W_in/W_outandb_*. Sparse layers are untouched. Detection is positive-only — declared and present — so no sparse block can be mis-detected by a naming quirk and stripped of its hooks.gate/in/out: on a sparse layer of the same modelgateis the router, so reusing it would makeblocks.N.mlp.gate.hook_outmean the router on some layers and a d_mlp projection on others.dense_*is what llada2_moe, llama4, and laguna already declared, so those three are fixed by the shared component with no edit.dense_in/dense_outalone bind the MLP-style alias set (nohook_pre_linear, noW_gate), which is what Switch Transformers'wi/woneeds.mlp_only_layers/decoder_sparse_stepcheckpoint crashed at boot instead of degrading. qwen3_next and llama4 also gain the router hooks they never mapped. qwen3_5_moe is deliberately excluded: HF builds its sparse block unconditionally, so its dense declarations were dead and its MoE submodules are required.optionalalone cannot say "required on sparse layers, absent on dense ones", which made a renamed HF router bind silently with no hooks — the failure this work removes. Adapters now declaresparse_required=(...); a sparse layer missing one raises from avalidate_after_setuphook called oncesetup_submodulesknows the skipped set. The tuple is validated at construction, since a typo would otherwise silently disable the guard. Symmetrically, a declareddense_gatethat does not resolve on a dense layer is treated as a rename and raises, rather than binding as ungated and aliasinghook_preto the up projection.get_params()keyed on submodules literally namedin/out, so dense MoE layers fell into a bareexcept AttributeErrorand were silently zero-filled — real weights on the component, zeros in the parameter dict. It now resolvesdense_*on dense binds, and the remaining zero-fill path logs a warning naming the layer instead of failing silent.hook_router_scoresrather than advertise a hook that can never fire (andforwardno longer calls it), the dispatch is symmetric so a rebinding harness cannot leave a chimera, andhook_aliases/property_aliasesare exempt fromGeneralizedComponent.__setattr__'s passthrough — without that, a per-layer rebind is forwarded to the wrapped HF module whenever it exposes the attribute and silently vanishes._collect_component_aliasesruns first on everyhook_dictaccess and previously raisedRecursionErroron a 2-cycle. The block walk is unified into one loop, warns on unresolved aliases naming the stale-fallback consequence, and is memoized against(registry size, alias generation)— size alone cannot see a dense↔sparse rebind, which leaves the registry the same size while changing what the aliases point at. List-valued targets are skipped by the template collector, whose consumer reverse-matches withstr.endswithand islru_cached.__setattr__passthrough survival, positive-detection-only, ungated dense, renamed-gate and renamed-router guards) bound through the realsetup_submodules; a self-checking roster that greps the adapters directory and fails if a dense-declaring adapter is outside the guards; alias-traversal tests for cycles and diamonds; and integration onkatuni4ka/tiny-random-deepseek-v3covering hook shapes, alias identities,get_paramsweights, logits parity, and cache invalidation across a rebind.Type of change
Checklist: