Skip to content

support loading fit checkpoints in JacobianLens.load() - #1574

Merged
jlarson4 merged 5 commits into
TransformerLensOrg:devfrom
priyanka25aug:jonah-review-1539
Aug 5, 2026
Merged

jlarson4 merged 5 commits into
TransformerLensOrg:devfrom
priyanka25aug:jonah-review-1539

Conversation

@priyanka25aug

Copy link
Copy Markdown
Contributor

What this does

Extends JacobianLens.load() to accept fit checkpoints (files saved by the fitting pipeline with a jacobian_sum key) in addition to the existing artifact format (files with a J key).

The conversion lives in a new _from_checkpoint_payload() classmethod:

  • Divides the running Jacobian sums by n_prompts to recover the per-prompt mean
  • Harvests safe scalar provenance keys (model_name, model_revision, corpus) from the flat payload namespace into metadata
  • Strips fit-reserved keys (transformer_lens_fit, transformer_lens_version, model_system, etc.) so they don't leak into the converted lens
  • Drops tensor-valued metadata fields that can't survive weights_only=True reload, recording their names and shapes in dropped_fields for transparency
  • Sets converted_from: "jacobian_lens_checkpoint" so merge() naturally refuses to mix converted and natively TL-fitted lenses (provenance keys differ)

Raises ValueError with a clear message if the file has neither key, or if n_prompts <= 0. Tuned-lens support deferred.

Files changed

  • transformer_lens/tools/analysis/jacobian_lens.py — new _FIT_RESERVED_KEYS and _CHECKPOINT_FLAT_PROVENANCE frozensets; updated load(); new _from_checkpoint_payload()
  • tests/unit/tools/test_jacobian_lens_import.py — unit tests on synthetic fixtures covering artifact regression, checkpoint round-trip, dtype preservation, n_prompts=0 guard, tensor field dropping, fit-key stripping, and merge provenance rejection; no model/oracle dependency
  • docs/source/content/jacobian_lens_fitting.md — "Importing an existing lens" section added

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Checklist

  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have not rewritten tests relating to key interfaces which would affect backward compatibility

Testing

uv run pytest tests/unit/tools/test_jacobian_lens_import.py -v

@priyanka25aug

Copy link
Copy Markdown
Contributor Author

Hi @jlarson4 — all format, type, docstring, and benchmark checks are passing. The remaining long-running jobs (compatibility across Python 3.10/3.11/3.12 and full coverage) are still in progress but looking clean so far.

Would you mind taking a look when you get a chance? Happy to make any changes based on your feedback.

@jlarson4 jlarson4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @priyanka25aug! Thanks for taking this on. Checkpoint import is a genuinely useful capability, and the conversion math, artifact-path safety, and merge sentinel are all done right.

A couple comments below that we should address before merging

Comment thread transformer_lens/tools/analysis/jacobian_lens.py Outdated
Comment thread transformer_lens/tools/analysis/jacobian_lens.py
Comment thread tests/unit/tools/test_jacobian_lens_import.py
Comment thread docs/source/content/jacobian_lens_fitting.md Outdated
…, fix tuned-lens note

- Remove "target_layer" from _FIT_RESERVED_KEYS so it survives checkpoint
  conversion and validate_model() can refuse non-final-target lenses
- Add test_load_checkpoint_mirrors_fit_payload_schema: fixture matches the
  exact keys fit() produces so format drift causes a test failure
- Fix tuned-lens note: it is the Jacobian artifact format that has no bias
  slot, not the tuned-lens format; tuned-lens translators are affine (weight + bias)
@priyanka25aug

priyanka25aug commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor Author

@jlarson4 All four of your review comments have been addressed in 08738f86 and 7aec2e7b:

  1. target_layer preservation — removed from _FIT_RESERVED_KEYS so it survives checkpoint conversion and validate_model() correctly raises for non-final-target checkpoints.
    1. Reference fixture — added test_load_checkpoint_mirrors_fit_payload_schema using the exact payload layout fit() produces, including flat provenance keys and nested fit-reserved metadata, with assertions that verify target_layer is preserved and fit-reserved keys are stripped.
    1. Tuned-lens note — corrected the direction: tuned-lens translators are affine (weight + bias), the Jacobian artifact format has no bias slot to receive the translation component.
    1. n_done key — _from_checkpoint_payload now reads payload.get("n_done", payload.get("n_prompts", 0)), preferring n_done (the key the reference writer emits) with n_prompts as a fallback so genuine checkpoints aren't silently rejected.
      All threads are resolved. Could you please re-review and merge when you're happy? Thank you!

