feat(backward_lens): generalize Backward Lens to dense-MLP decoder-only Bridges (GPT-2, Pythia, GPT-NeoX) - #1778
Open
janmenjayap wants to merge 3 commits into
Conversation
Replace the GPT2ArchitectureAdapter isinstance check and the Conv1D-only projection guard with the Bridge's own weight_layout_in_out oracle, so support is decided per MLP projection instead of by model class. Dense MLPBridge input/output projections now resolve to "in_out" (Conv1D, e.g. GPT-2) or "out_in" (torch.nn.Linear, e.g. Pythia/GPT-NeoX), and the resolved layout threads through gradient-factor capture instead of the previous hardcoded "in_out". A projection whose wrapped module the oracle cannot orient is rejected with a clear error. Rename _require_raw_gpt2_bridge, _get_gpt2_mlp_projections, _capture_gpt2_mlp_gradient_factors, _GPT2GradientCapture, and _GPT2LayerGradientFactors to their model-agnostic names, and update call sites and docstrings accordingly. Repurpose the non-Conv1D rejection test to assert rejection of an unorientable component, since torch.nn.Linear projections are now accepted. Behavior-preserving for GPT-2: unit and integration suites for this module pass unchanged.
Add model-free unit tests for dense-MLP projection discovery that exercise the Bridge weight-layout oracle directly, using real MLPBridge/LinearBridge instances rather than a booted model: a torch.nn.Linear MLP resolves to the "out_in" layout and accepts its transposed weight shape, a Conv1D MLP resolves to "in_out" with GPT-2-parity shapes, an unorientable wrapped module is rejected with a clear error, and a gated MLP is rejected regardless of orientation. Parametrize the core gradient-reconstruction integration test over a raw GPT-2 Bridge and a raw Pythia-70m Bridge sharing the same prompt and target token, asserting the same reconstruction tolerance bands for both and that each model's projections resolve to its own weight layout without any model-class conditional. Extend the weight/hook state-preservation and cleanup-on-failure integration tests to both models as well.
Update the Backward Lens doc and demo notebook to describe the generalized dense-MLP decoder-only contract (GPT-2 and Pythia/GPT-NeoX) instead of the prior GPT-2-only framing. Explain both weight layouts (Conv1D [in, out] and torch.nn.Linear [out, in]) and that BackwardLens reads the layout from the Bridge projection component rather than the model class. Add a minimal Pythia-70m reconstruction example to the doc and a corresponding single-layer demo cell in the notebook.
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
Backward Lens previously accepted only raw GPT-2
TransformerBridgemodels, gated by an explicitisinstance(model.adapter, GPT2ArchitectureAdapter)check and aConv1D-only projection guard.This PR removes both model-name/class conditionals and replaces them with a capability contract:
any dense (non-gated), decoder-only
TransformerBridgewhose MLPLinearBridgeprojections theBridge's own
weight_layout_in_outoracle can orient is now supported.The oracle resolves each projection's weight storage order per instance rather than per model
class:
Conv1D([in, out], e.g. GPT-2) resolves to"in_out".torch.nn.Linear([out, in], e.g. Pythia/GPT-NeoX) resolves to"out_in".ValueError.The existing
_build_linear_gradient_factorshelper already handled both layouts correctly; onlythe discovery and validation path was GPT-2-specific. No public API name changes, and GPT-2 behavior
is unchanged (verified below).
Fixes #1777
Motivation and context
Backward Lens's gradient-factorization math (imprint / shift interpretation, exact outer-product
reconstruction) has nothing to do with GPT-2 specifically — it only depends on the MLP being dense
and two-matrix, and on knowing each matrix's storage orientation. Hardcoding the GPT-2 adapter and
Conv1Dmeant every other dense-MLP decoder-only architecture already supported byTransformerBridge(Pythia, GPT-J, GPT-NeoX, OPT, ...) was rejected outright, even though nothingabout the tool's math required that restriction.
This generalizes the capability boundary to match what the Bridge itself can already tell us,
instead of adding a second, parallel per-model-family conditional alongside the Bridge's adapter
system.
Implementation details
_require_raw_gpt2_bridge->_require_raw_dense_mlp_bridge; drop theGPT2ArchitectureAdapterisinstance check. All other guards are unchanged:TransformerBridgeonly, not
compatibility_mode, not_weights_processed, single-device, non-gated MLP, atokenizer, and the standard
blocks/ln_final/unembedcomponents._get_gpt2_mlp_projections->_get_dense_mlp_projections; replace theisinstance(projection.original_component, Conv1D)guard withweight_layout_in_out(projection)fromtransformer_lens.model_bridge.generalized_components.mlp. Introduce a private_MLPLinearrecord carrying each projection alongside its resolved
weight_layout, and make the expectedweight shape orientation-aware (
(in, out)for"in_out",(out, in)for"out_in") so bothstorage orders validate against the same fixed in/out feature counts.
_capture_gpt2_mlp_gradient_factors->_capture_dense_mlp_gradient_factors,_GPT2GradientCapture->_DenseMLPGradientCapture, and_GPT2LayerGradientFactors->_MLPLayerGradientFactors. Thread each projection's resolvedweight_layoutinto_build_linear_gradient_factorsinstead of the previous hardcoded"in_out".BackwardLensand module/class docstrings to describe the dense-MLP decoder-onlycontract instead of GPT-2 specifically.
test_capture_rejects_a_non_conv1d_componentto assert rejection of an unorientablecomponent, since
torch.nn.Linearprojections are now accepted rather than rejected.Supported scope
This generalization supports:
TransformerBridgewith a dense, non-gated, two-matrix MLPwhose
LinearBridgeprojections wrap eitherConv1Dortorch.nn.Linear.Still out of scope, deferred to separate follow-up work:
Documentation and demonstration
docs/source/content/backward_lens.md: generalizes the introduction, gradient-factorizationsection (explains both
Conv1D[in, out]andnn.Linear[out, in]storage, and that thetool reads orientation from the Bridge component, not the model class), and requirements/
troubleshooting sections. Keeps the GPT-2 example and adds a minimal Pythia-70m example.
demos/Backward_Lens_Demo.ipynb: adds a short section stating the generalized contract plus onesmall single-layer Pythia-70m reconstruction cell, without duplicating the full GPT-2 walkthrough.
Dependencies
No new dependencies are required.
Validation
Observed local validation on this branch:
tests/unit/tools/test_backward_lens.py): 55 passedtests/integration/test_backward_lens.py, GPT-2 and Pythia-70m parametrized):35 passed
uv run mypy .:Success: no issues found in 398 source filesmake format: no changesmake test-pr(unit + docstring + acceptance + integration, full repository): 1462 passed,22 skipped, 197 deselected, 1 xfailed; one unrelated pre-existing failure,
test_granite_eager_scan_device_correctness[mps]intests/integration/model_bridge/test_granite_moe_hybrid_adapter.py(Granite MoE Hybrid SSMeager-scan vs. fused-scan numerics on MPS), confirmed unrelated to this change and predating this
branch.
Type of change
Checklist