Real checkpoint writers (reference package) store the prompt count as
n_done, not n_prompts. Prefer n_done with n_prompts as fallback so
genuine checkpoints are not rejected with n_prompts=0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NiwNUm3YFj9yAuSBuGDnd8
@jlarson4

jlarson4 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@priyanka25aug There is a CI failure that needs it be addressed still it looks like. The failure is an indicator of the remaining bug. n_done was one of three keys the reference writer disagrees with us about. Real checkpoints also carry no d_model (derivable from any jacobian_sum matrix's shape) and put target_layer at the top level rather than inside a nested metadata dict. With only the count fixed, a genuine checkpoint still cannot load, it just fails one line later with an unhandled KeyError. Worth opening write_checkpoint() (jlens/fitting.py:315-327 in anthropics/jacobian-lens@581d398) and working from its six keys directly rather than from my comments one at a time.

If the intent was a TransformerLens checkpoint format rather than the reference one, say so and we'll take the other fork: that would mean teaching fit() to write it (checkpoint_path / checkpoint_every, resumable), because nothing in either codebase emits the documented layout. The PR docstring describes the running-sum file "written during or after a fitting run", which reads as the reference format, so that is what I was assuming.

Suggested order:

  1. Derive d_model from a jacobian_sum matrix's shape rather than requiring the key, and harvest top-level target_layer alongside the flat provenance keys.
  2. Then make one fixture the verbatim six-key payload and assert both that it loads and that a checkpoint recording a non-final target_layer is refused by validate_model().
  3. test_load_checkpoint_with_zero_n_prompts_raises needs a decision rather than a patch: its jacobian_sum is empty, so it can never load however the schema lands. Give it a matrix and let it assert the round trip, and test the non-positive-count guard on its own synthetic file. Whichever error the empty-sums case raises, it should name the empty sums, rather than using n_prompts=0 or a bare KeyError.
  4. Update the docs schema table to the real key set – as written it documents n_prompts/d_model as required, which sends readers off to hand-build files.
  5. Small doc gap from 08738f86: the "Metadata handling" section still says keys written by fit() "are not carried over ... (transformer_lens_fit, transformer_lens_version, model_system, hook_convention, etc.)". target_layer is now a deliberate exception and documented in the code comment, but not the doc.

…_checkpoint() schema

- _from_checkpoint_payload: read n_done first (real checkpoints use n_done, not n_prompts)
- _from_checkpoint_payload: infer d_model from jacobian_sum matrix shape (real checkpoints have no d_model key)
- _from_checkpoint_payload: harvest top-level target_layer into metadata (reference format stores it at top level, not nested)
- _from_checkpoint_payload: guard empty jacobian_sum with a clear ValueError before attempting shape derivation
- load() docstring: update Fit checkpoint schema to reflect the real 6-key reference format
- tests: replace test_load_checkpoint_with_zero_n_prompts_raises with two tests
- tests: rewrite test_load_checkpoint_mirrors_fit_payload_schema to use verbatim 6-key reference payload
- tests: add test_load_checkpoint_harvests_flat_provenance_and_strips_fit_keys
- docs: update schema table — replace n_prompts/d_model with real 6-key format
- docs: note d_model inferred from matrix shape
- docs: document target_layer as deliberate exception to fit-key stripping
@priyanka25aug

Copy link
Copy Markdown
Contributor Author

Hi @jlarson4 — all five points from your second review are addressed in commit ff6cef7. Here's a summary of what changed:

  1. n_done key support: _from_checkpoint_payload() now reads n_done first (the real key written by write_checkpoint()), falling back to n_prompts for backward compatibility.
  2. d_model inferred from matrix shape: Removed the payload["d_model"] lookup (which would KeyError on real checkpoints). d_model is now derived from first_matrix.shape[0] of the first entry in jacobian_sum, with an explicit error if jacobian_sum is empty.
  3. Top-level target_layer harvested: Added a targeted harvest of the top-level target_layer key (where the reference implementation writes it) into metadata, so validate_model() can check the fitting target.
  4. Tests updated to use the verbatim 6-key reference schema: Replaced the old test (which had a structurally wrong payload) with:
    • test_load_checkpoint_with_zero_n_done_raises — checks n_done=0 triggers the n_prompts=0 error
    • test_load_checkpoint_with_empty_jacobian_sum_raises — checks empty jacobian_sum triggers the new error
    • test_load_checkpoint_mirrors_fit_payload_schema — rewritten to use the exact 6-key payload write_checkpoint() produces
    • test_load_checkpoint_harvests_flat_provenance_and_strips_fit_keys — new test covering flat-provenance harvest and fit-key stripping
  5. Docs updated: Schema table in jacobian_lens_fitting.md now reflects the real 6-key format (n_done, next_idx, target_layer, skip_first); added a note that d_model is inferred from the matrix shape; added a paragraph noting target_layer is a deliberate exception to fit-key stripping.
    CI is all green — the only failure is the pre-existing Activation_Patching_in_TL_Demo notebook flake, unrelated to this PR. Ready for re-review when you get a chance!

@jlarson4

jlarson4 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Thanks for covering all that @priyanka25aug, great work! Merging now

@jlarson4
jlarson4 merged commit 4491770 into TransformerLensOrg:dev Aug 5, 2026
49 of 50 checks passed
jlarson4 added a commit that referenced this pull request Aug 7, 2026
* Add Jacobian lens fitting guide (#1544)

* Quantify Jacobian Lens causal swap success (#1545)

Co-authored-by: Dreamer431 <113128214+Dreamer431@users.noreply.github.com>

* test(integration): add oracle parity test for JacobianLens (#1539 Tier-1) (#1543)

* test(integration): add oracle parity test for JacobianLens (#1539 Tier-1)

Compares TransformerBridge JacobianLens.readout() against the reference
anthropics/jacobian-lens oracle (pinned to 581d398) on google/gemma-2-2b-it
across 75 layer x prompt cells (5 prompts x 15 sampled layers).

Pass criteria per the #1539 spec (matching #1505 spike numbers):
  - Worst-case top-8 token overlap >= 7/8 in every cell
  - Spearman rank-correlation >= 0.95 on the top-64 logit union per cell

The oracle is installed at test time via pip from the pinned commit so
the threshold is reproducible independent of upstream drift. Reuses the
bridge's original_model + tokenizer to avoid a second model copy in RAM.

* style: apply black formatting (line-length=100)

* fix: remove unused Dict, Tuple typing imports (pycln)

* style: fix black formatting for py310 target (double blank lines)

* test: use pytest.importorskip for oracle dep; add oracle-parity CI workflow

Replace subprocess pip-install fixture with pytest.importorskip so the
test skips gracefully in standard uv venvs (no pip present) and does not
mutate the developer environment with no cleanup.

Add .github/workflows/oracle-parity.yml — a dedicated workflow that reads
ORACLE_COMMIT from the test file (single source of truth) and installs
the oracle out-of-band before running the @pytest.mark.slow suite.
Triggers on workflow_dispatch and on pushes that touch the test or
workflow file, keeping oracle runs opt-in for PR checks.

* Visual Encoders (ViT, DeiT) Support Rollout (#1546)

* Add ViTArchitectureAdapter to supported architectures

* Add ViTArchitectureAdapter to architecture factory

* Add ViT and DeiT models to model registry

* Add new model descriptions for Vision Transformers and Wav2Vec2

* Add ViTArchitectureAdapter for vision models

Implement ViT/DeiT architecture adapter for model bridging.

* Create vision_embedings.py

* Add VisionClassifierHeadBridge for CLS token classification

Implement VisionClassifierHeadBridge to handle CLS token slicing for classification.

* Add visual model configuration to transformer bridge

* Clarify pixel_values usage for multimodal and vision models

Updated documentation for pixel_values parameter to clarify its use with vision models.

* Update bridge.py

* Update bridge.py

* Rename vision_embedings.py to vision_embeddings.py

* Define vision model and classification architectures

Added vision model architectures and classification heads.

* Add support for vision architectures in transformers

* Refactor VisionClassifierHeadBridge to use pooled output

Updated the VisionClassifierHeadBridge to directly use an already-pooled CLS token instead of slicing from the sequence output. Adjusted the forward method to reflect this change and improved error handling for the original component.

* Update vit.py

* Add unit tests for ViTArchitectureAdapter

This file contains unit tests for the ViTArchitectureAdapter, covering component mapping, configuration flags, weight conversions, and model preparation methods.

* Create test_vit_adapter.py

* Update transformers.py

* Update vit.py

* Update vit.py

* Update vit.py

* Fix type hint for get_remote_component method

* Fix type hint for get_remote_component method

* Change import of torch to torch.nn in vit.py

* Update vit.py

* Re-add dummy 'mlp' attribute injection for ViTLayer

Reintroduce a patch_layers function to inject a dummy 'mlp' attribute into ViTLayer blocks for MLPBridge compatibility.

* Refactor ViTLayer handling by removing patch_layers

Removed the patch_layers function and its call, which injected a dummy 'mlp' attribute into ViTLayer blocks. Updated comments for clarity regarding the MLPBridge container.

* Add dummy 'mlp' attribute to ViTLayer blocks

Inject a dummy 'mlp' attribute into ViTLayer blocks to satisfy hasattr check for TransformerLens.

* Update vit.py

* Remove TestViTConfigNCtx and related test case

Removed deprecated TestViTConfigNCtx class and its test case for n_ctx.

* Enhance ViTLayer with MLP wrapper and fix forward method

Added a non-circular MLP wrapper to ViTLayer blocks and fixed tuple-chaining bug in forward method.

* Refactor ViT layer forward pass handling

Refactor forward pass handling for ViT layers to safely unpack tuple outputs and ensure compatibility with the model's internal structure.

* Refactor ViTLayer forward pass handling

Refactor forward pass handling for ViTLayer to fix tuple-chaining bug and ensure compatibility with HF model outputs.

* Fix tuple handling in ViTLayer forward method

Modified the forward method to handle tuple inputs and outputs for ViTLayer, ensuring compatibility with Tensor expectations.

* Reorder model prefix checks for better clarity

* Update vit.py

* Detect model class name in prepare_model method

Added detection for model class name in prepare_model method.

* Simplify prefix determination for ViT models

Refactor model prefix detection logic for ViT and DeiT models.

* Implement fixture for distilled DeiT model testing

Added a fixture to load the distilled DeiT model for testing.

* Update DeiT bridge tests for bare model handling

Refactor tests for DeiT bridge to accommodate bare model behavior and update assertions accordingly.

* Set architecture in Hugging Face model configuration

* Support DeiTLayer in patch_layers function

* sort

* Replace direct attribute assignment with setattr

* black fix

* fix formatting after merge

* Update vit.py

* Update ViT adapter test paths for consistency

* Remove redundant test for n_ctx in prepare_loading

Removed test for prepare_loading not affecting n_ctx.

* black sorted

* black reorder

* Refactor vit_bridge and vit_bare_bridge fixtures

* temp support up to transformers 5.8.0

* support transformers 5.13.0

* format fixed. Unit test all passed. Intergration test all passed. should be good to go

* Update vit.py

* Clarify tokenizer support in ViTArchitectureAdapter

Added comment to clarify the lack of tokenizer support for vision models.

* Remove head_dim assignment from hf_config

Removed unused head_dim assignment from hf_config.

* Update vit.py

* Update bridge.py

* Add VisionEmbeddingsBridge and VisionClassifierHeadBridge

* Update test_vit_adapter.py

* Update test_vit_adapter.py

* Update test_vit_adapter.py

* Update test_vit_adapter.py

* Improve compatibility mode error and output handling

Updated error message for clarity and added handling for last_hidden_state in output.

* Refactor test to check output type and shape

Update test to verify that the forward method returns a tensor instead of a raw HF output object. Adjust assertions to match the expected behavior after changes in bridge.py.

* Update bridge.py

* Update test_vit_adapter.py

* Remove obsolete tests from TestViTPrepareLoading

Removed deprecated tests for prepare_loading() in TestViTPrepareLoading.

* Update test_vit_adapter.py

* Update test_vit_adapter.py

* formatted

---------

Co-authored-by: Jonah Larson <jonahalarson@comcast.net>

* Add Starcoder2 architecture adapter (#1533)

Co-authored-by: jlarson4 <jonahalarson@comcast.net>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Docs: Jlens Qwen3.5-4b Demo (#1547)

* Run experiments with Qwen-3.5 architecture support

* Run notebook cells

* Add lfm2 tiny integration test (#1552)

* Add lfm2 tiny integration test

* Fix formatting

* Add AST (Audio Spectrogram Transformer) Adapter (#1484)

* chore: save WIP on V3 transformerbridge migration

re add ast import and add to factory
:wq
y

wq
:wq

* feat(ast): migrate AST adapter to V3 TransformerBridge and component_mapping

* refactor(ast): resolve PR feedback for docstrings, prefix-awareness, unit tests

* test(ast): split parity to integration folder, add load_weights boot test, fix audio classification load path, and add to ARCHITECTURE_DESCRIPTIONS

* fix(ast): union audio classification sets, specific boot test assertions and two comment typo fixes

* Verificaiton for lapa (#1556)

* ViT and AST model verification (#1582)

* verified a few models for ViT and AST

* improved vision testing for ViT models

* fix(bridge): return W_in/W_out/W_gate in TL orientation for nn.Linear-backed models (#1558)

* feat: respect prepend_bos and add return_input_tokens flag

* fix torch orientation bug

* review changes

* review changes

* pipeline fix

* deprecate remaining hooked entry points (#1592)

* deprecate remaining hooked entry points

* test: account for hooked transformer warning in notebook

* fix: address deprecation warning review feedback

* fix: correct encoder deprecation warning stacklevel

* support loading fit checkpoints in JacobianLens.load() (#1574)

* support loading fit checkpoints in JacobianLens.load()

* fix: black formatting and update conflicting test for checkpoint load

* address jlarson4 review: preserve target_layer, add reference fixture, fix tuned-lens note

- Remove "target_layer" from _FIT_RESERVED_KEYS so it survives checkpoint
  conversion and validate_model() can refuse non-final-target lenses
- Add test_load_checkpoint_mirrors_fit_payload_schema: fixture matches the
  exact keys fit() produces so format drift causes a test failure
- Fix tuned-lens note: it is the Jacobian artifact format that has no bias
  slot, not the tuned-lens format; tuned-lens translators are affine (weight + bias)

* address jlarson4 review: read n_done key in _from_checkpoint_payload

Real checkpoint writers (reference package) store the prompt count as
n_done, not n_prompts. Prefer n_done with n_prompts as fallback so
genuine checkpoints are not rejected with n_prompts=0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NiwNUm3YFj9yAuSBuGDnd8

* address jlarson4 review: align checkpoint loader with reference write_checkpoint() schema

- _from_checkpoint_payload: read n_done first (real checkpoints use n_done, not n_prompts)
- _from_checkpoint_payload: infer d_model from jacobian_sum matrix shape (real checkpoints have no d_model key)
- _from_checkpoint_payload: harvest top-level target_layer into metadata (reference format stores it at top level, not nested)
- _from_checkpoint_payload: guard empty jacobian_sum with a clear ValueError before attempting shape derivation
- load() docstring: update Fit checkpoint schema to reflect the real 6-key reference format
- tests: replace test_load_checkpoint_with_zero_n_prompts_raises with two tests
- tests: rewrite test_load_checkpoint_mirrors_fit_payload_schema to use verbatim 6-key reference payload
- tests: add test_load_checkpoint_harvests_flat_provenance_and_strips_fit_keys
- docs: update schema table — replace n_prompts/d_model with real 6-key format
- docs: note d_model inferred from matrix shape
- docs: document target_layer as deliberate exception to fit-key stripping

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Drop optional unused aliases on hybrid architectures (#1579)

* Drop optional unused aliases

* feat: assign fallbacks on pruned

* Improve unittests

* Add kurtosis-profile validation test for JacobianLens (#1539 Tier-1) (#1616)

* Add kurtosis-profile validation test for JacobianLens (#1539 Tier-1)

Asserts the workspace-band signature as relative structure rather than
absolute levels, per the cross-family measurement in #1539: band rise vs
the model's own early-third baseline, lens-specificity vs the logit-lens
control through the identical code path, a gpt2-small negative control,
and final-layer identity-transport agreement between arms.

* Fix gpt oss olmo3 parity (#1621)

* Resolution for issue 1619

* Updated for 1620

* Add verification script

* Fixed 1619 on HF

* Verification script repair

* fixing per-layer olmo

* cleanup

---------

Co-authored-by: abhi <abhinavbellapu@berkeley.edu>
Co-authored-by: emerardd <113128214+emerardd@users.noreply.github.com>
Co-authored-by: Dreamer431 <113128214+Dreamer431@users.noreply.github.com>
Co-authored-by: Mukund Pandey <mukund.pandey@gmail.com>
Co-authored-by: Jiankun Wei <72998341+david-wei-01001@users.noreply.github.com>
Co-authored-by: SanjidMzi <56235075+SanjidMzi@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kyle Yin Xu <123780557+KYinXu@users.noreply.github.com>
Co-authored-by: Syed Adil Ahmed <tensorcruncher@gmail.com>
Co-authored-by: Dylan <159935143+dylanberens@users.noreply.github.com>
Co-authored-by: Md.Sadiq <mohammadsadiq4950@gmail.com>
Co-authored-by: msaule <sau24006@byui.edu>
Co-authored-by: Priyanka Bajaj <42418272+priyanka25aug@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants