From 184dd1b5fa175374c86c0e4f3bd88e5c3f33f984 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Mon, 24 Aug 2026 01:43:18 -0700 Subject: [PATCH 01/17] feat(backward_lens): add gradient factorization contracts - Reconstruct linear weight gradients from forward inputs and output VJPs. - Support GPT-2 Conv1D and torch.nn.Linear weight layouts. - Add detached CPU contracts, validation, error metrics, and signed vocabulary rankings. - Cover numerical, validation, overflow, ownership, and ranking edge cases. --- tests/unit/tools/test_backward_lens.py | 240 ++++++++++++++++++ .../tools/analysis/backward_lens.py | 171 +++++++++++++ 2 files changed, 411 insertions(+) create mode 100644 tests/unit/tools/test_backward_lens.py create mode 100644 transformer_lens/tools/analysis/backward_lens.py diff --git a/tests/unit/tools/test_backward_lens.py b/tests/unit/tools/test_backward_lens.py new file mode 100644 index 000000000..53c04dd0f --- /dev/null +++ b/tests/unit/tools/test_backward_lens.py @@ -0,0 +1,240 @@ +"""Model-free tests for Backward Lens gradient-factor contracts.""" + +from dataclasses import FrozenInstanceError +from typing import cast + +import pytest +import torch +import torch.nn.functional as F + +from transformer_lens.tools.analysis.backward_lens import ( + LinearGradientFactors, + WeightLayout, + _build_linear_gradient_factors, + _rank_vocabulary_logits, +) + + +def test_reconstructs_gpt2_style_in_out_gradient_from_autograd() -> None: + torch.manual_seed(1) + inputs = torch.randn(7, 3) + weight = torch.randn(3, 5, requires_grad=True) + output_gradients = torch.randn(7, 5) + outputs = inputs @ weight + (weight_gradient,) = torch.autograd.grad(outputs, weight, grad_outputs=output_gradients) + + result = _build_linear_gradient_factors( + inputs, output_gradients, weight_gradient, weight_layout="in_out" + ) + + assert isinstance(result, LinearGradientFactors) + assert result.reconstructed_gradient.shape == (3, 5) + assert torch.allclose(result.reconstructed_gradient, weight_gradient) + assert result.absolute_reconstruction_error < 1e-6 + assert result.relative_reconstruction_error < 1e-6 + + +def test_reconstructs_out_in_gradient_from_autograd() -> None: + torch.manual_seed(2) + inputs = torch.randn(6, 3) + weight = torch.randn(5, 3, requires_grad=True) + output_gradients = torch.randn(6, 5) + outputs = F.linear(inputs, weight) + (weight_gradient,) = torch.autograd.grad(outputs, weight, grad_outputs=output_gradients) + + result = _build_linear_gradient_factors( + inputs, output_gradients, weight_gradient, weight_layout="out_in" + ) + + assert result.reconstructed_gradient.shape == (5, 3) + assert torch.allclose(result.reconstructed_gradient, weight_gradient) + assert result.absolute_reconstruction_error < 1e-6 + assert result.relative_reconstruction_error < 1e-6 + + +def test_asymmetric_dimensions_reject_wrong_orientation() -> None: + inputs = torch.randn(4, 2) + output_gradients = torch.randn(4, 5) + out_in_gradient = output_gradients.T @ inputs + + with pytest.raises(ValueError, match="does not match"): + _build_linear_gradient_factors( + inputs, output_gradients, out_in_gradient, weight_layout="in_out" + ) + + +def test_result_owns_detached_float32_copies_of_all_inputs() -> None: + inputs = torch.randn(3, 2, dtype=torch.float64, requires_grad=True) + output_gradients = torch.randn(3, 4, dtype=torch.float64) + weight_gradient = inputs.detach().T @ output_gradients + result = _build_linear_gradient_factors( + inputs, output_gradients, weight_gradient, weight_layout="in_out" + ) + saved_inputs = result.forward_inputs.clone() + saved_output_gradients = result.output_gradients.clone() + saved_weight_gradient = result.weight_gradient.clone() + + with torch.no_grad(): + inputs.add_(100) + output_gradients.add_(100) + weight_gradient.add_(100) + + assert result.forward_inputs.dtype == torch.float32 + assert result.forward_inputs.device.type == "cpu" + assert result.forward_inputs.grad_fn is None + assert torch.equal(result.forward_inputs, saved_inputs) + assert torch.equal(result.output_gradients, saved_output_gradients) + assert torch.equal(result.weight_gradient, saved_weight_gradient) + with pytest.raises(FrozenInstanceError): + setattr(result, "weight_layout", "out_in") + + +def test_zero_gradient_has_finite_zero_errors() -> None: + result = _build_linear_gradient_factors( + torch.zeros(3, 2), torch.zeros(3, 4), torch.zeros(2, 4), weight_layout="in_out" + ) + + assert result.absolute_reconstruction_error == 0.0 + assert result.relative_reconstruction_error == 0.0 + + +def test_zero_reference_with_nonzero_reconstruction_has_finite_relative_error() -> None: + result = _build_linear_gradient_factors( + torch.tensor([[1.0, 0.0]]), + torch.tensor([[1.0, 0.0, 0.0]]), + torch.zeros(2, 3), + weight_layout="in_out", + ) + + assert result.absolute_reconstruction_error == 1.0 + assert result.relative_reconstruction_error == 1.0 + assert torch.isfinite(torch.tensor(result.relative_reconstruction_error)) + + +@pytest.mark.parametrize( + ("inputs", "gradients", "weight", "message"), + [ + (torch.randn(2), torch.randn(3, 4), torch.randn(2, 4), "rank 2"), + (torch.randn(3, 2), torch.randn(4, 4), torch.randn(2, 4), "same number"), + (torch.empty(0, 2), torch.empty(0, 4), torch.randn(2, 4), "empty"), + (torch.full((3, 2), float("nan")), torch.ones(3, 4), torch.ones(2, 4), "finite"), + (torch.ones(3, 2), torch.full((3, 4), float("inf")), torch.ones(2, 4), "finite"), + (torch.ones(3, 2), torch.ones(3, 4), torch.full((2, 4), float("nan")), "finite"), + ], +) +def test_factor_validation_errors(inputs, gradients, weight, message) -> None: + with pytest.raises(ValueError, match=message): + _build_linear_gradient_factors(inputs, gradients, weight, weight_layout="in_out") + + +def test_rejects_invalid_layout_and_integer_factors() -> None: + with pytest.raises(TypeError, match="parameter 'weight_layout'"): + _build_linear_gradient_factors( + torch.randn(3, 2), + torch.randn(3, 4), + torch.randn(2, 4), + weight_layout=cast(WeightLayout, "other"), + ) + with pytest.raises(TypeError, match="floating"): + _build_linear_gradient_factors( + torch.ones(3, 2, dtype=torch.long), + torch.randn(3, 4), + torch.randn(2, 4), + weight_layout="in_out", + ) + + +def test_rejects_non_tensor_factors() -> None: + with pytest.raises(TypeError, match="torch.Tensor"): + _build_linear_gradient_factors( + cast(torch.Tensor, "not a tensor"), + torch.randn(3, 4), + torch.randn(2, 4), + weight_layout="in_out", + ) + + +def test_rejects_values_that_overflow_during_float32_conversion() -> None: + with pytest.raises(ValueError, match="remain finite"): + _build_linear_gradient_factors( + torch.full((3, 2), 1e300, dtype=torch.float64), + torch.ones(3, 4, dtype=torch.float64), + torch.full((2, 4), 3e300, dtype=torch.float64), + weight_layout="in_out", + ) + + +def test_rejects_nonfinite_float32_reconstruction() -> None: + large = torch.full((2, 1), 3e38) + with pytest.raises(ValueError, match="reconstruction"): + _build_linear_gradient_factors( + large, + large, + torch.zeros(1, 1), + weight_layout="in_out", + ) + + +def test_vocabulary_rankings_match_topk_without_mutation() -> None: + logits = torch.tensor([[1.0, -3.0, 7.0, 2.0], [8.0, 0.5, -2.0, 4.0]]) + original = logits.clone() + + top = _rank_vocabulary_logits(logits, k=2, largest=True) + bottom = _rank_vocabulary_logits(logits, k=2, largest=False) + + assert top.indices.tolist() == [[2, 3], [0, 3]] + assert top.values.tolist() == [[7.0, 2.0], [8.0, 4.0]] + assert bottom.indices.tolist() == [[1, 0], [2, 1]] + assert torch.equal(logits, original) + + +def test_one_dimensional_vocabulary_ranking_is_detached_cpu_copy() -> None: + logits = torch.tensor([2.0, -4.0, 8.0, 1.0], requires_grad=True) + bottom = _rank_vocabulary_logits(logits, k=2, largest=False) + saved_values = bottom.values.clone() + + with torch.no_grad(): + logits.add_(100) + + assert bottom.indices.tolist() == [1, 3] + assert bottom.values.tolist() == [-4.0, 1.0] + assert bottom.values.device.type == "cpu" + assert bottom.values.grad_fn is None + assert torch.equal(bottom.values, saved_values) + + +@pytest.mark.parametrize("k", [0, 5, True]) +def test_vocabulary_ranking_rejects_invalid_k(k) -> None: + with pytest.raises(ValueError, match="k must"): + _rank_vocabulary_logits(torch.randn(2, 4), k=k, largest=True) + + +def test_vocabulary_ranking_runtime_typechecking_rejects_non_integer_k() -> None: + with pytest.raises(TypeError, match="parameter 'k'"): + _rank_vocabulary_logits(torch.randn(2, 4), k=cast(int, 1.5), largest=True) + + +@pytest.mark.parametrize("largest", [1, "yes", None]) +def test_vocabulary_ranking_rejects_non_boolean_largest(largest) -> None: + with pytest.raises(TypeError, match="parameter 'largest'"): + _rank_vocabulary_logits(torch.randn(2, 4), k=1, largest=cast(bool, largest)) + + +@pytest.mark.parametrize( + "logits", + [ + torch.randn(2, 3, 4), + torch.empty(2, 0), + torch.ones(4, dtype=torch.long), + torch.tensor([0.0, float("nan")]), + torch.tensor([0.0, float("inf")]), + ], +) +def test_vocabulary_ranking_rejects_invalid_logits(logits) -> None: + with pytest.raises((TypeError, ValueError)): + _rank_vocabulary_logits(logits, k=1, largest=True) + + +def test_vocabulary_ranking_rejects_non_tensor_logits() -> None: + with pytest.raises(TypeError, match="torch.Tensor"): + _rank_vocabulary_logits(cast(torch.Tensor, [1.0, 2.0]), k=1, largest=True) diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py new file mode 100644 index 000000000..01c83933b --- /dev/null +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -0,0 +1,171 @@ +"""Model-free contracts for Backward Lens gradient factors. + +The Backward Lens represents a linear weight gradient as a sum of token-position +outer products. Bridge capture and vocabulary projection are added in later +commits; this module starts with independently testable tensor algebra. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal + +import torch + +WeightLayout = Literal["in_out", "out_in"] + + +@dataclass(frozen=True) +class LinearGradientFactors: + """Detached factors and reconstruction for one linear weight gradient. + + ``forward_inputs`` and ``output_gradients`` have shapes ``[position, in]`` + and ``[position, out]``. Gradient tensors use the requested storage layout. + All tensors are cloned to CPU in float32 so the result owns no autograd graph. + """ + + forward_inputs: torch.Tensor + output_gradients: torch.Tensor + weight_gradient: torch.Tensor + reconstructed_gradient: torch.Tensor + absolute_reconstruction_error: float + relative_reconstruction_error: float + weight_layout: WeightLayout + + +@dataclass(frozen=True) +class VocabularyRanking: + """Owned CPU copies of signed vocabulary rankings with shape ``[..., k]``. + + ``values`` preserves the floating dtype and sign of ``logits``; ``indices`` + has dtype ``torch.int64``. Both tensors are detached. + """ + + values: torch.Tensor + indices: torch.Tensor + + +def _validate_floating_matrix(name: str, tensor: torch.Tensor) -> None: + if not isinstance(tensor, torch.Tensor): + raise TypeError(f"{name} must be a torch.Tensor") + if tensor.ndim != 2: + raise ValueError(f"{name} must be rank 2; got shape {tuple(tensor.shape)}") + if 0 in tensor.shape: + raise ValueError(f"{name} must have no empty dimensions; got shape {tuple(tensor.shape)}") + if not tensor.is_floating_point(): + raise TypeError(f"{name} must have a floating dtype; got {tensor.dtype}") + if not bool(torch.isfinite(tensor).all()): + raise ValueError(f"{name} must contain only finite values") + + +def _reconstruction_errors( + reference: torch.Tensor, reconstruction: torch.Tensor +) -> tuple[float, float]: + """Return max absolute and symmetric scale-aware relative errors. + + The relative error is ``||reference - reconstruction||_F`` divided by the + maximum of the two input Frobenius norms and float32 epsilon. This remains + finite when one or both gradients are zero. + """ + difference = reference - reconstruction + absolute = float(difference.abs().max()) + scale = torch.maximum(reference.norm(), reconstruction.norm()).clamp_min( + torch.finfo(reference.dtype).eps + ) + relative = float(difference.norm() / scale) + return absolute, relative + + +def _to_detached_float32(name: str, tensor: torch.Tensor) -> torch.Tensor: + """Detach and convert a validated tensor, rejecting float32 overflow.""" + converted = tensor.detach().float() + if not bool(torch.isfinite(converted).all()): + raise ValueError(f"{name} must remain finite when converted to float32") + return converted + + +def _build_linear_gradient_factors( + forward_inputs: torch.Tensor, + output_gradients: torch.Tensor, + weight_gradient: torch.Tensor, + *, + weight_layout: WeightLayout, +) -> LinearGradientFactors: + """Reconstruct a weight gradient from aligned token-position factors. + + Args: + forward_inputs: Linear inputs with shape ``[position, in_features]``. + output_gradients: Loss gradients with respect to linear outputs, shape + ``[position, out_features]``. + weight_gradient: Independently computed gradient in ``weight_layout``. + weight_layout: ``"in_out"`` for GPT-2 ``Conv1D`` storage or + ``"out_in"`` for ``torch.nn.Linear`` storage. + + Returns: + Detached factors, the independent gradient, its reconstruction, and + reconstruction errors, all on CPU with float32 tensor values. + """ + _validate_floating_matrix("forward_inputs", forward_inputs) + _validate_floating_matrix("output_gradients", output_gradients) + _validate_floating_matrix("weight_gradient", weight_gradient) + if weight_layout not in ("in_out", "out_in"): + raise ValueError("weight_layout must be 'in_out' or 'out_in'") + if forward_inputs.shape[0] != output_gradients.shape[0]: + raise ValueError( + "forward_inputs and output_gradients must have the same number of positions; " + f"got {forward_inputs.shape[0]} and {output_gradients.shape[0]}" + ) + devices = {forward_inputs.device, output_gradients.device, weight_gradient.device} + if len(devices) != 1: + raise ValueError( + "forward_inputs, output_gradients, and weight_gradient must share a device" + ) + + inputs = _to_detached_float32("forward_inputs", forward_inputs) + gradients = _to_detached_float32("output_gradients", output_gradients) + canonical = inputs.T @ gradients + reconstruction = canonical if weight_layout == "in_out" else canonical.T + reference = _to_detached_float32("weight_gradient", weight_gradient) + if not bool(torch.isfinite(reconstruction).all()): + raise ValueError("the float32 outer-product reconstruction must contain only finite values") + if reference.shape != reconstruction.shape: + raise ValueError( + f"weight_gradient shape {tuple(reference.shape)} does not match the " + f"{weight_layout} reconstruction shape {tuple(reconstruction.shape)}" + ) + absolute, relative = _reconstruction_errors(reference, reconstruction) + return LinearGradientFactors( + forward_inputs=inputs.cpu().clone(), + output_gradients=gradients.cpu().clone(), + weight_gradient=reference.cpu().clone(), + reconstructed_gradient=reconstruction.cpu().clone(), + absolute_reconstruction_error=absolute, + relative_reconstruction_error=relative, + weight_layout=weight_layout, + ) + + +def _rank_vocabulary_logits(logits: torch.Tensor, *, k: int, largest: bool) -> VocabularyRanking: + """Return largest or smallest vocabulary logits and token ids per row. + + Ordering among exactly tied logits is intentionally unspecified and follows + :func:`torch.topk`. + """ + if not isinstance(logits, torch.Tensor): + raise TypeError("logits must be a torch.Tensor") + if logits.ndim not in (1, 2) or logits.shape[-1] == 0: + raise ValueError( + f"logits must have shape [vocab] or [position, vocab]; got {tuple(logits.shape)}" + ) + if not logits.is_floating_point(): + raise TypeError(f"logits must have a floating dtype; got {logits.dtype}") + if not bool(torch.isfinite(logits).all()): + raise ValueError("logits must contain only finite values") + if not isinstance(largest, bool): + raise TypeError(f"largest must be a bool; got {type(largest).__name__}") + if isinstance(k, bool) or not isinstance(k, int) or not 1 <= k <= logits.shape[-1]: + raise ValueError(f"k must be in [1, {logits.shape[-1]}]; got {k!r}") + ranked = torch.topk(logits.detach(), k=k, dim=-1, largest=largest, sorted=True) + return VocabularyRanking( + values=ranked.values.cpu().clone(), indices=ranked.indices.cpu().clone() + ) From 06008f2c81c715fb72692de7ec11692f31044751 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Mon, 24 Aug 2026 06:27:59 -0700 Subject: [PATCH 02/17] feat(backward_lens): capture GPT-2 MLP gradient factors - Validate raw GPT-2 Bridge models, prompts, targets, layers, and original Conv1D weights. - Capture both MLP projections with one forward pass and one autograd call. - Reconstruct exact weight gradients and return detached CPU-owned factors. - Preserve model state, RNG, gradients, and existing hooks with comprehensive integration coverage. --- tests/integration/test_backward_lens.py | 426 ++++++++++++++++++ .../tools/analysis/backward_lens.py | 303 ++++++++++++- 2 files changed, 725 insertions(+), 4 deletions(-) create mode 100644 tests/integration/test_backward_lens.py diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py new file mode 100644 index 000000000..c36bdb2c2 --- /dev/null +++ b/tests/integration/test_backward_lens.py @@ -0,0 +1,426 @@ +"""Integration tests for Backward Lens capture on a raw GPT-2 Bridge.""" + +import warnings +from collections.abc import Sequence + +import pytest +import torch +import torch.nn.functional as F + +PROMPT = "The capital of France is" +TARGET = " Paris" +LAYERS = (0, 11) + +DEVICE_DTYPE_CASES = [pytest.param("cpu", torch.bfloat16, id="cpu-bfloat16")] +if torch.cuda.is_available(): + DEVICE_DTYPE_CASES.append(pytest.param("cuda", torch.float16, id="cuda-float16")) +if torch.backends.mps.is_available(): + DEVICE_DTYPE_CASES.append(pytest.param("mps", torch.float32, id="mps-float32")) + + +@pytest.fixture(scope="module") +def gpt2_bridge(): + from transformer_lens.model_bridge import TransformerBridge + + return TransformerBridge.boot_transformers("gpt2", device="cpu", dtype=torch.float32) + + +@pytest.fixture(scope="module") +def gradient_capture(gpt2_bridge): + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + return _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, LAYERS) + + +def _projection_hook_snapshots(model, layer: int) -> list[tuple[int, ...]]: + mlp = model.blocks[layer].mlp + projections = (getattr(mlp, "in"), mlp.out) + return [ + tuple(hook_point._forward_hooks) + for projection in projections + for hook_point in (projection.hook_in, projection.hook_out) + ] + + +def test_real_gpt2_factors_reconstruct_both_mlp_weight_gradients( + gradient_capture, gpt2_bridge +) -> None: + assert gradient_capture.target_token_id == 6342 + assert gradient_capture.loss > 0 + assert gradient_capture.prompt_token_ids.shape == (1, 6) + assert gradient_capture.prompt_token_ids.device.type == "cpu" + expected_tokens = gpt2_bridge.to_tokens(PROMPT) + assert torch.equal(gradient_capture.prompt_token_ids, expected_tokens) + with torch.no_grad(): + logits = gpt2_bridge(expected_tokens) + expected_loss = F.cross_entropy( + logits[:, -1, :], torch.tensor([gradient_capture.target_token_id]) + ) + assert gradient_capture.loss == pytest.approx(float(expected_loss), abs=1e-6, rel=1e-6) + assert [result.layer for result in gradient_capture.layers] == list(LAYERS) + + for result in gradient_capture.layers: + first = result.input_projection + second = result.output_projection + assert first.forward_inputs.shape == (6, 768) + assert first.output_gradients.shape == (6, 3072) + assert first.weight_gradient.shape == (768, 3072) + assert second.forward_inputs.shape == (6, 3072) + assert second.output_gradients.shape == (6, 768) + assert second.weight_gradient.shape == (3072, 768) + for factors in (first, second): + assert factors.weight_layout == "in_out" + assert factors.reconstructed_gradient.shape == factors.weight_gradient.shape + for tensor in ( + factors.forward_inputs, + factors.output_gradients, + factors.weight_gradient, + factors.reconstructed_gradient, + ): + assert tensor.dtype == torch.float32 + assert tensor.device.type == "cpu" + assert tensor.grad_fn is None + assert torch.isfinite(tensor).all() + torch.testing.assert_close( + factors.reconstructed_gradient, + factors.weight_gradient, + atol=2e-6, + rtol=2e-5, + ) + assert factors.absolute_reconstruction_error <= 2e-6 + assert factors.relative_reconstruction_error <= 2e-5 + + +@pytest.mark.parametrize("projection", ["input_projection", "output_projection"]) +def test_final_layer_has_the_expected_rank_one_position_structure( + gradient_capture, projection: str +) -> None: + last_layer = gradient_capture.layers[-1] + factors = getattr(last_layer, projection) + row_norms = factors.output_gradients.norm(dim=-1) + relative_earlier_norm = row_norms[:-1].max() / row_norms[-1].clamp_min( + torch.finfo(row_norms.dtype).eps + ) + + assert relative_earlier_norm <= 1e-6 + assert int(torch.linalg.matrix_rank(factors.output_gradients)) == 1 + + +@pytest.mark.parametrize( + ("prompt", "target", "layers", "error", "match"), + [ + ("", TARGET, [0], ValueError, "prompt must not be empty"), + (PROMPT, "", [0], ValueError, "exactly one token"), + (PROMPT, " New York", [0], ValueError, "got 2 tokens"), + (PROMPT, TARGET, [], ValueError, "at least one"), + (PROMPT, TARGET, [0, 0], ValueError, "duplicate"), + (PROMPT, TARGET, [-1], ValueError, "must be in"), + (PROMPT, TARGET, [12], ValueError, "must be in"), + (PROMPT, TARGET, [True], TypeError, "layer"), + ], +) +def test_capture_rejects_invalid_analysis_inputs( + gpt2_bridge, + prompt: str, + target: str, + layers: Sequence[int], + error: type[Exception], + match: str, +) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + with pytest.raises(error, match=match): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, prompt, target, layers) + + +def test_capture_rejects_non_bridge_and_non_raw_states(gpt2_bridge, monkeypatch) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + with pytest.raises(TypeError, match="TransformerBridge only"): + _capture_gpt2_mlp_gradient_factors(object(), PROMPT, TARGET, [0]) + monkeypatch.setattr(gpt2_bridge, "compatibility_mode", True) + with pytest.raises(ValueError, match="compatibility mode"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + monkeypatch.setattr(gpt2_bridge, "compatibility_mode", False) + monkeypatch.setattr(gpt2_bridge, "_weights_processed", True) + with pytest.raises(ValueError, match="processed"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + monkeypatch.setattr(gpt2_bridge, "_weights_processed", False) + adapter = gpt2_bridge.adapter + monkeypatch.setattr(gpt2_bridge, "adapter", object()) + with pytest.raises(ValueError, match="GPT2ArchitectureAdapter"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + monkeypatch.setattr(gpt2_bridge, "adapter", adapter) + monkeypatch.setattr(gpt2_bridge.cfg, "gated_mlp", True) + with pytest.raises(ValueError, match="non-gated"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + monkeypatch.setattr(gpt2_bridge.cfg, "gated_mlp", False) + monkeypatch.setattr(gpt2_bridge, "tokenizer", None) + with pytest.raises(ValueError, match="tokenizer"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + + +def test_capture_rejects_a_frozen_original_weight(gpt2_bridge) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + weight = getattr(gpt2_bridge.blocks[0].mlp, "in").original_component.weight + original_requires_grad = weight.requires_grad + weight.requires_grad_(False) + try: + with pytest.raises(ValueError, match="trainable Parameter"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + finally: + weight.requires_grad_(original_requires_grad) + + +def test_capture_preserves_model_state_hooks_and_uses_one_autograd_call( + gpt2_bridge, monkeypatch +) -> None: + from transformer_lens.model_bridge.generalized_components.normalization import ( + NATIVE_PATH_BWD_FALLBACK_WARNING, + NATIVE_PATH_EDIT_FALLBACK_WARNING, + ) + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + mlp = gpt2_bridge.blocks[0].mlp + projections = (getattr(mlp, "in"), mlp.out) + weights = [projection.original_component.weight for projection in projections] + saved_grads = [weight.grad for weight in weights] + weight_copies = [weight.detach().clone() for weight in weights] + training = gpt2_bridge.training + outer_rng = torch.random.get_rng_state() + hook_calls = 0 + autograd_calls = 0 + original_grad = torch.autograd.grad + + def existing_hook(_tensor, hook=None) -> None: + nonlocal hook_calls + hook_calls += 1 + + def counting_grad(*args, **kwargs): + nonlocal autograd_calls + autograd_calls += 1 + return original_grad(*args, **kwargs) + + hook_point = mlp.out.hook_out + hook_point.add_hook(existing_hook) + existing_handle = hook_point.fwd_hooks[-1] + try: + gpt2_bridge.train(True) + torch.manual_seed(1234) + rng_before = torch.random.get_rng_state() + for index, weight in enumerate(weights): + weight.grad = torch.full_like(weight, index + 1.0) + grad_copies = [weight.grad.clone() for weight in weights] + requires_grad = [weight.requires_grad for weight in weights] + hooks_before = _projection_hook_snapshots(gpt2_bridge, 0) + monkeypatch.setattr(torch.autograd, "grad", counting_grad) + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + result = _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + + assert len(result.layers) == 1 + assert autograd_calls == 1 + assert hook_calls == 1 + assert not any( + str(warning.message) + in (NATIVE_PATH_BWD_FALLBACK_WARNING, NATIVE_PATH_EDIT_FALLBACK_WARNING) + for warning in caught + ) + assert torch.equal(torch.random.get_rng_state(), rng_before) + assert gpt2_bridge.training is True + assert _projection_hook_snapshots(gpt2_bridge, 0) == hooks_before + for weight, saved_weight, saved_grad, expected_requires_grad in zip( + weights, weight_copies, grad_copies, requires_grad, strict=True + ): + assert torch.equal(weight, saved_weight) + assert torch.equal(weight.grad, saved_grad) + assert weight.requires_grad is expected_requires_grad + finally: + existing_handle.hook.remove() + hook_point.fwd_hooks.remove(existing_handle) + + for weight, saved_grad in zip(weights, saved_grads, strict=True): + weight.grad = saved_grad + gpt2_bridge.train(training) + torch.random.set_rng_state(outer_rng) + + +def test_capture_reconstructs_with_existing_activation_edits(gpt2_bridge) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + mlp = gpt2_bridge.blocks[0].mlp + input_hook_point = getattr(mlp, "in").hook_in + output_hook_point = mlp.out.hook_out + + def scale_input(tensor, hook=None): + return tensor * 0.5 + + def shift_output(tensor, hook=None): + return tensor + 0.01 + + input_hook_point.add_hook(scale_input) + input_handle = input_hook_point.fwd_hooks[-1] + output_hook_point.add_hook(shift_output) + output_handle = output_hook_point.fwd_hooks[-1] + try: + result = _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + assert input_handle in input_hook_point.fwd_hooks + assert output_handle in output_hook_point.fwd_hooks + finally: + input_handle.hook.remove() + input_hook_point.fwd_hooks.remove(input_handle) + output_handle.hook.remove() + output_hook_point.fwd_hooks.remove(output_handle) + + for factors in ( + result.layers[0].input_projection, + result.layers[0].output_projection, + ): + torch.testing.assert_close( + factors.reconstructed_gradient, + factors.weight_gradient, + atol=2e-6, + rtol=2e-5, + ) + + +def test_capture_cleans_owned_hooks_when_autograd_raises(gpt2_bridge, monkeypatch) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + hook_point = gpt2_bridge.blocks[0].mlp.out.hook_out + hook_calls = 0 + + def existing_hook(tensor, hook=None) -> None: + nonlocal hook_calls + hook_calls += 1 + + def fail_autograd(*args, **kwargs): + raise RuntimeError("forced autograd failure") + + hook_point.add_hook(existing_hook) + existing_handle = hook_point.fwd_hooks[-1] + hooks_before = _projection_hook_snapshots(gpt2_bridge, 0) + monkeypatch.setattr(torch.autograd, "grad", fail_autograd) + try: + with pytest.raises(RuntimeError, match="forced autograd failure"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + assert hook_calls == 1 + assert existing_handle in hook_point.fwd_hooks + assert _projection_hook_snapshots(gpt2_bridge, 0) == hooks_before + finally: + existing_handle.hook.remove() + hook_point.fwd_hooks.remove(existing_handle) + + +def test_capture_removes_only_owned_hooks_when_forward_fails(gpt2_bridge) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + mlp = gpt2_bridge.blocks[0].mlp + projections = (getattr(mlp, "in"), mlp.out) + weights = [projection.original_component.weight for projection in projections] + saved_grads = [weight.grad for weight in weights] + weight_copies = [weight.detach().clone() for weight in weights] + training = gpt2_bridge.training + outer_rng = torch.random.get_rng_state() + hook_point = mlp.out.hook_out + + def fail(_module, _inputs, _output) -> None: + raise RuntimeError("forced existing-hook failure") + + existing_handle = hook_point.register_forward_hook(fail) + try: + gpt2_bridge.train(True) + torch.manual_seed(5678) + rng_before = torch.random.get_rng_state() + for index, weight in enumerate(weights): + weight.grad = torch.full_like(weight, index + 3.0) + grad_copies = [weight.grad.clone() for weight in weights] + requires_grad = [weight.requires_grad for weight in weights] + hooks_before = _projection_hook_snapshots(gpt2_bridge, 0) + with pytest.raises(RuntimeError, match="forced existing-hook failure"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + assert _projection_hook_snapshots(gpt2_bridge, 0) == hooks_before + assert torch.equal(torch.random.get_rng_state(), rng_before) + assert gpt2_bridge.training is True + for weight, saved_weight, saved_grad, expected_requires_grad in zip( + weights, weight_copies, grad_copies, requires_grad, strict=True + ): + assert torch.equal(weight, saved_weight) + assert torch.equal(weight.grad, saved_grad) + assert weight.requires_grad is expected_requires_grad + finally: + existing_handle.remove() + for weight, saved_grad in zip(weights, saved_grads, strict=True): + weight.grad = saved_grad + gpt2_bridge.train(training) + torch.random.set_rng_state(outer_rng) + + +@pytest.mark.parametrize(("device", "dtype"), DEVICE_DTYPE_CASES) +def test_tiny_gpt2_capture_supports_available_devices_and_reduced_precision( + gpt2_bridge, device: str, dtype: torch.dtype +) -> None: + from transformers import GPT2Config, GPT2LMHeadModel + + from transformer_lens.model_bridge import TransformerBridge + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + config = GPT2Config( + n_layer=2, + n_head=4, + n_embd=32, + n_inner=64, + n_positions=32, + vocab_size=gpt2_bridge.cfg.d_vocab, + resid_pdrop=0.1, + embd_pdrop=0.1, + attn_pdrop=0.1, + ) + hf_model = GPT2LMHeadModel(config).to(device=device, dtype=dtype).eval() + bridge = TransformerBridge.boot_transformers( + "gpt2", + hf_model=hf_model, + tokenizer=gpt2_bridge.tokenizer, + dtype=dtype, + ) + bridge.train(True) + if device == "cuda": + rng_before = torch.cuda.get_rng_state() + elif device == "mps": + rng_before = torch.mps.get_rng_state() + else: + rng_before = torch.random.get_rng_state() + + result = _capture_gpt2_mlp_gradient_factors(bridge, "Small test", " token", [0, 1]) + + if device == "cuda": + rng_after = torch.cuda.get_rng_state() + elif device == "mps": + rng_after = torch.mps.get_rng_state() + else: + rng_after = torch.random.get_rng_state() + assert torch.equal(rng_after, rng_before) + for layer in result.layers: + for factors in (layer.input_projection, layer.output_projection): + assert torch.isfinite(factors.weight_gradient).all() + assert factors.relative_reconstruction_error <= 5e-2 diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py index 01c83933b..e250f0c20 100644 --- a/transformer_lens/tools/analysis/backward_lens.py +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -1,16 +1,19 @@ -"""Model-free contracts for Backward Lens gradient factors. +"""Backward Lens gradient-factor contracts and GPT-2 Bridge capture. The Backward Lens represents a linear weight gradient as a sum of token-position -outer products. Bridge capture and vocabulary projection are added in later -commits; this module starts with independently testable tensor algebra. +outer products. This module provides independently testable tensor algebra and +the raw GPT-2 capture path; vocabulary projection is added separately. """ from __future__ import annotations +from collections.abc import Callable, Iterator, Sequence +from contextlib import contextmanager from dataclasses import dataclass -from typing import Literal +from typing import Any, Literal import torch +import torch.nn.functional as F WeightLayout = Literal["in_out", "out_in"] @@ -45,6 +48,29 @@ class VocabularyRanking: indices: torch.Tensor +@dataclass(frozen=True) +class _GPT2LayerGradientFactors: + """Detached gradient factors for both MLP projections in one GPT-2 layer.""" + + layer: int + input_projection: LinearGradientFactors + output_projection: LinearGradientFactors + + +@dataclass(frozen=True) +class _GPT2GradientCapture: + """Private Commit-2 result for one GPT-2 next-token loss. + + Tensor fields are detached, owned CPU copies. Public vocabulary-facing result + contracts are introduced with the projection API. + """ + + prompt_token_ids: torch.Tensor + target_token_id: int + loss: float + layers: tuple[_GPT2LayerGradientFactors, ...] + + def _validate_floating_matrix(name: str, tensor: torch.Tensor) -> None: if not isinstance(tensor, torch.Tensor): raise TypeError(f"{name} must be a torch.Tensor") @@ -169,3 +195,272 @@ def _rank_vocabulary_logits(logits: torch.Tensor, *, k: int, largest: bool) -> V return VocabularyRanking( values=ranked.values.cpu().clone(), indices=ranked.indices.cpu().clone() ) + + +def _validate_requested_layers(model: Any, layers: Sequence[int]) -> tuple[int, ...]: + if isinstance(layers, (str, bytes)) or not isinstance(layers, Sequence): + raise TypeError("layers must be a sequence of integer layer indices") + requested = tuple(layers) + if not requested: + raise ValueError("layers must contain at least one layer index") + for layer in requested: + if isinstance(layer, bool) or not isinstance(layer, int): + raise TypeError(f"each layer must be an integer; got {layer!r}") + if len(set(requested)) != len(requested): + raise ValueError("layers must not contain duplicate indices") + n_layers = int(model.cfg.n_layers) + invalid = [layer for layer in requested if not 0 <= layer < n_layers] + if invalid: + raise ValueError(f"layers must be in [0, {n_layers - 1}]; got {invalid}") + return requested + + +def _require_raw_gpt2_bridge(model: Any) -> None: + """Require the raw GPT-2 Bridge capabilities used by gradient capture.""" + from transformer_lens.model_bridge import TransformerBridge + from transformer_lens.model_bridge.supported_architectures.gpt2 import ( + GPT2ArchitectureAdapter, + ) + + if not isinstance(model, TransformerBridge): + raise TypeError( + "Backward Lens supports TransformerBridge only; load GPT-2 with " + "TransformerBridge.boot_transformers(...)." + ) + if getattr(model, "compatibility_mode", False): + raise ValueError( + "Backward Lens requires a raw TransformerBridge; compatibility mode is enabled" + ) + if getattr(model, "_weights_processed", False): + raise ValueError( + "Backward Lens requires original GPT-2 weights; this Bridge processed its weights" + ) + if not isinstance(model.adapter, GPT2ArchitectureAdapter): + raise ValueError( + "Backward Lens currently supports the GPT2ArchitectureAdapter only; " + f"got {type(model.adapter).__name__}" + ) + if bool(getattr(model.cfg, "gated_mlp", False)): + raise ValueError("Backward Lens currently requires dense, non-gated GPT-2 MLPs") + if model.tokenizer is None: + raise ValueError("Backward Lens requires a GPT-2 Bridge with a tokenizer") + for component in ("blocks", "ln_final", "unembed"): + if not hasattr(model, component): + raise ValueError(f"Backward Lens requires the standard {component} component") + + +def _get_gpt2_mlp_projections(model: Any, layers: tuple[int, ...]) -> dict[int, tuple[Any, Any]]: + """Return validated live GPT-2 Conv1D input/output projection bridges.""" + from transformers.pytorch_utils import Conv1D + + from transformer_lens.hook_points import HookPoint + from transformer_lens.model_bridge.generalized_components import ( + LinearBridge, + MLPBridge, + ) + + expected_shapes = ( + (int(model.cfg.d_model), int(model.cfg.d_mlp)), + (int(model.cfg.d_mlp), int(model.cfg.d_model)), + ) + projections: dict[int, tuple[Any, Any]] = {} + for layer in layers: + mlp = model.blocks[layer].mlp + if not isinstance(mlp, MLPBridge) or getattr(mlp, "gate", None) is not None: + raise ValueError(f"layer {layer} must have a dense, non-gated MLPBridge") + pair = (getattr(mlp, "in", None), getattr(mlp, "out", None)) + for name, projection, expected_shape in zip( + ("input", "output"), pair, expected_shapes, strict=True + ): + if not isinstance(projection, LinearBridge): + raise ValueError(f"layer {layer} {name} projection must be a LinearBridge") + if not isinstance(projection.original_component, Conv1D): + raise ValueError(f"layer {layer} {name} projection must wrap GPT-2 Conv1D") + weight = projection.original_component.weight + if not isinstance(weight, torch.nn.Parameter) or not weight.requires_grad: + raise ValueError( + f"layer {layer} {name} original weight must be a trainable Parameter" + ) + if not weight.is_floating_point() or tuple(weight.shape) != expected_shape: + raise ValueError( + f"layer {layer} {name} weight must have shape {expected_shape} " + f"and floating dtype; got {tuple(weight.shape)} and {weight.dtype}" + ) + if not isinstance(projection.hook_in, HookPoint) or not isinstance( + projection.hook_out, HookPoint + ): + raise ValueError(f"layer {layer} {name} projection is missing Bridge hook points") + projections[layer] = pair + return projections + + +def _capture_once( + captured: dict[tuple[int, str, str], torch.Tensor], key: tuple[int, str, str] +) -> Callable[[torch.nn.Module, tuple[Any, ...], Any], None]: + """Build a non-modifying PyTorch hook that records one tensor.""" + + def capture(_module: torch.nn.Module, _inputs: tuple[Any, ...], output: Any) -> None: + if key in captured: + raise RuntimeError(f"Backward Lens hook {key} fired more than once") + if not isinstance(output, torch.Tensor): + raise RuntimeError(f"Backward Lens hook {key} returned a non-tensor output") + captured[key] = output + + return capture + + +@contextmanager +def _capture_projection_tensors( + projections: dict[int, tuple[Any, Any]], +) -> Iterator[dict[tuple[int, str, str], torch.Tensor]]: + """Capture exact linear boundaries while preserving every pre-existing hook.""" + captured: dict[tuple[int, str, str], torch.Tensor] = {} + handles: list[Any] = [] + try: + for layer, pair in projections.items(): + for name, projection in zip(("input", "output"), pair, strict=True): + input_key = (layer, name, "forward_input") + output_key = (layer, name, "output") + # Existing hook_in edits must run first so this is the actual linear input. + handles.append( + projection.hook_in.register_forward_hook(_capture_once(captured, input_key)) + ) + # Capture the raw linear output before any existing hook_out edits. + handles.append( + projection.hook_out.register_forward_hook( + _capture_once(captured, output_key), prepend=True + ) + ) + yield captured + finally: + for handle in reversed(handles): + handle.remove() + + +def _single_batch_matrix(name: str, tensor: torch.Tensor) -> torch.Tensor: + if tensor.ndim != 3 or tensor.shape[0] != 1: + raise RuntimeError( + f"{name} must have shape [1, position, width]; got {tuple(tensor.shape)}" + ) + return tensor[0] + + +@contextmanager +def _preserve_model_rng(model: Any) -> Iterator[None]: + """Preserve CPU and every CUDA/MPS RNG used by the wrapped model.""" + parameter_devices = {parameter.device for parameter in model.original_model.parameters()} + cuda_devices = sorted( + { + device.index + for device in parameter_devices + if device.type == "cuda" and device.index is not None + } + ) + uses_mps = any(device.type == "mps" for device in parameter_devices) + mps_state = torch.mps.get_rng_state() if uses_mps else None + try: + with torch.random.fork_rng(devices=cuda_devices): + yield + finally: + if mps_state is not None: + torch.mps.set_rng_state(mps_state) + + +def _capture_gpt2_mlp_gradient_factors( + model: Any, + prompt: str, + target_token: str, + layers: Sequence[int], +) -> _GPT2GradientCapture: + """Capture exact GPT-2 MLP weight-gradient factors for one next-token loss. + + The analysis performs one grad-enabled forward and exactly one + :func:`torch.autograd.grad` call. It does not call ``backward``, touch + parameter ``.grad`` buffers, change training state, or remove caller hooks. + """ + _require_raw_gpt2_bridge(model) + requested_layers = _validate_requested_layers(model, layers) + projections = _get_gpt2_mlp_projections(model, requested_layers) + if not isinstance(prompt, str): + raise TypeError("prompt must be a string") + if prompt == "": + raise ValueError("prompt must not be empty") + if not isinstance(target_token, str): + raise TypeError("target_token must be a string") + + prompt_tokens = model.to_tokens(prompt) + target_tokens = model.to_tokens(target_token, prepend_bos=False) + if prompt_tokens.ndim != 2 or prompt_tokens.shape[0] != 1 or prompt_tokens.shape[1] == 0: + raise ValueError("prompt must tokenize to one non-empty sequence") + if target_tokens.ndim != 2 or tuple(target_tokens.shape) != (1, 1): + count = int(target_tokens.numel()) + raise ValueError( + "target_token must encode to exactly one token without BOS; " f"got {count} tokens" + ) + target_token_id = int(target_tokens.item()) + input_device = next(model.original_model.parameters()).device + prompt_tokens = prompt_tokens.to(input_device) + weights = [ + projection.original_component.weight + for layer in requested_layers + for projection in projections[layer] + ] + with _preserve_model_rng(model), torch.enable_grad(): + with _capture_projection_tensors(projections) as captured: + logits = model(prompt_tokens) + if not isinstance(logits, torch.Tensor) or logits.ndim != 3 or logits.shape[0] != 1: + raise RuntimeError( + "GPT-2 Bridge must return logits with shape [1, position, vocab]" + ) + target = torch.tensor([target_token_id], device=logits.device) + loss = F.cross_entropy(logits[:, -1, :], target) + if not bool(torch.isfinite(loss)): + raise ValueError("the next-token loss must be finite") + outputs = [ + captured[(layer, name, "output")] + for layer in requested_layers + for name in ("input", "output") + ] + gradients = torch.autograd.grad(loss, (*outputs, *weights), allow_unused=False) + + output_gradients = gradients[: len(outputs)] + weight_gradients = gradients[len(outputs) :] + layer_results = [] + for index, layer in enumerate(requested_layers): + input_offset = 2 * index + input_factors = _build_linear_gradient_factors( + _single_batch_matrix( + f"layer {layer} input projection input", + captured[(layer, "input", "forward_input")], + ), + _single_batch_matrix( + f"layer {layer} input projection gradient", output_gradients[input_offset] + ), + weight_gradients[input_offset], + weight_layout="in_out", + ) + output_factors = _build_linear_gradient_factors( + _single_batch_matrix( + f"layer {layer} output projection input", + captured[(layer, "output", "forward_input")], + ), + _single_batch_matrix( + f"layer {layer} output projection gradient", + output_gradients[input_offset + 1], + ), + weight_gradients[input_offset + 1], + weight_layout="in_out", + ) + layer_results.append( + _GPT2LayerGradientFactors( + layer=layer, + input_projection=input_factors, + output_projection=output_factors, + ) + ) + return _GPT2GradientCapture( + prompt_token_ids=prompt_tokens.detach().cpu().clone(), + target_token_id=target_token_id, + loss=float(loss.detach()), + layers=tuple(layer_results), + ) From a018bcc0586a4b89103c0bf9bd71dbb3c066f4f8 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Mon, 24 Aug 2026 09:13:58 -0700 Subject: [PATCH 03/17] feat(backward_lens): project GPT-2 MLP gradient factors into vocabulary space - Add public BackwardLens.analyze(prompt, target_token, layers, normalized=False) returning detached, CPU-owned result dataclasses. - Project residual-width factors through fresh ln_final and unembed, with an optional Normalized Logit Lens for low-norm factors. - Expose factor norms, zero-norm masks, signed top/bottom vocabulary rankings, token decoding, and raw-gradient target ranks. - Export the public result contracts and cover the API with model-free unit tests and GPT-2 integration tests. --- tests/integration/test_backward_lens.py | 119 ++++++- tests/unit/tools/test_backward_lens.py | 136 ++++++++ transformer_lens/tools/analysis/__init__.py | 26 ++ .../tools/analysis/backward_lens.py | 299 +++++++++++++++++- 4 files changed, 570 insertions(+), 10 deletions(-) diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index c36bdb2c2..d993021e1 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -34,6 +34,13 @@ def gradient_capture(gpt2_bridge): return _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, LAYERS) +@pytest.fixture(scope="module") +def backward_result(gpt2_bridge): + from transformer_lens.tools.analysis import BackwardLens + + return BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, LAYERS, normalized=True) + + def _projection_hook_snapshots(model, layer: int) -> list[tuple[int, ...]]: mlp = model.blocks[layer].mlp projections = (getattr(mlp, "in"), mlp.out) @@ -108,6 +115,106 @@ def test_final_layer_has_the_expected_rank_one_position_structure( assert int(torch.linalg.matrix_rank(factors.output_gradients)) == 1 +def test_public_result_metadata_and_error_summaries(backward_result) -> None: + from transformer_lens.tools.analysis import BackwardLensResult + + assert isinstance(backward_result, BackwardLensResult) + assert backward_result.prompt == PROMPT + assert backward_result.target_token == TARGET + assert backward_result.target_token_id == 6342 + assert backward_result.prompt_token_ids.shape == (6,) + assert [layer.layer for layer in backward_result.layers] == list(LAYERS) + assert backward_result.includes_normalized_logits is True + assert backward_result.max_absolute_reconstruction_error <= 2e-6 + assert backward_result.max_relative_reconstruction_error <= 2e-5 + assert backward_result.layer(11) is backward_result.layers[-1] + assert not hasattr(backward_result, "model") + with pytest.raises(KeyError, match="was not analyzed"): + backward_result.layer(5) + + +def test_public_projections_match_fresh_model_readout(backward_result, gpt2_bridge) -> None: + unembed_weight = gpt2_bridge.W_U + for layer in backward_result.layers: + matrices = ( + (layer.input_projection, layer.input_projection.factors.forward_inputs), + (layer.output_projection, layer.output_projection.factors.output_gradients), + ) + for matrix, rows in matrices: + rows_on_model = rows.to( + device=unembed_weight.device, dtype=unembed_weight.dtype + ).unsqueeze(0) + with torch.no_grad(): + direct = gpt2_bridge.unembed(gpt2_bridge.ln_final(rows_on_model)).squeeze(0) + torch.testing.assert_close(matrix.vocabulary_logits, direct.float().cpu()) + + norms = rows.norm(dim=-1) + zero_mask = norms == 0 + normalized_rows = rows / torch.where( + zero_mask, torch.ones_like(norms), norms + ).unsqueeze(-1) + with torch.no_grad(): + direct_normalized = gpt2_bridge.unembed( + gpt2_bridge.ln_final( + normalized_rows.to( + device=unembed_weight.device, + dtype=unembed_weight.dtype, + ).unsqueeze(0) + ) + ).squeeze(0) + assert matrix.normalized_vocabulary_logits is not None + torch.testing.assert_close( + matrix.normalized_vocabulary_logits, + direct_normalized.float().cpu(), + ) + torch.testing.assert_close(matrix.factor_norms, norms) + assert torch.equal(matrix.zero_norm_mask, zero_mask) + for logits in (matrix.vocabulary_logits, matrix.normalized_vocabulary_logits): + assert logits.shape == (6, gpt2_bridge.cfg.d_vocab) + assert logits.dtype == torch.float32 + assert logits.device.type == "cpu" + assert logits.grad_fn is None + assert torch.isfinite(logits).all() + + +def test_public_rankings_decoding_and_target_ranks(backward_result, gpt2_bridge) -> None: + output = backward_result.layer(11).output_projection + direct_top = torch.topk(output.vocabulary_logits, k=3, dim=-1, largest=True) + direct_bottom = torch.topk(output.vocabulary_logits, k=3, dim=-1, largest=False) + + assert torch.equal(output.top(k=3).indices, direct_top.indices) + assert torch.equal(output.bottom(k=3).indices, direct_bottom.indices) + assert output.top_tokens(gpt2_bridge.tokenizer, k=3)[0] == [ + gpt2_bridge.tokenizer.decode([token_id]) for token_id in direct_top.indices[0].tolist() + ] + assert output.bottom_tokens(gpt2_bridge.tokenizer, k=3)[0] == [ + gpt2_bridge.tokenizer.decode([token_id]) for token_id in direct_bottom.indices[0].tolist() + ] + target_ranks = output.gradient_descent_target_ranks(backward_result.target_token_id) + normalized_target_ranks = output.gradient_descent_target_ranks( + backward_result.target_token_id, normalized=True + ) + assert target_ranks.shape == (6,) + assert normalized_target_ranks.shape == (6,) + assert target_ranks.dtype == torch.int64 + + +def test_public_api_defaults_to_raw_projection_only(gpt2_bridge) -> None: + from transformer_lens.tools.analysis import BackwardLens + + result = BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, [0]) + assert result.includes_normalized_logits is False + for matrix in ( + result.layers[0].input_projection, + result.layers[0].output_projection, + ): + assert matrix.normalized_vocabulary_logits is None + with pytest.raises(ValueError, match="were not requested"): + matrix.logits(normalized=True) + with pytest.raises(TypeError): + BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, [0], normalized=1) + + @pytest.mark.parametrize( ("prompt", "target", "layers", "error", "match"), [ @@ -381,9 +488,7 @@ def test_tiny_gpt2_capture_supports_available_devices_and_reduced_precision( from transformers import GPT2Config, GPT2LMHeadModel from transformer_lens.model_bridge import TransformerBridge - from transformer_lens.tools.analysis.backward_lens import ( - _capture_gpt2_mlp_gradient_factors, - ) + from transformer_lens.tools.analysis import BackwardLens config = GPT2Config( n_layer=2, @@ -411,7 +516,7 @@ def test_tiny_gpt2_capture_supports_available_devices_and_reduced_precision( else: rng_before = torch.random.get_rng_state() - result = _capture_gpt2_mlp_gradient_factors(bridge, "Small test", " token", [0, 1]) + result = BackwardLens(bridge).analyze("Small test", " token", [0, 1], normalized=True) if device == "cuda": rng_after = torch.cuda.get_rng_state() @@ -421,6 +526,10 @@ def test_tiny_gpt2_capture_supports_available_devices_and_reduced_precision( rng_after = torch.random.get_rng_state() assert torch.equal(rng_after, rng_before) for layer in result.layers: - for factors in (layer.input_projection, layer.output_projection): + for matrix in (layer.input_projection, layer.output_projection): + factors = matrix.factors assert torch.isfinite(factors.weight_gradient).all() assert factors.relative_reconstruction_error <= 5e-2 + assert torch.isfinite(matrix.vocabulary_logits).all() + assert matrix.normalized_vocabulary_logits is not None + assert torch.isfinite(matrix.normalized_vocabulary_logits).all() diff --git a/tests/unit/tools/test_backward_lens.py b/tests/unit/tools/test_backward_lens.py index 53c04dd0f..4939b9465 100644 --- a/tests/unit/tools/test_backward_lens.py +++ b/tests/unit/tools/test_backward_lens.py @@ -1,6 +1,7 @@ """Model-free tests for Backward Lens gradient-factor contracts.""" from dataclasses import FrozenInstanceError +from types import SimpleNamespace from typing import cast import pytest @@ -8,13 +9,46 @@ import torch.nn.functional as F from transformer_lens.tools.analysis.backward_lens import ( + BackwardLensMatrixResult, LinearGradientFactors, WeightLayout, _build_linear_gradient_factors, + _factor_norms_and_normalized_rows, + _project_residual_factors, _rank_vocabulary_logits, ) +class _ReadoutModel: + def __init__(self, *, affine: bool = True): + self.cfg = SimpleNamespace(d_model=3) + self.ln_final = torch.nn.LayerNorm(3, elementwise_affine=affine) + self.unembed = torch.nn.Linear(3, 5, bias=False) + with torch.no_grad(): + self.unembed.weight.copy_(torch.arange(15, dtype=torch.float32).reshape(5, 3) / 10) + + @property + def W_U(self) -> torch.Tensor: + return self.unembed.weight.T + + +def _matrix_result(logits: torch.Tensor) -> BackwardLensMatrixResult: + factors = _build_linear_gradient_factors( + torch.tensor([[1.0, 2.0], [3.0, 4.0]]), + torch.tensor([[0.5, -1.0], [2.0, 1.5]]), + torch.tensor([[6.5, 3.5], [9.0, 4.0]]), + weight_layout="in_out", + ) + return BackwardLensMatrixResult( + factors=factors, + projected_factor="forward_inputs", + factor_norms=torch.tensor([1.0, 2.0]), + zero_norm_mask=torch.tensor([False, False]), + vocabulary_logits=logits.clone(), + normalized_vocabulary_logits=(logits * 2).clone(), + ) + + def test_reconstructs_gpt2_style_in_out_gradient_from_autograd() -> None: torch.manual_seed(1) inputs = torch.randn(7, 3) @@ -238,3 +272,105 @@ def test_vocabulary_ranking_rejects_invalid_logits(logits) -> None: def test_vocabulary_ranking_rejects_non_tensor_logits() -> None: with pytest.raises(TypeError, match="torch.Tensor"): _rank_vocabulary_logits(cast(torch.Tensor, [1.0, 2.0]), k=1, largest=True) + + +def test_projection_matches_fresh_final_norm_and_unembedding() -> None: + model = _ReadoutModel() + factors = torch.tensor([[1.0, -2.0, 4.0], [0.25, 0.5, -0.75]]) + + actual = _project_residual_factors(model, factors) + expected = model.unembed(model.ln_final(factors.unsqueeze(0))).squeeze(0) + + torch.testing.assert_close(actual, expected) + assert actual.dtype == torch.float32 + assert actual.device.type == "cpu" + assert actual.grad_fn is None + + +def test_normalized_rows_preserve_norms_and_handle_zero_without_nan() -> None: + factors = torch.tensor([[3.0, 4.0, 0.0], [0.0, 0.0, 0.0]]) + + norms, zero_mask, normalized = _factor_norms_and_normalized_rows(factors) + + assert norms.tolist() == [5.0, 0.0] + assert zero_mask.tolist() == [False, True] + torch.testing.assert_close(normalized[0], torch.tensor([0.6, 0.8, 0.0])) + assert torch.equal(normalized[1], torch.zeros(3)) + assert torch.isfinite(normalized).all() + + +def test_normalized_projection_changes_low_norm_readout_but_keeps_zero_finite() -> None: + model = _ReadoutModel() + factors = torch.tensor([[1e-6, -2e-6, 1e-6], [0.0, 0.0, 0.0]]) + _, _, normalized = _factor_norms_and_normalized_rows(factors) + + raw_logits = _project_residual_factors(model, factors) + normalized_logits = _project_residual_factors(model, normalized) + + assert not torch.allclose(raw_logits[0], normalized_logits[0]) + assert torch.isfinite(normalized_logits).all() + + +def test_projection_sign_symmetry_depends_on_final_norm_bias() -> None: + factors = torch.tensor([[1.0, -2.0, 4.0]]) + bias_free = _ReadoutModel(affine=False) + biased = _ReadoutModel(affine=True) + with torch.no_grad(): + biased.ln_final.bias.fill_(0.25) + + torch.testing.assert_close( + _project_residual_factors(bias_free, -factors), + -_project_residual_factors(bias_free, factors), + ) + assert not torch.allclose( + _project_residual_factors(biased, -factors), + -_project_residual_factors(biased, factors), + ) + + +def test_matrix_result_rankings_decoding_and_target_rank_conventions() -> None: + logits = torch.tensor([[3.0, 1.0, -2.0, 0.0], [0.0, -1.0, 4.0, 2.0]]) + result = _matrix_result(logits) + + assert result.top(k=2).indices.tolist() == [[0, 1], [2, 3]] + assert result.bottom(k=2).indices.tolist() == [[2, 3], [1, 0]] + assert result.target_ranks(2, largest=True).tolist() == [3, 0] + assert result.gradient_descent_target_ranks(2).tolist() == [0, 3] + assert torch.equal(result.logits(normalized=True), logits * 2) + + tokenizer = SimpleNamespace(decode=lambda ids: f"token-{ids[0]}") + assert result.top_tokens(tokenizer, k=1) == [["token-0"], ["token-2"]] + assert result.bottom_tokens(tokenizer, k=1) == [["token-2"], ["token-1"]] + + +def test_matrix_result_rejects_missing_normalized_logits_and_invalid_target() -> None: + logits = torch.randn(2, 4) + result = _matrix_result(logits) + result_without_normalized = BackwardLensMatrixResult( + factors=result.factors, + projected_factor=result.projected_factor, + factor_norms=result.factor_norms, + zero_norm_mask=result.zero_norm_mask, + vocabulary_logits=result.vocabulary_logits, + ) + + with pytest.raises(ValueError, match="were not requested"): + result_without_normalized.logits(normalized=True) + with pytest.raises(ValueError, match="target_token_id"): + result.target_ranks(4, largest=True) + with pytest.raises(TypeError, match="decode"): + result.top_tokens(object(), k=1) + + +def test_public_backward_lens_symbols_are_exported() -> None: + from transformer_lens.tools.analysis import ( + BackwardLens, + BackwardLensLayerResult, + BackwardLensMatrixResult, + BackwardLensResult, + ) + + assert BackwardLens.__name__ == "BackwardLens" + assert BackwardLensLayerResult.__name__ == "BackwardLensLayerResult" + assert BackwardLensMatrixResult.__name__ == "BackwardLensMatrixResult" + assert BackwardLensResult.__name__ == "BackwardLensResult" diff --git a/transformer_lens/tools/analysis/__init__.py b/transformer_lens/tools/analysis/__init__.py index 16e6c752c..b1170a527 100644 --- a/transformer_lens/tools/analysis/__init__.py +++ b/transformer_lens/tools/analysis/__init__.py @@ -5,6 +5,8 @@ new analyses may target the ``TransformerBridge`` API exclusively. Tools: + - backward_lens: GPT-2 MLP weight-gradient factors projected into vocabulary + space with explicit raw-gradient sign semantics. - direct_logit_attribution: Direct Logit Attribution (DLA) over components, layers, or attention heads. - direct_path_patching: Direct path patching for head-to-head circuit @@ -16,6 +18,16 @@ attention-head OQ/OK/OV affinity. """ +from transformer_lens.tools.analysis.backward_lens import ( + BackwardLens, + BackwardLensLayerResult, + BackwardLensMatrixResult, + BackwardLensResult, + LinearGradientFactors, + ProjectedFactor, + VocabularyRanking, + WeightLayout, +) from transformer_lens.tools.analysis.direct_logit_attribution import ( DirectLogitAttribution, direct_logit_attribution, @@ -49,7 +61,14 @@ ) __all__ = [ +<<<<<<< HEAD "AttentionHeadRef", +======= + "BackwardLens", + "BackwardLensLayerResult", + "BackwardLensMatrixResult", + "BackwardLensResult", +>>>>>>> c49c1cd1 (feat(backward_lens): project GPT-2 MLP gradient factors into vocabulary space) "DirectLogitAttribution", "HeadAffinityPair", "HeadAffinityResult", @@ -58,10 +77,17 @@ "JSpaceVarianceProfile", "JacobianLens", "JacobianLensReadout", +<<<<<<< HEAD "ProjectionKernelResult", "RandomSubspaceReference", "SubspaceBasis", "attention_head_subspace_affinity", +======= + "LinearGradientFactors", + "ProjectedFactor", + "VocabularyRanking", + "WeightLayout", +>>>>>>> c49c1cd1 (feat(backward_lens): project GPT-2 MLP gradient factors into vocabulary space) "direct_logit_attribution", "estimate_occupancy", "get_act_patch_direct_path", diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py index e250f0c20..a40e95bb4 100644 --- a/transformer_lens/tools/analysis/backward_lens.py +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -1,8 +1,8 @@ -"""Backward Lens gradient-factor contracts and GPT-2 Bridge capture. +"""Backward Lens gradient-factor capture and vocabulary projection. The Backward Lens represents a linear weight gradient as a sum of token-position -outer products. This module provides independently testable tensor algebra and -the raw GPT-2 capture path; vocabulary projection is added separately. +outer products and projects residual-width factors into the model vocabulary. +The public API currently supports raw GPT-2 ``TransformerBridge`` models. """ from __future__ import annotations @@ -16,6 +16,7 @@ import torch.nn.functional as F WeightLayout = Literal["in_out", "out_in"] +ProjectedFactor = Literal["forward_inputs", "output_gradients"] @dataclass(frozen=True) @@ -23,8 +24,10 @@ class LinearGradientFactors: """Detached factors and reconstruction for one linear weight gradient. ``forward_inputs`` and ``output_gradients`` have shapes ``[position, in]`` - and ``[position, out]``. Gradient tensors use the requested storage layout. - All tensors are cloned to CPU in float32 so the result owns no autograd graph. + and ``[position, out]``. ``output_gradients`` and ``weight_gradient`` preserve + the raw ``d(loss)/d(tensor)`` sign; they are not negated into update directions. + Gradient tensors use the requested storage layout. All tensors are cloned to + CPU in float32 so the result owns no autograd graph. """ forward_inputs: torch.Tensor @@ -48,6 +51,129 @@ class VocabularyRanking: indices: torch.Tensor +@dataclass(frozen=True) +class BackwardLensMatrixResult: + """Factors and vocabulary readouts for one GPT-2 MLP weight matrix. + + ``factors`` contains the full linear factorization. ``projected_factor`` says + whether its residual-width ``forward_inputs`` or raw-gradient + ``output_gradients`` were decoded. ``factor_norms`` and ``zero_norm_mask`` + have shape ``[position]`` with float32 and bool dtypes. ``vocabulary_logits`` + has shape ``[position, d_vocab]`` and contains signed, pre-softmax raw logits. + ``normalized_vocabulary_logits`` has the same shape when requested. Every + tensor is a detached CPU-owned value; gradient descent subtracts raw gradients. + """ + + factors: LinearGradientFactors + projected_factor: ProjectedFactor + factor_norms: torch.Tensor + zero_norm_mask: torch.Tensor + vocabulary_logits: torch.Tensor + normalized_vocabulary_logits: torch.Tensor | None = None + + def logits(self, *, normalized: bool = False) -> torch.Tensor: + """Return raw or requested Normalized Logit Lens logits.""" + if not isinstance(normalized, bool): + raise TypeError("normalized must be a bool") + if not normalized: + return self.vocabulary_logits + if self.normalized_vocabulary_logits is None: + raise ValueError("normalized logits were not requested during analysis") + return self.normalized_vocabulary_logits + + def top(self, *, k: int, normalized: bool = False) -> VocabularyRanking: + """Return the largest signed logits and token ids per position.""" + return _rank_vocabulary_logits(self.logits(normalized=normalized), k=k, largest=True) + + def bottom(self, *, k: int, normalized: bool = False) -> VocabularyRanking: + """Return the smallest signed logits and token ids per position.""" + return _rank_vocabulary_logits(self.logits(normalized=normalized), k=k, largest=False) + + def top_tokens(self, tokenizer: Any, *, k: int, normalized: bool = False) -> list[list[str]]: + """Decode the largest-``k`` vocabulary ids for every position.""" + return _decode_vocabulary_ranking(self.top(k=k, normalized=normalized), tokenizer) + + def bottom_tokens(self, tokenizer: Any, *, k: int, normalized: bool = False) -> list[list[str]]: + """Decode the smallest-``k`` vocabulary ids for every position.""" + return _decode_vocabulary_ranking(self.bottom(k=k, normalized=normalized), tokenizer) + + def target_ranks( + self, + target_token_id: int, + *, + largest: bool, + normalized: bool = False, + ) -> torch.Tensor: + """Return zero-based target ranks per position in the requested ordering. + + ``largest=True`` gives rank zero to the largest logit. ``largest=False`` + gives rank zero to the smallest, which is the useful raw-gradient + convention for the second MLP matrix because gradient descent subtracts it. + Ties receive the same competition rank. + """ + logits = self.logits(normalized=normalized) + if isinstance(target_token_id, bool) or not isinstance(target_token_id, int): + raise TypeError("target_token_id must be an integer") + if not 0 <= target_token_id < logits.shape[-1]: + raise ValueError( + f"target_token_id must be in [0, {logits.shape[-1] - 1}]; " f"got {target_token_id}" + ) + if not isinstance(largest, bool): + raise TypeError("largest must be a bool") + target = logits[:, target_token_id].unsqueeze(-1) + comparisons = logits > target if largest else logits < target + return comparisons.sum(dim=-1, dtype=torch.int64).cpu().clone() + + def gradient_descent_target_ranks( + self, target_token_id: int, *, normalized: bool = False + ) -> torch.Tensor: + """Return ascending raw-gradient target ranks (rank zero is smallest).""" + return self.target_ranks(target_token_id, largest=False, normalized=normalized) + + +@dataclass(frozen=True) +class BackwardLensLayerResult: + """Vocabulary-facing input/output MLP matrix results for one indexed layer.""" + + layer: int + input_projection: BackwardLensMatrixResult + output_projection: BackwardLensMatrixResult + + +@dataclass(frozen=True) +class BackwardLensResult: + """Detached result of one :meth:`BackwardLens.analyze` call. + + ``prompt`` and ``target_token`` echo the analyzed inputs; ``target_token_id`` + is the single vocabulary id the target text encodes to. ``loss`` is the raw + scalar cross-entropy of the final-position next-token prediction against the + target; it preserves the ``d(loss)/d(...)`` sign convention and is not negated. + ``prompt_token_ids`` is an owned CPU int64 tensor with shape ``[position]``; + position zero is the prepended BOS, and every residual-width factor in + ``layers`` is aligned to these same positions. ``layers`` preserves requested + order. Maximum errors summarize both matrices over every requested layer. + ``includes_normalized_logits`` records whether the Normalized Logit Lens was + computed. No model or tokenizer reference is retained. + """ + + prompt: str + prompt_token_ids: torch.Tensor + target_token: str + target_token_id: int + loss: float + layers: tuple[BackwardLensLayerResult, ...] + max_absolute_reconstruction_error: float + max_relative_reconstruction_error: float + includes_normalized_logits: bool + + def layer(self, layer: int) -> BackwardLensLayerResult: + """Return one requested layer result or raise ``KeyError``.""" + for result in self.layers: + if result.layer == layer: + return result + raise KeyError(f"layer {layer} was not analyzed") + + @dataclass(frozen=True) class _GPT2LayerGradientFactors: """Detached gradient factors for both MLP projections in one GPT-2 layer.""" @@ -197,6 +323,84 @@ def _rank_vocabulary_logits(logits: torch.Tensor, *, k: int, largest: bool) -> V ) +def _decode_vocabulary_ranking(ranking: VocabularyRanking, tokenizer: Any) -> list[list[str]]: + """Decode a two-dimensional vocabulary ranking without retaining a tokenizer.""" + decode = getattr(tokenizer, "decode", None) + if not callable(decode): + raise TypeError("tokenizer must provide a callable decode method") + if ranking.indices.ndim != 2: + raise ValueError("decoded matrix rankings must have shape [position, k]") + return [[str(decode([token_id])) for token_id in row.tolist()] for row in ranking.indices] + + +@torch.no_grad() +def _project_residual_factors(model: Any, factors: torch.Tensor) -> torch.Tensor: + """Apply fresh final normalization and unembedding to residual-width rows.""" + _validate_floating_matrix("factors", factors) + if factors.shape[-1] != int(model.cfg.d_model): + raise ValueError( + f"factors must have width d_model={model.cfg.d_model}; got {factors.shape[-1]}" + ) + unembed_weight = model.W_U + if not isinstance(unembed_weight, torch.Tensor) or unembed_weight.ndim != 2: + raise ValueError("the GPT-2 Bridge must expose a rank-2 unembedding weight") + batched = ( + factors.detach().to(device=unembed_weight.device, dtype=unembed_weight.dtype).unsqueeze(0) + ) + logits = model.unembed(model.ln_final(batched)).squeeze(0) + if logits.ndim != 2 or logits.shape != (factors.shape[0], unembed_weight.shape[1]): + raise RuntimeError( + "final normalization and unembedding must return [position, d_vocab]; " + f"got {tuple(logits.shape)}" + ) + projected = logits.detach().float() + if not bool(torch.isfinite(projected).all()): + raise ValueError("vocabulary projection must remain finite in float32") + return projected.cpu().clone() + + +def _factor_norms_and_normalized_rows( + factors: torch.Tensor, +) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """Return original L2 norms, exact-zero mask, and safely unit-normalized rows.""" + _validate_floating_matrix("factors", factors) + rows = _to_detached_float32("factors", factors).cpu().clone() + norms = rows.norm(dim=-1) + zero_mask = norms == 0 + denominators = torch.where(zero_mask, torch.ones_like(norms), norms) + normalized = rows / denominators.unsqueeze(-1) + return norms.clone(), zero_mask.clone(), normalized + + +def _build_matrix_result( + model: Any, + factors: LinearGradientFactors, + *, + projected_factor: ProjectedFactor, + include_normalized_logits: bool, +) -> BackwardLensMatrixResult: + if projected_factor not in ("forward_inputs", "output_gradients"): + raise ValueError("projected_factor must be 'forward_inputs' or 'output_gradients'") + if not isinstance(include_normalized_logits, bool): + raise TypeError("include_normalized_logits must be a bool") + rows = ( + factors.forward_inputs if projected_factor == "forward_inputs" else factors.output_gradients + ) + norms, zero_mask, normalized_rows = _factor_norms_and_normalized_rows(rows) + raw_logits = _project_residual_factors(model, rows) + normalized_logits = ( + _project_residual_factors(model, normalized_rows) if include_normalized_logits else None + ) + return BackwardLensMatrixResult( + factors=factors, + projected_factor=projected_factor, + factor_norms=norms, + zero_norm_mask=zero_mask, + vocabulary_logits=raw_logits, + normalized_vocabulary_logits=normalized_logits, + ) + + def _validate_requested_layers(model: Any, layers: Sequence[int]) -> tuple[int, ...]: if isinstance(layers, (str, bytes)) or not isinstance(layers, Sequence): raise TypeError("layers must be a sequence of integer layer indices") @@ -464,3 +668,88 @@ def _capture_gpt2_mlp_gradient_factors( loss=float(loss.detach()), layers=tuple(layer_results), ) + + +class BackwardLens: + """Analyze GPT-2 MLP weight gradients in the output vocabulary basis. + + The analyzer accepts a fresh, raw GPT-2 :class:`TransformerBridge`. Results + retain no model or tokenizer reference and contain detached CPU-owned tensors. + Raw backward signals are loss gradients; gradient descent subtracts them. + """ + + def __init__(self, model: Any): + """Validate and retain the raw GPT-2 Bridge used for analyses.""" + _require_raw_gpt2_bridge(model) + self._model = model + + def analyze( + self, + prompt: str, + target_token: str, + layers: Sequence[int], + *, + normalized: bool = False, + ) -> BackwardLensResult: + """Analyze one final-position, one-token target loss. + + Args: + prompt: Non-empty unbatched prompt text. + target_token: Text encoding to exactly one token without BOS. + layers: Unique GPT-2 layer indices in desired result order. + normalized: Also project unit-normalized nonzero factors using the + Normalized Logit Lens. Raw projections are always returned. + + Returns: + Detached gradient factors, vocabulary logits, norms, reconstruction + errors, and target metadata. + """ + if not isinstance(normalized, bool): + raise TypeError("normalized must be a bool") + capture = _capture_gpt2_mlp_gradient_factors(self._model, prompt, target_token, layers) + layer_results: list[BackwardLensLayerResult] = [] + absolute_errors: list[float] = [] + relative_errors: list[float] = [] + for layer in capture.layers: + input_result = _build_matrix_result( + self._model, + layer.input_projection, + projected_factor="forward_inputs", + include_normalized_logits=normalized, + ) + output_result = _build_matrix_result( + self._model, + layer.output_projection, + projected_factor="output_gradients", + include_normalized_logits=normalized, + ) + layer_results.append( + BackwardLensLayerResult( + layer=layer.layer, + input_projection=input_result, + output_projection=output_result, + ) + ) + absolute_errors.extend( + ( + layer.input_projection.absolute_reconstruction_error, + layer.output_projection.absolute_reconstruction_error, + ) + ) + relative_errors.extend( + ( + layer.input_projection.relative_reconstruction_error, + layer.output_projection.relative_reconstruction_error, + ) + ) + return BackwardLensResult( + prompt=prompt, + prompt_token_ids=capture.prompt_token_ids[0].clone(), + target_token=target_token, + target_token_id=capture.target_token_id, + loss=capture.loss, + layers=tuple(layer_results), + max_absolute_reconstruction_error=max(absolute_errors), + max_relative_reconstruction_error=max(relative_errors), + includes_normalized_logits=normalized, + ) From 9b20a4502ce09f6ebafbc5e45abbf6e003153979 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Tue, 25 Aug 2026 04:53:12 -0700 Subject: [PATCH 04/17] docs(backward_lens): document GPT-2 gradient factor analysis - Derive the FF1 and FF2 gradient factorizations, vocabulary projections, and tensor-shape contracts. - Document the public API, raw Bridge and single-token restrictions, state-safety guarantees, and error behavior. - Explain raw versus normalized projections, zero-factor handling, gradient-versus-SGD signs, interpretation limits, and troubleshooting. - Add the guide to the documentation index and cite Katz et al. without copying external code or assets. --- docs/source/content/backward_lens.md | 176 +++++++++++++++++++++++++++ docs/source/index.md | 1 + 2 files changed, 177 insertions(+) create mode 100644 docs/source/content/backward_lens.md diff --git a/docs/source/content/backward_lens.md b/docs/source/content/backward_lens.md new file mode 100644 index 000000000..17a4ae91d --- /dev/null +++ b/docs/source/content/backward_lens.md @@ -0,0 +1,176 @@ +# Backward Lens + +Backward Lens projects factors of MLP weight gradients into a language model's +vocabulary space. It is useful for inspecting which token directions are associated +with the forward inputs and backward signals that compose a gradient. A readable +vocabulary projection is a diagnostic, not by itself evidence that a token or neuron +causes a model behavior. + +TransformerLens currently provides a focused GPT-2 implementation through +`TransformerBridge`. It follows the method introduced by +[Katz et al. (2024)](https://aclanthology.org/2024.emnlp-main.142/). + +## Gradient factorization + +For one linear projection and one prompt, let $x_i \in \mathbb{R}^{d_{in}}$ be +the input at token position $i$, and let +$\delta_i = \partial L / \partial y_i \in \mathbb{R}^{d_{out}}$ be the loss +gradient at its output. GPT-2 stores `Conv1D` weights in `[in, out]` order, so + +$$ +\nabla_W L = \sum_i x_i \delta_i^\mathsf{T} = X^\mathsf{T}\Delta. +$$ + +`BackwardLens` captures both factors and independently computes the weight gradient. +Each matrix result includes the reconstructed gradient and maximum absolute and +scale-aware relative reconstruction errors. + +### The two GPT-2 MLP matrices + +The two projections expose different residual-width factors: + +| Result | Weight shape | Projected factor | Shape before vocabulary projection | +|---|---:|---|---:| +| `input_projection` (FF1 / `c_fc`) | `[d_model, d_mlp]` | Forward input $x_i$ | `[position, d_model]` | +| `output_projection` (FF2 / `c_proj`) | `[d_mlp, d_model]` | Backward signal $\delta_i$ | `[position, d_model]` | + +The FF1 readout therefore describes the residual-stream directions entering the MLP. +The FF2 readout describes raw loss gradients at the MLP output. These are different +quantities and should not be interpreted interchangeably. + +## Vocabulary projection + +For each residual-width row $v$, the lens computes a fresh readout + +$$ +P(v) = \operatorname{Unembed}(\operatorname{LN}_{final}(v)). +$$ + +Final-normalization statistics are recomputed independently for every factor. The +implementation does not reuse normalization scales cached during the model's forward +pass. `vocabulary_logits` contains signed, pre-softmax logits; it does not contain +probabilities. + +When `normalized=True`, the analysis also computes the Normalized Logit Lens: + +$$ +P_{norm}(v) = P\left(\frac{v}{\lVert v\rVert_2}\right). +$$ + +Exact-zero rows remain zero before projection and are identified by `zero_norm_mask`. +The original float32 norms are retained in `factor_norms`. Normalization is most useful +when comparing directions whose norms differ greatly, especially very small backward +signals. Because final LayerNorm has an epsilon and may have a bias, raw and normalized +projections need not be identical. + +## Sign convention + +All backward signals and weight gradients preserve the raw `d(loss) / d(tensor)` +sign. Gradient descent subtracts them: + +$$ +W_{new} = W - \eta \nabla_W L. +$$ + +For FF2 backward signals, `bottom(...)` and +`gradient_descent_target_ranks(...)` inspect the smallest raw-gradient logits, which +are often the most relevant ordering for the subtracted update. Do not simply negate +projected logits: final LayerNorm bias and epsilon mean that projection is not exactly +sign-symmetric. + +## Minimal example + +```python +import torch + +from transformer_lens.model_bridge import TransformerBridge +from transformer_lens.tools.analysis import BackwardLens + +model = TransformerBridge.boot_transformers( + "openai-community/gpt2", + device="cuda" if torch.cuda.is_available() else "cpu", + dtype=torch.float32, +) + +result = BackwardLens(model).analyze( + prompt="The capital of France is", + target_token=" Paris", + layers=[0, 6, 11], + normalized=True, +) + +last_layer = result.layer(11) +ff1 = last_layer.input_projection +ff2 = last_layer.output_projection + +ff1_tokens = ff1.top_tokens(model.tokenizer, k=5) +ff2_update_tokens = ff2.bottom_tokens(model.tokenizer, k=5) +target_ranks = ff2.gradient_descent_target_ranks( + result.target_token_id, + normalized=True, +) +``` + +`target_token` must encode to exactly one token without a beginning-of-sequence token. +For GPT-2 tokenization, a leading space is often significant. The prompt is tokenized +normally, including its prepended BOS token; all position-indexed factors and readouts +align with `result.prompt_token_ids`. + +## Result structure + +`BackwardLens.analyze(...)` returns a detached `BackwardLensResult`: + +- `prompt`, `prompt_token_ids`, `target_token`, and `target_token_id` record the inputs. +- `loss` is final-position cross-entropy against the one-token target. +- `layers` preserves the requested layer order; `result.layer(index)` retrieves one. +- Each layer has `input_projection` and `output_projection` matrix results. +- Each matrix exposes `factors`, `factor_norms`, `zero_norm_mask`, and raw vocabulary + logits. `normalized_vocabulary_logits` is present only when requested. +- `top(...)` and `bottom(...)` return signed values and token ids. +- `top_tokens(...)` and `bottom_tokens(...)` decode ids with a caller-provided + tokenizer. Results deliberately retain no model or tokenizer reference. +- Target ranks are zero-based competition ranks, so tied logits receive the same rank. +- Maximum reconstruction errors summarize both MLP matrices over all requested layers. + +Returned tensors are detached, owned CPU copies. Factors, reconstructed gradients, +norms, and vocabulary logits use float32; token ids and ranks use int64. + +## Requirements and non-goals + +The current implementation requires: + +- A freshly booted, raw `TransformerBridge` using `GPT2ArchitectureAdapter`. +- Original, trainable GPT-2 `Conv1D` weights and a dense, non-gated MLP. +- Compatibility mode and weight processing to remain disabled. +- One non-empty prompt, one single-token target, and unique valid layer indices. + +It does not currently support batched prompts, multi-token target losses, gated MLPs, +other architecture families, compatibility-mode weights, model editing, or causal +claims about the displayed vocabulary rankings. + +## Model-state safety + +An analysis uses one gradient-enabled forward pass and one `torch.autograd.grad` call. +It does not call `backward()` or modify parameter `.grad` buffers. It preserves model +weights, `requires_grad` flags, train/eval state, existing hooks, and CPU/CUDA/MPS RNG +state, and it removes only its own temporary hooks on success or failure. Existing +activation-editing hooks still affect the analyzed computation. + +## Troubleshooting + +| Symptom | Cause and resolution | +|---|---| +| Raw-Bridge or processed-weight error | Reboot with `TransformerBridge.boot_transformers(...)`; do not enable compatibility mode or process weights. | +| Target encodes to zero or multiple tokens | Choose text that maps to one GPT-2 token without BOS; check leading whitespace. | +| Duplicate or out-of-range layer error | Pass a non-empty sequence of unique indices in `[0, model.cfg.n_layers)`. | +| Normalized logits were not requested | Call `analyze(..., normalized=True)` before using `logits(normalized=True)` or normalized ranks. | +| FF2 ranking appears sign-reversed | Remember that results are raw loss gradients and gradient descent subtracts them; inspect bottom tokens or ascending target ranks. | +| Results change when custom hooks are installed | Existing hooks are intentionally respected; remove them to analyze the unmodified model computation. | + +## References + +- Shahar Katz, Yonatan Belinkov, Mor Geva, and Lior Wolf. 2024. + [Backward Lens: Projecting Language Model Gradients into the Vocabulary Space](https://aclanthology.org/2024.emnlp-main.142/). + *Proceedings of EMNLP 2024*, pages 2390–2422. +- [Authors' research demonstration](https://github.com/shacharKZ/BackwardLens). + TransformerLens does not import, vendor, or depend on that repository's code. \ No newline at end of file diff --git a/docs/source/index.md b/docs/source/index.md index 1feea3122..565bb91cf 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -60,6 +60,7 @@ content/ssm_interpretability content/projection_kernel content/jacobian_lens_fitting generated/demos/Jacobian_Lens_Decomposition_Demo +content/backward_lens content/debugging_numerical_divergence generated/demos/Main_Demo generated/demos/Exploratory_Analysis_Demo From 89c214bf44a34f8f85d8086dfef9e547b1842b74 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Tue, 25 Aug 2026 04:53:25 -0700 Subject: [PATCH 05/17] docs(backward_lens): add executed GPT-2 gradient factor demo - Add an executed GPT-2-small walkthrough of MLP gradient factorization and vocabulary projection. - Visualize reconstruction error, numerical rank, layer-position token directions, VJP norms, and target ranks. - Compare raw and normalized projections, expose gradient-update sign semantics, mark near-zero signals, and sort cumulative reconstruction by contribution. - Save reproducible outputs and register all ten notebook cells in CI and the Makefile notebook test target. --- .github/workflows/checks.yml | 1 + demos/Backward_Lens_Demo.ipynb | 1714 ++++++++++++++++++++++++++++++++ makefile | 1 + 3 files changed, 1716 insertions(+) create mode 100644 demos/Backward_Lens_Demo.ipynb diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d40428a30..3cb338493 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -430,6 +430,7 @@ jobs: - "Activation_Patching_in_TL_Demo" - "ARENA_Content" - "BERT" + - "Backward_Lens_Demo" - "Bridge_Evals_Demo" - "Exploratory_Analysis_Demo" # - "Grokking_Demo" diff --git a/demos/Backward_Lens_Demo.ipynb b/demos/Backward_Lens_Demo.ipynb new file mode 100644 index 000000000..b95f1adb2 --- /dev/null +++ b/demos/Backward_Lens_Demo.ipynb @@ -0,0 +1,1714 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "97d2c037", + "metadata": {}, + "source": [ + "# Backward Lens: GPT-2 gradient factors in vocabulary space\n", + "\n", + "This notebook demonstrates `BackwardLens` on a small, fixed set of GPT-2 next-token targets. It reproduces the *method* of projecting forward inputs and backward signals into vocabulary space; it is not a paper-scale replication and makes no causal claim from token rankings alone. All examples and layers are declared before analysis, and all outcomes are shown without filtering.\n", + "\n", + "The implementation follows [Katz et al. (2024)](https://aclanthology.org/2024.emnlp-main.142/). Gradient signs below are raw `d(loss) / d(tensor)` signs; gradient descent subtracts them." + ] + }, + { + "cell_type": "markdown", + "id": "64c3d670", + "metadata": {}, + "source": [ + "## Geometry\n", + "\n", + "For GPT-2's `[in, out]` `Conv1D` weight layout, token-position factors reconstruct the gradient as\n", + "\n", + "$$\\nabla_W L = \\sum_i x_i \\delta_i^\\mathsf{T} = X^\\mathsf{T}\\Delta.$$\n", + "\n", + "For FF1 (`c_fc`), the residual-width factor is the forward input $x_i$. For FF2 (`c_proj`), it is the backward signal $\\delta_i$. `BackwardLens` applies a fresh `ln_final` and unembedding to those residual-width rows." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "4955da53", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:57:32.909944Z", + "iopub.status.busy": "2026-08-25T09:57:32.909775Z", + "iopub.status.idle": "2026-08-25T09:58:00.150543Z", + "shell.execute_reply": "2026-08-25T09:58:00.149530Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
promptone-token target
0The capital of France isParis
1The capital of Italy isRome
2The largest planet in the Solar System isJupiter
\n", + "
" + ], + "text/plain": [ + " prompt one-token target\n", + "0 The capital of France is Paris\n", + "1 The capital of Italy is Rome\n", + "2 The largest planet in the Solar System is Jupiter" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import gc\n", + "import os\n", + "import warnings\n", + "\n", + "os.environ[\"HF_HUB_DISABLE_PROGRESS_BARS\"] = \"1\"\n", + "os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import torch\n", + "from huggingface_hub.utils import logging as hf_hub_logging\n", + "from IPython.display import display\n", + "from transformers.utils import logging as hf_logging\n", + "\n", + "from transformer_lens.model_bridge import TransformerBridge\n", + "from transformer_lens.tools.analysis import BackwardLens\n", + "\n", + "hf_hub_logging.set_verbosity_error()\n", + "hf_logging.set_verbosity_error()\n", + "hf_logging.disable_progress_bar()\n", + "torch.manual_seed(0)\n", + "\n", + "DEVICE = \"cpu\" # Stable saved outputs and portable notebook validation.\n", + "DETAILED_LAYERS = (0, 2, 4, 6, 8, 10, 11)\n", + "EXAMPLES = (\n", + " (\"The capital of France is\", \" Paris\"),\n", + " (\"The capital of Italy is\", \" Rome\"),\n", + " (\"The largest planet in the Solar System is\", \" Jupiter\"),\n", + ")\n", + "\n", + "pd.DataFrame(EXAMPLES, columns=[\"prompt\", \"one-token target\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8b1857ae", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:00.152849Z", + "iopub.status.busy": "2026-08-25T09:58:00.152540Z", + "iopub.status.idle": "2026-08-25T09:58:00.376603Z", + "shell.execute_reply": "2026-08-25T09:58:00.375699Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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OxyiZ9LWyx1eHk0xd/VWXLmdqO1wSIuORl7B8u0H0aubL+wb1NN2OPQfJZFI8NbQl3E+6hJW/b2J90q1xMUr0Vi+7k907tAKTRu5aDscegqJKNMPY//hU1i0fD2aNGkCn44d0aZNG5ibm2szPnoKmUyG69euITIyEufOnUP7Nh74bNo4GBkaajs0omdWUFCIz7/5GTdvxWNod18EtHGHk62NtsOiF0AIgZt3U3E08gq2HTuLbgEdMe39t6Cvx9ePkXbJ75W8W7VCjy7+8GrRAgYGfJ93TSKEQGJyMsIiIrDrYAi823jg84/egSHPo85TSlwA4OTZC/h331Fcio6BRCKBg4M9TE3NYMgbYZ1SUlKC/Lw83H9wH3l5+XCwt0W3Lh3w+mt9mLRQrVJQUIglv2/EibBIFBYVobFjAzRybADzOiYw5CDtWqdIKkV2XgGu3rmLlLQMWJqboVd3X4wZ/iqTFtIZh0PPYt0/e5CUcg9mpqZo2bw5LMzNYWJszDESOqxYKkVuXh5ux8fjbkoKTOuYoJu/DyaMGcKkpYZQSVzkMjKzcfJMJG7HJyEnNw9FxXyfgi7R09ODuakp6terC1+f1mjc0JkXS6rVCgqLEB4ZjRNnInHvwUPk5ORBxq4atY6hgT7MzUzh4mQP/87t0KZlCxgYMEEl3SOEwO07d3E8LAJXb9xCTk6+4l0gpJsM9EuvLw729RHQ2RvtWrvzy94aptzEhYiIiIiISFfwuTsREREREek8Ji5ERERERKTzmLgQEREREZHOY+JCREREREQ6j4kLERERERHpPCYuRERERESk85i4EBERERGRzmPiQkREREREOo+JCxERERER6TwmLkREREREpPOYuBARERERkc5j4kJERERERDqPiQsREREREek8Ji5ERERERKTzmLgQEREREZHOY+JCREREREQ6j4kLERERERHpPCYuRERERESk85i4EBERERGRzmPiQkREREREOo+JCxERERER6TwmLkREREREpPOYuBARERERkc5j4kJERERERDqPiQsREREREek8Ji5ERERERKTzmLgQEREREZHOY+JCREREREQ6j4kLERERERHpPCYuRERERESk85i4EBERERGRzmPiQkREREREOo+JCxERERER6TwmLkREREREpPOYuBARERERkc5j4kJERERERDqPiQsRERHRf0RG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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Original schematic: each position contributes one outer product.\n", + "fig, ax = plt.subplots(figsize=(10, 3.2))\n", + "ax.axis(\"off\")\n", + "boxes = [\n", + " (0.04, 0.55, \"forward row\\n$x_i$\", \"#dbeafe\"),\n", + " (0.28, 0.55, r\"$\\otimes$\", \"white\"),\n", + " (0.43, 0.55, \"backward row\\n\" r\"$\\delta_i$\", \"#fee2e2\"),\n", + " (0.67, 0.55, \"$=$\", \"white\"),\n", + " (0.80, 0.55, \"rank-one\\ncontribution\", \"#ede9fe\"),\n", + "]\n", + "for x, y, label, color in boxes:\n", + " ax.text(\n", + " x,\n", + " y,\n", + " label,\n", + " ha=\"center\",\n", + " va=\"center\",\n", + " fontsize=12,\n", + " bbox=dict(boxstyle=\"round,pad=0.5\", facecolor=color, edgecolor=\"#475569\"),\n", + " )\n", + "ax.annotate(\n", + " \"sum over prompt positions\",\n", + " xy=(0.80, 0.27),\n", + " xytext=(0.43, 0.12),\n", + " arrowprops=dict(arrowstyle=\"->\", color=\"#475569\"),\n", + " color=\"#334155\",\n", + ")\n", + "ax.set_title(\"Outer-product decomposition of one MLP weight gradient\", fontsize=14)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ea451106", + "metadata": {}, + "source": [ + "## Load a raw GPT-2 Bridge\n", + "\n", + "Backward Lens requires original GPT-2 weights. Do not enable compatibility mode or process the weights. CPU float32 is used deliberately so the saved notebook is portable; change `DEVICE` for interactive exploration." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a5007e02", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:00.378673Z", + "iopub.status.busy": "2026-08-25T09:58:00.378471Z", + "iopub.status.idle": "2026-08-25T09:58:05.234585Z", + "shell.execute_reply": "2026-08-25T09:58:05.233676Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded a raw GPT-2 TransformerBridge.\n" + ] + } + ], + "source": [ + "with warnings.catch_warnings():\n", + " warnings.simplefilter(\"ignore\")\n", + " model = TransformerBridge.boot_transformers(\n", + " \"gpt2\", device=DEVICE, dtype=torch.float32\n", + " )\n", + "model.eval()\n", + "lens = BackwardLens(model)\n", + "print(\"Loaded a raw GPT-2 TransformerBridge.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "60ea683d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:05.236464Z", + "iopub.status.busy": "2026-08-25T09:58:05.236247Z", + "iopub.status.idle": "2026-08-25T09:58:05.627247Z", + "shell.execute_reply": "2026-08-25T09:58:05.626152Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Analyzed 6 positions across 7 layers.\n" + ] + } + ], + "source": [ + "def final_position_summary(result):\n", + " rows = []\n", + " for layer_result in result.layers:\n", + " ff2 = layer_result.output_projection\n", + " rows.append(\n", + " {\n", + " \"layer\": layer_result.layer,\n", + " \"raw target rank\": int(\n", + " ff2.gradient_descent_target_ranks(result.target_token_id)[-1]\n", + " ),\n", + " \"normalized target rank\": int(\n", + " ff2.gradient_descent_target_ranks(\n", + " result.target_token_id, normalized=True\n", + " )[-1]\n", + " ),\n", + " \"final-position VJP norm\": float(ff2.factor_norms[-1]),\n", + " }\n", + " )\n", + " return pd.DataFrame(rows)\n", + "\n", + "\n", + "prompt, target = EXAMPLES[0]\n", + "primary_result = lens.analyze(prompt, target, DETAILED_LAYERS, normalized=True)\n", + "primary_layer_summary = final_position_summary(primary_result)\n", + "print(\n", + " f\"Analyzed {len(primary_result.prompt_token_ids)} positions across \"\n", + " f\"{len(primary_result.layers)} layers.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "8c36830d", + "metadata": {}, + "source": [ + "## Vocabulary-facing factor tables\n", + "\n", + "FF1 top tokens describe forward residual directions. FF2 bottom tokens use the ascending raw-gradient ordering relevant to a subtracted gradient-descent update. The layer-by-position table keeps those two factors separate. The final-position comparison shows how unit normalization changes FF2 token ordering while retaining and coloring by the original VJP norm.\n", + "\n", + "The sign panel compares the largest raw-gradient logits with the ordering obtained by subtracting those same projected logits. It is deliberately labeled as an ordering diagnostic, not as $P(-v)$: final LayerNorm makes direct factor negation a different operation. None of these tables are direct model predictions." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "663bd83f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:05.629628Z", + "iopub.status.busy": "2026-08-25T09:58:05.629323Z", + "iopub.status.idle": "2026-08-25T09:58:06.029280Z", + "shell.execute_reply": "2026-08-25T09:58:06.028359Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
layerpositionprompt tokenFF1 top raw directionsFF2 bottom raw (update ordering)
000<|endoftext|><|endoftext|>, Advertisements, \\n%], Thumbnail, 女
101TheThe, the,  Theixel, Medium, iop
202capitalcapital, capital, CapitalinventoryQuantity, catentry, }}}
303ofof, of,  OfArchdemon, ortium,  Difference
404FranceFrance, France,  Frenchanton,  Seasons, figure
505isis,  also,  wasadr, adian, umn
620<|endoftext|>,,  the, \\nometric,  Associates, 76561
721Thelatest,  following,  Officialorescent, oro, ulhu
822capitalcapital,  punishment,  penaltyillary, fig,  Variant
923ofthe,  course,  interestuably, iqueness, uments
1024FranceConnection,  France, 'sEmerald, baugh, DIT
1125isnow,  not,  alsoEStreamFrame, ORPG,  Dise
1240<|endoftext|>,,  the,  andOTOS, ë,  istg
1341Theresa,  latest,  Bottom��,  Eth, �
1442capitalcapital,  punishment,  citynings, BILITIES, cles
1543ofthe,  Reach,  coursemotivating,  Appearance,  Cups
1644France's, ,,  Telecom��,  Antar,  Explosive
1745isnow,  not,  alsoDiseases,  Firstly,  Feet
1860<|endoftext|>,, .,  andSON, leys, VAL
1961Theresa, oret,  Latest�, ��, esters
2062capitalcity,  capital,  citiesappings, UES, =-=-
2163ofthe,  State,  Britishヘラ, TABLE,  �
2264France's, ,,  and, /_, ciation
2365isnow,  officially,  notTycoon, 76561,  Osiris
2480<|endoftext|>,, \\n, .Paris, xes, SON
2581Theresa, oret,  followingNotting, ournals, EngineDebug
2682capitalcity,  capital,  citiesParis, Denver, Philadelphia
2783ofSouth,  British,  ChinaFrames, Reviewer, selage
2884France's, ,,  hasciation, ㅋㅋ,  Spac
2985isnow,  officially,  stillParis, Paris,  Cologne
30100<|endoftext|>\\n,  the, ,Paris, ゼウス,  Hollande
31101Theresa, oret,  latest�醒, ��, ���
32102capitalcity,  cities,  capitalParis, ��, �
33103ofChina,  Kazakhstan,  Ukraine�醒, ���, Paris
34104Francehas,  is, 'scffff, eteenth,  mathemat
35105isnow,  officially,  rapidlyParis, Paris,  Copenhagen
36110<|endoftext|>The, Still, \\n�, �, \u0010
37111Theresa, odore,  latest�, �, \u0010
38112capitalcity,  cities,  Manila�, �, \u0010
39113ofRomania,  Kazakhstan,  Armenia�, �, \u0010
40114Francehas,  is, 's�, �, \u0010
41115isundergoing,  reeling,  hostingParis, Paris,  Copenhagen
\n", + "
" + ], + "text/plain": [ + " layer position prompt token FF1 top raw directions \\\n", + "0 0 0 <|endoftext|> <|endoftext|>, Advertisements, \\n \n", + "1 0 1 The The, the, The \n", + "2 0 2 capital capital, capital, Capital \n", + "3 0 3 of of, of, Of \n", + "4 0 4 France France, France, French \n", + "5 0 5 is is, also, was \n", + "6 2 0 <|endoftext|> ,, the, \\n \n", + "7 2 1 The latest, following, Official \n", + "8 2 2 capital capital, punishment, penalty \n", + "9 2 3 of the, course, interest \n", + "10 2 4 France Connection, France, 's \n", + "11 2 5 is now, not, also \n", + "12 4 0 <|endoftext|> ,, the, and \n", + "13 4 1 The resa, latest, Bottom \n", + "14 4 2 capital capital, punishment, city \n", + "15 4 3 of the, Reach, course \n", + "16 4 4 France 's, ,, Telecom \n", + "17 4 5 is now, not, also \n", + "18 6 0 <|endoftext|> ,, ., and \n", + "19 6 1 The resa, oret, Latest \n", + "20 6 2 capital city, capital, cities \n", + "21 6 3 of the, State, British \n", + "22 6 4 France 's, ,, and \n", + "23 6 5 is now, officially, not \n", + "24 8 0 <|endoftext|> ,, \\n, . \n", + "25 8 1 The resa, oret, following \n", + "26 8 2 capital city, capital, cities \n", + "27 8 3 of South, British, China \n", + "28 8 4 France 's, ,, has \n", + "29 8 5 is now, officially, still \n", + "30 10 0 <|endoftext|> \\n, the, , \n", + "31 10 1 The resa, oret, latest \n", + "32 10 2 capital city, cities, capital \n", + "33 10 3 of China, Kazakhstan, Ukraine \n", + "34 10 4 France has, is, 's \n", + "35 10 5 is now, officially, rapidly \n", + "36 11 0 <|endoftext|> The, Still, \\n \n", + "37 11 1 The resa, odore, latest \n", + "38 11 2 capital city, cities, Manila \n", + "39 11 3 of Romania, Kazakhstan, Armenia \n", + "40 11 4 France has, is, 's \n", + "41 11 5 is undergoing, reeling, hosting \n", + "\n", + " FF2 bottom raw (update ordering) \n", + "0 %], Thumbnail, 女 \n", + "1 ixel, Medium, iop \n", + "2 inventoryQuantity, catentry, }}} \n", + "3 Archdemon, ortium, Difference \n", + "4 anton, Seasons, figure \n", + "5 adr, adian, umn \n", + "6 ometric, Associates, 76561 \n", + "7 orescent, oro, ulhu \n", + "8 illary, fig, Variant \n", + "9 uably, iqueness, uments \n", + "10 Emerald, baugh, DIT \n", + "11 EStreamFrame, ORPG, Dise \n", + "12 OTOS, ë, istg \n", + "13 ��, Eth, � \n", + "14 nings, BILITIES, cles \n", + "15 motivating, Appearance, Cups \n", + "16 ��, Antar, Explosive \n", + "17 Diseases, Firstly, Feet \n", + "18 SON, leys, VAL \n", + "19 �, ��, esters \n", + "20 appings, UES, =-=- \n", + "21 ヘラ, TABLE, � \n", + "22     , /_, ciation \n", + "23 Tycoon, 76561, Osiris \n", + "24 Paris, xes, SON \n", + "25 Notting, ournals, EngineDebug \n", + "26 Paris, Denver, Philadelphia \n", + "27 Frames, Reviewer, selage \n", + "28 ciation, ㅋㅋ, Spac \n", + "29 Paris, Paris, Cologne \n", + "30 Paris, ゼウス, Hollande \n", + "31 �醒, ��, ��� \n", + "32 Paris, ��, � \n", + "33 �醒, ���, Paris \n", + "34 cffff, eteenth, mathemat \n", + "35 Paris, Paris, Copenhagen \n", + "36 �, �, \u0010 \n", + "37 �, �, \u0010 \n", + "38 �, �, \u0010 \n", + "39 �, �, \u0010 \n", + "40 �, �, \u0010 \n", + "41 Paris, Paris, Copenhagen " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 layerVJP normraw FF2 bottom tokensnormalized FF2 bottom tokens
006.557e-01adr, adian, umn, itia, igmaticadr, adian, umn, itia, igmatic
121.442e+00EStreamFrame, ORPG, Dise, misconception, senalEStreamFrame, ORPG, Dise, misconception, senal
241.329e+00 Diseases, Firstly, Feet, heses, Differences Diseases, Firstly, Feet, heses, Differences
361.042e+00 Tycoon, 76561, Osiris, Modes, Memories Tycoon, 76561, Osiris, Modes, Memories
484.872e-01 Paris, Paris, Cologne, Copenhagen, Rome Paris, Paris, Cologne, Copenhagen, Rome
5102.749e-01 Paris, Paris, Copenhagen, Cologne, Kyoto Paris, Paris, Copenhagen, Cologne, Kyoto
6112.609e-01 Paris, Paris, Copenhagen, Marse, France Paris, Paris, Copenhagen, Marse, France
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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WFiY1q5dqwMHDih37tzq1KmTw3w0efLkcWgvIiJC48eP15QpU/TKK6/c0b7NnDlTJ06c0KZNm7Rv3z6VKFFCDz30kP1LpCRFR0frwQcf1PLlyzVnzhydO3dOH3/8sb7++mu1adPGnpAYP3683nnnHU2cOFFnzpzR1q1bFRwcrA8++CDN7Xfu3NnePz/++KN9H5MTU6lxNp5kzvRxam5nfyTp/PnzatKkiQ4dOqRVq1YpLCxMTZs21YsvvpjmOrcbY3r74ttvv9Xhw4e1Zs0arV69Wj4+Plq5cqXatWun++67T/v379e6deu0d+9ezZkzJ8X29u3bp9q1a+vkyZP6448/FBUVpY8//lgfffSRBg0alKL+//73PzVp0kT79+93+JKfluHDh6tx48Y6fPiwli5dqpCQEA0ZMsShzvLly9W+fXtVr15dBw4c0Pr163Xw4EGn5yhbu3atJKWahE5PHGvXrlWTJk3k7e2tbdu26ejRo3rkkUf01FNP2W9dmj59ut5++21JUkxMjP38vtm2e/fuba+XlJSk/fv3q1WrVurZs6dWrFhx031Lz7G8mdSO26RJkxQcHKzWrVtr//79Cg8PV7NmzdSqVSuHxMa7776r4OBg1a9fX6GhoQoPD9fw4cPtCaC4uDhVqFBBnTp1su/n9cne9Ni/f78kqXjx4pL+OyaVK1d2at6watWqqVatWvrqq69SLPv666/l4uKiPn36OJTXqVNHBw4cUEREhFPxvffee3J3d9fWrVs1f/58ubq66vfff1ejRo1UoEABbdmyRRERERo2bJgGDRrkkCSbMGGCvX+uXr2qLVu2qEiRImrTpo3CwsLs9aZOnar+/furZ8+eOnbsmObNm6evvvoq1QTt119/rS5duqhBgwY6ePCg9uzZo3Llyql169aaN2+eU/t0vY0bN6pHjx4KCgpK8zb6fPnyqUyZMlqzZk262wcA3AEDALgrTp06ZSSl+m/MmDHprpeaGTNmGEmmd+/eTsWUP39+06dPH4ey0NBQI8n8+OOP9rINGzYYSebhhx92qPv3338bSWbmzJm33NYbb7xhfH197a/37t1rJJnvvvvulusmb79FixYO5fHx8aZYsWKmefPm9rJ33nnHSDLr1q1zqPvdd9857FfLli1No0aNbrrd1GJMrX9uVt/ZeO60j293f95++21js9nM7t27HepO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8OHDio6O1l9//aVevXrZ26tSpYq2b9/ucOVSRujQoYMSExPT/QS4vn37as2aNfrggw/04IMPqmLFiinqxMfHa/369emeN+16rq6umjZtmtauXavHH39c//zzj65cuaIjR45o9uzZatKkiaT/EuGVK1fWxIkTtWvXLl28eFE//PCDFixY4HCbqYeHh9566y0tWbJEb7/9ts6dO6fDhw+rT58+ql27tsO2PTw8NH78eK1atUovv/yyTp06pePHj2vw4MHauXOn/dZwSRowYIDOnz+vkSNH6vz58zp48KD69u2rOnXqpNinKlWq6MyZM9q4cWOKhKgk+99Zhw4dbrvfAADpR2IKALKpb775RqdOnVJ4eLhKlizpMFmuzWZz+nH0Ge3JJ5/U+++/r08++USFChVSz549NWjQIDVr1ixD2i5QoIBq166tChUq6NChQ/rjjz9Uv359ex1/f3+tW7dOTZo0UXBwsPLly6enn35aTzzxhP744w/7LS/vvPOOtm3bpocfflj+/v5q1qyZAgICtHbtWvttganx9PTU9OnTtXXrVvvtSDeblN7ZeO7U7e5P3rx5tXr1apUsWVKNGzdWyZIltXz5co0bN86+vxklI/qiRYsWWrx4sXbu3KmyZcuqfv36Kl26tHr06JGibqVKlRQaGqqyZcuqR48eyps3r+rWraupU6dq6NChKlWqVIbt283iXbRokbZt26YyZcqofv36KlmypH2Cb2f6d/DgwZo/f75TidC0NG7cWKtWrVJ0dLT9Spg5c+boyy+/1GuvvWav16pVKz3//PMaMWKEfRLrm12tNW3aNHXs2FHdunVTQECAJk2apO+++07+/v63jCk9xzK9XnzxRS1evFibNm3SAw88oHz58qlLly7av3+/3nrrLXu9kSNH6qefflJISIiqVq2qkiVLauLEiRo2bJi9zltvvaXKlSurSpUqstlsqSaCbkfdunV1//3367vvvkvXen369JGbm1uak55L/11dGh0drYEDB95RjG3bttXGjRsVHx+v5s2bK0+ePPbjlpwccnFx0cKFC1WmTBk1atRIpUqV0tKlS/Xjjz+mmPR/6NCh+uKLL/Tdd9+pWLFi6tixo5544gnVqFEjxbYHDBigX375RWvWrFGZMmVUoUIF7d27V0uXLrU/MEGSateurenTp2v+/PkKCAhQjx499PTTT6d6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+61T693s+HTo0EFXrlzRwoULnY4jvUqUKKE+ffpo8uTJiouLsx+joKAgubq6avfu3Q71U3sKXbIlS5akuJpq4cKFKl++vP1qvzsZWyVfkZPMGJPiKc/t27eXMUY//vhjmu1k5li6WbNmio+P1/Llyx3KnR3P3en613Omv5o3b674+HgtW7bModzZxHZmnKN347tOTkNiCsimOnbsqNOnT+vtt99WTEyMDhw4oG7duqlWrVpOt/H2228rIiJCTz75pMLDw3X27Fm9/fbbqV72OnHiRCUlJaldu3Zau3atYmJidPr0aS1btkxdunTRrl27FBERoVatWmnhwoU6efKk4uPjtXHjRq1YsUJNmza1t5U8F9KSJUvu6PLwjNy/m+nYsaNmz56tP//8U5cuXdLq1as1ZMgQNW7c2KHeW2+9pTNnzuiJJ57QkSNHdObMGb3++uv2D6dbcaaP0ysj+nrcuHGKjY1V9+7d9e+//yoqKkofffSRvv76a7344ov2gdqd9ndasTp7XgEA0taxY0cdPHhQH3zwgWJiYrR371517dr1lkma640bN05hYWEaOHCgjh8/roiICL3yyivKly9firqTJ09WdHS02rdvr40bN+rSpUs6efKkli5dqvbt2yssLExhYWFq166dfvvtN506dUpxcXEKCQnR2rVr1axZM3tbyZ8PixcvduqzbMKECTpz5oweffRRhYWF6dy5c5owYYLmzJmjV199NcUVUzcKCAjQsGHDNG3aNH3++ec6f/689uzZo/79+6tBgwYOdfPly6ePPvpIM2fO1IsvvqiDBw8qLi5O//77r6ZPn66uXbs607V3JCAgQM8995ymTp2qL7/8UhcuXNDevXs1YMAA1a9f/5br161bV4UKFdIbb7yhsLAwRUZGasqUKTp+/LhT2/f29lbp0qW1atWqFLczdenSRV27dtWAAQP09ddf69SpU4qNjdX27ds1evRovfPOO7ezy+rfv78++ugj7du3T3FxcTp27Ji+/vpreXt7q27dupL+S5A+9thj+vLLL7Vy5UrFxMRo4cKFWrFiRZpjs7Jly2rIkCE6fvy4Tp06peeee06hoaGaMGGCU/t7Mx07dtTly5c1cuRIXbhwQWFhYerdu7cqVarkUK9Lly7q0KGDhg0bpq+++kpnzpxRRESEZs2apVdfffWOYnBGr169VK1aNQ0cOFAhISGKiYnRsmXLtGjRIvtVaJm5fjJn+6tnz56qXr26Bg0apNWrVysmJkZLly7VkiVLnNpeZpyjmXl8cqy7OdM6AOckPzEirX/JT36ZMmWKKVOmjPHy8jL33Xef+fHHH+1Pwkh+4kvyU/lufNJEsuXLl5vatWsbT09PU6JECTN58mT700A2b97sUPfkyZPm6aefNiVLljTu7u6maNGipm3btmb+/Pn2J/T89ddfpkuXLqZo0aLG29vblCtXzowcOdL+5Ixkr7zyiilSpIhxcXExkszKlSvT1UeDBg0yNWvWvGU9Z/cv+Ul358+fT9HGxYsXzYABA0zhwoWNj4+PadGihdm9e7d55JFHHJ62Y4wxK1asMHXq1DGenp4mMDDQfPDBB2bRokVOPZXOGOf6OK1Y03r6THr6Oq24tmzZYh5++GHj5+dnPDw8TJUqVcxnn32WYn1n+zut7aQVq7PnFQDci5KfypfWv379+hljjHn//fdNqVKljJeXl6lWrZqZN2+emTJlisP78c2eZGaMMb/99pu5//77jYeHhylVqpT54osvHJ60d73w8HAzYMAAU6JECePu7m6KFy9uOnToYH777TeTlJRkkpKSzNKlS02HDh1MkSJFTK5cuUyFChXMa6+9ZmJjYx3aev75503hwoWNzWZzeFJtWtatW2datWplfH19jaenp6lRo4b56quvHOokP2Eu+UnG10tMTDTjxo0zJUqUMJ6enqZOnTpmw4YN5rHHHkv1KXcrVqwwDz/8sMmXL5/x9PQ0FSpUMIMGDXJ4Al/16tVN69atU403rafypVb/ueeeM56eniniHTt2rAkMDDSenp6mXr16ZvPmzSY4ONiULl067Y76/7Zu3WoaN25sfHx8TKFChcyzzz5rH4+++uqr9nqpPZXPmP8+/6tVq2Y8PDwczrnk2D755BNTq1YtkytXLuPn52dq1qxpxo8fb6Kioowx//fEs+ufrnwz4eHhZvjw4aZSpUrGy8vLFClSxHTt2tVs3brVoV5UVJTp2bOn8fX1Nf7+/qZv374mNjY2zafyTZkyxcyaNcuUK1fOPt755ZdfUmw/rf291X58/fXXpkKFCsbLy8tUqFDBfPXVV2bevHlGktmwYYO93rVr18y7775rKleubH8SZJ8+fczRo0dvGcPNnsqXWlxlypQxjzzyiENZRESEeeyxx4y/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27bp3Xff1YsvvqgPPvhAGzZssDqkm/rmm2+0d+9eq8MAAAD3uGydmEqvKVOmqGLFilaHAQAAcpC4uDj16tVLzZo105EjR1S0aFGdOnVKI0aM0COPPGJ1eAAAAFmam9UBfPLJJypRooTy5Mmj33//XQEBAfrf//4nSfr777+1aNEiXb16VQ888IAeeeQR2Wy2VNuZNGmSypQpo86dOzuUv/baa2rWrJmaN2+uffv26dKlS5Kkq1ev6oUXXtDzzz+vLVu2aOfOnSpUqJCeeuop+fv729dPSkrSnDlztG3bNgUFBal79+5699131bdvX913332Z0ykAACDbGDZsmJYtW6YdO3aoZMmSDst27drl8Hrz5s1atGiR4uLi9OCDD6pTp04OY5vXXntN7du3V0REhDZv3iwfHx899dRTCggIcGjn4MGD+vnnn3XhwgVVqlRJvXr1kqenZ4p2oqKitGHDBuXKlUu9e/dW8eLFU8S/dOnSNOtMnjxZ//77r2w2mwICAtSiRQvVq1cvRRt//PGHli9froCAAHXs2FHz5s3TAw88oBYtWmRKzAAAIOew/Iqp+fPn69lnn9WLL74oPz8/BQYGSpImTpyotm3b6sqVK/L19dXbb7+t9u3byxiTajvXrl3TyJEjHcq2bdumsWPH2gc019/Kd/XqVX366adq1aqVlixZIn9/f/3www9q2LChEhIS7G0MGDBA//vf/+Tl5aV///1XdevW1ccff6zDhw9nRncAAIBs5MyZM5o+fbpGjhyZIiklSVWrVrX/f9q0aWrUqJEuXLggb29vDR48WL1793ao/80336h79+6aOHGicuXKpWXLlqlGjRo6efKkvc68efNUu3ZtHT9+XPnz59e3336rWrVq2X98S26nZ8+e+uSTT+Tj46PVq1erRo0aioyMdNjem2++edM6JUqUUMWKFVW+fHlFRUWpbdu2+uyzzxzaeP/999W5c2clJCQoMjJSLVq00IQJExQaGpopMQMAgJzFZtLK9NwlLVu21KFDh7R//355eHhIkg4cOKAqVapo586d9lvvYmJiVLZsWX3xxRf2q6JsNpv+/PNPtWzZUqdPn1ZgYKBWrVqlBx98UNJ/k53v2LFDISEhKerHxsbK19dXL7/8st59911J0rlz5xQQEKDly5erSZMm2rlzp2rUqKH169fbfx38/PPPNWTIEC1atEjt27dPsT/x8fGKj4+3v46OjlZgYKAW5a0nH5vlF6gBAJDhmkWGWB2C06Kjo+Xv76+LFy/Kz8/vjttbvHixOnTooI0bN6pu3bpp1rtw4YJKliyp8ePHa8iQIZJkH2esWLFCTZs2lSQVL15cBQoU0NatW+Xm5qbExETVrVtXderU0WeffabY2FgFBgbq22+/VYcOHST9d3V3nTp1FBwcbP+Rrnjx4qpZs6YWLFggSUpISFBQUJBef/11DRo0yOk6N5o/f76eeuopRUVFyWazKSoqSkFBQfrkk0/Up08fSdKqVavUrFkzvf/++xo+fHiGxnyjtMZdpR8Pk6uH780OHQBY6sD0AlaHAGSq9Iy5skSm5KGHHrInpaT/Bnne3t76/PPP7VdIGWPk7u6ubdu2pbhdT5ICAgLUvn17ff3113rwwQcVHx+vH374QZMmTbrptlu3bm3/f4ECBVSwYEH7VVWrV69WUFCQwyXrvXr1sg8oUzN+/Hi9+eabTu03AADI3i5evChJKly48E3r/f3337p48aLDFVLVqlVLkZiSpG7dusnN7b8hmqurq3r27KmvvvpKkrRmzRpduHBBy5Yt0/Lly2WMkTFG8fHx2rZtm8M2rx/juLm5qXz58ikeAnOrOgkJCZo3b5527dql8+fPKzo6WhcuXNCpU6dUtGhR/f3337p06ZJ69OhhX6dp06YOtx5mdMzXY9wFAED2lyUSU3nz5nV4ffbsWfn6+qps2bIO5SNHjlSNGjXSbKd///7q2bOnPv74Yy1atEjXrl1T9+7db7rtXLlyObx2dXW138p35swZFSjgmMn29/eXu7t7mu2NGjVKL7zwgv118i93AAAg5ylYsKAk6fjx46neypfs9OnT8vLykq+v41U8hQsX1unTpx3Kbhx7FCxY0F7n7NmzcnNzU4UKFRzqlC9fXkFBQQ5lNxvjOFMnISFBzZo1U1RUlLp06aKyZcsqOjpa0n/jm6JFi+rMmTPy8fGRl5dXmvuQ0TFfj3EXAADZX5ZITN2oSJEiunjxogYNGnTTJNCN2rRpozx58uinn37Sjz/+qO7du8vHx+e24yhWrJhOnDjhUBYREaFr166luY6np6fDRJ4AACDnqlevnnLlyqUlS5aoYcOGadYLDAxUXFyczp0755C0OXr0qBo0aOBQ9/jx4w6vjx07Zk+2FClSRAkJCWrXrp1KlSqVgXuS0t9//63169crMjJSefLkkSStX79er7/+ur1OsWLFFBsbq+joaPtl+sYYhzmxMjNmxl0AAGR/lk9+npquXbsqISFBY8eOdSj/559/FBYWluZ6rq6ueuqpp/T+++9r+fLl6tev3x3F0aZNG507d84+14EkTZ069Y7aBAAAOYefn59eeuklTZ48WevWrXNYdu3aNfsYonbt2ipWrJg+/vhj+/I///xT+/bts8+7lGzWrFn2K5NiYmI0c+ZMdezYUZLUuHFjFStWTK+++qoSExPt65w4ccJhsvGMkNx+8hxOSUlJ+uCDDxzq1KlTR4ULF3YYH82ZM0dRUVH213czZgAAkP1kySumihcvrh9++EF9+vTR77//rsqVKyssLEzR0dH69ddfb7puv379NG7cOJUrVy7FL5DpVbJkSb3++uvq0aOHOnbsqJiYGJ05c0bu7u5yccmSOT0AAHCXvf7660pMTFSrVq1Ut25dlStXTqdPn9bu3bv1yCOPqFOnTvL29tYXX3yh7t27a+PGjcqbN68WL16s0aNHp5imoFChQnrggQfUoEEDrV+/Xt7e3ho+fLgkycvLS7/++qu6dOmiqlWrqm7dujpx4oTCw8P1zTffZOh+1a1bVw0aNFC9evXUrFkzbdu2TTabzaFOrly59NFHH+mJJ57QmjVr5O3trR07diggIMA+VrqbMQMAgOzH8sTUs88+q0KFCqUo79Kli5o1a6YVK1bo/Pnz6tevnxo0aOCQEJoyZYr9qX3JSpQooUKFCqlv374p2ry+vqenp6ZMmZLikvKxY8c6THY+evRotW3bVqGhoQoKClKNGjVUqFAh5c+f/472GwAA5AwuLi56++23NWzYMIWEhOjs2bMqWrSoatas6TAJeLt27RQWFqa//vpLcXFxGjNmjCpXrpyivSFDhqhu3br6+++/1blzZz388MMOczjVqVNHhw4d0ooVK3TixAmVKlVKDRs2dKhz43hGSjnmulUdV1dXrVy5Ur///rtOnjyp3r17q3bt2po5c6bDfvXs2VM1a9bUmjVrFBAQoEaNGqlixYoOY6WMihkAAOQ8NpP82LscYunSperatavCw8MzZCCzbds23X///fZfCCdMmKB3331Xp06dSjHRZ2qSH5G4KG89+dgszwMCAJDhmkWGWB2C09Lz6GIrFC9eXGPHjtWTTz5pdShO27Vrl8qXL2+f62n+/Pnq2rWrDh06lOnzYN0o+fiWfjxMrh6+t14BACxyYHqBW1cCsrH0jLlyTKbk6NGjeueddzR//nw9//zzGfbr2ubNm/XEE0+oVq1aCg8P19atW/X11187lZQCAADI6U6dOqVHHnlEdevW1fnz5/XXX3/p3XffvetJKQAAkD3lmMSUt7e3qlatqo4dO6pt27YZ1u7gwYPVsmVLbdy4Ublz51a9evUcLl8HAADIKKndzpbVPfTQQ1q1apVCQkJks9n08ccfq3Tp0laHBQAAsokcdytfVsOtfACAnI5b+ZBVcCsfgOyCW/mQ06VnzMWj5QAAAAAAAGAJElMAAAAAAACwBIkpAAAAAAAAWILEFAAAAAAAACxBYgoAAAAAAACWIDEFAAAAAAAAS5CYAgAAAAAAgCVITAEAAAAAAMASJKYAAAAAAABgCRJTAAAAAAAAsASJKQAAAAAAAFjCzeoA7hWNj/whPz8/q8MAAADI8UI/yc+4CwCAbIIrpgAAAAAAAGAJElMAAAAAAACwBIkpAAAAAAAAWILEFAAAAAAAACxBYgoAAAAAAACWIDEFAAAAAAAAS5CYAgAAAAAAgCVITAEAAAAAAMASJKYAAAAAAABgCTerA7hXrCnZWj42uhsAkD00iwyxOgTgtt3/TKRcPa5aHQaAbO7A9AJWhwDcE7hiCgAAAAAAAJYgMQUAAAAAAABLkJgCAAAAAACAJUhMAQAAAAAAwBIkpgAAAAAAAGAJElMAAAAAAACwBIkpAAAAAAAAWILEFAAAAAAAACxBYgoAAAAAAACWIDEFAAAAAAAAS5CYAgAAAAAAgCVITAEAAAAAAMASJKYAAAAAAABgCRJTAAAAAAAAsMRdT0xFRUVpwIABqlGjhpo2baoLFy6oYsWK2rdvX7raqVixojZs2HDTOjVr1tSqVavStQ4AAMC95OLFizp37pyuXbuWYtnly5d17tw5Xbp0yYLIAADAveCuJ6befPNN7d27VzNmzNBXX32lhIQE7d+/X3FxcelqZ//+/bccJO3fv1+xsbHpWgcAAOBe0rp1axUsWFDjxo1Lsaxz584qWLCgXn31VQsiAwAA94K7npjavn27HnroIdWoUUNlypRR3rx5tXfvXlWqVCnTt7137141aNAg07cDAACQnZQrV07ffPONkpKS7GXh4eEKCQlRUFCQQ93kK6wiIyMd6l/v/Pnzio+PlyRdunQpxQ+Dly9fTjOWpKSkmy4HAAA5y11NTFWpUkWbN2/WlClTVLFiRVWsWFH33XefOnfurMOHD9vrXbt2Te+9954aNmyoOnXqaPDgwTp16tRN2z537pwGDBig+++/X507d9by5ctT1OncubN27Ngh6b8BUcWKFbVkyRL1799fderUUfv27bVp0yaHdSIiItSvXz+Hdm+8RRAAACA7a926tRISEhzGT1999ZXat2+vAgUKONR94oknVLFiRZUvX14+Pj5q27atjh496lCnatWqGjFihGrUqKGAgACNGTNGkjRx4kQVKFBAhQoVUtGiRTV58mQZYyRJsbGx6tu3r3x9fVWwYEGVLFlSs2bNyuQ9BwAAVruriam5c+eqQoUKeuyxxzR//nzNnz9fM2bMSHErX7du3bRkyRKNGTNGn376qSSpXr16Drfl3ahjx47as2ePPvroI/Xr108DBgxI8Wvb9bfyJSUlaf/+/erbt68aNWqkqVOnKigoSG3atNH58+clScYYtW/fXvv377e3O3DgQIWGht40FgAAgOzE1dVVTz75pL766itJ/42TZsyYoX79+qWou2DBAvsVU6dPn1aePHnUt2/fFPVmzpypTz/9VDExMZo4caK2bdumUaNG6bffflNsbKx27Niho0eP6sqVK5Kknj176vz58zpy5IguXbqkGTNmaPDgwQoJCcncnQcAAJZyu5sbq1Chgry8vFSgQAFVrFhR0n9XOl1v9erVWrZsmSIiIuTr6ytJqlWrlipWrKg5c+akOkD6888/tWXLFh05ckTFihWTJLm7u6tt27a3jOn1119Xnz59JEnVqlXTV199pY0bN6pt27ZatmyZduzYoSNHjqho0aKSJC8vLz300ENpthcfH2+/dF2SoqOjbxkDAACA1fr27asqVaooKipKmzZtkjFGDz30kEaPHp1q/YSEBCUkJOiZZ55Ro0aNdOXKFXl7e9uXDxgwQA8++KD9dUREhHLlyqXKlStLkgoWLKhJkyZJkv755x8tWbJE+/btk7u7u86fP69q1aqpXbt2+vHHH9WoUaNUY2DcBQBA9ndXE1POCAkJUVJSkurXr2+/tNsYoxMnTujAgQOprhMaGqpy5crZk1KS1LRpU6e2V716dfv/3d3dVaBAAZ05c0aStGPHDpUtW9ae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LFhQknT69Gnlzp1buXPndqhz5swZeXp6yt/f36G+MUYnTpxQoUKF5OHhoYsXL8rX11cuLinvbjTGKCoqSvny5ZPNZtPJkyeVL18+BnAAACDbadasmaZPn56ifOHChapatao8PDy0detWFStWTLVq1dKKFSt05swZ2Ww2BQQEqEaNGvL39091XR8fH23fvl25cuVSvXr15ObmONRMSEjQ5s2bFRkZqVq1aqlIkSIOy69du6aNGzfqwoULqlixosqVK5fxHQAAALKUO5pjqmfPnhowYIAaNmyooKAgDRkyRJIUHh6uhx56SPnz51dgYKDKli2r33//3WHdI0eOqE2bNvLz81NgYKAaNmyof//9177cmTYCAwMVEhIiSXrmmWc0YMAAh+WXLl1S6dKl7etdX//SpUsKDAzUW2+9pVKlSqlkyZLy9fXVxx9/7NDGmjVrVKpUKQUGBip//vwaPXq0ypUrp7/++utOug4AACBLefrppzVw4EA9+OCD+uabb7Rjxw5J0tq1azV//nz9+uuvevnll1WmTBmtWLEixbqDBg1S/fr1NWXKFPXs2VMNGzbU1atX7XV27dqlihUr6rHHHtPnn3+uBg0a6KuvvrIv/+eff1SxYkU9++yz+vLLL/Xggw/qqaeekjHm7nQAAACwxB3fyjd37lwtWLBA7dq1kyTFx8erVatW6ty5sxYtWiRPT0/99NNPCg4O1s6dO1W6dGnFxcWpRYsWqlKlik6fPq38+fNr/fr12rBhg8qVK+dUGzd67LHH1Lt3b8XGxtqvmpo/f75cXFzUsWPHNONftGiRQkJCVLx4cf3000969NFH1aFDB5UqVUqXL19W9+7d1aNHD02aNElXrlxRcHCwLl++nGZ78fHxDhOsR0dH327XAgAAZLiwsDDNnj3b/jooKEj169eXJB07dkw7duxwuCLq9ddfd1j/gw8+0KBBgxx+UJSks2fPaufOnfLx8VFUVJRKlSqluXPnqlevXkpISFDnzp1Vt25dzZw5U+7u7rp69ao9wZWYmKiuXbtqwIABGjlypCQpKipK999/v2bOnKknn3wy1X1h3AUAQPZ3x0/la9++vT0pJUkLFizQ2bNnNWzYMEVFRenUqVNq2LChypUrp0WLFkmSfv31V504cUJff/218ufPL0lq0KCBnnjiCafbuFG7du3k6empefPm2cu+//57PfLII/L29k4z/tGjR6t48eKSpO7du8vHx0ehoaGS/ktsXb58WePHj5ebm5t8fX01ceLEm/bH+PHj5e/vb/8XGBh4qy4EAAC4a8LDwzV//nz7vy1bttiXPfHEEylu05P+e7LxsmXLNGfOHLm6uurgwYM6f/68Q53HH39cPj4+kqR8+fKpWrVq2r9/vyQpJCREYWFhGj9+vNzd3SVJHh4eatOmjSRp3bp1OnDggIoWLapffvlFP//8s/766y+VKVNGK1euTHNfGHcBAJD93fEVU2XLlnV4vWvXLl2+fFl16tRJUffixYuSpD179qhMmTL2pNSNnGnjRh4eHgoODtb333+vxx9/XGfPntWff/6Z4va/G904gPH19bX/2nbw4EGVKVNGuXLlsi+vXLmyXF1d02xv1KhReuGFF+yvo6OjGSQBAIAsI605piSlmPNJkoYPH66pU6eqdu3aKliwoP32vDNnzihv3rz2evny5XNYz9PTU3FxcZKko0ePytPTU0FBQalu98iRI3Jzc9Nvv/3mUF6oUCFVqlQpzX1h3AUAQPZ3x4mpGye1dHV1VZEiRXTkyJGbrnP9Zdc3cqaN1PTu3VvNmzdXRESEfv75ZxUqVEjNmjVLVxvX8/DwcJgbQfrvUvOkpKQ01/H09JSnp+dtbxMAAMAqNpvN4fWePXs0adIk/fPPP6pcubIkafv27Vq4cGG65n7KkyeP4uPjFRMTI19f3xTL/fz8lJCQoE8//TTNHy5Tw7gLAIDs745v5btRvXr1FB4ebr8d7nrJCZ169erpyJEjKeYmuH75rdpITaNGjVSsWDHNmTNH33//vR599NFUn7LnrKpVq+rgwYM6c+aMvWz9+vVMwgkAAO4Jp0+flru7u8qUKWMv++WXX9LdTsOGDeXt7a3vvvvOofzs2bOS/hvD5cqVS1988YXD8qSkJJ0+ffo2IgcAANnFHV8xdaPWrVurVatWeuSRRzRp0iRVrlxZYWFhmjZtmp577jk1bdpUrVu3VqNGjdSlSxdNnDhRgYGB+uOPP5SQkKCXX37ZqTZSY7PZ9Oijj+qjjz7S4cOH9fnnn9/RvrRt21YVK1ZU7969NWHCBMXExOjpp5+WzWZL8YsiAABATpN8+15wcLC6dOmirVu3as6cOeluJ3/+/Prwww/17LPPau/evapevbo2bdokLy8vTZkyRfnz59cnn3yiQYMG6dChQ2rQoIFOnDihX3/9Va+99poeeeSRTNg7AACQFdzRFVMFCxZMMUGmzWbTokWL1L9/f40fP17t27fXp59+qqeeesqeULLZbFqyZIk6d+6sESNGqGfPnjp16pSGDh3qdBuSVKxYMXl5eTlsv3fv3rp69aoaN26s6tWrOyy7vr6Li4uKFSuW4vLvIkWK2CfudHFx0eLFi5UnTx51795db731lsaNGydXV9cU2wUAAMjqWrZsmeocnpLUqVMnlSpVyqHM19dXGzZs0H333adVq1YpICBAISEh6tGjh/z8/G66brNmzRzGYoMGDdLatWvl7u6ujRs3qkGDBpo8ebJ9+VNPPaW///5bhQsX1po1a5SUlKQffviBpBQAADmczXBf2k0ZYxyujtqyZYvq1Kmjo0ePOjW5ZnR0tPz9/bUobz352DL8AjUAAO5Is8gQq0O4q5I/ly9evOiQWEHOkHx8Sz8eJlePlHNZAcD1DkwvYHUIQI6VnjEXmZJbeOONN1SqVCn7vFcvvPCC2rVrxxNfAAAAAAAA7lCGT36e0zz99NNav369goODNWbMGHXt2vW2Jv0EAAAAAACAI66YuoXChQtr2rRpVocBAAAAAACQ43DFFAAAAAAAACxBYgoAAAAAAACWIDEFAAAAAAAAS5CYAgAAAAAAgCVITAEAAAAAAMASJKYAAAAAAABgCRJTAAAAAAAAsASJKQAAAAAAAFiCxBQAAAAAAAAsQWIKAAAAAAAAliAxBQAAAAAAAEu4WR3AvaLxkT/k5+dndRgAAAA5Xugn+Rl3AQCQTXDFFAAAAAAAACxBYgoAAAAAAACWIDEFAAAAAAAAS5CYAgAAAAAAgCVITAEAAAAAAMASJKYAAAAAAABgCRJTAAAAAAAAsASJKQAAAAAAAFiCxBQAAAAAAAAsQWIKAAAAAAAAlnCzOoCczhgjSYqOjrY4EgAAkPx5nPz5jJyFcRcAAFlDesZcJKYyWWRkpCQpMDDQ4kgAAECymJgY+fv7Wx0GMhjjLgAAshZnxlwkpjJZvnz5JElHjx7NsQPg6OhoBQYG6tixY/Lz87M6nAzH/mVv7F/2xv5lb1lx/4wxiomJUdGiRa0OBZngXhh33S1Z8e83O6M/Mw59mbHoz4xDXzpKz5iLxFQmc3H5bxovf3//HH9y+vn55eh9ZP+yN/Yve2P/srestn8kLHKue2ncdbdktb/f7I7+zDj0ZcaiPzMOffl/nB1zMfk5AAAAAAAALEFiCgAAAAAAAJYgMZXJPD09NWbMGHl6elodSqbJ6fvI/mVv7F/2xv5lbzl9/5D1cM5lHPoyY9GfGYe+zFj0Z8ahL2+fzfC8ZAAAAAAAAFiAK6YAAAAAAABgCRJTAAAAAAAAsASJKQAAAAAAAFiCxFQmO378uLZu3aqLFy9aHUqGM8bo0KFD2rNnj+Lj460OJ9MkJSVp3bp12rlzp9WhZIqjR49qx44dSkhIsDqUDHf+/HmFhobq33//zRH7FxERobVr1+rChQtp1kl+z7lZnazqwoULWrt2rSIiIlJdnt3fc2JjY7Vu3TodO3bspvWS33N27NhxlyLLGPHx8dqwYYMOHTp003rHjh3Tjh07dO3atbsUGe4VZ8+e1ZYtW3TmzBmrQ8lWjDE6fPiw/vnnH8XFxaVZj/5Nn4MHD2rt2rWKiYlJsSwpKUm7du3Srl27lJSUZEF02cuBAwe0d+9epTU18pkzZzg3nXD58mXt3r1bu3fv1uXLl1Otw7mZtp07d2rLli1pLk9MTNTOnTtv2nfO1LlnGWSKuLg4ExwcbLy9vU2lSpWMl5eX+fDDD60OK8N88cUXJigoyJQpU8ZUqFDB5M2b13z11VdWh5Up3nzzTePi4mLq1q1rdSgZKiwszDz44IMmb968platWqZs2bJm1apVVoeVIRITE82AAQOMt7e3uf/++03x4sVNiRIlzMqVK60O7baEhoaa7t27m0KFChlJZunSpSnqxMfHmx49eji857z//vsWRJt+//77r+nbt68pUqSIsdls5ssvv0xRZ/r06aZkyZKmdOnSpmLFiiZPnjxm2rRpFkSbfidOnDBDhw41RYoUMR4eHubtt9++af1x48YZFxcXU7NmzbsU4Z05f/68GTFihClWrJjx8fExgwYNSrXekSNHTKNGjUyePHlMrVq1TJkyZcyKFSvucrTIqYYPH248PT1N5cqVjaenp3n22WdNUlKS1WFleTNnzjRlypQxpUqVMpUqVTL+/v7mk08+SVGP/k2f8PBwkz9/fiPJbNiwwWHZ9u3bTalSpUyRIkVM0aJFTalSpcz27dstijRrW79+valQoYIpWrSouf/++839999v9u/f71Bn2LBhDufmsGHDLIo2a/vkk0+Mn5+fqVSpkqlUqZLx8/NL8bfOuZm6L774wlStWtXkyZPHFCtWLNU627ZtM0FBQaZYsWImICDAlClTxuzatSvdde5lJKYyyejRo03RokXN8ePHjTHGLFq0yEgy69atsziyjPHWW2+ZY8eO2V9/8803xsXFxWzbts3CqDLemjVrTOnSpU3Pnj1zVGIqNjbWlClTxnTt2tVcvnzZGGPMqVOnzC+//GJxZBnjhx9+MK6urvY3+8TERPPEE0+YoKAgawO7TbNmzTI//vijOXHiRJqJqTfeeMMEBASYo0ePGmOM+e2334zNZjOrV6++2+Gm2+LFi8306dPNpUuXjKenZ6qJqbFjx5rw8HD76++++87YbDazZcuWuxnqbVmzZo35+OOPzYULF0xQUNBNE1Pr1683JUuWNI8++mi2SUzt3r3bjB8/3kRERJgHH3ww1cTU5cuXTbly5UynTp3s7zkRERHm559/vtvhIgeaNWuW8fb2Nn///bcxxpgdO3aYXLly5dgfzDLSO++8Yw4fPmx//dNPPxmbzWbWrl1rL6N/0+fatWumQYMGZsSIESkSU9euXTPlypUzjz32mElKSjJJSUmmZ8+eply5ciYhIcHCqLOew4cPG19fXzNixAiTmJhojDHmn3/+cfhBY8aMGSZXrlz25Mm2bduMt7e3mTlzpiUxZ1Xh4eEpfvj7/PPPjc1ms4+tODfT9tJLL5kdO3aY8ePHp5qYunr1qildurTp06ePMcaYpKQk061bN1OxYkX7uetMnXsdialMUrRoUTN69GiHsho1aph+/fpZFFHm8/b2Np9++qnVYWSYyMhIExQUZFavXm0GDRqUoxJTn376qfHw8DBnz561OpRMMXnyZJM3b16HsqlTpxofH59s/Qvv2bNn00xMlShRwowcOdKhrFatWvYPwOwircRUanLnzm0mT56cyRFlrJslps6fP29KlSplVqxYYYYOHZptElPXSysx9cUXXxh3d3cTERFhQVTI6Zo3b26Cg4Mdynr27GkefPBBiyLK3goUKGDeffdd+2v6N31GjRplgoODzd69e1MkplasWGEkmX379tnLdu/ebSRl26u6M8uQIUNMqVKlbpoUady4senZs6dDWXBwsGnSpEkmR5e9/P3330aS2b17t71s165dRpL9ogLOzVtLKzG1bNkyI8kcPHjQXrZ9+3YjyYSEhDhd517HHFOZ4MyZMzp58qRq1qzpUF6nTh2FhoZaFFXm2r17t65cuaKyZctaHUqG6du3rx599FE1btzY6lAy3PLly1W/fn3ly5dPO3fu1IEDB3LEHEzJHnvsMRUvXlwDBgzQn3/+qVmzZum9997T+PHjZbPZrA4vw0VFReno0aP31HvO3r17FRsbm6Pec/r376/g4GA1a9bM6lAy3PLly1WnTh0VLFhQu3btynHvObBWaGjoPfX+l5nCwsIUFRXl8N5K/zpv+fLl+v777/XFF1+kujw0NFQ+Pj6qUKGCvey+++5Trly56M8bLF++XA8//LASEhK0bds2hYeHp5hjinPTOQ888IB69OihoUOHavHixVq0aJGefvppPfroo7r//vslcW7eidDQUPn7+6tMmTL2surVq8vDw8Ped87Uude5WR1AThQVFSVJyp8/v0N5/vz57ctykitXruipp55S/fr11bJlS6vDyRBTpkzRiRMn9PPPP1sdSqY4efKkfHx8VLNmTSUkJOjChQtycXHRzJkz1bRpU6vDu2P58+fXM888o1GjRmnLli06e/asKlasqPbt21sdWqa4195z4uLi9NRTT6lOnTpq3bq11eFkiKlTpyosLEw//PCD1aFkipMnT8rDw0O1atXS1atX7Q8EmTFjhpo3b25xdMjOjDG6cOFCqu9/ly9fVnx8vDw9PS2KLnu5du2annzySVWrVk0dOnSQRP+mx9mzZ/XEE0/ou+++U758+VKdiDsqKipFX0o59/P6Tpw8eVKRkZGqVKmS/P39dfz4cQUGBmr27NkqX768EhISFBMTk+q5GR0drcTERLm6uloUfdYzePBgDRgwQMOHD5f03yTcb7/9tn055+btc6bv6N9b44qpTODu7i5JKZ5scuXKFXl4eFgRUqa5evWqgoODdeHCBc2dO1cuLtn/lDp27JhefvllDRkyRJs2bdLatWt1+vRpxcTEaO3atYqOjrY6xDvm7u6u5cuXa9y4cdq1a5fCw8P18MMPq3v37jd9Ik928dVXX+mFF17Q6tWrtX37dh07dkwVKlRQs2bNcsT+3ehees+5du2aunfvrrNnz+rXX3/NEYPOEydO6MUXX9SQIUO0efNmrV27VqdOnVJsbKzWrl2bI57q6u7urpUrV2rMmDH295xOnTqpe/fuaT4ZCHCGzWaTm5tbqu9/0v+9P+LmEhMT9eijjyo8PFzz58+39xv967znnntO1apVk4eHh9auXatt27ZJ+u9JXvv27ZP0X3+lNg7JiZ/Xd8rd3V2LFy/WkiVLFBoaqqNHjypfvnzq06ePJMnV1VUuLi6pnpsuLi45YnyQUf755x+1atVKY8aM0b59+7Rv3z69/vrratWqlfbs2SOJc/NOONN39O+tZf8sQhZUrFgxubq66sSJEw7lJ06cUIkSJSyKKuMlJ6X279+vlStXqkiRIlaHlCEuX76smjVr6uuvv9bIkSM1cuRIbdq0SceOHdPIkSMVHh5udYh3rGTJkipRooQefvhhSZKLi4sGDhyos2fP2gdP2dnixYvVpEkTValSRdJ/+zdkyBCFh4dr586dFkeX8YoWLSp3d/cc/55z7do19ejRQ7t379bKlStVrFgxq0PKEFeuXNEDDzygmTNn2t9zNmzYoBMnTmjkyJEKCwuzOsQ7VrJkSRUtWlQdO3aU9N+X3YEDByoyMtI+KAZuV4kSJVJ9/ytevHiO+MEssyUmJuqxxx7Tpk2btHLlSgUFBTksp3+dkydPHsXExNjfxydNmiRJ+vTTT/X9999LkoKCghQZGenwBfXKlSs6f/58jvq8zgglS5ZU48aNValSJUmSt7e3+vTpo82bNys+Pl42m02BgYE5fuyTEX7//XflypVLvXv3tpc9/vjj8vb21u+//y6Jc/NOBAUF6dy5c7p69aq97NKlS7p48aK975ypc6/j0yQTeHl5qVGjRlq4cKG97MqVK/rzzz/VqlUrCyPLONeuXVO3bt20Z88erVq1SsWLF7c6pAxToUIFrV271uFfp06dVLlyZa1du1ZVq1a1OsQ71rp1a50/f97+i6ckHT9+XJJUsGBBq8LKMAULFkwxUDl27Jh9WU7j7u6uJk2aOLznxMXF6Y8//sgx7zkJCQnq2bOntm/frlWrVuWoD/GyZcumeM/p2rWr/b0oef6H7Kx169a6cOGCLl26ZC/LSe85sFarVq20ePFi+/wzxhgtXLgwx7z/ZabExET17t1b69ev16pVq1S6dOkUdehf53z22WcO7+PJyagvvvjCfstUixYtZIzRb7/9Zl8vuW+5rdlR69atU4zljh8/Lj8/P/vto6mdm4sWLeLcvEHBggV16dIlnT9/3l4WFRWlS5cu2T+DOTdvX4sWLXTt2jV7kk+SFi5cKBcXF3vfOVPnXsccU5lk7NixatasmV5++WU1bNhQn376qfLkyaPBgwdbHVqGeOyxx7R8+XJNnz5dR44c0ZEjRyT996taTvrCmFP16NFDH3/8sR555BENHTpUUVFReu211/TYY4/liKtQBg8erBkzZqhv377q3r27IiIi9Prrr6tdu3YqVaqU1eGl27lz57Rv3z77LV3//POPcufOrcDAQPsv22+//baaNGmi4cOHq3Hjxvr888+VO3duPf3001aG7pTo6Gj7lWzGGB08eFBr165VoUKFVL58eUnSE088oaVLl2r69Ok6evSojh49KkkOfZBVxcXFaevWrZKk+Ph4HT16VGvXrlWePHnsV/VlZ8YYrVu3TtJ/x/L06dNau3atcuXKpQceeECSFBwcrMmTJ6tr16763//+pwsXLmj06NHq0aNHlj9+yPpGjBihOXPm6PHHH1ePHj00d+5cHT58WL/88ovVoWV5AwYM0Lx58zRt2jSdPHlSJ0+elPTf1f/Jn5f0b8YJDAzU0KFDNWTIEMXGxkqShg8frmeeeUaBgYEWR5e1vPDCC/ruu+80cOBAPfLII/r33381YcIEjRo1yl5n1KhRqlmzpp588kkFBwfr559/1vHjxzVy5EgLI896OnXqpOLFi6tjx44aPny4jDGaNGmSvUzi3LyZ3bt368KFCwoPD9fVq1e1du1aSVKtWrXk5eWlUqVKafDgwRo0aJAuXryoxMREDR8+XM8995z9jiJn6tzrbObGxxsgw2zYsEGffPKJTp8+rSpVqmjkyJE55sR76KGHUp0XpE+fPhowYIAFEWWuiRMn6t9//03zKSvZUUxMjCZOnKj169fL399fDz30kPr165dj7sn/559/9Omnn+rQoUPy9fVVkyZNNHDgwGw5Sevy5cs1ZsyYFOW9e/d2SHZv2rRJU6ZM0alTp3TfffdpxIgR2SLRuGvXLg0ZMiRFeevWrfXaa69Jktq2bauYmJgUdW7sg6zoxIkT6tGjR4ry2rVr68MPP0x1nQ8//FD//POPpk+fntnh3bGrV6+m+mtfUFCQ/YoBSYqNjdWkSZO0bt06+fn5qWXLlurfv7/c3PiNDHfuwIEDev/993Xo0CGVKlVKw4cPt98ChLR16tRJkZGRKcq7d++u//3vf/bX9G/6HT16VI8++qimTZumypUr28uTkpL0+eefa/HixZKk9u3ba/Dg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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def clean_token(token):\n", + " return token.replace(\"\\n\", \"\\\\n\")\n", + "\n", + "\n", + "token_ids = primary_result.prompt_token_ids.tolist()\n", + "position_tokens = [\n", + " clean_token(model.tokenizer.decode([token_id])) for token_id in token_ids\n", + "]\n", + "\n", + "layer_position_rows = []\n", + "for layer_result in primary_result.layers:\n", + " ff1_top = layer_result.input_projection.top_tokens(model.tokenizer, k=3)\n", + " ff2_bottom = layer_result.output_projection.bottom_tokens(model.tokenizer, k=3)\n", + " for position, prompt_token in enumerate(position_tokens):\n", + " layer_position_rows.append(\n", + " {\n", + " \"layer\": layer_result.layer,\n", + " \"position\": position,\n", + " \"prompt token\": prompt_token,\n", + " \"FF1 top raw directions\": \", \".join(\n", + " map(clean_token, ff1_top[position])\n", + " ),\n", + " \"FF2 bottom raw (update ordering)\": \", \".join(\n", + " map(clean_token, ff2_bottom[position])\n", + " ),\n", + " }\n", + " )\n", + "display(pd.DataFrame(layer_position_rows))\n", + "\n", + "final_position = -1\n", + "norm_comparison_rows = []\n", + "for layer_result in primary_result.layers:\n", + " ff2 = layer_result.output_projection\n", + " raw_bottom = ff2.bottom_tokens(model.tokenizer, k=5)[final_position]\n", + " normalized_bottom = ff2.bottom_tokens(\n", + " model.tokenizer, k=5, normalized=True\n", + " )[final_position]\n", + " norm_comparison_rows.append(\n", + " {\n", + " \"layer\": layer_result.layer,\n", + " \"VJP norm\": float(ff2.factor_norms[final_position]),\n", + " \"raw FF2 bottom tokens\": \", \".join(map(clean_token, raw_bottom)),\n", + " \"normalized FF2 bottom tokens\": \", \".join(\n", + " map(clean_token, normalized_bottom)\n", + " ),\n", + " }\n", + " )\n", + "norm_comparison = pd.DataFrame(norm_comparison_rows)\n", + "display(\n", + " norm_comparison.style.set_uuid(\"ff2-norm-comparison\")\n", + " .background_gradient(cmap=\"viridis\", subset=[\"VJP norm\"])\n", + " .format({\"VJP norm\": \"{:.3e}\"})\n", + ")\n", + "\n", + "last = primary_result.layer(DETAILED_LAYERS[-1]).output_projection\n", + "raw_top = last.top(k=5)\n", + "raw_bottom = last.bottom(k=5)\n", + "top_ids = raw_top.indices[final_position].tolist()\n", + "bottom_ids = raw_bottom.indices[final_position].tolist()\n", + "top_tokens = [clean_token(model.tokenizer.decode([token_id])) for token_id in top_ids]\n", + "bottom_tokens = [\n", + " clean_token(model.tokenizer.decode([token_id])) for token_id in bottom_ids\n", + "]\n", + "\n", + "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n", + "axes[0].barh(top_tokens, raw_top.values[final_position].numpy(), color=\"#be123c\")\n", + "axes[0].invert_yaxis()\n", + "axes[0].set(\n", + " xlabel=\"projected raw-gradient logit\",\n", + " title=\"Largest +gradient logits\",\n", + ")\n", + "axes[1].barh(\n", + " bottom_tokens, -raw_bottom.values[final_position].numpy(), color=\"#2563eb\"\n", + ")\n", + "axes[1].invert_yaxis()\n", + "axes[1].set(\n", + " xlabel=\"negative projected raw-gradient logit\",\n", + " title=\"Largest ordering after subtracting gradient\",\n", + ")\n", + "fig.suptitle(\"FF2 final-position sign ordering (not a direct P(-v) readout)\")\n", + "fig.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2bc454cb", + "metadata": {}, + "source": [ + "## Reconstruction error and compact gradient rank\n", + "\n", + "The gradient rank is estimated from the small factor core rather than by taking an expensive SVD of the full MLP matrix. The estimate uses a relative singular-value threshold of $10^{-5}$ and is bounded by the number of prompt positions." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "323e645c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:06.032990Z", + "iopub.status.busy": "2026-08-25T09:58:06.032758Z", + "iopub.status.idle": "2026-08-25T09:58:06.425298Z", + "shell.execute_reply": "2026-08-25T09:58:06.424567Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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zhg9erRzndzcXEOS8eSTT5Z73WX7GjVqlHHjjTcae/bsMbZu3WosWbLE2LBhgyHJ+PDDD8s9LzQ01Jg8ebLL2Ole/6lyFBcXG5KMmTNnOsfMqBcAoP7r2rWrERISUun1d+zYYQQGBhoDBgwwNm3aZGRkZBivv/664ePjY9x8883O9d544w1DknHdddcZc+fONbKysowlS5YYAQEBxgMPPGDcc889xptvvmlkZmYay5YtMwIDA43bbrvNZV9l27jmmmuMV1991cjIyDA2bdpknHvuuUazZs2MQ4cOnTRnamqqMWfOHMPT09OYO3euczwpKcmIjIw0unTpYvz0009GTk6OsWPHDuOxxx4zFi5caBiGYbz33nuGJGPTpk2VqsmWLVsMPz8/Y9iwYcaOHTuM9PR044UXXjBGjx5tSDI+++yzcq/p2muvNf79738b6enpxvz58428vLxKv4atW7cajRs3Ni655BJj+/btRnp6uvHss89WuL+PP/7YkGRs2LDBOXb48GGjTZs2Ro8ePYyVK1caubm5xooVK4z27dsbF198seFwOFyyTpgwwXjttdeMjIwM448//jDat29vnHPOOS5Zq1ozd9ShKvnK1t25c6dhGIbx559/Gq1btzZ69eplJCUlVSozUBvRmALqsO+++86QZDz11FNVet7QoUMNScayZcsqtX5hYaHh4+NjPP/884ZhGEZ+fr4RGBhoXHXVVS7r5eTkGMHBwadtTBUVFRkRERHGeeedV25fU6dONUJCQozCwkKXrGvWrHFZb8SIEUZ0dLTLWHUaUxXVoayub7zxhsv4sWPHjPDwcGcj8MsvvzQkGf/+979Puo+PPvrIkGS8++67LuOrVq1yadiUNVo6d+5slJaWOtdLT083PD09jUcffdQ5dttttxnh4eGnfG2VaUxFRUUZJSUlLsuq0piqzOuvamPKjHoBAOq/Jk2aGD169Kj0+tddd53RqFEjIyUlxWX8gQcecGlMlDUCZsyY4bLeHXfcYfj5+Rn333+/y/idd95p+Pr6ury/lm3jrrvucll3//79hpeXlzFt2rTT5h0zZozRt29f5+MpU6YYPj4+p/wgpqpNlmuvvdYIDAw0srOzXcbHjx9/0sbUnXfeWaltV/Qaxo0bZwQEBDg/dCozduzYSjWmbr/9dqNRo0YuH5wahmH8/PPPhiTjm2++ccn6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lqYPP/xQ999/vyRp9OjR6t+/v+655x599dVXys3N1a+//qrx48erbdu2mjp1apXz3X777Xr++eeVkJCggoICHTp0SG+++aZ8fHzUr18/SZKnp6c6deqkX375RRkZGZXetpeXlyZOnKh33nlHP/zwg3Jzc/X1119r0aJFLj8DlX39Vc1hRr0AAPVfq1attHDhQm3cuFEXXHCB8z0sPz9fK1as0PDhw/Xoo486P2SZOXOmPDw8dNVVV2nLli3KysrSW2+9pRdffFGTJk1St27d3J4xKSlJb7zxhrKysrRlyxZdc8018vf3d75nSsfnKvrqq6/02Wef6ejRo/rtt9903XXXlWtezZo1S82bN9fll1+uJUuWKDc3V7t27dLjjz/u/GCta9eustls+vrrryvVaHn00UdVVFSkcePGadeuXcrIyNDLL79c4VnMp1LZ1/Doo4+quLhY1157rXbu3KmMjAw9//zzlW4KPfHEEwoNDdWwYcP0008/KTs7W2lpafr55591/fXXl5tjqjKqWrNTqWwdqiswMFDff/+9+vXrp6FDh+qbb75xy3YBK9CYAuqBZ555RsuWLVNGRoZGjRqloKAgtWzZUmPGjNGuXbv0wQcf6MEHHzztdry8vPTll1+qZcuW6tevn9q3b6+ff/5Z77//frnG1t/+9jfNmTNHb7zxhlq1aqXRo0fr1ltvVUxMTKUyl82JlJWVpfPOO09BQUEaMmSIvv/+ez333HPVqsPUqVM1atQoXXjhhfLw8DijU7bff/99Pfvss3rrrbfUsWNHhYeH66abbpK3t7emTZvmXG/BggWaNWuW/v3vfysqKkrdu3fX119/rTvuuEPS8cbMd999pxtvvFF33HGHQkJCdNVVV2nw4MFauXKlgoKCqpzt4YcfVnp6uq655hoFBwerV69eysrKcn5yVua1115Tbm6uwsPDZbPZNH78+Ept/7nnntPIkSM1ZswYtWrVSgsWLFB8fHyF9Tzd669qDjPqBQBoGC666CJt3rxZ5557ru6++26FhYWpRYsWuu222xQeHq41a9bI09NTktSpUyetXr1aTZs21aBBg9S8eXM9//zzevLJJ/XGG2+Ykm/WrFnavn27OnTooN69e8vf31+//PKLy/xETz75pOLi4jR16lQ1a9ZM9957r1555RVFRUW5bKvs9QwaNEgTJkxQ06ZNNXz4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SDr+CV5BQYH8/f318ssv67///a9bAwIAADR0+/bt04QJEzRhwgT16tVLPXr0cPmqbGOqsLBQr7zyijp27KjAwEAFBARo/PjxzF8FAEAd4OXto2SPCElS2t5NFqdxn2o1po4ePSp/f39JUosWLZSYmCjp+PwG2dnZbgsHAAAAqX379kpOTj7j7WzatElpaWn67rvvdOzYMf3xxx/avHmzJk2a5IaUAADAbBmN2kiSjh7aanES96n25OdlhgwZojvvvFOTJ0/Whx9+qN69e7sjFwAAAP7foEGD9MADD+jZZ5/VqFGjyk1+HhAQoICAgNNup1evXurVq5fzcXR0tEaPHq0333zT7ZkBAID7FQS2k46ukO3ITqujuE21zph64403nN//4x//UJMmTXT33XcrOztbr7/+utvCAQAA4PjNV3bt2qUZM2aoS5cuioyMdPl68cUXq7S9vLw8HTlyRCtWrNB7772nm266yaTkAADAnTybd5IkNc7dY3ES96nWGVNTpkxxft+8eXN99tlnbgsEAAAAV1OnTtXYsWNPurxp06ZV2t6VV16pX3/9Vfn5+Ro1apRmzJhx0nULCwtVWFjofJyTkyNJcjgccjgcVdpvZTgcDhmGYcq2Gzpqaw7qah5qaw7qap6aqG1Aqy7SRql54f5a/W9YlWxnfCkfAAAAzNW0adMqN59O5YcffpAk7dy5U9ddd51GjhzpHPur2bNna9asWeXGMzMzVVJS4rZMZRwOh3Jzc2UYhuz2ap3cj5Ogtuagruahtuagruapidr6hkZKkporQ/v27VFj/yBT9nOmcnNzK71upRtTffv2rfRGV69eXel1AQAAUF56errS09OdDalT3Tmvuo2rDh066Mknn9SwYcN08OBBtWrVqtw6Dz74oKZPn+58nJOTo8jISAUHB1dqXquqcjgcstlsCg4O5g8mN6O25qCu5qG25qCu5qmJ2oaEhChNwWqmTBVkHFBkm7am7OdMeXpW/jyoSq85fPjwaoUBAABA1f3rX//SrFmzNHPmTEmq8KylMjNnztTjjz9erf2UXZrn5eVV4XIfHx/5+PiUG7fb7aYddNtsNlO335BRW3NQV/NQW3NQV/PURG1TvVurWVGmcg5ulf2cC0zbz5moyuuvdGPqkUceqVaY+iY+Pl7x8fEqLS21OgoAAKjHpk+frilTpjjPSjpxjs+/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AAAAWIJL+arJMAxJUk5Ojtu37XA4lJubK09PT05hdzNqax5qaw7qah5qaw7q6qrsOKHsuKE+MPMYSOJnyEzU1hzU1TzU1hzU1TzU9n+qcgxEY6qK4uPjFR8fr6KiIklSZGSkxYkAAEBtl5ubW2/u5pubmyuJYyAAAHB6lTkGshn16SO8GuRwOJSUlCR/f3+338o4JydHkZGROnDggAICAty67YaO2pqH2pqDupqH2pqDuroyDEO5ubmKiIioN5+cmnkMJPEzZCZqaw7qah5qaw7qah5q+z9VOQbijKlqstvtatWqlan7CAgIaPA/zGahtuahtuagruahtuagrv9TX86UKlMTx0ASP0NmorbmoK7mobbmoK7mobbHVfYYqH58dAcAAAAAAIA6h8YUAAAAAAAALEFjqhby8fHRzJkz5ePjY3WUeofamofamoO6mofamoO64kzxM2QeamsO6moeamsO6moeals9TH4OAAAAAAAAS3DGFAAAAAAAACxBYwoAAAAAAACWoDEFAAAAAAAAS9CYqoUOHjyodevWKSsry+oo9Upubq42bNiglJQUq6PUSw6HQytXrtSff/5pdZR65dixY9qwYYMOHz5sdZR6JSMjQ+vXr9fOnTtVWlpqdZw6y+FwaO3atdq0adNJ1yksLNT69eu1ffv2GkyGuqqoqEgbNmzQtm3brI5SrxiGod27dyshIUGFhYVWx6mXEhMTtWLFCo7f3Wzfvn36888/VVJSYnWUeqO0tFQ7d+7U+vXrlZGRYXWcOi05OVkrVqxQdnb2Sdc5cOCA1q1bp5ycnBpMVvfQmKpFioqKNHbsWHXs2FE33nijwsPD9fzzz1sdq85LTEzUNddco8jISE2aNEmdOnXSJZdcotTUVKuj1StPPfWUBg8erFtuucXqKPXGc889p+bNm+vGG29U//79NWXKFA7MzlBpaakmT56sVq1aacqUKbrgggvUvn17LV++3OpodUpxcbGeeeYZdejQQUOGDNGdd95Z4XrffPONWrZsqauvvlr9+vVTr169lJycXMNpUVf89NNPatWqla666ioNHDhQPXr00IEDB6yOVee9/vrrio6O1tChQzV69GiFh4fr7bfftjpWvZKamqoBAwZo0KBBWr16tdVx6oXdu3erf//+OvvsszV58mTFxMTwXu0Gy5cvV/v27XXhhRdqypQpatWqlSZPnsyHdFW0bt06jRkzRt27d9egQYO0YcOGcusUFBToqquuUqdOnXTDDTcoLCxMc+bMsSBt3UBjqhaZPXu2li1bpu3btyshIUGffvqp7r//fv3yyy9WR6vTdu/erauvvlqZmZnasGGDEhMTlZaWpttuu83qaPXG8uXLNW/ePF1zzTVWR6k34uPj9fjjj+ubb77Rpk2btHv3bg0aNIhPus/QBx98oPfee0/r1q3T+vXrtX//fg0YMEA33XST1dHqlPz8fGVlZemHH37QmDFjKlwnPT1d1157re666y7t3r1bSUlJ8vLy0uTJk2s4LeqCrKwsXX311br55pu1Z88eJSUlKSAgQBMmTLA6Wp13+PBhrVixQrt27dK2bdv04osv6uabb67wDylUnWEYmjBhgm644Qaro9QbeXl5GjJkiFq1aqVDhw5p7dq1WrZsmdLT062OVudNnDhRgwYN0r59+7R+/XqtWbNG7777rj788EOro9Upmzdv1rXXXqvffvvtpOvMmjVLa9as0e7du7V161Z98MEHmjZt2imf05DRmKpF3n77bU2cOFGRkZGSpGHDhumcc87hU60zdNFFF+nqq6+WzWaTJAUHB2vs2LFasWKFxcnqh4yMDN1www2aO3euAgMDrY5TL5SUlOiJJ57Q1KlTNXDgQOf4hAkT1LhxYwuT1X1paWkKDAxUTEyMJMlut6tv375KS0uzOFndEhgYqGeeeUZt27Y96Toff/yxSktL9be//U2S5Ovrq3vvvVfffvstZ02hnM8++0xHjx7VAw88IEny9vbWAw88oKVLlyoxMdHacHXco48+qlatWjkfT5w4UT4+Plq1apWFqeqP5557TqWlpbrrrrusjlJvvP3220pOTtZ//vMf+fr6SpLCw8N15ZVXWpys7ktLS9O5554ru/14GyA2NlZBQUEcB1XRxIkTdfXVV8vLy+uk68ydO1dTpkxReHi4JOmKK65QbGys5s6dW1Mx6xQaU7VERkaG9u/fr3POOcdlvE+fPnyiZYK1a9eqffv2VseoFyZNmqTrrrtOgwcPtjpKvbFp0yalpqZqxIgRSk1N1fr165WZmWl1rHrhxhtvVIsWLXTbbbfphx9+0HvvvacXXnhBzzzzjNXR6p0NGzaoS5cu8vPzc4716dNHhmFo48aN1gVDrbRhwwZ16NBBAQEBzrE+ffo4l8F9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YwoAAAAAAACWYI4pAAAAAAAAWILGFAAAAAAAACxBYwoAAAAAAACWoDEFAAAAAAAAS9CYAgAAAAAAgCVoTAEAAAAAAMASNKYAAAAAAABgCRpTACDp6NGjWrBggfLy8qyOAgAAUKN+++03rV692uoYABoom2EYhtUhAMBqiYmJio6O1s6dO9W+fXur4wAAANSYiRMnqqSkRPPnz7c6CoAGiDOmAAAAAAAAYAlPqwMAQG3kcDj00UcfSZI8PT0VFRWlHj16yNPzf782Fy5cqLPPPlvR0dEuz/3444/Vu3dvRUVFSZKysrK0evVq2e129ezZU82aNXOum52drW+++UZXXHGFtm/frl27dqlv375q2bKl+S8SAACgAn/++acSEhIkScHBwTrrrLMUHh7uXL59+3bt2LFDI0aMcHne1q1btXv3bg0fPtw5tmHDBu3bt09t2rRR9+7dZbf/79yIlStXysfHRx06dNCqVatks9k0dOhQk18dgNqGxhQAVKC0tFSff/65JKm4uFgbNmxQYGCgvv32W7Vo0UKS9N5772nRokV69913nc9buXKlxo4dqz179kiS5s+frzvvvFM9e/aUl5eXfvvtN7344ouaNGmSJOnAgQMaN26cLr/8ciUmJiomJkZt2rShMQUAACyzZcsWLVq0SJKUnp6uVatW6ZlnntGdd94pSSooKNCoUaO0c+dOtWvXzvm8adOmqXXr1ho+fLgyMzM1evRo7d27V927d9eWLVvUokULLV68WKGhoZKkl156Sfv379eRI0fUsWNHde/encYU0AAxxxQA6PRzTJWWlmr48OFq37695syZI0n66quvdM011yg5OVkBAQGSpMmTJ+vAgQP6/vvvlZCQoD59+mjJkiXq06ePJGn58uW65JJLtG3bNrVp00abN29Wt27dNHHiRL399tuy2Ww196IBAAB0+jmmVq5cqYsuukh79uxRRESEJKl3794aOnSonnrqKUnS/v37FR0dreXLl6t///66/vrrVVRUpA8//FCenp4qKSnRiBEj1KZNG7322muSpDFjxujLL7/Uxo0b1blz55p5sQBqHc6YAoBT2LRpkxITE3X06FGFhYVpzZo1zmWXXnqpQkJCtGDBAt1yyy06evSoPvroI73xxhuSjp8t1bJlS+3fv1/79u1T2ecAfn5++vXXX9WmTRvntu68806aUgAAoNbIysrSxo0blZqaKofDIS8vL/3xxx/OxtSUKVP01FNP6YknnpDdbtfcuXPVoUMH9e/f33lMNGPGDH3++ecyDEOGYah169ZaunSpy34uueQSmlJAA0djCgAqkJeXp0svvVQ7duzQOeeco4CAAO3du1dpaWnOdTw8PJxnOt1yyy366KOP5OXlpSuvvFLS8bOwjh07pk8++cRl20OGDFFQUJDL2InzNgAAAFhp/vz5uuOOO9S5c2e1atVK3t7eKi0tVWpqqnOd6667Tn/729/0/fffa+jQoXrnnXd0++23S5IOHTqkkpIS/f7779q5c6fLtgcOHOjymGMgADSmAKACr7/+utLS0rR//375+vpKkp555hnnqedlJk+erL///e9KSEjQ22+/reuuu04+Pj6SpICAAIWFhWnBggWn3R9nSwEAgNrAMAzFxcXplVde0U033eQca9y4sU6cBcbf31/XXHON3n77bXl5eengwYO68cYbJck5xcHUqVN12WWXnXJ/HAMBsJ9+FQBoeFJSUtSmTRtnU8owDH366afl1ouKitJFF12khx56SCtWrNDkyZOdyy699FL9/vvvWrt2rctzsrKylJ+fb+4LAAAAqIZjx44pJydHnTp1co599dVXOnbsWLl1p0yZosWLF+uFF17QZZddprCwMElSWFiYunfvXu4DPen42VQAcCLOmAKACowcOVIvvvii7r//fnXo0EEfffSRdu7cqcDAwHLr3nzzzbrmmmvUo0cP9ezZ0zk+atQojRs3TkOGDNG0adPUpk0bJSQkaPHixfrll1/k5+dXky8JAADgtPz8/DRkyBDdfvvtmjp1qpKSkhQfH+/8sO5E/fv3V/v27fXNN98472Zc5vXXX9fQoUM1dOhQXXnllcrLy9P333+vc845R7Nnz66hVwOgLuCMKQCQ1LhxY1177bXy9/eXdHz+gyVLlujo0aNatWqVrr76ai1YsEAjRowo99zhw4fLw8NDkyZNchm32WyaP3++5s+fr8zMTK1atUqRkZFas2aNcz6FoKAgXXvttWrUqJH5LxIAAKAC5557rvr16+d8/Omnn+q6667TihUrlJeXpyVLlmjSpEmKjo4u99xRo0apRYsWuvzyy13G+/Tpo4SEBJ1//vlatWqVUlNTNWPGDJem1MCBA9W7d2/zXhiAOsFmnHihMACgyhYvXqxx48bpwIEDCgkJsToOAABAjTnnnHM0ZMgQPfPMM1ZHAVBHcSkfAFTTwYMH9dNPP+mpp57S7bffTlMKAAA0GN98841++OEH7dy5U1988YXVcQDUYTSmAKCaDh06pO+++04TJ07Uvffea3UcAACAGrNkyRLl5OTou+++U0REhNVxANRhXMoHAAAAAAAASzD5OQAAAAAAACxBYwoAAAAAAACWoDEFAAAAAAAAS9CYAgAAAAAAgCVoTAEAAAAAAMASNKYAAAAAAABgCRpTAAAAAAAAsASNKQAAAAAAAFiCxhQAAAAAAAAs8X8Qr1RS5/heoQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
layermatrixrelative reconstruction errorcompact numerical rank
00FF10.000e+006
10FF20.000e+006
22FF10.000e+006
32FF20.000e+006
44FF10.000e+006
54FF20.000e+006
66FF10.000e+006
76FF20.000e+006
88FF10.000e+006
98FF20.000e+006
1010FF10.000e+006
1110FF20.000e+006
1211FF10.000e+001
1311FF20.000e+001
\n", + "
" + ], + "text/plain": [ + " layer matrix relative reconstruction error compact numerical rank\n", + "0 0 FF1 0.000e+00 6\n", + "1 0 FF2 0.000e+00 6\n", + "2 2 FF1 0.000e+00 6\n", + "3 2 FF2 0.000e+00 6\n", + "4 4 FF1 0.000e+00 6\n", + "5 4 FF2 0.000e+00 6\n", + "6 6 FF1 0.000e+00 6\n", + "7 6 FF2 0.000e+00 6\n", + "8 8 FF1 0.000e+00 6\n", + "9 8 FF2 0.000e+00 6\n", + "10 10 FF1 0.000e+00 6\n", + "11 10 FF2 0.000e+00 6\n", + "12 11 FF1 0.000e+00 1\n", + "13 11 FF2 0.000e+00 1" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def compact_gradient_rank(factors, relative_threshold=1e-5):\n", + " x = factors.forward_inputs\n", + " delta = factors.output_gradients\n", + " _, rx = torch.linalg.qr(x.T, mode=\"reduced\")\n", + " _, rd = torch.linalg.qr(delta.T, mode=\"reduced\")\n", + " singular_values = torch.linalg.svdvals(rx @ rd.T)\n", + " threshold = singular_values.max() * relative_threshold\n", + " return int((singular_values > threshold).sum())\n", + "\n", + "\n", + "diagnostic_rows = []\n", + "for layer_result in primary_result.layers:\n", + " for name, matrix in (\n", + " (\"FF1\", layer_result.input_projection),\n", + " (\"FF2\", layer_result.output_projection),\n", + " ):\n", + " diagnostic_rows.append(\n", + " {\n", + " \"layer\": layer_result.layer,\n", + " \"matrix\": name,\n", + " \"relative reconstruction error\": (\n", + " matrix.factors.relative_reconstruction_error\n", + " ),\n", + " \"compact numerical rank\": compact_gradient_rank(matrix.factors),\n", + " }\n", + " )\n", + "diagnostics = pd.DataFrame(diagnostic_rows)\n", + "\n", + "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n", + "for matrix_name, group in diagnostics.groupby(\"matrix\"):\n", + " axes[0].plot(\n", + " group[\"layer\"],\n", + " np.maximum(group[\"relative reconstruction error\"], 1e-12),\n", + " marker=\"o\",\n", + " label=matrix_name,\n", + " )\n", + " axes[1].plot(\n", + " group[\"layer\"],\n", + " group[\"compact numerical rank\"],\n", + " marker=\"o\",\n", + " label=matrix_name,\n", + " )\n", + "axes[0].set(\n", + " yscale=\"log\",\n", + " xlabel=\"layer\",\n", + " ylabel=\"relative error\",\n", + " title=\"Gradient reconstruction\",\n", + ")\n", + "axes[1].set(xlabel=\"layer\", ylabel=\"estimated rank\", title=\"Compact gradient rank\")\n", + "for ax in axes:\n", + " ax.grid(alpha=0.25)\n", + " ax.legend()\n", + "fig.tight_layout()\n", + "plt.show()\n", + "diagnostics_for_display = diagnostics.copy()\n", + "diagnostics_for_display[\"relative reconstruction error\"] = diagnostics_for_display[\n", + " \"relative reconstruction error\"\n", + "].map(lambda value: f\"{value:.3e}\")\n", + "display(diagnostics_for_display)" + ] + }, + { + "cell_type": "markdown", + "id": "79b42321", + "metadata": {}, + "source": [ + "## Target-token ranks: raw versus Normalized Logit Lens\n", + "\n", + "Ranks are zero-based and ascending in raw-gradient space: rank 0 is the smallest projected FF2 gradient logit. Lower is therefore the update-relevant ordering, but this remains a directional diagnostic rather than a prediction of an optimizer step." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e87a4974", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:06.427228Z", + "iopub.status.busy": "2026-08-25T09:58:06.427019Z", + "iopub.status.idle": "2026-08-25T09:58:06.625088Z", + "shell.execute_reply": "2026-08-25T09:58:06.624388Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
layerraw target ranknormalized target rankfinal-position VJP norm
0040650406430.655670
1220776207921.442085
24561656281.329280
36221622171.042345
48000.487238
510000.274904
611000.260877
\n", + "
" + ], + "text/plain": [ + " layer raw target rank normalized target rank final-position VJP norm\n", + "0 0 40650 40643 0.655670\n", + "1 2 20776 20792 1.442085\n", + "2 4 5616 5628 1.329280\n", + "3 6 2216 2217 1.042345\n", + "4 8 0 0 0.487238\n", + "5 10 0 0 0.274904\n", + "6 11 0 0 0.260877" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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OQL1sPyKx4KytbXN0/bvv/9y+vRp/vnnH52bSk6ePJnvGI86AqfkMwddt27dCAgIYM6cObi5uTF9+vQ8fWbMmMGUKVM4fvw4e/fu5ccff2TJkiVs27aNdu3aFXTXgP/lL79c3717lzJlyujkIL98GMJ/7959mFqtZsmSJUyaNCnPvH9xcXHaa0T1ibFr16507dqVixcvsm/fPtauXUvfvn155513mDVrVuF3RAgzkrt3hRCPtGHDBgYNGkSzZs34559/CnUK9MEH68OnWiG36MvIyMDT01On/ciRI9qjSk/y4IL93bt367QnJCRw9uxZnbbQ0FBUKhWXLl165KTUD07V2dra5olXHykpKQB59nHdunVPPTbAp59+yuzZs5kxY8YjJze2srKicePGvPHGG+zfvx+VSqVzU01BNWvWDMg9wviwzMxM9u7dS5MmTbCwMO9HSXp6Ojk5OXnyvWPHDtRqtUG2UbNmTV566SU2bdpEw4YNDfazFMKUpOgTQuRr+/bt9OvXj8aNG7Nx40bt0bWCCggIAMgzVYiDgwMhISH8/PPP2tO0165dY+rUqU+8K/iBevXq0bVrVz799FPthL1paWlMnDiRatWq6fStVq0aEyZM4OOPP2bFihXa6+qys7PZtWsXb775pk7MFy9eJCMjo1D7+l9NmjTBxsaGL7/8kqysLAD+/vtvbayGMH36dBYvXsy8efMYPXq0dr9WrlzJ999/ry08Ibc41mg0BAYGFno7derUoXfv3syfP19b+GVkZDBu3Dhu377Nu+++a5gdegoeHh7UqFGD5cuXa0+JX7x4kVmzZul92h7g1KlTvPvuuzpPQbl69So3b97UK5dCmJsUfUKIfL3yyitkZGRw8+ZNGjZsmOfo2JMMGDCANm3a0Lx5c6pWraozT9+KFSsoW7Ysfn5+eHt706VLFz755JNCXQy/cuVKQkNDadq0KZ6entSqVYsBAwbg7e2tc5E9wPz58/n00095++23sbe3x8/PDycnJz766CPatm2r7ffWW2+h0WioUKFCgebpexQ/Pz9+/PFHNmzYgLu7O+XLl+e7777j66+/1mu8Rxk9ejS//vory5cvZ9CgQWRlZdGsWTNOnDiBj48P3t7eeHh48NJLLzFr1ixefvllvbbz888/M2TIELp3706FChVwcnJi165drFu3TmfeRHNavXo1Go0G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8+ZkZmZKcf0U3n//fZ2c/teZM2c4evQob775JpaWlgC89dZbxMbGsnHjRlOFWSxJ0VcMnD9/nszMTEJCQrRtlpaWNGjQgJMnT5oxspLv2LFjVK1a1dxhlDgjR45k8ODBtGrVytyhlEiRkZFcuXKF7t27k5CQwPHjx7UFijCcDh060L59e1544QU2bdrEH3/8wZQpU3j99depVKmSucMrsR587j38mejp6Ym3t7d8Jj6BlbkDEE+WkJAAgJubm067m5sbd+7cMUdIpcKiRYvYt28fe/bsMXcoJcpXX31FVFQUv/32m7lDKbFu374NwPbt2xkzZgyVKlXi4sWLdO/enR9//BFbW1szR1gylClThvHjx/PKK68wZcoU0tPTKVeuHEOHDjV3aCVaQkIC9vb2ed7Hbm5u2s9LkT850lcMlClTBoD79+/rtKenp2NtbW2OkEq8tWvXMmHCBBYvXvzY0wyicCIjI5k6dSpjx47l8OHD7Nu3j+joaFJSUti3b5/cNGMgD/5mHDp0iCtXrnDixAkuXrzIzp07+eijj8wcXcmxa9cu+vTpww8//MDZs2e5evUqQ4cOpXXr1vIPuRGVKVOGjIwM/vtAMflMfDIp+ooBPz8/AKKionTao6Ki8PX1NUdIJdq6desYPHgwCxcuZOTIkeYOp0RJS0sjJCSEpUuXMn36dKZPn87hw4eJjIxk+vTpREREmDvEEuHB34zBgwdTrlw5AHx9fXn22WfZu3evOUMrUf755x8CAgLo1KmTtu2VV17h3r177Ny504yRlWx+fn7k5OQQExOjbdNoNERHR8tn4hNI0VcMVK1alcqVK/PXX39p227dusXx48fp0KGDGSMref7880+ee+45vvzyS5lGxAhq1KjBvn37dL569uxJYGAg+/bto27duuYOsURwc3OjYcOGef5RvHXrFuXLlzdTVCVP+fLliY+P15mb78F8iJJn42nZsiU2NjY6n4m7d+8mKSlJPhOfQK7pKyZmz57N0KFDqVChAjVq1ODjjz8mJCSEvn37mju0EuPff/9lwIABDB48mNq1a2vv2rWysqJJkyZmjk6Iwvn000/p2bMnFStWpF69emzZsoU9e/bINaoGNGTIED799FP69OnD2LFjSU9P56OPPqJevXpyk9JTuHz5MrGxsVy5cgVA+7e4Xr16ODo64uTkxJtvvsmUKVNQqVQ4ODgwffp0Bg8eTL169cwZepGnUv57UlwUWZs2beKHH34gKSmJxo0bM3XqVJnuwoAWLVqU7xQ45cqVY9OmTWaIqHSYO3cuV65cYfHixeYOpcQ5cOAA33zzDTExMVSpUoVx48bJ0VQDi4yM5IsvvuD8+fNYW1vTsGFDxo8fL3+bn8IHH3zAli1b8rR/++23Ou/f5cuXs3btWjIzM+nYsSPjx4+Xa/qeQIo+IYQQQohSQK7pE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIA9i8eTOXL182dxhCCPFIUvQJIYQBvPHGG2zcuNHcYQghxCNJ0SeEEEIIUQpYmTsAIYQoiXbt2kV0dDQqlQoPDw/q16+v8zzWgwcPotFoaN68uc56+/fvR6VS0axZMwCys7M5dOgQCQkJ1KxZk+rVq+v0//vvv6lduza2trYcO3YMT09PGjVqZPwdFEIUO1L0CSGEEezfv58zZ86gKAoRERFcuXKFVatW0aFDBwDOnTvHW2+9RVRUFFZWuX+Ks7Ky6NOnD++//z7NmjXjwoUL9OjRAzs7O/z9/Tl8+DCdOnVi+fLlWFjknqgZP348VatWJTw8nODgYLp06SJFnxAiX1L0CSGEEbz11ls6r7/88ktGjx7N9evXAXjuued4/fXX2bBhA7169QJgw4YNpKSkMGjQIDQaDX379mX48OG8/fbbACQlJVG/fn2WLl3KSy+9pB07IiKCsLAwnJ2dTbJvQojiSYo+IYQwkqioKM6dO0diYiIqlYobN24QFxdH+fLlcXBw4LnnnmPp0qXaom/p0qX06dMHZ2dnDhw4wIULF/D29ub3339HURQURaFq1ars3LlTp+h7/vnnpeATQjyRFH1CCGEE06dP56uvvqJRo0aUL1+erKwsAGJjYylfvjwAL730Ei1atCA6OhrInfZly5YtANy4cQMLCws2b96sM66bmxuBgYE6bZ6ensbeHSFECSBFnxBCGNjly5f59NNPOXXqFPXq1QPg7Nmz/PnnnyiKou3XuHFjatWqxU8//YSiKPj4+NCmTRsAHB0d0Wg0fPHFF3h4eDx2eyqVyng7I4QoMWTKFiGEMLDo6GgsLCyoVq2atu3333/Pt+9LL73EsmXLWLZsGS+88IK2gGvRogVly5Zl8eLFOv01Go32yKAQQhSGHOkTQggDCwkJoVKlSvTr14++ffty4sQJVq1alW/f559/nmnTppGZmcmIESO07c7Oznz77be8+OKLXL9+nebNm3Pnzh3WrVvH9OnTGTBggIn2RghRUsiRPiGEMIBnn32WGjVqAFC2bFkOHjxIvXr12LVrF+XLl2ffvn0MHDgwzw0XLi4utGrVivbt2+Pj46Oz7Pnnn+fEiRN4enqyd+9esrOzWbFihU7B1717dwICAoy+f0KI4k+lPHyBiRBCCJNSq9V4eXmxbNky+vXrZ+5whBAlmBR9QghhBhqNhtWrV7NmzRouX77M6dOnsbS0NHdYQogSTE7vCiGEGeTk5PDXX3/h4+PDP//8IwWfEMLo5EifEEIIIUQpIEf6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKgf8DrrvcCf+PXBcAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(primary_layer_summary.round({\"final-position VJP norm\": 8}))\n", + "\n", + "fig, ax = plt.subplots(figsize=(7, 4))\n", + "ax.plot(\n", + " primary_layer_summary[\"layer\"],\n", + " primary_layer_summary[\"raw target rank\"],\n", + " marker=\"o\",\n", + " label=\"raw factor\",\n", + ")\n", + "ax.plot(\n", + " primary_layer_summary[\"layer\"],\n", + " primary_layer_summary[\"normalized target rank\"],\n", + " marker=\"s\",\n", + " label=\"unit-normalized factor\",\n", + ")\n", + "ax.set(\n", + " xlabel=\"layer\",\n", + " ylabel=\"ascending target rank (lower is smaller)\",\n", + " title=f\"FF2 target ranks for {target!r}\",\n", + ")\n", + "ax.grid(alpha=0.25)\n", + "ax.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1d7baa6d", + "metadata": {}, + "source": [ + "## VJP norm heatmap\n", + "\n", + "FF2 factor norms are norms of the residual-width vector-Jacobian products (VJPs). The logarithmic heatmap exposes both token-position and layer variation without discarding low-norm rows." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "942c9dce", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:06.627148Z", + "iopub.status.busy": "2026-08-25T09:58:06.626949Z", + "iopub.status.idle": "2026-08-25T09:58:06.902195Z", + "shell.execute_reply": "2026-08-25T09:58:06.901485Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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MD8vFXwlSpRQuZZ29uzZqFChAlq2bAlDQ0McPHgQM2bMQLdu3fJk+j4/w8pCQ0OlDqHQMTAwwN27d6UOg4j0GJNVonzUpk0bpKenSx1GkREQEIA+ffrA0dERs2fPzrdREamelwq3AQMGYMaMGZgyZQoiIiLg5uaG0aNHK251okv8DBMRkT7gmlUiIiIiIiLSO1yzSkRERERERHqHySoRERERERHpnSKxZlUul+P58+ewsrKCTCaTOhwiIiIioiJBCIG4uDiULFkSBgYFa5wsOTkZqampWrU1MTGBmZmZjiMqeopEsvr8+XO4u7tLHQYRERERUZEUGhpaoG5RlJycDE9PV0REvNGqvbOzM548ecKENZeKRLJqZWUFAAj5oR+szUwkjobymqy8i9QhUD6R1yna930sSgyO/St1CJRPUm+8lDoEyicmjTykDoHyQWxiCjx8flH8PF5QpKamIiLiDZ6G/A5rawuN2sbGJqJ0qS+QmprKZDWXikSymjn119rMBNbmTFYLO5kl/1EoKuQa/udBBZeBhanUIVA+STXl/9NFhQm/10VKQV2KZ13MDNbFzDVrJJfnTTBFUJFIVomIiIiIiDQml2uefDJZ1Rkmq0RERERERKowWZUUk1UiIiIiIiJVhHh3aNqGdILJKhERERERkSpyocXIKpNVXWGySkREREREpAqnAUuqYN2Zl4iIiIiIiIoEjqwSERERERGpwpFVSTFZJSIiIiIiUoXJqqSYrBIREREREakitEhWBZNVXWGySkREREREpIJMyCHTMPnUtD5lj8kqERERERGRKpwGLCkmq0RERERERKrIheb3TeV9VnWGt64hIiIiIiIivVOgRlblcjkMDJhfExERERFRPuA0YEkViGTV398fy5YtQ1RUFKpUqYKffvoJrVq1kjosIiIiIiIqzPIhWZXL5di+fTsuXLgAW1tb9OvXD5UqVcp1G2361Td6P0y5atUqzJ07F9u2bUNMTAx69OiBzp0748mTJ1KHRkREREREhZkQ725Fo9Gh2ZrVnj17Yvr06XBxccGzZ89Qq1Yt/Pvvv7luo02/+kbvR1aXLFmCoUOHok2bNgAAPz8/bNy4EatWrcL8+fMljo6IiIiIiAqtPB5ZPXLkCPbs2YNbt26hatWqAID09HSMHz8eV69e1bqNNv3qI70eWX39+jUePHiAFi1aKMpk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kQKTVSIiIiIiIhWEln8AIDY2VulISUnReXwhISE4cuQIWrVqlW2dhw8fonTp0kplnp6einPq1pECpwETERERERGppPkGS5m7Abu7uyuVzpo1C35+fkplISEhmDNnTo69ffrpp+jVq1eW8ri4OPTo0QM1atTA6NGjs22flJQER0dHpTIrKyvFOXXrSIHJKhERERERkUpCizvRvGsQGhoKa2trRampqWmWmpaWlmjYsGGOvZUqVSpLWUJCAjp16oSMjAz89ddfMDExyba9tbU1oqOjlcrevHmjOKduHSkUiWQ1c/vohPh4iSMhIiIiIgCITU2VOgTKB3FpaQDy/nYueed/03o1ZW1t/dFEz9HREb6+vhr1m5iYiE6dOiEmJgYnT56Evb19jvWrVauGgIAApbLbt2/DwMAAlStXVruOJEQREBoaKvDuVxw8ePDgwYMHDx48ePDI5yM0NFTqlEAjSUlJwtnZWevrdXZ2FklJSTqPKzExUXh5eYkaNWqIV69eqazz7NkzMXToUHHv3j0hhBBBQUFCJpOJwMBAIYQQaWlpomnTpqJjx46KNurUkYJMiAL7aw61yeVyPH/+HFZWVpDJNJ1zXjDFxsbC3d09y/QDKnz4XhcdfK+LDr7XRQff66KjqL7XQgjExcWhZMmSSrvYFgTJyclI1XIGgImJCczMzHQcETB69GisWLEC3t7ecHBwUJR7eHhg5syZAIBr166hdu3aOHXqFLy8vAAAP/74I/z9/dG6dWs8evQICQkJOHnypGITJXXr5LcikawWRbGxsbCxsUFMTEyR+gexKOJ7XXTwvS46+F4XHXyviw6+16QLp06dwqNHj7KUOzg4oHv37gDerTX9888/0alTJ7i4uCjqBAcH49KlS7C1tUXr1q1hbm6epR916uQnJquFFP9BLDr4XhcdfK+LDr7XRQff66KD7zWR5grWWDwREREREREVCUxWCylTU1PMmjVL5RbZVLjwvS46+F4XHXyviw6+10UH32sizXEaMBEREREREekdjqwSERERERGR3mGySkRERERERHqHySoRERERERHpHSarRERE+SgpKQlDhgzB/v37pQ6FiIhIrzFZJSKSWGhoKOrUqYOQkBCpQ6F80LFjR6SkpKBVq1ZSh0JERKTXuBswEZHE5HI5WrRoAZlMhr///hsGBvw9YmF1//59VKxYETEx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0hLnz5/HypUr4ePjg+joaJw7dw4ZGRnYu3cvAKBjx45ZktWP9ZPpY9fx77//olu3bqhcuTJsbGxw7949LFiwAD179lRM8f3iiy+yjSsyMlJRR93nzIy9cePGSEhIQLFixXD69GmsWrVKKXYiIiL6gCAiogIvLS1NABA1a9YUcXFxQgghHj58KCwtLcX27duV6jRv3lwkJycr2v7xxx/C1NRU3Lp1SwghRHJysmjRooXo0qWLos6uXbsEALFy5UpF2dChQwUAsXHjRkVZr169RPfu3bO0mzhxoqJs5cqVolixYuLly5dCCCECAwOFqalpjtenTj/qXEfnzp3FuHHjFI/j4+PFkSNHhBBCHDp0SBgaGirOqYrrwzqavHbLly9XlC1atEiUKFEix2smIiIq6rhmlYioEBk1ahSKFSsGAChbtiw+++yzLLvXjhgxAqamporHAQEB6NatG6pWrQoAMDU1xTfffIPAwEAkJCQo6hkaGmLo0KGKx40aNYKZmRkGDhyoVHb//v0scU2ePFnx92HDhsHY2BgHDx7U+Ppy6ked6zA3N8eTJ08QFxcHALC0tETbtm01jiOTJq/d8OHDFY+9vLzw8uVLvH37VuvnJiIiKuyYrBIRFSKZU08zeXp6IiQkRKnMxcVF6XFISAjKlCmjVFa2bFkAwLNnzxRlxYoVU1qLaWpqChsbGxgYGCiVJScnK/VlZmamtI7V0NAQHh4eWeL6mI/1o851+Pv7Iy4uDk5OTmjZsiUWLlyolFRqKjevHYAsrxURERH9D5NVIqJCJDo6OstjR0dHpTKZTKb02NHREW/evFEqy3z8YVttJCcnIykp6aNx5bYfda6jbNmyOHXqFJ49e4bRo0dj+/bt6NKli0ZxvC+vXzsiIqKijMkqEVEhkrkZEACkpaUhMDAQTZo0ybFN06ZN8ddffyElJUVRtmvXLlSoUAHFixfXSVz79u1T/P3KlSsIDQ1F48aNAbwbdUxLS4NcLs9VP+pcR3h4OIB3iWTPnj0xbdo0XLhwQeVzqRNXfrx2RERERRV3AyYiKkROnDgBHx8fNGrUCAEBAUhLS8P48eNzbDN+/Hhs2rQJrVq1wsCBA3Hr1i2sXr1aKTHMDWNjY3z99dd49OgRLC0tsXDhQvTt2xe1atUCAFStWhUmJiaYMmUK6tatq/LWNer0o851DB8+HNbW1mjWrBmEEFi+fDl69uypMm5VcX0or187IiKioowjq0REhciWLVtQu3ZtXLx4EQ0bNsSFCxdga2sLADAwMECvXr1QokQJpTaWlpaKe5j+999/MDIywvnz59GhQwdFHXd3d3z22WdK7UqXLg1vb2+lsnLlyqFz585KZRYWFvjnn3+QmpqKa9eu4bvvvsPGjRsV54sXL47jx48jJSUF+/btw82bN1Ve28f6Uec6AgMD4e3tjdu3b+POnTuYPn061q9fD+DdWt5evXrlGNeHdbR97WxtbdGrVy+Ym5urvFYiIiICZEIIIXUQRESUO+np6TA2NsapU6fg5eUldTgKf/zxB3x9fXO9662u+iEiIqKCgyOrREREREREpHeYrBIRFQLZTfGVmqopsFL2Q0RERAUHpwETERERERGR3uHIKhEREREREekdJqtERERERESkd5isEhERERERkd5hskpERERERER6h8kqERERERER6R0mq0RERERERKR3mKwSERERERGR3mGySkRERERERHqHySoRERERERHpnf8DCdyAae3PEwIAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Near-zero threshold: 1.442e-08\n" + ] + } + ], + "source": [ + "vjp_norms = np.stack(\n", + " [layer.output_projection.factor_norms.numpy() for layer in primary_result.layers]\n", + ")\n", + "near_zero_threshold = max(\n", + " float(vjp_norms.max()) * 1e-8, torch.finfo(torch.float32).tiny\n", + ")\n", + "near_zero_mask = vjp_norms <= near_zero_threshold\n", + "log_vjp_norms = np.log10(np.maximum(vjp_norms, near_zero_threshold))\n", + "masked_log_vjp_norms = np.ma.masked_where(near_zero_mask, log_vjp_norms)\n", + "cmap = plt.colormaps[\"magma\"].copy()\n", + "cmap.set_bad(color=\"#d1d5db\")\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 4.5))\n", + "image = ax.imshow(masked_log_vjp_norms, aspect=\"auto\", cmap=cmap)\n", + "ax.set_yticks(range(len(DETAILED_LAYERS)), labels=DETAILED_LAYERS)\n", + "ax.set_xticks(\n", + " range(len(position_tokens)), labels=position_tokens, rotation=35, ha=\"right\"\n", + ")\n", + "for layer_index, position_index in np.argwhere(near_zero_mask):\n", + " label = \"0\" if vjp_norms[layer_index, position_index] == 0 else \"≈0\"\n", + " ax.text(position_index, layer_index, label, ha=\"center\", va=\"center\")\n", + "ax.set(\n", + " xlabel=\"prompt position\",\n", + " ylabel=\"layer\",\n", + " title=\"FF2 VJP norms by layer and position (gray = zero/near-zero)\",\n", + ")\n", + "fig.colorbar(image, ax=ax, label=\"log10 L2 norm\")\n", + "fig.tight_layout()\n", + "plt.show()\n", + "print(f\"Near-zero threshold: {near_zero_threshold:.3e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "cd704a4a", + "metadata": {}, + "source": [ + "## Beyond-paper diagnostic: contribution size and cumulative reconstruction\n", + "\n", + "For one middle-layer FF2 gradient, the Frobenius norm of position $i$'s outer product is $\\lVert x_i\\rVert_2\\lVert\\delta_i\\rVert_2$. The contribution bars retain prompt order. The cumulative curve adds positions from largest to smallest contribution norm and reports how much of the full reconstructed gradient remains." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "22bdaffd", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:06.904275Z", + "iopub.status.busy": "2026-08-25T09:58:06.904065Z", + "iopub.status.idle": "2026-08-25T09:58:07.249926Z", + "shell.execute_reply": "2026-08-25T09:58:07.249050Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABKYAAAGGCAYAAABBiol3AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvctoD+AAAAAlwSFlzAAAPYQAAD2EBqD+naQAAxhlJREFUeJzs3XdYFFcXBvB3doGl9yIo2BURBRQUFAv2hj1qoondaGLXxBq70STGFo0m9hgTjZrYxa6xKyhWVOwFpDcLq7Dz/UHYzxVQWBZG4P09zz7J3pm5c+bsys6enXtHEEVRBBERERERERERUSGTSR0AERERERERERGVTCxMERERERERERGRJFiYIiIiIiIiIiIiSbAwRUREREREREREkmBhioiIiIiIiIiIJMHCFBERERERERERSYKFKSIiIiIiIiIikgQLU0REREREREREJAkWpoiIiIiIiIiISBIsTBERFYItW7ZAEASEhoaq21auXAlBEHD79m1J4/iQfOjxEREVNkEQMHny5CLTL33Yfv/9dwiCgBs3bkgditqHGBPlrEePHqhUqZJGW6tWreDp6Sl5HIVpx44dcHd3h4GBAQRBwLNnz9CxY0e4urpKFlNRxsIU0TscPHgQgiBg+fLlUoeiMzdv3kTPnj1RqlQpmJqaomHDhti3b997t8vMRXaPNz8UcrtefHw8Fi5ciPr168PKygr29vYICAjA3r17C+S4dSXz5Onq1atSh5KjohAjEVFBe/XqFZYtW4bGjRvD2toahoaGqFChAtq0aYNNmzbh1atXUodYoFJTUyEIAqZNmyZ1KCWGFD84fchxlFRFpThRFOL8UGOMjIxEjx490LVrVzx79gyiKMLU1LRA9vWh5kDXWJgiKkFOnDgBb29v2Nra4syZM4iPj8ePP/6IhQsX5rqPZcuWQRRFjUd2Jz7vW+/rr7/GzJkz8emnn+Lu3bu4fPky6tSpgzZt2uQpnqKia9euEEWx0H9N+lDjyMmHHh8RFQ2xsbGoX78+pk6dih49euDKlStITk7Gvn374OXlhc8++wx///231GFKShRFzJo1S+owqJD16tULoiiWiC+6VDA2btz4QRQ9pYzj5MmTePnyJbp37w4DAwN1+7Zt23jln5b0pA6AiApHamoqPv74Y3Tq1AmLFi1St/v4+EhylVL16tUxdepUODs7q9u+++47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nJycpA6JiIiIiKhYqlGjBlxcXNCrVy/Y29ujadOmMDU1xfHjx1G1alWpwyOiN3COKSIiIiIiIiIikgSvmCIiIiIiIiIiIkmwMEVERERERERERJLQkzqAwqBSqRAREQEzMzMIgiB1OERERFQEiKKIlJQUODk5QSYrub/l8TyKiIiI8iov51ElojAVEREBZ2dnqcMgIiKiIujRo0coU6aM1GFIhudRREREpK3cnEeViMKUmZkZgIyEmJubSxxNwVCpVEhISICVlVWJ/lU3v5hH3WEudYe51B3mUjdKSh6Tk5Ph7OysPo8oqQr6PKqkvJ8+VMy/tJh/aTH/0mL+pVXQ+c/LeVSJKExlXnZubm5erAtTaWlpMDc35z/qfGAedYe51B3mUneYS90oaXks6cPXCvo8qqS9nz40zL+0mH9pMf/SYv6lVVj5z815FF99IiIiIiIiIiKSBAtTREREREREREQkCRamiIiIiIiIiIhIEixMERERERERERGRJErE5OdERERE9OER09OReCoUiXceQlbRBVb1PCHI5VKHRURERIWIhSkiIiIiKnQxO48hfOIiKCNiAACPACic7FD52xGwC2wkbXBERERUaDiUj4iIiIgKVczOY7jad7K6KJVJGRmDq30nI2bnMYkiIyIiosLGwhQRERERFRoxPR3hExcBYnYLM/4TPmkxxPT0Qo2LiIiIpMHCFBEREREVmsTTl7NcKaVBBJRPopF4+nLhBUVERESSYWGKiIiIiArNq6g4na5HRERERRsnPyciIipiOraMkHT/giDCwTEFUZFKiKIgaSzb9jlJun/KOwMHG52uR0REREUbr5giIiIiokJj6VcTCic74B01TZmhAUzcyhdeUERERCQZFqaIiIiIqNAIcjkqfzvivyfZr6NKfYULLQbj2dXbhRcYERERSYKFKSIiIiIqVHaBjeC+ZhYUjnYa7YrS9qg4bQgMXRzx8t4ThLQajKebgiSKkoiIiAoD55giIiIiokJnF9gItm38kXAqFPF3HsK6ogus6nlCkMvh2Ksdrn8+A/GHziLsi9lIOn8NlWcPg0xhIHXYREREpGO8YoqIiIiIJCHI5bCs7wXLdg1gWd8LglwOANC3MkfNjd+j3Nd9AUFAxJptuBg4DKlPoiWOmIiIiHSNhSkiIiIi+uAIMhnKj+uHmn9+Bz1LMySHXEdwk/5I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KY+xMEVEREREREREb5VGo0Hjxo2xbt06vfYSJUrg66+/xq+//orIyEilfciQIUhISEBcXFye5qS8x8IUEREREREREb11n3zyCfbt24f9+/frtXfp0gVOTk567W3atEFMTAycnZ3zOiblMRamiIiIiIiIiOit69atG9q3b49Bgwbhzp07etcqVqyI1NRUvTZTU9O8jEcqYWGKiIiIiIiIiPLE+vXrYW5ujtatW+PatWsAgIsXL+L8+fPo3LmzyulIDSxMEREREREREVGesLGxwZEjR2BlZYVatWqhfv368PLywqJFi1C/fn2145EKNCIiaocgIiIiIiIioqIlPDwc9+/fh6enJ2xsbNSOQyphYYqIiIiIiIiIiFTBpXxERERERERERKQKFqaIiIiIiIiIiEgVLEwREREREREREZEqWJgiIiIiIiIiIiJVsDBFRERERERERESqYGGKiIiIiIiIiIhUwcIUERERERERERGpgoUpIiIiIiIiIiJSBQtTRERERERERESkChamiIiIiIiIiIhIFSxMERERERERERGRKliYIiIiIiIiIiIiVbAwRUREREREREREqvg/hzNSLYSdScMAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "middle_layer = DETAILED_LAYERS[len(DETAILED_LAYERS) // 2]\n", + "factors = primary_result.layer(middle_layer).output_projection.factors\n", + "contribution_norms = (\n", + " factors.forward_inputs.norm(dim=-1) * factors.output_gradients.norm(dim=-1)\n", + ").numpy()\n", + "sorted_positions = np.argsort(-contribution_norms, kind=\"stable\")\n", + "running = torch.zeros_like(factors.reconstructed_gradient)\n", + "full_norm = factors.reconstructed_gradient.norm().clamp_min(\n", + " torch.finfo(torch.float32).eps\n", + ")\n", + "remaining_error = []\n", + "for position in sorted_positions:\n", + " x_row = factors.forward_inputs[position]\n", + " delta_row = factors.output_gradients[position]\n", + " running += torch.outer(x_row, delta_row)\n", + " remaining_error.append(\n", + " float((factors.reconstructed_gradient - running).norm() / full_norm)\n", + " )\n", + "sorted_labels = [\n", + " f\"{position}:{position_tokens[position]}\" for position in sorted_positions\n", + "]\n", + "\n", + "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n", + "axes[0].bar(range(len(position_tokens)), contribution_norms, color=\"#4f46e5\")\n", + "axes[0].set_xticks(\n", + " range(len(position_tokens)), labels=position_tokens, rotation=35, ha=\"right\"\n", + ")\n", + "axes[0].set(\n", + " ylabel=\"outer-product Frobenius norm\",\n", + " title=f\"Layer {middle_layer} FF2 contribution size\",\n", + ")\n", + "axes[1].plot(range(len(sorted_positions)), remaining_error, marker=\"o\", color=\"#be123c\")\n", + "axes[1].set_xticks(\n", + " range(len(sorted_positions)), labels=sorted_labels, rotation=35, ha=\"right\"\n", + ")\n", + "axes[1].set(\n", + " ylabel=\"relative error remaining\",\n", + " title=\"Cumulative reconstruction: largest contribution first\",\n", + ")\n", + "for ax in axes:\n", + " ax.grid(alpha=0.25)\n", + "fig.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4f832556", + "metadata": {}, + "source": [ + "## Predeclared examples: report every outcome\n", + "\n", + "The remaining examples are analyzed only at GPT-2's final layer to keep the demo compact. The table reports whether unit normalization improves, worsens, or leaves unchanged the target's ascending raw-gradient rank. No example is removed based on its result." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "5ba7e1da", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-25T09:58:07.251966Z", + "iopub.status.busy": "2026-08-25T09:58:07.251755Z", + "iopub.status.idle": "2026-08-25T09:58:07.976273Z", + "shell.execute_reply": "2026-08-25T09:58:07.975466Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prompttargetraw target ranknormalized target ranknormalization outcomeVJP norm
0The capital of France isParis00unchanged0.260877
1The capital of Italy isRome00unchanged0.262014
2The largest planet in the Solar System isJupiter00unchanged0.280860
\n", + "
" + ], + "text/plain": [ + " prompt target raw target rank \\\n", + "0 The capital of France is Paris 0 \n", + "1 The capital of Italy is Rome 0 \n", + "2 The largest planet in the Solar System is Jupiter 0 \n", + "\n", + " normalized target rank normalization outcome VJP norm \n", + "0 0 unchanged 0.260877 \n", + "1 0 unchanged 0.262014 \n", + "2 0 unchanged 0.280860 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Normalization outcomes: {'unchanged': 3}\n" + ] + } + ], + "source": [ + "comparison_rows = []\n", + "for example_index, (example_prompt, example_target) in enumerate(EXAMPLES):\n", + " if example_index == 0:\n", + " result = primary_result\n", + " else:\n", + " result = lens.analyze(\n", + " example_prompt,\n", + " example_target,\n", + " [DETAILED_LAYERS[-1]],\n", + " normalized=True,\n", + " )\n", + " summary = final_position_summary(result).iloc[-1]\n", + " raw_rank = int(summary[\"raw target rank\"])\n", + " normalized_rank = int(summary[\"normalized target rank\"])\n", + " outcome = (\n", + " \"improved\"\n", + " if normalized_rank < raw_rank\n", + " else \"worsened\" if normalized_rank > raw_rank else \"unchanged\"\n", + " )\n", + " comparison_rows.append(\n", + " {\n", + " \"prompt\": example_prompt,\n", + " \"target\": example_target,\n", + " \"raw target rank\": raw_rank,\n", + " \"normalized target rank\": normalized_rank,\n", + " \"normalization outcome\": outcome,\n", + " \"VJP norm\": float(summary[\"final-position VJP norm\"]),\n", + " }\n", + " )\n", + " if example_index != 0:\n", + " del result\n", + " gc.collect()\n", + "\n", + "comparison = pd.DataFrame(comparison_rows)\n", + "display(comparison.round({\"VJP norm\": 8}))\n", + "print(\n", + " \"Normalization outcomes:\",\n", + " comparison[\"normalization outcome\"].value_counts().to_dict(),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e3df0a77", + "metadata": {}, + "source": [ + "## Interpretation checklist\n", + "\n", + "- FF1 and FF2 tables project different factors; do not compare them as if they were the same signal.\n", + "- FF2 uses raw loss-gradient signs. SGD subtracts the gradient, so ascending ranks are highlighted.\n", + "- LayerNorm bias and epsilon make projection non-linear enough that negating projected logits is not an exact update readout.\n", + "- Normalized Logit Lens isolates direction from factor norm, but it need not improve target rank. The table above records null and contradictory outcomes.\n", + "- Small reconstruction error validates the factorization numerically; it does not validate a semantic interpretation.\n", + "- This fixed GPT-2 sample demonstrates tooling and diagnostics, not population-level evidence about language-model learning or memory.\n", + "\n", + "See the [Backward Lens reference documentation](../docs/source/content/backward_lens.md) and the [authors' research repository](https://github.com/shacharKZ/BackwardLens) for more context." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "transformer-lens", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/makefile b/makefile index d9655e7d0..cd791941c 100644 --- a/makefile +++ b/makefile @@ -45,6 +45,7 @@ docstring-test: notebook-test: $(RUN) pytest --nbval-sanitize-with demos/doc_sanitize.cfg demos/BERT.ipynb $(RERUN_ARGS) + $(RUN) pytest --nbval-sanitize-with demos/doc_sanitize.cfg demos/Backward_Lens_Demo.ipynb $(RERUN_ARGS) $(RUN) pytest --nbval-sanitize-with demos/doc_sanitize.cfg demos/Bridge_Evals_Demo.ipynb $(RERUN_ARGS) $(RUN) pytest --nbval-sanitize-with demos/doc_sanitize.cfg demos/Exploratory_Analysis_Demo.ipynb $(RERUN_ARGS) $(RUN) pytest --nbval-sanitize-with demos/doc_sanitize.cfg demos/Main_Demo.ipynb $(RERUN_ARGS) From bb8a49c73889f98ea584a1592010b2d3e4353dff Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Tue, 25 Aug 2026 05:38:07 -0700 Subject: [PATCH 06/17] fix(backward_lens): stabilize demo notebook validation - Suppress the narrow Typeguard instrumentation warning emitted during fresh TransformerLens imports. - Display target ranks as stable vocabulary percentiles while retaining exact ranks in the analysis data. - Regenerate all notebook outputs and verify all ten nbval cells pass. --- demos/Backward_Lens_Demo.ipynb | 184 +++++++++++++++++++-------------- 1 file changed, 109 insertions(+), 75 deletions(-) diff --git a/demos/Backward_Lens_Demo.ipynb b/demos/Backward_Lens_Demo.ipynb index b95f1adb2..b08f0e930 100644 --- a/demos/Backward_Lens_Demo.ipynb +++ b/demos/Backward_Lens_Demo.ipynb @@ -32,10 +32,10 @@ "id": "4955da53", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:57:32.909944Z", - "iopub.status.busy": "2026-08-25T09:57:32.909775Z", - "iopub.status.idle": "2026-08-25T09:58:00.150543Z", - "shell.execute_reply": "2026-08-25T09:58:00.149530Z" + "iopub.execute_input": "2026-08-25T12:32:57.795254Z", + "iopub.status.busy": "2026-08-25T12:32:57.795099Z", + "iopub.status.idle": "2026-08-25T12:33:18.848214Z", + "shell.execute_reply": "2026-08-25T12:33:18.847447Z" } }, "outputs": [ @@ -111,9 +111,16 @@ "from huggingface_hub.utils import logging as hf_hub_logging\n", "from IPython.display import display\n", "from transformers.utils import logging as hf_logging\n", + "from typeguard import InstrumentationWarning\n", "\n", - "from transformer_lens.model_bridge import TransformerBridge\n", - "from transformer_lens.tools.analysis import BackwardLens\n", + "with warnings.catch_warnings():\n", + " warnings.filterwarnings(\n", + " \"ignore\",\n", + " message=\"@typechecked only supports instrumenting functions wrapped.*\",\n", + " category=InstrumentationWarning,\n", + " )\n", + " from transformer_lens.model_bridge import TransformerBridge\n", + " from transformer_lens.tools.analysis import BackwardLens\n", "\n", "hf_hub_logging.set_verbosity_error()\n", "hf_logging.set_verbosity_error()\n", @@ -137,10 +144,10 @@ "id": "8b1857ae", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:00.152849Z", - "iopub.status.busy": "2026-08-25T09:58:00.152540Z", - "iopub.status.idle": "2026-08-25T09:58:00.376603Z", - "shell.execute_reply": "2026-08-25T09:58:00.375699Z" + "iopub.execute_input": "2026-08-25T12:33:18.850251Z", + "iopub.status.busy": "2026-08-25T12:33:18.850033Z", + "iopub.status.idle": "2026-08-25T12:33:19.314665Z", + "shell.execute_reply": "2026-08-25T12:33:19.313924Z" } }, "outputs": [ @@ -203,10 +210,10 @@ "id": "a5007e02", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:00.378673Z", - "iopub.status.busy": "2026-08-25T09:58:00.378471Z", - "iopub.status.idle": "2026-08-25T09:58:05.234585Z", - "shell.execute_reply": "2026-08-25T09:58:05.233676Z" + "iopub.execute_input": "2026-08-25T12:33:19.316601Z", + "iopub.status.busy": "2026-08-25T12:33:19.316402Z", + "iopub.status.idle": "2026-08-25T12:33:20.761341Z", + "shell.execute_reply": "2026-08-25T12:33:20.760494Z" } }, "outputs": [ @@ -235,10 +242,10 @@ "id": "60ea683d", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:05.236464Z", - "iopub.status.busy": "2026-08-25T09:58:05.236247Z", - "iopub.status.idle": "2026-08-25T09:58:05.627247Z", - "shell.execute_reply": "2026-08-25T09:58:05.626152Z" + "iopub.execute_input": "2026-08-25T12:33:20.763333Z", + "iopub.status.busy": "2026-08-25T12:33:20.763127Z", + "iopub.status.idle": "2026-08-25T12:33:21.160832Z", + "shell.execute_reply": "2026-08-25T12:33:21.159838Z" } }, "outputs": [ @@ -299,10 +306,10 @@ "id": "663bd83f", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:05.629628Z", - "iopub.status.busy": "2026-08-25T09:58:05.629323Z", - "iopub.status.idle": "2026-08-25T09:58:06.029280Z", - "shell.execute_reply": "2026-08-25T09:58:06.028359Z" + "iopub.execute_input": "2026-08-25T12:33:21.162685Z", + "iopub.status.busy": "2026-08-25T12:33:21.162465Z", + "iopub.status.idle": "2026-08-25T12:33:21.532448Z", + "shell.execute_reply": "2026-08-25T12:33:21.531699Z" } }, "outputs": [ @@ -865,7 +872,7 @@ "\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -983,10 +990,10 @@ "id": "323e645c", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:06.032990Z", - "iopub.status.busy": "2026-08-25T09:58:06.032758Z", - "iopub.status.idle": "2026-08-25T09:58:06.425298Z", - "shell.execute_reply": "2026-08-25T09:58:06.424567Z" + "iopub.execute_input": "2026-08-25T12:33:21.534515Z", + "iopub.status.busy": "2026-08-25T12:33:21.534298Z", + "iopub.status.idle": "2026-08-25T12:33:21.903397Z", + "shell.execute_reply": "2026-08-25T12:33:21.902682Z" } }, "outputs": [ @@ -1230,10 +1237,10 @@ "id": "e87a4974", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:06.427228Z", - "iopub.status.busy": "2026-08-25T09:58:06.427019Z", - "iopub.status.idle": "2026-08-25T09:58:06.625088Z", - "shell.execute_reply": "2026-08-25T09:58:06.624388Z" + "iopub.execute_input": "2026-08-25T12:33:21.905215Z", + "iopub.status.busy": "2026-08-25T12:33:21.904993Z", + "iopub.status.idle": "2026-08-25T12:33:22.094360Z", + "shell.execute_reply": "2026-08-25T12:33:22.093654Z" } }, "outputs": [ @@ -1259,74 +1266,83 @@ " \n", " \n", " layer\n", - " raw target rank\n", - " normalized target rank\n", " final-position VJP norm\n", + " raw target rank percentile\n", + " normalized target rank percentile\n", " \n", " \n", " \n", " \n", " 0\n", " 0\n", - " 40650\n", - " 40643\n", " 0.655670\n", + " 80.88\n", + " 80.87\n", " \n", " \n", " 1\n", " 2\n", - " 20776\n", - " 20792\n", " 1.442085\n", + " 41.34\n", + " 41.37\n", " \n", " \n", " 2\n", " 4\n", - " 5616\n", - " 5628\n", " 1.329280\n", + " 11.17\n", + " 11.20\n", " \n", " \n", " 3\n", " 6\n", - " 2216\n", - " 2217\n", " 1.042345\n", + " 4.41\n", + " 4.41\n", " \n", " \n", " 4\n", " 8\n", - " 0\n", - " 0\n", " 0.487238\n", + " 0.00\n", + " 0.00\n", " \n", " \n", " 5\n", " 10\n", - " 0\n", - " 0\n", " 0.274904\n", + " 0.00\n", + " 0.00\n", " \n", " \n", " 6\n", " 11\n", - " 0\n", - " 0\n", " 0.260877\n", + " 0.00\n", + " 0.00\n", " \n", " \n", "\n", "" ], "text/plain": [ - " layer raw target rank normalized target rank final-position VJP norm\n", - "0 0 40650 40643 0.655670\n", - "1 2 20776 20792 1.442085\n", - "2 4 5616 5628 1.329280\n", - "3 6 2216 2217 1.042345\n", - "4 8 0 0 0.487238\n", - "5 10 0 0 0.274904\n", - "6 11 0 0 0.260877" + " layer final-position VJP norm raw target rank percentile \\\n", + "0 0 0.655670 80.88 \n", + "1 2 1.442085 41.34 \n", + "2 4 1.329280 11.17 \n", + "3 6 1.042345 4.41 \n", + "4 8 0.487238 0.00 \n", + "5 10 0.274904 0.00 \n", + "6 11 0.260877 0.00 \n", + "\n", + " normalized target rank percentile \n", + "0 80.87 \n", + "1 41.37 \n", + "2 11.20 \n", + "3 4.41 \n", + "4 0.00 \n", + "5 0.00 \n", + "6 0.00 " ] }, "metadata": {}, @@ -1334,7 +1350,7 @@ }, { "data": { - "image/png": "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OQL1sPyKx4KytbXN0/bvv/9y+vRp/vnnH52bSk6ePJnvGI86AqfkMwddt27dCAgIYM6cObi5uTF9+vQ8fWbMmMGUKVM4fvw4e/fu5ccff2TJkiVs27aNdu3aFXTXgP/lL79c3717lzJlyujkIL98GMJ/7959mFqtZsmSJUyaNCnPvH9xcXHaa0T1ibFr16507dqVixcvsm/fPtauXUvfvn155513mDVrVuF3RAgzkrt3hRCPtGHDBgYNGkSzZs34559/CnUK9MEH68OnWiG36MvIyMDT01On/ciRI9qjSk/y4IL93bt367QnJCRw9uxZnbbQ0FBUKhWXLl165KTUD07V2dra5olXHykpKQB59nHdunVPPTbAp59+yuzZs5kxY8YjJze2srKicePGvPHGG+zfvx+VSqVzU01BNWvWDMg9wviwzMxM9u7dS5MmTbCwMO9HSXp6Ojk5OXnyvWPHDtRqtUG2UbNmTV566SU2bdpEw4YNDfazFMKUpOgTQuRr+/bt9OvXj8aNG7Nx40bt0bWCCggIAMgzVYiDgwMhISH8/PPP2tO0165dY+rUqU+8K/iBevXq0bVrVz799FPthL1paWlMnDiRatWq6fStVq0aEyZM4OOPP2bFihXa6+qys7PZtWsXb775pk7MFy9eJCMjo1D7+l9NmjTBxsaGL7/8kqysLAD+/vtvbayGMH36dBYvXsy8efMYPXq0dr9WrlzJ999/ry08Ibc41mg0BAYGFno7derUoXfv3syfP19b+GVkZDBu3Dhu377Nu+++a5gdegoeHh7UqFGD5cuXa0+JX7x4kVmzZul92h7g1KlTvPvuuzpPQbl69So3b97UK5dCmJsUfUKIfL3yyitkZGRw8+ZNGjZsmOfo2JMMGDCANm3a0Lx5c6pWraozT9+KFSsoW7Ysfn5+eHt706VLFz755JNCXQy/cuVKQkNDadq0KZ6entSqVYsBAwbg7e2tc5E9wPz58/n00095++23sbe3x8/PDycnJz766CPatm2r7ffWW2+h0WioUKFCgebpexQ/Pz9+/PFHNmzYgLu7O+XLl+e7777j66+/1mu8Rxk9ejS//vory5cvZ9CgQWRlZdGsWTNOnDiBj48P3t7eeHh48NJLLzFr1ixefvllvbbz888/M2TIELp3706FChVwcnJi165drFu3TmfeRHNavXo1Go0G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8+ZkZmZKcf0U3n//fZ2c/teZM2c4evQob775JpaWlgC89dZbxMbGsnHjRlOFWSxJ0VcMnD9/nszMTEJCQrRtlpaWNGjQgJMnT5oxspLv2LFjVK1a1dxhlDgjR45k8ODBtGrVytyhlEiRkZFcuXKF7t27k5CQwPHjx7UFijCcDh060L59e1544QU2bdrEH3/8wZQpU3j99depVKmSucMrsR587j38mejp6Ym3t7d8Jj6BlbkDEE+WkJAAgJubm067m5sbd+7cMUdIpcKiRYvYt28fe/bsMXcoJcpXX31FVFQUv/32m7lDKbFu374NwPbt2xkzZgyVKlXi4sWLdO/enR9//BFbW1szR1gylClThvHjx/PKK68wZcoU0tPTKVeuHEOHDjV3aCVaQkIC9vb2ed7Hbm5u2s9LkT850lcMlClTBoD79+/rtKenp2NtbW2OkEq8tWvXMmHCBBYvXvzY0wyicCIjI5k6dSpjx47l8OHD7Nu3j+joaFJSUti3b5/cNGMgD/5mHDp0iCtXrnDixAkuXrzIzp07+eijj8wcXcmxa9cu+vTpww8//MDZs2e5evUqQ4cOpXXr1vIPuRGVKVOGjIwM/vtAMflMfDIp+ooBPz8/AKKionTao6Ki8PX1NUdIJdq6desYPHgwCxcuZOTIkeYOp0RJS0sjJCSEpUuXMn36dKZPn87hw4eJjIxk+vTpREREmDvEEuHB34zBgwdTrlw5AHx9fXn22WfZu3evOUMrUf755x8CAgLo1KmTtu2VV17h3r177Ny504yRlWx+fn7k5OQQExOjbdNoNERHR8tn4hNI0VcMVK1alcqVK/PXX39p227dusXx48fp0KGDGSMref7880+ee+45vvzyS5lGxAhq1KjBvn37dL569uxJYGAg+/bto27duuYOsURwc3OjYcOGef5RvHXrFuXLlzdTVCVP+fLliY+P15mb78F8iJJn42nZsiU2NjY6n4m7d+8mKSlJPhOfQK7pKyZmz57N0KFDqVChAjVq1ODjjz8mJCSEvn37mju0EuPff/9lwIABDB48mNq1a2vv2rWysqJJkyZmjk6Iwvn000/p2bMnFStWpF69emzZsoU9e/bINaoGNGTIED799FP69OnD2LFjSU9P56OPPqJevXpyk9JTuHz5MrGxsVy5cgVA+7e4Xr16ODo64uTkxJtvvsmUKVNQqVQ4ODgwffp0Bg8eTL169cwZepGnUv57UlwUWZs2beKHH34gKSmJxo0bM3XqVJnuwoAWLVqU7xQ45cqVY9OmTWaIqHSYO3cuV65cYfHixeYOpcQ5cOAA33zzDTExMVSpUoVx48bJ0VQDi4yM5IsvvuD8+fNYW1vTsGFDxo8fL3+bn8IHH3zAli1b8rR/++23Ou/f5cuXs3btWjIzM+nYsSPjx4+Xa/qeQIo+IYQQQohSQK7pE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIIYQoBaToE0IIA9i8eTOXL182dxhCCPFIUvQJIYQBvPHGG2zcuNHcYQghxCNJ0SeEEEIIUQpYmTsAIYQoiXbt2kV0dDQqlQoPDw/q16+v8zzWgwcPotFoaN68uc56+/fvR6VS0axZMwCys7M5dOgQCQkJ1KxZk+rVq+v0//vvv6lduza2trYcO3YMT09PGjVqZPwdFEIUO1L0CSGEEezfv58zZ86gKAoRERFcuXKFVatW0aFDBwDOnTvHW2+9RVRUFFZWuX+Ks7Ky6NOnD++//z7NmjXjwoUL9OjRAzs7O/z9/Tl8+DCdOnVi+fLlWFjknqgZP348VatWJTw8nODgYLp06SJFnxAiX1L0CSGEEbz11ls6r7/88ktGjx7N9evXAXjuued4/fXX2bBhA7169QJgw4YNpKSkMGjQIDQaDX379mX48OG8/fbbACQlJVG/fn2WLl3KSy+9pB07IiKCsLAwnJ2dTbJvQojiSYo+IYQwkqioKM6dO0diYiIqlYobN24QFxdH+fLlcXBw4LnnnmPp0qXaom/p0qX06dMHZ2dnDhw4wIULF/D29ub3339HURQURaFq1ars3LlTp+h7/vnnpeATQjyRFH1CCGEE06dP56uvvqJRo0aUL1+erKwsAGJjYylfvjwAL730Ei1atCA6OhrInfZly5YtANy4cQMLCws2b96sM66bmxuBgYE6bZ6ensbeHSFECSBFnxBCGNjly5f59NNPOXXqFPXq1QPg7Nmz/PnnnyiKou3XuHFjatWqxU8//YSiKPj4+NCmTRsAHB0d0Wg0fPHFF3h4eDx2eyqVyng7I4QoMWTKFiGEMLDo6GgsLCyoVq2atu3333/Pt+9LL73EsmXLWLZsGS+88IK2gGvRogVly5Zl8eLFOv01Go32yKAQQhSGHOkTQggDCwkJoVKlSvTr14++ffty4sQJVq1alW/f559/nmnTppGZmcmIESO07c7Oznz77be8+OKLXL9+nebNm3Pnzh3WrVvH9OnTGTBggIn2RghRUsiRPiGEMIBnn32WGjVqAFC2bFkOHjxIvXr12LVrF+XLl2ffvn0MHDgwzw0XLi4utGrVivbt2+Pj46Oz7Pnnn+fEiRN4enqyd+9esrOzWbFihU7B1717dwICAoy+f0KI4k+lPHyBiRBCCJNSq9V4eXmxbNky+vXrZ+5whBAlmBR9QghhBhqNhtWrV7NmzRouX77M6dOnsbS0NHdYQogSTE7vCiGEGeTk5PDXX3/h4+PDP//8IwWfEMLo5EifEEIIIUQpIEf6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKASn6hBBCCCFKgf8DrrvcCf+PXBcAAAAASUVORK5CYII=", + "image/png": "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", "text/plain": [ "
" ] @@ -1344,25 +1360,43 @@ } ], "source": [ - "display(primary_layer_summary.round({\"final-position VJP norm\": 8}))\n", + "# Exact ranks can move by one across numeric backends when vocabulary logits are nearly tied.\n", + "# Keep them in primary_layer_summary, but display stable vocabulary percentiles.\n", + "vocab_size = primary_result.layers[0].output_projection.vocabulary_logits.shape[-1]\n", + "rank_percentiles = primary_layer_summary[[\"layer\", \"final-position VJP norm\"]].copy()\n", + "rank_percentiles[\"raw target rank percentile\"] = (\n", + " 100 * primary_layer_summary[\"raw target rank\"] / vocab_size\n", + ")\n", + "rank_percentiles[\"normalized target rank percentile\"] = (\n", + " 100 * primary_layer_summary[\"normalized target rank\"] / vocab_size\n", + ")\n", + "display(\n", + " rank_percentiles.round(\n", + " {\n", + " \"final-position VJP norm\": 8,\n", + " \"raw target rank percentile\": 2,\n", + " \"normalized target rank percentile\": 2,\n", + " }\n", + " )\n", + ")\n", "\n", "fig, ax = plt.subplots(figsize=(7, 4))\n", "ax.plot(\n", - " primary_layer_summary[\"layer\"],\n", - " primary_layer_summary[\"raw target rank\"],\n", + " rank_percentiles[\"layer\"],\n", + " rank_percentiles[\"raw target rank percentile\"],\n", " marker=\"o\",\n", " label=\"raw factor\",\n", ")\n", "ax.plot(\n", - " primary_layer_summary[\"layer\"],\n", - " primary_layer_summary[\"normalized target rank\"],\n", + " rank_percentiles[\"layer\"],\n", + " rank_percentiles[\"normalized target rank percentile\"],\n", " marker=\"s\",\n", " label=\"unit-normalized factor\",\n", ")\n", "ax.set(\n", " xlabel=\"layer\",\n", - " ylabel=\"ascending target rank (lower is smaller)\",\n", - " title=f\"FF2 target ranks for {target!r}\",\n", + " ylabel=\"ascending target-rank percentile (lower is smaller)\",\n", + " title=f\"FF2 target-rank percentiles for {target!r}\",\n", ")\n", "ax.grid(alpha=0.25)\n", "ax.legend()\n", @@ -1385,10 +1419,10 @@ "id": "942c9dce", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:06.627148Z", - "iopub.status.busy": "2026-08-25T09:58:06.626949Z", - "iopub.status.idle": "2026-08-25T09:58:06.902195Z", - "shell.execute_reply": "2026-08-25T09:58:06.901485Z" + "iopub.execute_input": "2026-08-25T12:33:22.096409Z", + "iopub.status.busy": "2026-08-25T12:33:22.096206Z", + "iopub.status.idle": "2026-08-25T12:33:22.353692Z", + "shell.execute_reply": "2026-08-25T12:33:22.352837Z" } }, "outputs": [ @@ -1459,10 +1493,10 @@ "id": "22bdaffd", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:06.904275Z", - "iopub.status.busy": "2026-08-25T09:58:06.904065Z", - "iopub.status.idle": "2026-08-25T09:58:07.249926Z", - "shell.execute_reply": "2026-08-25T09:58:07.249050Z" + "iopub.execute_input": "2026-08-25T12:33:22.355448Z", + "iopub.status.busy": "2026-08-25T12:33:22.355245Z", + "iopub.status.idle": "2026-08-25T12:33:22.659861Z", + "shell.execute_reply": "2026-08-25T12:33:22.659138Z" } }, "outputs": [ @@ -1539,10 +1573,10 @@ "id": "5ba7e1da", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T09:58:07.251966Z", - "iopub.status.busy": "2026-08-25T09:58:07.251755Z", - "iopub.status.idle": "2026-08-25T09:58:07.976273Z", - "shell.execute_reply": "2026-08-25T09:58:07.975466Z" + "iopub.execute_input": "2026-08-25T12:33:22.661706Z", + "iopub.status.busy": "2026-08-25T12:33:22.661502Z", + "iopub.status.idle": "2026-08-25T12:33:23.435570Z", + "shell.execute_reply": "2026-08-25T12:33:23.434589Z" } }, "outputs": [ From f7ec736b631040ab28ad0d2d5ae339765a2a78b3 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Tue, 25 Aug 2026 05:53:11 -0700 Subject: [PATCH 07/17] fix(backward_lens): stabilize rank percentile output --- demos/Backward_Lens_Demo.ipynb | 142 ++++++++++++++++----------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/demos/Backward_Lens_Demo.ipynb b/demos/Backward_Lens_Demo.ipynb index b08f0e930..289f1b1d0 100644 --- a/demos/Backward_Lens_Demo.ipynb +++ b/demos/Backward_Lens_Demo.ipynb @@ -32,10 +32,10 @@ "id": "4955da53", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:32:57.795254Z", - "iopub.status.busy": "2026-08-25T12:32:57.795099Z", - "iopub.status.idle": "2026-08-25T12:33:18.848214Z", - "shell.execute_reply": "2026-08-25T12:33:18.847447Z" + "iopub.execute_input": "2026-08-25T12:44:47.624715Z", + "iopub.status.busy": "2026-08-25T12:44:47.624549Z", + "iopub.status.idle": "2026-08-25T12:45:09.079784Z", + "shell.execute_reply": "2026-08-25T12:45:09.079138Z" } }, "outputs": [ @@ -144,10 +144,10 @@ "id": "8b1857ae", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:18.850251Z", - "iopub.status.busy": "2026-08-25T12:33:18.850033Z", - "iopub.status.idle": "2026-08-25T12:33:19.314665Z", - "shell.execute_reply": "2026-08-25T12:33:19.313924Z" + "iopub.execute_input": "2026-08-25T12:45:09.082051Z", + "iopub.status.busy": "2026-08-25T12:45:09.081830Z", + "iopub.status.idle": "2026-08-25T12:45:09.552544Z", + "shell.execute_reply": "2026-08-25T12:45:09.551698Z" } }, "outputs": [ @@ -210,10 +210,10 @@ "id": "a5007e02", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:19.316601Z", - "iopub.status.busy": "2026-08-25T12:33:19.316402Z", - "iopub.status.idle": "2026-08-25T12:33:20.761341Z", - "shell.execute_reply": "2026-08-25T12:33:20.760494Z" + "iopub.execute_input": "2026-08-25T12:45:09.554766Z", + "iopub.status.busy": "2026-08-25T12:45:09.554567Z", + "iopub.status.idle": "2026-08-25T12:45:11.184850Z", + "shell.execute_reply": "2026-08-25T12:45:11.184018Z" } }, "outputs": [ @@ -242,10 +242,10 @@ "id": "60ea683d", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:20.763333Z", - "iopub.status.busy": "2026-08-25T12:33:20.763127Z", - "iopub.status.idle": "2026-08-25T12:33:21.160832Z", - "shell.execute_reply": "2026-08-25T12:33:21.159838Z" + "iopub.execute_input": "2026-08-25T12:45:11.186889Z", + "iopub.status.busy": "2026-08-25T12:45:11.186541Z", + "iopub.status.idle": "2026-08-25T12:45:11.652220Z", + "shell.execute_reply": "2026-08-25T12:45:11.651188Z" } }, "outputs": [ @@ -306,10 +306,10 @@ "id": "663bd83f", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:21.162685Z", - "iopub.status.busy": "2026-08-25T12:33:21.162465Z", - "iopub.status.idle": "2026-08-25T12:33:21.532448Z", - "shell.execute_reply": "2026-08-25T12:33:21.531699Z" + "iopub.execute_input": "2026-08-25T12:45:11.654825Z", + "iopub.status.busy": "2026-08-25T12:45:11.654487Z", + "iopub.status.idle": "2026-08-25T12:45:12.028505Z", + "shell.execute_reply": "2026-08-25T12:45:12.027732Z" } }, "outputs": [ @@ -872,7 +872,7 @@ "\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -990,10 +990,10 @@ "id": "323e645c", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:21.534515Z", - "iopub.status.busy": "2026-08-25T12:33:21.534298Z", - "iopub.status.idle": "2026-08-25T12:33:21.903397Z", - "shell.execute_reply": "2026-08-25T12:33:21.902682Z" + "iopub.execute_input": "2026-08-25T12:45:12.030560Z", + "iopub.status.busy": "2026-08-25T12:45:12.030352Z", + "iopub.status.idle": "2026-08-25T12:45:12.394537Z", + "shell.execute_reply": "2026-08-25T12:45:12.393701Z" } }, "outputs": [ @@ -1237,10 +1237,10 @@ "id": "e87a4974", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:21.905215Z", - "iopub.status.busy": "2026-08-25T12:33:21.904993Z", - "iopub.status.idle": "2026-08-25T12:33:22.094360Z", - "shell.execute_reply": "2026-08-25T12:33:22.093654Z" + "iopub.execute_input": "2026-08-25T12:45:12.396360Z", + "iopub.status.busy": "2026-08-25T12:45:12.396150Z", + "iopub.status.idle": "2026-08-25T12:45:12.576218Z", + "shell.execute_reply": "2026-08-25T12:45:12.575495Z" } }, "outputs": [ @@ -1276,50 +1276,50 @@ " 0\n", " 0\n", " 0.655670\n", - " 80.88\n", - " 80.87\n", + " 80.9\n", + " 80.9\n", " \n", " \n", " 1\n", " 2\n", " 1.442085\n", - " 41.34\n", - " 41.37\n", + " 41.3\n", + " 41.4\n", " \n", " \n", " 2\n", " 4\n", " 1.329280\n", - " 11.17\n", - " 11.20\n", + " 11.2\n", + " 11.2\n", " \n", " \n", " 3\n", " 6\n", " 1.042345\n", - " 4.41\n", - " 4.41\n", + " 4.4\n", + " 4.4\n", " \n", " \n", " 4\n", " 8\n", " 0.487238\n", - " 0.00\n", - " 0.00\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 5\n", " 10\n", " 0.274904\n", - " 0.00\n", - " 0.00\n", + " 0.0\n", + " 0.0\n", " \n", " \n", " 6\n", " 11\n", " 0.260877\n", - " 0.00\n", - " 0.00\n", + " 0.0\n", + " 0.0\n", " \n", " \n", "\n", @@ -1327,22 +1327,22 @@ ], "text/plain": [ " layer final-position VJP norm raw target rank percentile \\\n", - "0 0 0.655670 80.88 \n", - "1 2 1.442085 41.34 \n", - "2 4 1.329280 11.17 \n", - "3 6 1.042345 4.41 \n", - "4 8 0.487238 0.00 \n", - "5 10 0.274904 0.00 \n", - "6 11 0.260877 0.00 \n", + "0 0 0.655670 80.9 \n", + "1 2 1.442085 41.3 \n", + "2 4 1.329280 11.2 \n", + "3 6 1.042345 4.4 \n", + "4 8 0.487238 0.0 \n", + "5 10 0.274904 0.0 \n", + "6 11 0.260877 0.0 \n", "\n", " normalized target rank percentile \n", - "0 80.87 \n", - "1 41.37 \n", - "2 11.20 \n", - "3 4.41 \n", - "4 0.00 \n", - "5 0.00 \n", - "6 0.00 " + "0 80.9 \n", + "1 41.4 \n", + "2 11.2 \n", + "3 4.4 \n", + "4 0.0 \n", + "5 0.0 \n", + "6 0.0 " ] }, "metadata": {}, @@ -1374,8 +1374,8 @@ " rank_percentiles.round(\n", " {\n", " \"final-position VJP norm\": 8,\n", - " \"raw target rank percentile\": 2,\n", - " \"normalized target rank percentile\": 2,\n", + " \"raw target rank percentile\": 1,\n", + " \"normalized target rank percentile\": 1,\n", " }\n", " )\n", ")\n", @@ -1419,10 +1419,10 @@ "id": "942c9dce", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:22.096409Z", - "iopub.status.busy": "2026-08-25T12:33:22.096206Z", - "iopub.status.idle": "2026-08-25T12:33:22.353692Z", - "shell.execute_reply": "2026-08-25T12:33:22.352837Z" + "iopub.execute_input": "2026-08-25T12:45:12.578056Z", + "iopub.status.busy": "2026-08-25T12:45:12.577858Z", + "iopub.status.idle": "2026-08-25T12:45:12.830955Z", + "shell.execute_reply": "2026-08-25T12:45:12.830175Z" } }, "outputs": [ @@ -1493,10 +1493,10 @@ "id": "22bdaffd", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:22.355448Z", - "iopub.status.busy": "2026-08-25T12:33:22.355245Z", - "iopub.status.idle": "2026-08-25T12:33:22.659861Z", - "shell.execute_reply": "2026-08-25T12:33:22.659138Z" + "iopub.execute_input": "2026-08-25T12:45:12.832874Z", + "iopub.status.busy": "2026-08-25T12:45:12.832677Z", + "iopub.status.idle": "2026-08-25T12:45:13.127717Z", + "shell.execute_reply": "2026-08-25T12:45:13.126964Z" } }, "outputs": [ @@ -1573,10 +1573,10 @@ "id": "5ba7e1da", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:33:22.661706Z", - "iopub.status.busy": "2026-08-25T12:33:22.661502Z", - "iopub.status.idle": "2026-08-25T12:33:23.435570Z", - "shell.execute_reply": "2026-08-25T12:33:23.434589Z" + "iopub.execute_input": "2026-08-25T12:45:13.129441Z", + "iopub.status.busy": "2026-08-25T12:45:13.129250Z", + "iopub.status.idle": "2026-08-25T12:45:13.840565Z", + "shell.execute_reply": "2026-08-25T12:45:13.839968Z" } }, "outputs": [ From b422742f316c15434a79563f0e1b20a12424cd45 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Tue, 25 Aug 2026 06:34:23 -0700 Subject: [PATCH 08/17] fix(backward_lens): support beartype test instrumentation --- tests/unit/tools/test_backward_lens.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/unit/tools/test_backward_lens.py b/tests/unit/tools/test_backward_lens.py index 4939b9465..628f65371 100644 --- a/tests/unit/tools/test_backward_lens.py +++ b/tests/unit/tools/test_backward_lens.py @@ -7,6 +7,7 @@ import pytest import torch import torch.nn.functional as F +from beartype.roar import BeartypeCallHintParamViolation from transformer_lens.tools.analysis.backward_lens import ( BackwardLensMatrixResult, @@ -162,7 +163,7 @@ def test_factor_validation_errors(inputs, gradients, weight, message) -> None: def test_rejects_invalid_layout_and_integer_factors() -> None: - with pytest.raises(TypeError, match="parameter 'weight_layout'"): + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="weight_layout"): _build_linear_gradient_factors( torch.randn(3, 2), torch.randn(3, 4), @@ -179,7 +180,7 @@ def test_rejects_invalid_layout_and_integer_factors() -> None: def test_rejects_non_tensor_factors() -> None: - with pytest.raises(TypeError, match="torch.Tensor"): + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="torch.Tensor"): _build_linear_gradient_factors( cast(torch.Tensor, "not a tensor"), torch.randn(3, 4), @@ -244,13 +245,13 @@ def test_vocabulary_ranking_rejects_invalid_k(k) -> None: def test_vocabulary_ranking_runtime_typechecking_rejects_non_integer_k() -> None: - with pytest.raises(TypeError, match="parameter 'k'"): + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="parameter.*k"): _rank_vocabulary_logits(torch.randn(2, 4), k=cast(int, 1.5), largest=True) @pytest.mark.parametrize("largest", [1, "yes", None]) def test_vocabulary_ranking_rejects_non_boolean_largest(largest) -> None: - with pytest.raises(TypeError, match="parameter 'largest'"): + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="parameter.*largest"): _rank_vocabulary_logits(torch.randn(2, 4), k=1, largest=cast(bool, largest)) @@ -270,7 +271,7 @@ def test_vocabulary_ranking_rejects_invalid_logits(logits) -> None: def test_vocabulary_ranking_rejects_non_tensor_logits() -> None: - with pytest.raises(TypeError, match="torch.Tensor"): + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="torch.Tensor"): _rank_vocabulary_logits(cast(torch.Tensor, [1.0, 2.0]), k=1, largest=True) From 06416322e5f0fb61a19c9e36a82a60dd44558656 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Tue, 25 Aug 2026 22:54:38 -0700 Subject: [PATCH 09/17] fix(backward_lens): support beartype coverage instrumentation --- tests/integration/test_backward_lens.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index d993021e1..b07cd875f 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -6,6 +6,7 @@ import pytest import torch import torch.nn.functional as F +from beartype.roar import BeartypeCallHintParamViolation PROMPT = "The capital of France is" TARGET = " Paris" @@ -211,7 +212,7 @@ def test_public_api_defaults_to_raw_projection_only(gpt2_bridge) -> None: assert matrix.normalized_vocabulary_logits is None with pytest.raises(ValueError, match="were not requested"): matrix.logits(normalized=True) - with pytest.raises(TypeError): + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="normalized"): BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, [0], normalized=1) From 04d638607cc48fb5fd1b2abf82b5a9bf745357fe Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Wed, 26 Aug 2026 01:01:11 -0700 Subject: [PATCH 10/17] fix(backward_lens): ignore setup-cell notebook output --- demos/Backward_Lens_Demo.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/demos/Backward_Lens_Demo.ipynb b/demos/Backward_Lens_Demo.ipynb index 289f1b1d0..41b91406f 100644 --- a/demos/Backward_Lens_Demo.ipynb +++ b/demos/Backward_Lens_Demo.ipynb @@ -97,6 +97,7 @@ } ], "source": [ + "# NBVAL_IGNORE_OUTPUT\n", "import gc\n", "import os\n", "import warnings\n", From de06271da704059df94aec5ee5788d7df7ee1b5f Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Wed, 2 Sep 2026 06:48:16 -0700 Subject: [PATCH 11/17] feat(backward_lens): bound vocabulary readout retention --- demos/Backward_Lens_Demo.ipynb | 138 +++++++------ docs/source/content/backward_lens.md | 29 ++- tests/integration/test_backward_lens.py | 63 +++++- tests/unit/tools/test_backward_lens.py | 143 +++++++++++++- transformer_lens/tools/analysis/__init__.py | 6 - .../tools/analysis/backward_lens.py | 187 +++++++++++++++--- 6 files changed, 448 insertions(+), 118 deletions(-) diff --git a/demos/Backward_Lens_Demo.ipynb b/demos/Backward_Lens_Demo.ipynb index 41b91406f..6e15b1238 100644 --- a/demos/Backward_Lens_Demo.ipynb +++ b/demos/Backward_Lens_Demo.ipynb @@ -32,10 +32,10 @@ "id": "4955da53", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:44:47.624715Z", - "iopub.status.busy": "2026-08-25T12:44:47.624549Z", - "iopub.status.idle": "2026-08-25T12:45:09.079784Z", - "shell.execute_reply": "2026-08-25T12:45:09.079138Z" + "iopub.execute_input": "2026-09-02T13:04:37.335213Z", + "iopub.status.busy": "2026-09-02T13:04:37.335044Z", + "iopub.status.idle": "2026-09-02T13:05:32.078419Z", + "shell.execute_reply": "2026-09-02T13:05:32.077709Z" } }, "outputs": [ @@ -145,10 +145,10 @@ "id": "8b1857ae", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:09.082051Z", - "iopub.status.busy": "2026-08-25T12:45:09.081830Z", - "iopub.status.idle": "2026-08-25T12:45:09.552544Z", - "shell.execute_reply": "2026-08-25T12:45:09.551698Z" + "iopub.execute_input": "2026-09-02T13:05:32.080857Z", + "iopub.status.busy": "2026-09-02T13:05:32.080627Z", + "iopub.status.idle": "2026-09-02T13:05:32.517362Z", + "shell.execute_reply": "2026-09-02T13:05:32.516603Z" } }, "outputs": [ @@ -211,10 +211,10 @@ "id": "a5007e02", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:09.554766Z", - "iopub.status.busy": "2026-08-25T12:45:09.554567Z", - "iopub.status.idle": "2026-08-25T12:45:11.184850Z", - "shell.execute_reply": "2026-08-25T12:45:11.184018Z" + "iopub.execute_input": "2026-09-02T13:05:32.519185Z", + "iopub.status.busy": "2026-09-02T13:05:32.518979Z", + "iopub.status.idle": "2026-09-02T13:05:34.263223Z", + "shell.execute_reply": "2026-09-02T13:05:34.262420Z" } }, "outputs": [ @@ -243,10 +243,10 @@ "id": "60ea683d", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:11.186889Z", - "iopub.status.busy": "2026-08-25T12:45:11.186541Z", - "iopub.status.idle": "2026-08-25T12:45:11.652220Z", - "shell.execute_reply": "2026-08-25T12:45:11.651188Z" + "iopub.execute_input": "2026-09-02T13:05:34.265510Z", + "iopub.status.busy": "2026-09-02T13:05:34.265279Z", + "iopub.status.idle": "2026-09-02T13:05:34.682954Z", + "shell.execute_reply": "2026-09-02T13:05:34.682165Z" } }, "outputs": [ @@ -307,10 +307,10 @@ "id": "663bd83f", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:11.654825Z", - "iopub.status.busy": "2026-08-25T12:45:11.654487Z", - "iopub.status.idle": "2026-08-25T12:45:12.028505Z", - "shell.execute_reply": "2026-08-25T12:45:12.027732Z" + "iopub.execute_input": "2026-09-02T13:05:34.685339Z", + "iopub.status.busy": "2026-09-02T13:05:34.685077Z", + "iopub.status.idle": "2026-09-02T13:05:35.033441Z", + "shell.execute_reply": "2026-09-02T13:05:35.032697Z" } }, "outputs": [ @@ -873,7 +873,7 @@ "\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -991,10 +991,10 @@ "id": "323e645c", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:12.030560Z", - "iopub.status.busy": "2026-08-25T12:45:12.030352Z", - "iopub.status.idle": "2026-08-25T12:45:12.394537Z", - "shell.execute_reply": "2026-08-25T12:45:12.393701Z" + "iopub.execute_input": "2026-09-02T13:05:35.035652Z", + "iopub.status.busy": "2026-09-02T13:05:35.035444Z", + "iopub.status.idle": "2026-09-02T13:05:35.408876Z", + "shell.execute_reply": "2026-09-02T13:05:35.408222Z" } }, "outputs": [ @@ -1238,10 +1238,10 @@ "id": "e87a4974", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:12.396360Z", - "iopub.status.busy": "2026-08-25T12:45:12.396150Z", - "iopub.status.idle": "2026-08-25T12:45:12.576218Z", - "shell.execute_reply": "2026-08-25T12:45:12.575495Z" + "iopub.execute_input": "2026-09-02T13:05:35.410940Z", + "iopub.status.busy": "2026-09-02T13:05:35.410743Z", + "iopub.status.idle": "2026-09-02T13:05:35.592085Z", + "shell.execute_reply": "2026-09-02T13:05:35.591305Z" } }, "outputs": [ @@ -1363,7 +1363,7 @@ "source": [ "# Exact ranks can move by one across numeric backends when vocabulary logits are nearly tied.\n", "# Keep them in primary_layer_summary, but display stable vocabulary percentiles.\n", - "vocab_size = primary_result.layers[0].output_projection.vocabulary_logits.shape[-1]\n", + "vocab_size = primary_result.layers[0].output_projection.vocabulary_size\n", "rank_percentiles = primary_layer_summary[[\"layer\", \"final-position VJP norm\"]].copy()\n", "rank_percentiles[\"raw target rank percentile\"] = (\n", " 100 * primary_layer_summary[\"raw target rank\"] / vocab_size\n", @@ -1420,10 +1420,10 @@ "id": "942c9dce", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:12.578056Z", - "iopub.status.busy": "2026-08-25T12:45:12.577858Z", - "iopub.status.idle": "2026-08-25T12:45:12.830955Z", - "shell.execute_reply": "2026-08-25T12:45:12.830175Z" + "iopub.execute_input": "2026-09-02T13:05:35.593912Z", + "iopub.status.busy": "2026-09-02T13:05:35.593713Z", + "iopub.status.idle": "2026-09-02T13:05:35.846776Z", + "shell.execute_reply": "2026-09-02T13:05:35.846027Z" } }, "outputs": [ @@ -1494,10 +1494,10 @@ "id": "22bdaffd", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:12.832874Z", - "iopub.status.busy": "2026-08-25T12:45:12.832677Z", - "iopub.status.idle": "2026-08-25T12:45:13.127717Z", - "shell.execute_reply": "2026-08-25T12:45:13.126964Z" + "iopub.execute_input": "2026-09-02T13:05:35.848878Z", + "iopub.status.busy": "2026-09-02T13:05:35.848671Z", + "iopub.status.idle": "2026-09-02T13:05:36.145144Z", + "shell.execute_reply": "2026-09-02T13:05:36.144305Z" } }, "outputs": [ @@ -1565,7 +1565,7 @@ "source": [ "## Predeclared examples: report every outcome\n", "\n", - "The remaining examples are analyzed only at GPT-2's final layer to keep the demo compact. The table reports whether unit normalization improves, worsens, or leaves unchanged the target's ascending raw-gradient rank. No example is removed based on its result." + "The remaining examples are analyzed only at GPT-2's final layer to keep the demo compact. The table reports one-decimal vocabulary rank percentiles so nearly tied logits remain stable across numeric backends. It also records whether unit normalization improves, worsens, or leaves unchanged the target's exact ascending raw-gradient rank. No example is removed based on its result." ] }, { @@ -1574,10 +1574,10 @@ "id": "5ba7e1da", "metadata": { "execution": { - "iopub.execute_input": "2026-08-25T12:45:13.129441Z", - "iopub.status.busy": "2026-08-25T12:45:13.129250Z", - "iopub.status.idle": "2026-08-25T12:45:13.840565Z", - "shell.execute_reply": "2026-08-25T12:45:13.839968Z" + "iopub.execute_input": "2026-09-02T13:05:36.147093Z", + "iopub.status.busy": "2026-09-02T13:05:36.146767Z", + "iopub.status.idle": "2026-09-02T13:05:36.888196Z", + "shell.execute_reply": "2026-09-02T13:05:36.887405Z" } }, "outputs": [ @@ -1604,8 +1604,8 @@ " \n", " prompt\n", " target\n", - " raw target rank\n", - " normalized target rank\n", + " raw target rank percentile\n", + " normalized target rank percentile\n", " normalization outcome\n", " VJP norm\n", " \n", @@ -1615,8 +1615,8 @@ " 0\n", " The capital of France is\n", " Paris\n", - " 0\n", - " 0\n", + " 0.0\n", + " 0.0\n", " unchanged\n", " 0.260877\n", " \n", @@ -1624,8 +1624,8 @@ " 1\n", " The capital of Italy is\n", " Rome\n", - " 0\n", - " 0\n", + " 0.0\n", + " 0.0\n", " unchanged\n", " 0.262014\n", " \n", @@ -1633,8 +1633,8 @@ " 2\n", " The largest planet in the Solar System is\n", " Jupiter\n", - " 0\n", - " 0\n", + " 0.0\n", + " 0.0\n", " unchanged\n", " 0.280860\n", " \n", @@ -1643,15 +1643,20 @@ "" ], "text/plain": [ - " prompt target raw target rank \\\n", - "0 The capital of France is Paris 0 \n", - "1 The capital of Italy is Rome 0 \n", - "2 The largest planet in the Solar System is Jupiter 0 \n", + " prompt target \\\n", + "0 The capital of France is Paris \n", + "1 The capital of Italy is Rome \n", + "2 The largest planet in the Solar System is Jupiter \n", "\n", - " normalized target rank normalization outcome VJP norm \n", - "0 0 unchanged 0.260877 \n", - "1 0 unchanged 0.262014 \n", - "2 0 unchanged 0.280860 " + " raw target rank percentile normalized target rank percentile \\\n", + "0 0.0 0.0 \n", + "1 0.0 0.0 \n", + "2 0.0 0.0 \n", + "\n", + " normalization outcome VJP norm \n", + "0 unchanged 0.260877 \n", + "1 unchanged 0.262014 \n", + "2 unchanged 0.280860 " ] }, "metadata": {}, @@ -1680,6 +1685,7 @@ " summary = final_position_summary(result).iloc[-1]\n", " raw_rank = int(summary[\"raw target rank\"])\n", " normalized_rank = int(summary[\"normalized target rank\"])\n", + " vocab_size = result.layers[-1].output_projection.vocabulary_size\n", " outcome = (\n", " \"improved\"\n", " if normalized_rank < raw_rank\n", @@ -1689,8 +1695,8 @@ " {\n", " \"prompt\": example_prompt,\n", " \"target\": example_target,\n", - " \"raw target rank\": raw_rank,\n", - " \"normalized target rank\": normalized_rank,\n", + " \"raw target rank percentile\": 100 * raw_rank / vocab_size,\n", + " \"normalized target rank percentile\": 100 * normalized_rank / vocab_size,\n", " \"normalization outcome\": outcome,\n", " \"VJP norm\": float(summary[\"final-position VJP norm\"]),\n", " }\n", @@ -1700,7 +1706,15 @@ " gc.collect()\n", "\n", "comparison = pd.DataFrame(comparison_rows)\n", - "display(comparison.round({\"VJP norm\": 8}))\n", + "display(\n", + " comparison.round(\n", + " {\n", + " \"raw target rank percentile\": 1,\n", + " \"normalized target rank percentile\": 1,\n", + " \"VJP norm\": 8,\n", + " }\n", + " )\n", + ")\n", "print(\n", " \"Normalization outcomes:\",\n", " comparison[\"normalization outcome\"].value_counts().to_dict(),\n", diff --git a/docs/source/content/backward_lens.md b/docs/source/content/backward_lens.md index 17a4ae91d..6f1f3f772 100644 --- a/docs/source/content/backward_lens.md +++ b/docs/source/content/backward_lens.md @@ -48,8 +48,10 @@ $$ Final-normalization statistics are recomputed independently for every factor. The implementation does not reuse normalization scales cached during the model's forward -pass. `vocabulary_logits` contains signed, pre-softmax logits; it does not contain -probabilities. +pass. The retained rankings contain signed, pre-softmax values; they do not contain +probabilities. By default, each matrix keeps only the 10 largest and 10 smallest +values and token ids per position. Set `top_k` to change that bound or +`return_full_logits=True` to also retain the full vocabulary tensors. When `normalized=True`, the analysis also computes the Normalized Logit Lens: @@ -97,6 +99,7 @@ result = BackwardLens(model).analyze( target_token=" Paris", layers=[0, 6, 11], normalized=True, + top_k=10, ) last_layer = result.layer(11) @@ -124,16 +127,26 @@ align with `result.prompt_token_ids`. - `loss` is final-position cross-entropy against the one-token target. - `layers` preserves the requested layer order; `result.layer(index)` retrieves one. - Each layer has `input_projection` and `output_projection` matrix results. -- Each matrix exposes `factors`, `factor_norms`, `zero_norm_mask`, and raw vocabulary - logits. `normalized_vocabulary_logits` is present only when requested. -- `top(...)` and `bottom(...)` return signed values and token ids. +- Each matrix exposes `factors`, `factor_norms`, `zero_norm_mask`, `vocabulary_size`, + and retained raw `top_ranking` and `bottom_ranking` values and token ids. +- `top(...)` and `bottom(...)` return prefixes of the retained signed rankings, so + their `k` cannot exceed the `top_k` passed to `analyze(...)`. - `top_tokens(...)` and `bottom_tokens(...)` decode ids with a caller-provided tokenizer. Results deliberately retain no model or tokenizer reference. -- Target ranks are zero-based competition ranks, so tied logits receive the same rank. +- The analyzed target's exact ranks are always retained as zero-based competition + ranks, so tied logits receive the same rank. Ranking another token requires full + logits. +- `vocabulary_logits` is present only with `return_full_logits=True`. + `normalized_top_ranking`, `normalized_bottom_ranking`, and normalized target ranks + are present with `normalized=True`; full `normalized_vocabulary_logits` requires + both options. +- `includes_normalized_logits` and `includes_full_logits` record the requested modes. - Maximum reconstruction errors summarize both MLP matrices over all requested layers. Returned tensors are detached, owned CPU copies. Factors, reconstructed gradients, -norms, and vocabulary logits use float32; token ids and ranks use int64. +norms, retained values, and optional vocabulary logits use float32; token ids and +ranks use int64. The bounded default avoids retaining a +`[layer, matrix, position, d_vocab]` collection of full tensors. ## Requirements and non-goals @@ -164,6 +177,8 @@ activation-editing hooks still affect the analyzed computation. | Target encodes to zero or multiple tokens | Choose text that maps to one GPT-2 token without BOS; check leading whitespace. | | Duplicate or out-of-range layer error | Pass a non-empty sequence of unique indices in `[0, model.cfg.n_layers)`. | | Normalized logits were not requested | Call `analyze(..., normalized=True)` before using `logits(normalized=True)` or normalized ranks. | +| Full logits were not retained | Call `analyze(..., return_full_logits=True)` before using `logits(...)` or ranking a token other than the analyzed target. | +| Requested `k` exceeds retained `top_k` | Increase `top_k` in `analyze(...)`; accessor methods cannot recover discarded rankings. | | FF2 ranking appears sign-reversed | Remember that results are raw loss gradients and gradient descent subtracts them; inspect bottom tokens or ascending target ranks. | | Results change when custom hooks are installed | Existing hooks are intentionally respected; remove them to analyze the unmodified model computation. | diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index b07cd875f..bfd0ebd9e 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -2,6 +2,7 @@ import warnings from collections.abc import Sequence +from typing import Any, cast import pytest import torch @@ -39,7 +40,14 @@ def gradient_capture(gpt2_bridge): def backward_result(gpt2_bridge): from transformer_lens.tools.analysis import BackwardLens - return BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, LAYERS, normalized=True) + return BackwardLens(gpt2_bridge).analyze( + PROMPT, + TARGET, + LAYERS, + normalized=True, + top_k=3, + return_full_logits=True, + ) def _projection_hook_snapshots(model, layer: int) -> list[tuple[int, ...]]: @@ -126,6 +134,7 @@ def test_public_result_metadata_and_error_summaries(backward_result) -> None: assert backward_result.prompt_token_ids.shape == (6,) assert [layer.layer for layer in backward_result.layers] == list(LAYERS) assert backward_result.includes_normalized_logits is True + assert backward_result.includes_full_logits is True assert backward_result.max_absolute_reconstruction_error <= 2e-6 assert backward_result.max_relative_reconstruction_error <= 2e-5 assert backward_result.layer(11) is backward_result.layers[-1] @@ -147,6 +156,7 @@ def test_public_projections_match_fresh_model_readout(backward_result, gpt2_brid ).unsqueeze(0) with torch.no_grad(): direct = gpt2_bridge.unembed(gpt2_bridge.ln_final(rows_on_model)).squeeze(0) + assert matrix.vocabulary_logits is not None torch.testing.assert_close(matrix.vocabulary_logits, direct.float().cpu()) norms = rows.norm(dim=-1) @@ -180,6 +190,9 @@ def test_public_projections_match_fresh_model_readout(backward_result, gpt2_brid def test_public_rankings_decoding_and_target_ranks(backward_result, gpt2_bridge) -> None: output = backward_result.layer(11).output_projection + assert output.vocabulary_logits is not None + assert output.top_ranking.indices.shape == (6, 3) + assert output.bottom_ranking.indices.shape == (6, 3) direct_top = torch.topk(output.vocabulary_logits, k=3, dim=-1, largest=True) direct_bottom = torch.topk(output.vocabulary_logits, k=3, dim=-1, largest=False) @@ -205,15 +218,40 @@ def test_public_api_defaults_to_raw_projection_only(gpt2_bridge) -> None: result = BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, [0]) assert result.includes_normalized_logits is False + assert result.includes_full_logits is False for matrix in ( result.layers[0].input_projection, result.layers[0].output_projection, ): + assert matrix.vocabulary_logits is None assert matrix.normalized_vocabulary_logits is None + assert matrix.top_ranking.indices.shape == (6, 10) + assert matrix.bottom_ranking.indices.shape == (6, 10) + assert matrix.normalized_top_ranking is None + assert matrix.normalized_bottom_ranking is None + assert matrix.gradient_descent_target_ranks(result.target_token_id).shape == (6,) + with pytest.raises(ValueError, match="return_full_logits=True"): + matrix.logits() with pytest.raises(ValueError, match="were not requested"): matrix.logits(normalized=True) with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="normalized"): - BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, [0], normalized=1) + BackwardLens(gpt2_bridge).analyze(PROMPT, TARGET, [0], normalized=cast(bool, 1)) + + +def test_public_api_validates_retention_options_before_capture(gpt2_bridge) -> None: + from transformer_lens.tools.analysis import BackwardLens + + lens = BackwardLens(gpt2_bridge) + with pytest.raises(ValueError, match="top_k must be in"): + lens.analyze(PROMPT, TARGET, [0], top_k=0) + with pytest.raises(ValueError, match="top_k must be in"): + lens.analyze(PROMPT, TARGET, [0], top_k=gpt2_bridge.cfg.d_vocab + 1) + with pytest.raises(TypeError, match="top_k must be an integer"): + lens.analyze(PROMPT, TARGET, [0], top_k=True) + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="top_k"): + lens.analyze(PROMPT, TARGET, [0], top_k=cast(int, 1.5)) + with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="return_full_logits"): + lens.analyze(PROMPT, TARGET, [0], return_full_logits=cast(bool, 1)) @pytest.mark.parametrize( @@ -502,7 +540,7 @@ def test_tiny_gpt2_capture_supports_available_devices_and_reduced_precision( embd_pdrop=0.1, attn_pdrop=0.1, ) - hf_model = GPT2LMHeadModel(config).to(device=device, dtype=dtype).eval() + hf_model = cast(Any, GPT2LMHeadModel)(config).to(device=device, dtype=dtype).eval() bridge = TransformerBridge.boot_transformers( "gpt2", hf_model=hf_model, @@ -531,6 +569,19 @@ def test_tiny_gpt2_capture_supports_available_devices_and_reduced_precision( factors = matrix.factors assert torch.isfinite(factors.weight_gradient).all() assert factors.relative_reconstruction_error <= 5e-2 - assert torch.isfinite(matrix.vocabulary_logits).all() - assert matrix.normalized_vocabulary_logits is not None - assert torch.isfinite(matrix.normalized_vocabulary_logits).all() + assert matrix.vocabulary_logits is None + assert matrix.normalized_vocabulary_logits is None + assert torch.isfinite(matrix.top_ranking.values).all() + assert torch.isfinite(matrix.bottom_ranking.values).all() + assert matrix.normalized_top_ranking is not None + assert matrix.normalized_bottom_ranking is not None + assert torch.isfinite(matrix.normalized_top_ranking.values).all() + assert torch.isfinite(matrix.normalized_bottom_ranking.values).all() + for ranking in ( + matrix.top_ranking, + matrix.bottom_ranking, + matrix.normalized_top_ranking, + matrix.normalized_bottom_ranking, + ): + assert ranking.values.device.type == "cpu" + assert ranking.indices.device.type == "cpu" diff --git a/tests/unit/tools/test_backward_lens.py b/tests/unit/tools/test_backward_lens.py index 628f65371..e959388a2 100644 --- a/tests/unit/tools/test_backward_lens.py +++ b/tests/unit/tools/test_backward_lens.py @@ -14,6 +14,7 @@ LinearGradientFactors, WeightLayout, _build_linear_gradient_factors, + _build_matrix_result, _factor_norms_and_normalized_rows, _project_residual_factors, _rank_vocabulary_logits, @@ -33,20 +34,66 @@ def W_U(self) -> torch.Tensor: return self.unembed.weight.T -def _matrix_result(logits: torch.Tensor) -> BackwardLensMatrixResult: +def _matrix_result( + logits: torch.Tensor, + *, + normalized: bool = True, + return_full_logits: bool = True, + top_k: int = 2, +) -> BackwardLensMatrixResult: factors = _build_linear_gradient_factors( torch.tensor([[1.0, 2.0], [3.0, 4.0]]), torch.tensor([[0.5, -1.0], [2.0, 1.5]]), torch.tensor([[6.5, 3.5], [9.0, 4.0]]), weight_layout="in_out", ) + normalized_logits = logits * 2 if normalized else None + target_token_id = 2 return BackwardLensMatrixResult( factors=factors, projected_factor="forward_inputs", factor_norms=torch.tensor([1.0, 2.0]), zero_norm_mask=torch.tensor([False, False]), - vocabulary_logits=logits.clone(), - normalized_vocabulary_logits=(logits * 2).clone(), + vocabulary_size=logits.shape[-1], + target_token_id=target_token_id, + top_ranking=_rank_vocabulary_logits(logits, k=top_k, largest=True), + bottom_ranking=_rank_vocabulary_logits(logits, k=top_k, largest=False), + target_largest_ranks=(logits > logits[:, target_token_id, None]).sum( + dim=-1, dtype=torch.int64 + ), + target_smallest_ranks=(logits < logits[:, target_token_id, None]).sum( + dim=-1, dtype=torch.int64 + ), + normalized_top_ranking=( + _rank_vocabulary_logits(normalized_logits, k=top_k, largest=True) + if normalized_logits is not None + else None + ), + normalized_bottom_ranking=( + _rank_vocabulary_logits(normalized_logits, k=top_k, largest=False) + if normalized_logits is not None + else None + ), + normalized_target_largest_ranks=( + (normalized_logits > normalized_logits[:, target_token_id, None]).sum( + dim=-1, dtype=torch.int64 + ) + if normalized_logits is not None + else None + ), + normalized_target_smallest_ranks=( + (normalized_logits < normalized_logits[:, target_token_id, None]).sum( + dim=-1, dtype=torch.int64 + ) + if normalized_logits is not None + else None + ), + vocabulary_logits=logits.clone() if return_full_logits else None, + normalized_vocabulary_logits=( + normalized_logits.clone() + if return_full_logits and normalized_logits is not None + else None + ), ) @@ -337,6 +384,7 @@ def test_matrix_result_rankings_decoding_and_target_rank_conventions() -> None: assert result.bottom(k=2).indices.tolist() == [[2, 3], [1, 0]] assert result.target_ranks(2, largest=True).tolist() == [3, 0] assert result.gradient_descent_target_ranks(2).tolist() == [0, 3] + assert result.target_ranks(1, largest=True).tolist() == [1, 3] assert torch.equal(result.logits(normalized=True), logits * 2) tokenizer = SimpleNamespace(decode=lambda ids: f"token-{ids[0]}") @@ -347,13 +395,7 @@ def test_matrix_result_rankings_decoding_and_target_rank_conventions() -> None: def test_matrix_result_rejects_missing_normalized_logits_and_invalid_target() -> None: logits = torch.randn(2, 4) result = _matrix_result(logits) - result_without_normalized = BackwardLensMatrixResult( - factors=result.factors, - projected_factor=result.projected_factor, - factor_norms=result.factor_norms, - zero_norm_mask=result.zero_norm_mask, - vocabulary_logits=result.vocabulary_logits, - ) + result_without_normalized = _matrix_result(logits, normalized=False) with pytest.raises(ValueError, match="were not requested"): result_without_normalized.logits(normalized=True) @@ -363,6 +405,87 @@ def test_matrix_result_rejects_missing_normalized_logits_and_invalid_target() -> result.top_tokens(object(), k=1) +@pytest.mark.parametrize("k", [0, 3, True]) +def test_matrix_result_rejects_invalid_retained_ranking_k(k) -> None: + result = _matrix_result(torch.randn(2, 4)) + + with pytest.raises(ValueError, match="retained top_k=2"): + result.top(k=cast(int, k)) + + +def test_matrix_result_keeps_rankings_and_analyzed_target_ranks_without_full_logits() -> None: + result = _matrix_result( + torch.tensor([[3.0, 1.0, -2.0, 0.0], [0.0, -1.0, 4.0, 2.0]]), + return_full_logits=False, + ) + + assert result.vocabulary_logits is None + assert result.normalized_vocabulary_logits is None + assert result.top(k=2).indices.tolist() == [[0, 1], [2, 3]] + assert result.bottom(k=2).indices.tolist() == [[2, 3], [1, 0]] + assert result.gradient_descent_target_ranks(2).tolist() == [0, 3] + assert result.gradient_descent_target_ranks(2, normalized=True).tolist() == [0, 3] + with pytest.raises(ValueError, match="return_full_logits=True"): + result.logits() + with pytest.raises(ValueError, match="return_full_logits=True"): + result.target_ranks(1, largest=True) + + +@pytest.mark.parametrize("normalized", [False, True]) +@pytest.mark.parametrize("return_full_logits", [False, True]) +def test_build_matrix_result_bounds_storage_and_matches_projection( + normalized: bool, return_full_logits: bool +) -> None: + model = _ReadoutModel() + inputs = torch.tensor([[1.0, -2.0, 4.0], [0.25, 0.5, -0.75]]) + output_gradients = torch.tensor([[0.5, -1.0, 2.0], [1.5, 0.25, -0.5]]) + factors = _build_linear_gradient_factors( + inputs, + output_gradients, + inputs.T @ output_gradients, + weight_layout="in_out", + ) + direct = _project_residual_factors(model, inputs) + + result = _build_matrix_result( + model, + factors, + projected_factor="forward_inputs", + include_normalized_logits=normalized, + target_token_id=2, + top_k=2, + return_full_logits=return_full_logits, + ) + + direct_top = torch.topk(direct, k=2, dim=-1, largest=True) + direct_bottom = torch.topk(direct, k=2, dim=-1, largest=False) + assert torch.equal(result.top_ranking.values, direct_top.values) + assert torch.equal(result.top_ranking.indices, direct_top.indices) + assert torch.equal(result.bottom_ranking.values, direct_bottom.values) + assert torch.equal(result.bottom_ranking.indices, direct_bottom.indices) + assert (result.vocabulary_logits is not None) is return_full_logits + assert (result.normalized_top_ranking is not None) is normalized + assert (result.normalized_bottom_ranking is not None) is normalized + assert (result.normalized_vocabulary_logits is not None) is (normalized and return_full_logits) + + +def test_ranking_accessors_return_owned_retained_prefixes() -> None: + result = _matrix_result(torch.tensor([[3.0, 1.0, -2.0, 0.0]])) + top = result.top(k=1) + bottom = result.bottom(k=1, normalized=True) + + top.values.add_(100) + top.indices.add_(100) + bottom.values.add_(100) + bottom.indices.add_(100) + + assert result.top_ranking.values.tolist() == [[3.0, 1.0]] + assert result.top_ranking.indices.tolist() == [[0, 1]] + assert result.normalized_bottom_ranking is not None + assert result.normalized_bottom_ranking.values.tolist() == [[-4.0, 0.0]] + assert result.normalized_bottom_ranking.indices.tolist() == [[2, 3]] + + def test_public_backward_lens_symbols_are_exported() -> None: from transformer_lens.tools.analysis import ( BackwardLens, diff --git a/transformer_lens/tools/analysis/__init__.py b/transformer_lens/tools/analysis/__init__.py index b1170a527..071ae9411 100644 --- a/transformer_lens/tools/analysis/__init__.py +++ b/transformer_lens/tools/analysis/__init__.py @@ -61,14 +61,11 @@ ) __all__ = [ -<<<<<<< HEAD "AttentionHeadRef", -======= "BackwardLens", "BackwardLensLayerResult", "BackwardLensMatrixResult", "BackwardLensResult", ->>>>>>> c49c1cd1 (feat(backward_lens): project GPT-2 MLP gradient factors into vocabulary space) "DirectLogitAttribution", "HeadAffinityPair", "HeadAffinityResult", @@ -77,17 +74,14 @@ "JSpaceVarianceProfile", "JacobianLens", "JacobianLensReadout", -<<<<<<< HEAD "ProjectionKernelResult", "RandomSubspaceReference", "SubspaceBasis", "attention_head_subspace_affinity", -======= "LinearGradientFactors", "ProjectedFactor", "VocabularyRanking", "WeightLayout", ->>>>>>> c49c1cd1 (feat(backward_lens): project GPT-2 MLP gradient factors into vocabulary space) "direct_logit_attribution", "estimate_occupancy", "get_act_patch_direct_path", diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py index a40e95bb4..b0fc74baa 100644 --- a/transformer_lens/tools/analysis/backward_lens.py +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -17,6 +17,7 @@ WeightLayout = Literal["in_out", "out_in"] ProjectedFactor = Literal["forward_inputs", "output_gradients"] +DEFAULT_TOP_K = 10 @dataclass(frozen=True) @@ -58,36 +59,66 @@ class BackwardLensMatrixResult: ``factors`` contains the full linear factorization. ``projected_factor`` says whether its residual-width ``forward_inputs`` or raw-gradient ``output_gradients`` were decoded. ``factor_norms`` and ``zero_norm_mask`` - have shape ``[position]`` with float32 and bool dtypes. ``vocabulary_logits`` - has shape ``[position, d_vocab]`` and contains signed, pre-softmax raw logits. - ``normalized_vocabulary_logits`` has the same shape when requested. Every - tensor is a detached CPU-owned value; gradient descent subtracts raw gradients. + have shape ``[position]`` with float32 and bool dtypes. Largest and smallest + signed rankings are always retained. Full ``vocabulary_logits`` are present + only when explicitly requested. Normalized rankings and optional full logits + are present when the Normalized Logit Lens is requested. Every retained tensor + is a detached CPU-owned value; gradient descent subtracts raw gradients. """ factors: LinearGradientFactors projected_factor: ProjectedFactor factor_norms: torch.Tensor zero_norm_mask: torch.Tensor - vocabulary_logits: torch.Tensor + vocabulary_size: int + target_token_id: int + top_ranking: VocabularyRanking + bottom_ranking: VocabularyRanking + target_largest_ranks: torch.Tensor + target_smallest_ranks: torch.Tensor + normalized_top_ranking: VocabularyRanking | None = None + normalized_bottom_ranking: VocabularyRanking | None = None + normalized_target_largest_ranks: torch.Tensor | None = None + normalized_target_smallest_ranks: torch.Tensor | None = None + vocabulary_logits: torch.Tensor | None = None normalized_vocabulary_logits: torch.Tensor | None = None def logits(self, *, normalized: bool = False) -> torch.Tensor: - """Return raw or requested Normalized Logit Lens logits.""" + """Return opted-in raw or Normalized Logit Lens full logits.""" if not isinstance(normalized, bool): raise TypeError("normalized must be a bool") - if not normalized: - return self.vocabulary_logits - if self.normalized_vocabulary_logits is None: + if normalized and self.normalized_top_ranking is None: raise ValueError("normalized logits were not requested during analysis") - return self.normalized_vocabulary_logits + logits = self.normalized_vocabulary_logits if normalized else self.vocabulary_logits + if logits is None: + kind = "normalized " if normalized else "" + raise ValueError( + f"full {kind}logits were not retained; call " + "BackwardLens.analyze(..., return_full_logits=True)" + ) + return logits + + def _retained_ranking(self, *, normalized: bool, largest: bool) -> VocabularyRanking: + if not isinstance(normalized, bool): + raise TypeError("normalized must be a bool") + if normalized: + ranking = self.normalized_top_ranking if largest else self.normalized_bottom_ranking + if ranking is None: + raise ValueError("normalized logits were not requested during analysis") + return ranking + return self.top_ranking if largest else self.bottom_ranking def top(self, *, k: int, normalized: bool = False) -> VocabularyRanking: - """Return the largest signed logits and token ids per position.""" - return _rank_vocabulary_logits(self.logits(normalized=normalized), k=k, largest=True) + """Return up to the retained largest signed logits and token ids.""" + return _slice_vocabulary_ranking( + self._retained_ranking(normalized=normalized, largest=True), k=k + ) def bottom(self, *, k: int, normalized: bool = False) -> VocabularyRanking: - """Return the smallest signed logits and token ids per position.""" - return _rank_vocabulary_logits(self.logits(normalized=normalized), k=k, largest=False) + """Return up to the retained smallest signed logits and token ids.""" + return _slice_vocabulary_ranking( + self._retained_ranking(normalized=normalized, largest=False), k=k + ) def top_tokens(self, tokenizer: Any, *, k: int, normalized: bool = False) -> list[list[str]]: """Decode the largest-``k`` vocabulary ids for every position.""" @@ -109,20 +140,37 @@ def target_ranks( ``largest=True`` gives rank zero to the largest logit. ``largest=False`` gives rank zero to the smallest, which is the useful raw-gradient convention for the second MLP matrix because gradient descent subtracts it. - Ties receive the same competition rank. + Ties receive the same competition rank. The analyzed target's ranks are + always retained; other token ids require opted-in full logits. """ - logits = self.logits(normalized=normalized) if isinstance(target_token_id, bool) or not isinstance(target_token_id, int): raise TypeError("target_token_id must be an integer") - if not 0 <= target_token_id < logits.shape[-1]: + if not 0 <= target_token_id < self.vocabulary_size: raise ValueError( - f"target_token_id must be in [0, {logits.shape[-1] - 1}]; " f"got {target_token_id}" + f"target_token_id must be in [0, {self.vocabulary_size - 1}]; " + f"got {target_token_id}" ) if not isinstance(largest, bool): raise TypeError("largest must be a bool") - target = logits[:, target_token_id].unsqueeze(-1) - comparisons = logits > target if largest else logits < target - return comparisons.sum(dim=-1, dtype=torch.int64).cpu().clone() + if not isinstance(normalized, bool): + raise TypeError("normalized must be a bool") + if normalized: + ranks = ( + self.normalized_target_largest_ranks + if largest + else self.normalized_target_smallest_ranks + ) + if ranks is None: + raise ValueError("normalized logits were not requested during analysis") + else: + ranks = self.target_largest_ranks if largest else self.target_smallest_ranks + if target_token_id == self.target_token_id: + return ranks.clone() + return _target_vocabulary_ranks( + self.logits(normalized=normalized), + target_token_id=target_token_id, + largest=largest, + ) def gradient_descent_target_ranks( self, target_token_id: int, *, normalized: bool = False @@ -153,7 +201,9 @@ class BackwardLensResult: ``layers`` is aligned to these same positions. ``layers`` preserves requested order. Maximum errors summarize both matrices over every requested layer. ``includes_normalized_logits`` records whether the Normalized Logit Lens was - computed. No model or tokenizer reference is retained. + computed. ``includes_full_logits`` records whether full vocabulary tensors + were retained in addition to bounded rankings. No model or tokenizer reference + is retained. """ prompt: str @@ -165,6 +215,7 @@ class BackwardLensResult: max_absolute_reconstruction_error: float max_relative_reconstruction_error: float includes_normalized_logits: bool + includes_full_logits: bool def layer(self, layer: int) -> BackwardLensLayerResult: """Return one requested layer result or raise ``KeyError``.""" @@ -323,6 +374,26 @@ def _rank_vocabulary_logits(logits: torch.Tensor, *, k: int, largest: bool) -> V ) +def _slice_vocabulary_ranking(ranking: VocabularyRanking, *, k: int) -> VocabularyRanking: + """Return an owned prefix of an already sorted vocabulary ranking.""" + retained = ranking.indices.shape[-1] + if isinstance(k, bool) or not isinstance(k, int) or not 1 <= k <= retained: + raise ValueError(f"k must be in [1, retained top_k={retained}]; got {k!r}") + return VocabularyRanking( + values=ranking.values[..., :k].clone(), + indices=ranking.indices[..., :k].clone(), + ) + + +def _target_vocabulary_ranks( + logits: torch.Tensor, *, target_token_id: int, largest: bool +) -> torch.Tensor: + """Return zero-based competition ranks for one vocabulary id per row.""" + target = logits[:, target_token_id].unsqueeze(-1) + comparisons = logits > target if largest else logits < target + return comparisons.sum(dim=-1, dtype=torch.int64).cpu().clone() + + def _decode_vocabulary_ranking(ranking: VocabularyRanking, tokenizer: Any) -> list[list[str]]: """Decode a two-dimensional vocabulary ranking without retaining a tokenizer.""" decode = getattr(tokenizer, "decode", None) @@ -356,7 +427,7 @@ def _project_residual_factors(model: Any, factors: torch.Tensor) -> torch.Tensor projected = logits.detach().float() if not bool(torch.isfinite(projected).all()): raise ValueError("vocabulary projection must remain finite in float32") - return projected.cpu().clone() + return projected.clone() def _factor_norms_and_normalized_rows( @@ -378,11 +449,16 @@ def _build_matrix_result( *, projected_factor: ProjectedFactor, include_normalized_logits: bool, + target_token_id: int, + top_k: int, + return_full_logits: bool, ) -> BackwardLensMatrixResult: if projected_factor not in ("forward_inputs", "output_gradients"): raise ValueError("projected_factor must be 'forward_inputs' or 'output_gradients'") if not isinstance(include_normalized_logits, bool): raise TypeError("include_normalized_logits must be a bool") + if not isinstance(return_full_logits, bool): + raise TypeError("return_full_logits must be a bool") rows = ( factors.forward_inputs if projected_factor == "forward_inputs" else factors.output_gradients ) @@ -391,13 +467,50 @@ def _build_matrix_result( normalized_logits = ( _project_residual_factors(model, normalized_rows) if include_normalized_logits else None ) + top_ranking = _rank_vocabulary_logits(raw_logits, k=top_k, largest=True) + bottom_ranking = _rank_vocabulary_logits(raw_logits, k=top_k, largest=False) + target_largest_ranks = _target_vocabulary_ranks( + raw_logits, target_token_id=target_token_id, largest=True + ) + target_smallest_ranks = _target_vocabulary_ranks( + raw_logits, target_token_id=target_token_id, largest=False + ) + normalized_top_ranking = None + normalized_bottom_ranking = None + normalized_target_largest_ranks = None + normalized_target_smallest_ranks = None + if normalized_logits is not None: + normalized_top_ranking = _rank_vocabulary_logits(normalized_logits, k=top_k, largest=True) + normalized_bottom_ranking = _rank_vocabulary_logits( + normalized_logits, k=top_k, largest=False + ) + normalized_target_largest_ranks = _target_vocabulary_ranks( + normalized_logits, target_token_id=target_token_id, largest=True + ) + normalized_target_smallest_ranks = _target_vocabulary_ranks( + normalized_logits, target_token_id=target_token_id, largest=False + ) return BackwardLensMatrixResult( factors=factors, projected_factor=projected_factor, factor_norms=norms, zero_norm_mask=zero_mask, - vocabulary_logits=raw_logits, - normalized_vocabulary_logits=normalized_logits, + vocabulary_size=raw_logits.shape[-1], + target_token_id=target_token_id, + top_ranking=top_ranking, + bottom_ranking=bottom_ranking, + target_largest_ranks=target_largest_ranks, + target_smallest_ranks=target_smallest_ranks, + normalized_top_ranking=normalized_top_ranking, + normalized_bottom_ranking=normalized_bottom_ranking, + normalized_target_largest_ranks=normalized_target_largest_ranks, + normalized_target_smallest_ranks=normalized_target_smallest_ranks, + vocabulary_logits=raw_logits.cpu().clone() if return_full_logits else None, + normalized_vocabulary_logits=( + normalized_logits.cpu().clone() + if return_full_logits and normalized_logits is not None + else None + ), ) @@ -690,6 +803,8 @@ def analyze( layers: Sequence[int], *, normalized: bool = False, + top_k: int = DEFAULT_TOP_K, + return_full_logits: bool = False, ) -> BackwardLensResult: """Analyze one final-position, one-token target loss. @@ -699,13 +814,24 @@ def analyze( layers: Unique GPT-2 layer indices in desired result order. normalized: Also project unit-normalized nonzero factors using the Normalized Logit Lens. Raw projections are always returned. + top_k: Number of largest and smallest values and token ids retained + per matrix and position. Defaults to 10. + return_full_logits: Also retain full vocabulary tensors on CPU. + Defaults to ``False`` to keep result size bounded. Returns: - Detached gradient factors, vocabulary logits, norms, reconstruction - errors, and target metadata. + Detached gradient factors, bounded vocabulary rankings, norms, + reconstruction errors, target metadata, and optional full logits. """ if not isinstance(normalized, bool): raise TypeError("normalized must be a bool") + if isinstance(top_k, bool) or not isinstance(top_k, int): + raise TypeError("top_k must be an integer") + vocabulary_size = int(self._model.cfg.d_vocab) + if not 1 <= top_k <= vocabulary_size: + raise ValueError(f"top_k must be in [1, {vocabulary_size}]; got {top_k!r}") + if not isinstance(return_full_logits, bool): + raise TypeError("return_full_logits must be a bool") capture = _capture_gpt2_mlp_gradient_factors(self._model, prompt, target_token, layers) layer_results: list[BackwardLensLayerResult] = [] absolute_errors: list[float] = [] @@ -716,12 +842,18 @@ def analyze( layer.input_projection, projected_factor="forward_inputs", include_normalized_logits=normalized, + target_token_id=capture.target_token_id, + top_k=top_k, + return_full_logits=return_full_logits, ) output_result = _build_matrix_result( self._model, layer.output_projection, projected_factor="output_gradients", include_normalized_logits=normalized, + target_token_id=capture.target_token_id, + top_k=top_k, + return_full_logits=return_full_logits, ) layer_results.append( BackwardLensLayerResult( @@ -752,4 +884,5 @@ def analyze( max_absolute_reconstruction_error=max(absolute_errors), max_relative_reconstruction_error=max(relative_errors), includes_normalized_logits=normalized, + includes_full_logits=return_full_logits, ) From 834258759c04ed9f14306f501a31e83553dca3d1 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Wed, 2 Sep 2026 09:05:13 -0700 Subject: [PATCH 12/17] fix(backward_lens): harden capture preconditions --- tests/integration/test_backward_lens.py | 87 ++++++++++++++++++- .../tools/analysis/backward_lens.py | 32 +++++-- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index bfd0ebd9e..7f3452b3a 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -12,6 +12,7 @@ PROMPT = "The capital of France is" TARGET = " Paris" LAYERS = (0, 11) +OVERLONG_PROMPT = " token" * 1024 DEVICE_DTYPE_CASES = [pytest.param("cpu", torch.bfloat16, id="cpu-bfloat16")] if torch.cuda.is_available(): @@ -265,6 +266,14 @@ def test_public_api_validates_retention_options_before_capture(gpt2_bridge) -> N (PROMPT, TARGET, [-1], ValueError, "must be in"), (PROMPT, TARGET, [12], ValueError, "must be in"), (PROMPT, TARGET, [True], TypeError, "layer"), + pytest.param( + OVERLONG_PROMPT, + TARGET, + [0], + ValueError, + r"token count 1025.*n_ctx=1024", + id="over-n-ctx", + ), ], ) def test_capture_rejects_invalid_analysis_inputs( @@ -300,11 +309,11 @@ def test_capture_rejects_non_bridge_and_non_raw_states(gpt2_bridge, monkeypatch) monkeypatch.setattr(gpt2_bridge, "_weights_processed", False) adapter = gpt2_bridge.adapter monkeypatch.setattr(gpt2_bridge, "adapter", object()) - with pytest.raises(ValueError, match="GPT2ArchitectureAdapter"): + with pytest.raises(NotImplementedError, match="GPT2ArchitectureAdapter"): _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) monkeypatch.setattr(gpt2_bridge, "adapter", adapter) monkeypatch.setattr(gpt2_bridge.cfg, "gated_mlp", True) - with pytest.raises(ValueError, match="non-gated"): + with pytest.raises(NotImplementedError, match="non-gated"): _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) monkeypatch.setattr(gpt2_bridge.cfg, "gated_mlp", False) monkeypatch.setattr(gpt2_bridge, "tokenizer", None) @@ -312,6 +321,80 @@ def test_capture_rejects_non_bridge_and_non_raw_states(gpt2_bridge, monkeypatch) _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) +def test_capture_rejects_inference_mode(gpt2_bridge) -> None: + from transformer_lens.tools.analysis.backward_lens import ( + _capture_gpt2_mlp_gradient_factors, + ) + + with torch.inference_mode(): + with pytest.raises(ValueError, match="inference_mode"): + _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + + +def test_capture_rejects_multi_device_before_projection_validation( + gpt2_bridge, monkeypatch +) -> None: + from transformer_lens.tools.analysis import backward_lens + + def projection_validation_must_not_run(*_args, **_kwargs) -> None: + raise AssertionError("projection validation ran for a multi-device Bridge") + + monkeypatch.setattr(gpt2_bridge.cfg, "n_devices", 2) + monkeypatch.setattr( + backward_lens, + "_get_gpt2_mlp_projections", + projection_validation_must_not_run, + ) + with pytest.raises(ValueError, match=r"single-device.*co-located.*n_devices=2"): + backward_lens._capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) + + +def test_capture_rejects_a_per_layer_gate(gpt2_bridge, monkeypatch) -> None: + from transformer_lens.tools.analysis.backward_lens import _get_gpt2_mlp_projections + + mlp = gpt2_bridge.blocks[0].mlp + monkeypatch.setattr(mlp, "gate", torch.nn.Identity(), raising=False) + with pytest.raises(ValueError, match="dense, non-gated MLPBridge"): + _get_gpt2_mlp_projections(gpt2_bridge, (0,)) + + +def test_capture_rejects_a_non_conv1d_component(gpt2_bridge, monkeypatch) -> None: + from transformer_lens.tools.analysis.backward_lens import _get_gpt2_mlp_projections + + projection = getattr(gpt2_bridge.blocks[0].mlp, "in") + monkeypatch.setitem(projection._modules, "_original_component", torch.nn.Linear(1, 1)) + with pytest.raises(ValueError, match="input projection must wrap GPT-2 Conv1D"): + _get_gpt2_mlp_projections(gpt2_bridge, (0,)) + + +@pytest.mark.parametrize( + ("invalidity", "match"), + [ + ("shape", r"weight must have shape \(768, 3072\)"), + ("dtype", r"floating dtype.*torch.int64"), + ], + ids=["wrong-shape", "non-floating-dtype"], +) +def test_capture_rejects_an_invalid_weight( + gpt2_bridge, + monkeypatch, + invalidity: str, + match: str, +) -> None: + from transformer_lens.tools.analysis.backward_lens import _get_gpt2_mlp_projections + + component = getattr(gpt2_bridge.blocks[0].mlp, "in").original_component + if invalidity == "shape": + replacement_weight = torch.nn.Parameter(torch.empty(1, 1)) + else: + replacement_weight = torch.nn.Parameter( + torch.empty_like(component.weight, dtype=torch.int64), requires_grad=False + ) + monkeypatch.setattr(component, "weight", replacement_weight) + with pytest.raises(ValueError, match=match): + _get_gpt2_mlp_projections(gpt2_bridge, (0,)) + + def test_capture_rejects_a_frozen_original_weight(gpt2_bridge) -> None: from transformer_lens.tools.analysis.backward_lens import ( _capture_gpt2_mlp_gradient_factors, diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py index b0fc74baa..085d91b9b 100644 --- a/transformer_lens/tools/analysis/backward_lens.py +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -552,13 +552,19 @@ def _require_raw_gpt2_bridge(model: Any) -> None: raise ValueError( "Backward Lens requires original GPT-2 weights; this Bridge processed its weights" ) - if not isinstance(model.adapter, GPT2ArchitectureAdapter): + if int(model.cfg.n_devices) > 1: raise ValueError( + "Backward Lens requires a single-device TransformerBridge because the MLP " + "projections, final normalization, and unembed must be co-located; " + f"device-map dispatch with cfg.n_devices={model.cfg.n_devices} is not supported" + ) + if not isinstance(model.adapter, GPT2ArchitectureAdapter): + raise NotImplementedError( "Backward Lens currently supports the GPT2ArchitectureAdapter only; " f"got {type(model.adapter).__name__}" ) if bool(getattr(model.cfg, "gated_mlp", False)): - raise ValueError("Backward Lens currently requires dense, non-gated GPT-2 MLPs") + raise NotImplementedError("Backward Lens currently requires dense, non-gated GPT-2 MLPs") if model.tokenizer is None: raise ValueError("Backward Lens requires a GPT-2 Bridge with a tokenizer") for component in ("blocks", "ln_final", "unembed"): @@ -594,7 +600,7 @@ def _get_gpt2_mlp_projections(model: Any, layers: tuple[int, ...]) -> dict[int, if not isinstance(projection.original_component, Conv1D): raise ValueError(f"layer {layer} {name} projection must wrap GPT-2 Conv1D") weight = projection.original_component.weight - if not isinstance(weight, torch.nn.Parameter) or not weight.requires_grad: + if not isinstance(weight, torch.nn.Parameter): raise ValueError( f"layer {layer} {name} original weight must be a trainable Parameter" ) @@ -603,6 +609,10 @@ def _get_gpt2_mlp_projections(model: Any, layers: tuple[int, ...]) -> dict[int, f"layer {layer} {name} weight must have shape {expected_shape} " f"and floating dtype; got {tuple(weight.shape)} and {weight.dtype}" ) + if not weight.requires_grad: + raise ValueError( + f"layer {layer} {name} original weight must be a trainable Parameter" + ) if not isinstance(projection.hook_in, HookPoint) or not isinstance( projection.hook_out, HookPoint ): @@ -695,6 +705,11 @@ def _capture_gpt2_mlp_gradient_factors( :func:`torch.autograd.grad` call. It does not call ``backward``, touch parameter ``.grad`` buffers, change training state, or remove caller hooks. """ + if torch.is_inference_mode_enabled(): + raise ValueError( + "Backward Lens cannot capture gradients inside torch.inference_mode(); " + "exit inference_mode before running the analysis" + ) _require_raw_gpt2_bridge(model) requested_layers = _validate_requested_layers(model, layers) projections = _get_gpt2_mlp_projections(model, requested_layers) @@ -705,10 +720,17 @@ def _capture_gpt2_mlp_gradient_factors( if not isinstance(target_token, str): raise TypeError("target_token must be a string") - prompt_tokens = model.to_tokens(prompt) - target_tokens = model.to_tokens(target_token, prepend_bos=False) + prompt_tokens = model.to_tokens(prompt, truncate=False) if prompt_tokens.ndim != 2 or prompt_tokens.shape[0] != 1 or prompt_tokens.shape[1] == 0: raise ValueError("prompt must tokenize to one non-empty sequence") + prompt_token_count = int(prompt_tokens.shape[1]) + context_size = int(model.cfg.n_ctx) + if prompt_token_count > context_size: + raise ValueError( + f"prompt token count {prompt_token_count} exceeds model context limit " + f"n_ctx={context_size}" + ) + target_tokens = model.to_tokens(target_token, prepend_bos=False) if target_tokens.ndim != 2 or tuple(target_tokens.shape) != (1, 1): count = int(target_tokens.numel()) raise ValueError( From 4be30480e93a3eae393e23f555e10ccb58489af2 Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Wed, 2 Sep 2026 12:55:36 -0700 Subject: [PATCH 13/17] refactor(backward_lens): align tensor validation contracts --- tests/unit/tools/test_backward_lens.py | 58 +++++---- .../tools/analysis/backward_lens.py | 114 ++++++++++-------- 2 files changed, 103 insertions(+), 69 deletions(-) diff --git a/tests/unit/tools/test_backward_lens.py b/tests/unit/tools/test_backward_lens.py index e959388a2..2340fe298 100644 --- a/tests/unit/tools/test_backward_lens.py +++ b/tests/unit/tools/test_backward_lens.py @@ -2,22 +2,20 @@ from dataclasses import FrozenInstanceError from types import SimpleNamespace -from typing import cast import pytest import torch import torch.nn.functional as F -from beartype.roar import BeartypeCallHintParamViolation from transformer_lens.tools.analysis.backward_lens import ( BackwardLensMatrixResult, LinearGradientFactors, - WeightLayout, _build_linear_gradient_factors, _build_matrix_result, _factor_norms_and_normalized_rows, _project_residual_factors, _rank_vocabulary_logits, + _single_batch_matrix, ) @@ -210,12 +208,12 @@ def test_factor_validation_errors(inputs, gradients, weight, message) -> None: def test_rejects_invalid_layout_and_integer_factors() -> None: - with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="weight_layout"): + with pytest.raises(ValueError, match="weight_layout"): _build_linear_gradient_factors( torch.randn(3, 2), torch.randn(3, 4), torch.randn(2, 4), - weight_layout=cast(WeightLayout, "other"), + weight_layout="other", ) with pytest.raises(TypeError, match="floating"): _build_linear_gradient_factors( @@ -227,9 +225,9 @@ def test_rejects_invalid_layout_and_integer_factors() -> None: def test_rejects_non_tensor_factors() -> None: - with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="torch.Tensor"): + with pytest.raises(TypeError, match="torch.Tensor"): _build_linear_gradient_factors( - cast(torch.Tensor, "not a tensor"), + "not a tensor", torch.randn(3, 4), torch.randn(2, 4), weight_layout="in_out", @@ -291,15 +289,15 @@ def test_vocabulary_ranking_rejects_invalid_k(k) -> None: _rank_vocabulary_logits(torch.randn(2, 4), k=k, largest=True) -def test_vocabulary_ranking_runtime_typechecking_rejects_non_integer_k() -> None: - with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="parameter.*k"): - _rank_vocabulary_logits(torch.randn(2, 4), k=cast(int, 1.5), largest=True) +def test_vocabulary_ranking_rejects_non_integer_k() -> None: + with pytest.raises(ValueError, match="k must"): + _rank_vocabulary_logits(torch.randn(2, 4), k=1.5, largest=True) @pytest.mark.parametrize("largest", [1, "yes", None]) def test_vocabulary_ranking_rejects_non_boolean_largest(largest) -> None: - with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="parameter.*largest"): - _rank_vocabulary_logits(torch.randn(2, 4), k=1, largest=cast(bool, largest)) + with pytest.raises(TypeError, match="largest must be a bool"): + _rank_vocabulary_logits(torch.randn(2, 4), k=1, largest=largest) @pytest.mark.parametrize( @@ -318,8 +316,8 @@ def test_vocabulary_ranking_rejects_invalid_logits(logits) -> None: def test_vocabulary_ranking_rejects_non_tensor_logits() -> None: - with pytest.raises((TypeError, BeartypeCallHintParamViolation), match="torch.Tensor"): - _rank_vocabulary_logits(cast(torch.Tensor, [1.0, 2.0]), k=1, largest=True) + with pytest.raises(TypeError, match="torch.Tensor"): + _rank_vocabulary_logits([1.0, 2.0], k=1, largest=True) def test_projection_matches_fresh_final_norm_and_unembedding() -> None: @@ -376,6 +374,20 @@ def test_projection_sign_symmetry_depends_on_final_norm_bias() -> None: ) +@pytest.mark.parametrize( + ("tensor", "message"), + [ + ("not a tensor", "torch.Tensor"), + (torch.randn(2, 3), "shape"), + (torch.randn(2, 3, 4), "shape"), + (torch.ones(1, 3, 4, dtype=torch.long), "floating dtype"), + ], +) +def test_single_batch_matrix_rejects_invalid_internal_tensors(tensor, message) -> None: + with pytest.raises(RuntimeError, match=message): + _single_batch_matrix("captured tensor", tensor) + + def test_matrix_result_rankings_decoding_and_target_rank_conventions() -> None: logits = torch.tensor([[3.0, 1.0, -2.0, 0.0], [0.0, -1.0, 4.0, 2.0]]) result = _matrix_result(logits) @@ -410,7 +422,7 @@ def test_matrix_result_rejects_invalid_retained_ranking_k(k) -> None: result = _matrix_result(torch.randn(2, 4)) with pytest.raises(ValueError, match="retained top_k=2"): - result.top(k=cast(int, k)) + result.top(k=k) def test_matrix_result_keeps_rankings_and_analyzed_target_ranks_without_full_logits() -> None: @@ -470,7 +482,12 @@ def test_build_matrix_result_bounds_storage_and_matches_projection( def test_ranking_accessors_return_owned_retained_prefixes() -> None: - result = _matrix_result(torch.tensor([[3.0, 1.0, -2.0, 0.0]])) + result = _matrix_result(torch.tensor([[3.0, 1.0, -2.0, 0.0], [0.0, -1.0, 4.0, 2.0]])) + saved_top_values = result.top_ranking.values.clone() + saved_top_indices = result.top_ranking.indices.clone() + assert result.normalized_bottom_ranking is not None + saved_bottom_values = result.normalized_bottom_ranking.values.clone() + saved_bottom_indices = result.normalized_bottom_ranking.indices.clone() top = result.top(k=1) bottom = result.bottom(k=1, normalized=True) @@ -479,11 +496,10 @@ def test_ranking_accessors_return_owned_retained_prefixes() -> None: bottom.values.add_(100) bottom.indices.add_(100) - assert result.top_ranking.values.tolist() == [[3.0, 1.0]] - assert result.top_ranking.indices.tolist() == [[0, 1]] - assert result.normalized_bottom_ranking is not None - assert result.normalized_bottom_ranking.values.tolist() == [[-4.0, 0.0]] - assert result.normalized_bottom_ranking.indices.tolist() == [[2, 3]] + assert torch.equal(result.top_ranking.values, saved_top_values) + assert torch.equal(result.top_ranking.indices, saved_top_indices) + assert torch.equal(result.normalized_bottom_ranking.values, saved_bottom_values) + assert torch.equal(result.normalized_bottom_ranking.indices, saved_bottom_indices) def test_public_backward_lens_symbols_are_exported() -> None: diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py index 085d91b9b..45ae9a5b4 100644 --- a/transformer_lens/tools/analysis/backward_lens.py +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -10,10 +10,11 @@ from collections.abc import Callable, Iterator, Sequence from contextlib import contextmanager from dataclasses import dataclass -from typing import Any, Literal +from typing import Any, Literal, cast import torch import torch.nn.functional as F +from jaxtyping import Bool, Float, Int WeightLayout = Literal["in_out", "out_in"] ProjectedFactor = Literal["forward_inputs", "output_gradients"] @@ -31,10 +32,10 @@ class LinearGradientFactors: CPU in float32 so the result owns no autograd graph. """ - forward_inputs: torch.Tensor - output_gradients: torch.Tensor - weight_gradient: torch.Tensor - reconstructed_gradient: torch.Tensor + forward_inputs: Float[torch.Tensor, "position in_features"] + output_gradients: Float[torch.Tensor, "position out_features"] + weight_gradient: Float[torch.Tensor, "weight_dim_0 weight_dim_1"] + reconstructed_gradient: Float[torch.Tensor, "weight_dim_0 weight_dim_1"] absolute_reconstruction_error: float relative_reconstruction_error: float weight_layout: WeightLayout @@ -48,8 +49,8 @@ class VocabularyRanking: has dtype ``torch.int64``. Both tensors are detached. """ - values: torch.Tensor - indices: torch.Tensor + values: Float[torch.Tensor, "*leading k"] + indices: Int[torch.Tensor, "*leading k"] @dataclass(frozen=True) @@ -68,22 +69,22 @@ class BackwardLensMatrixResult: factors: LinearGradientFactors projected_factor: ProjectedFactor - factor_norms: torch.Tensor - zero_norm_mask: torch.Tensor + factor_norms: Float[torch.Tensor, "position"] + zero_norm_mask: Bool[torch.Tensor, "position"] vocabulary_size: int target_token_id: int top_ranking: VocabularyRanking bottom_ranking: VocabularyRanking - target_largest_ranks: torch.Tensor - target_smallest_ranks: torch.Tensor + target_largest_ranks: Int[torch.Tensor, "position"] + target_smallest_ranks: Int[torch.Tensor, "position"] normalized_top_ranking: VocabularyRanking | None = None normalized_bottom_ranking: VocabularyRanking | None = None - normalized_target_largest_ranks: torch.Tensor | None = None - normalized_target_smallest_ranks: torch.Tensor | None = None - vocabulary_logits: torch.Tensor | None = None - normalized_vocabulary_logits: torch.Tensor | None = None + normalized_target_largest_ranks: Int[torch.Tensor, "position"] | None = None + normalized_target_smallest_ranks: Int[torch.Tensor, "position"] | None = None + vocabulary_logits: Float[torch.Tensor, "position d_vocab"] | None = None + normalized_vocabulary_logits: Float[torch.Tensor, "position d_vocab"] | None = None - def logits(self, *, normalized: bool = False) -> torch.Tensor: + def logits(self, *, normalized: bool = False) -> Float[torch.Tensor, "position d_vocab"]: """Return opted-in raw or Normalized Logit Lens full logits.""" if not isinstance(normalized, bool): raise TypeError("normalized must be a bool") @@ -134,7 +135,7 @@ def target_ranks( *, largest: bool, normalized: bool = False, - ) -> torch.Tensor: + ) -> Int[torch.Tensor, "position"]: """Return zero-based target ranks per position in the requested ordering. ``largest=True`` gives rank zero to the largest logit. ``largest=False`` @@ -174,7 +175,7 @@ def target_ranks( def gradient_descent_target_ranks( self, target_token_id: int, *, normalized: bool = False - ) -> torch.Tensor: + ) -> Int[torch.Tensor, "position"]: """Return ascending raw-gradient target ranks (rank zero is smallest).""" return self.target_ranks(target_token_id, largest=False, normalized=normalized) @@ -207,7 +208,7 @@ class BackwardLensResult: """ prompt: str - prompt_token_ids: torch.Tensor + prompt_token_ids: Int[torch.Tensor, "position"] target_token: str target_token_id: int loss: float @@ -242,13 +243,13 @@ class _GPT2GradientCapture: contracts are introduced with the projection API. """ - prompt_token_ids: torch.Tensor + prompt_token_ids: Int[torch.Tensor, "1 position"] target_token_id: int loss: float layers: tuple[_GPT2LayerGradientFactors, ...] -def _validate_floating_matrix(name: str, tensor: torch.Tensor) -> None: +def _validate_floating_matrix(name: str, tensor: Any) -> Float[torch.Tensor, "rows columns"]: if not isinstance(tensor, torch.Tensor): raise TypeError(f"{name} must be a torch.Tensor") if tensor.ndim != 2: @@ -259,10 +260,12 @@ def _validate_floating_matrix(name: str, tensor: torch.Tensor) -> None: raise TypeError(f"{name} must have a floating dtype; got {tensor.dtype}") if not bool(torch.isfinite(tensor).all()): raise ValueError(f"{name} must contain only finite values") + return cast(Float[torch.Tensor, "rows columns"], tensor) def _reconstruction_errors( - reference: torch.Tensor, reconstruction: torch.Tensor + reference: Float[torch.Tensor, "rows columns"], + reconstruction: Float[torch.Tensor, "rows columns"], ) -> tuple[float, float]: """Return max absolute and symmetric scale-aware relative errors. @@ -279,7 +282,9 @@ def _reconstruction_errors( return absolute, relative -def _to_detached_float32(name: str, tensor: torch.Tensor) -> torch.Tensor: +def _to_detached_float32( + name: str, tensor: Float[torch.Tensor, "rows columns"] +) -> Float[torch.Tensor, "rows columns"]: """Detach and convert a validated tensor, rejecting float32 overflow.""" converted = tensor.detach().float() if not bool(torch.isfinite(converted).all()): @@ -288,11 +293,11 @@ def _to_detached_float32(name: str, tensor: torch.Tensor) -> torch.Tensor: def _build_linear_gradient_factors( - forward_inputs: torch.Tensor, - output_gradients: torch.Tensor, - weight_gradient: torch.Tensor, + forward_inputs: Any, + output_gradients: Any, + weight_gradient: Any, *, - weight_layout: WeightLayout, + weight_layout: Any, ) -> LinearGradientFactors: """Reconstruct a weight gradient from aligned token-position factors. @@ -308,33 +313,34 @@ def _build_linear_gradient_factors( Detached factors, the independent gradient, its reconstruction, and reconstruction errors, all on CPU with float32 tensor values. """ - _validate_floating_matrix("forward_inputs", forward_inputs) - _validate_floating_matrix("output_gradients", output_gradients) - _validate_floating_matrix("weight_gradient", weight_gradient) + validated_inputs = _validate_floating_matrix("forward_inputs", forward_inputs) + validated_gradients = _validate_floating_matrix("output_gradients", output_gradients) + validated_weight = _validate_floating_matrix("weight_gradient", weight_gradient) if weight_layout not in ("in_out", "out_in"): raise ValueError("weight_layout must be 'in_out' or 'out_in'") - if forward_inputs.shape[0] != output_gradients.shape[0]: + validated_layout = cast(WeightLayout, weight_layout) + if validated_inputs.shape[0] != validated_gradients.shape[0]: raise ValueError( "forward_inputs and output_gradients must have the same number of positions; " - f"got {forward_inputs.shape[0]} and {output_gradients.shape[0]}" + f"got {validated_inputs.shape[0]} and {validated_gradients.shape[0]}" ) - devices = {forward_inputs.device, output_gradients.device, weight_gradient.device} + devices = {validated_inputs.device, validated_gradients.device, validated_weight.device} if len(devices) != 1: raise ValueError( "forward_inputs, output_gradients, and weight_gradient must share a device" ) - inputs = _to_detached_float32("forward_inputs", forward_inputs) - gradients = _to_detached_float32("output_gradients", output_gradients) + inputs = _to_detached_float32("forward_inputs", validated_inputs) + gradients = _to_detached_float32("output_gradients", validated_gradients) canonical = inputs.T @ gradients - reconstruction = canonical if weight_layout == "in_out" else canonical.T - reference = _to_detached_float32("weight_gradient", weight_gradient) + reconstruction = canonical if validated_layout == "in_out" else canonical.T + reference = _to_detached_float32("weight_gradient", validated_weight) if not bool(torch.isfinite(reconstruction).all()): raise ValueError("the float32 outer-product reconstruction must contain only finite values") if reference.shape != reconstruction.shape: raise ValueError( f"weight_gradient shape {tuple(reference.shape)} does not match the " - f"{weight_layout} reconstruction shape {tuple(reconstruction.shape)}" + f"{validated_layout} reconstruction shape {tuple(reconstruction.shape)}" ) absolute, relative = _reconstruction_errors(reference, reconstruction) return LinearGradientFactors( @@ -344,11 +350,11 @@ def _build_linear_gradient_factors( reconstructed_gradient=reconstruction.cpu().clone(), absolute_reconstruction_error=absolute, relative_reconstruction_error=relative, - weight_layout=weight_layout, + weight_layout=validated_layout, ) -def _rank_vocabulary_logits(logits: torch.Tensor, *, k: int, largest: bool) -> VocabularyRanking: +def _rank_vocabulary_logits(logits: Any, *, k: Any, largest: Any) -> VocabularyRanking: """Return largest or smallest vocabulary logits and token ids per row. Ordering among exactly tied logits is intentionally unspecified and follows @@ -368,7 +374,8 @@ def _rank_vocabulary_logits(logits: torch.Tensor, *, k: int, largest: bool) -> V raise TypeError(f"largest must be a bool; got {type(largest).__name__}") if isinstance(k, bool) or not isinstance(k, int) or not 1 <= k <= logits.shape[-1]: raise ValueError(f"k must be in [1, {logits.shape[-1]}]; got {k!r}") - ranked = torch.topk(logits.detach(), k=k, dim=-1, largest=largest, sorted=True) + validated_logits = cast(Float[torch.Tensor, "*leading d_vocab"], logits) + ranked = torch.topk(validated_logits.detach(), k=k, dim=-1, largest=largest, sorted=True) return VocabularyRanking( values=ranked.values.cpu().clone(), indices=ranked.indices.cpu().clone() ) @@ -386,8 +393,8 @@ def _slice_vocabulary_ranking(ranking: VocabularyRanking, *, k: int) -> Vocabula def _target_vocabulary_ranks( - logits: torch.Tensor, *, target_token_id: int, largest: bool -) -> torch.Tensor: + logits: Float[torch.Tensor, "position d_vocab"], *, target_token_id: int, largest: bool +) -> Int[torch.Tensor, "position"]: """Return zero-based competition ranks for one vocabulary id per row.""" target = logits[:, target_token_id].unsqueeze(-1) comparisons = logits > target if largest else logits < target @@ -405,7 +412,9 @@ def _decode_vocabulary_ranking(ranking: VocabularyRanking, tokenizer: Any) -> li @torch.no_grad() -def _project_residual_factors(model: Any, factors: torch.Tensor) -> torch.Tensor: +def _project_residual_factors( + model: Any, factors: Float[torch.Tensor, "position d_model"] +) -> Float[torch.Tensor, "position d_vocab"]: """Apply fresh final normalization and unembedding to residual-width rows.""" _validate_floating_matrix("factors", factors) if factors.shape[-1] != int(model.cfg.d_model): @@ -431,8 +440,12 @@ def _project_residual_factors(model: Any, factors: torch.Tensor) -> torch.Tensor def _factor_norms_and_normalized_rows( - factors: torch.Tensor, -) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + factors: Float[torch.Tensor, "position width"], +) -> tuple[ + Float[torch.Tensor, "position"], + Bool[torch.Tensor, "position"], + Float[torch.Tensor, "position width"], +]: """Return original L2 norms, exact-zero mask, and safely unit-normalized rows.""" _validate_floating_matrix("factors", factors) rows = _to_detached_float32("factors", factors).cpu().clone() @@ -664,12 +677,17 @@ def _capture_projection_tensors( handle.remove() -def _single_batch_matrix(name: str, tensor: torch.Tensor) -> torch.Tensor: +def _single_batch_matrix(name: str, tensor: Any) -> Float[torch.Tensor, "position width"]: + if not isinstance(tensor, torch.Tensor): + raise RuntimeError(f"{name} must be a torch.Tensor") if tensor.ndim != 3 or tensor.shape[0] != 1: raise RuntimeError( f"{name} must have shape [1, position, width]; got {tuple(tensor.shape)}" ) - return tensor[0] + matrix = tensor[0] + if not matrix.is_floating_point(): + raise RuntimeError(f"{name} must have a floating dtype; got {matrix.dtype}") + return cast(Float[torch.Tensor, "position width"], matrix) @contextmanager From adfc7c693441dfad9c124101d3d23941fd0beafa Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Wed, 2 Sep 2026 14:02:09 -0700 Subject: [PATCH 14/17] test(backward_lens): strengthen gradient behavior coverage --- tests/integration/test_backward_lens.py | 40 +++++++++++++++++++++++-- tests/unit/tools/test_backward_lens.py | 10 ++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index 7f3452b3a..b9ba4739a 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -110,6 +110,40 @@ def test_real_gpt2_factors_reconstruct_both_mlp_weight_gradients( assert factors.relative_reconstruction_error <= 2e-5 +def test_gradient_descent_weight_update_increases_target_logit( + gradient_capture, gpt2_bridge +) -> None: + layer_result = gradient_capture.layers[-1] + weight = gpt2_bridge.blocks[layer_result.layer].mlp.out.original_component.weight + original_weight = weight.detach().clone() + weight_gradient = layer_result.output_projection.weight_gradient.to( + device=weight.device, dtype=weight.dtype + ) + parameter_name = next( + name + for name, parameter in gpt2_bridge.original_model.named_parameters() + if parameter is weight + ) + updated_weight = original_weight - 1e-4 * weight_gradient + + with torch.no_grad(): + baseline_output = torch.func.functional_call( + gpt2_bridge.original_model, + {parameter_name: original_weight}, + (gradient_capture.prompt_token_ids.to(weight.device),), + ) + updated_output = torch.func.functional_call( + gpt2_bridge.original_model, + {parameter_name: updated_weight}, + (gradient_capture.prompt_token_ids.to(weight.device),), + ) + + baseline_target_logit = baseline_output.logits[0, -1, gradient_capture.target_token_id] + updated_target_logit = updated_output.logits[0, -1, gradient_capture.target_token_id] + assert float(updated_target_logit - baseline_target_logit) > 1e-4 + torch.testing.assert_close(weight, original_weight, rtol=0, atol=0) + + @pytest.mark.parametrize("projection", ["input_projection", "output_projection"]) def test_final_layer_has_the_expected_rank_one_position_structure( gradient_capture, projection: str @@ -498,12 +532,12 @@ def test_capture_reconstructs_with_existing_activation_edits(gpt2_bridge) -> Non def scale_input(tensor, hook=None): return tensor * 0.5 - def shift_output(tensor, hook=None): - return tensor + 0.01 + def scale_output(tensor, hook=None): + return tensor * 3.0 input_hook_point.add_hook(scale_input) input_handle = input_hook_point.fwd_hooks[-1] - output_hook_point.add_hook(shift_output) + output_hook_point.add_hook(scale_output) output_handle = output_hook_point.fwd_hooks[-1] try: result = _capture_gpt2_mlp_gradient_factors(gpt2_bridge, PROMPT, TARGET, [0]) diff --git a/tests/unit/tools/test_backward_lens.py b/tests/unit/tools/test_backward_lens.py index 2340fe298..2095b91c1 100644 --- a/tests/unit/tools/test_backward_lens.py +++ b/tests/unit/tools/test_backward_lens.py @@ -510,7 +510,9 @@ def test_public_backward_lens_symbols_are_exported() -> None: BackwardLensResult, ) - assert BackwardLens.__name__ == "BackwardLens" - assert BackwardLensLayerResult.__name__ == "BackwardLensLayerResult" - assert BackwardLensMatrixResult.__name__ == "BackwardLensMatrixResult" - assert BackwardLensResult.__name__ == "BackwardLensResult" + _ = ( + BackwardLens, + BackwardLensLayerResult, + BackwardLensMatrixResult, + BackwardLensResult, + ) From b6b1f419157a3b8ebdfaade0c1946e7b60e9ce6a Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Wed, 2 Sep 2026 23:04:53 -0700 Subject: [PATCH 15/17] docs(backward_lens): clarify factor and token semantics --- docs/source/content/backward_lens.md | 9 ++++++--- tests/integration/test_backward_lens.py | 2 ++ transformer_lens/tools/analysis/backward_lens.py | 7 ++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/source/content/backward_lens.md b/docs/source/content/backward_lens.md index 6f1f3f772..cc9e6fe6d 100644 --- a/docs/source/content/backward_lens.md +++ b/docs/source/content/backward_lens.md @@ -34,7 +34,8 @@ The two projections expose different residual-width factors: | `input_projection` (FF1 / `c_fc`) | `[d_model, d_mlp]` | Forward input $x_i$ | `[position, d_model]` | | `output_projection` (FF2 / `c_proj`) | `[d_mlp, d_model]` | Backward signal $\delta_i$ | `[position, d_model]` | -The FF1 readout therefore describes the residual-stream directions entering the MLP. +The FF1 readout therefore describes the layer-normalized residual state entering the +MLP (post-`ln_2`, including its gain and bias). The FF2 readout describes raw loss gradients at the MLP output. These are different quantities and should not be interpreted interchangeably. @@ -116,8 +117,9 @@ target_ranks = ff2.gradient_descent_target_ranks( `target_token` must encode to exactly one token without a beginning-of-sequence token. For GPT-2 tokenization, a leading space is often significant. The prompt is tokenized -normally, including its prepended BOS token; all position-indexed factors and readouts -align with `result.prompt_token_ids`. +according to the model and tokenizer configuration, so it includes a prepended BOS +only when that configuration requests one. Treat `result.prompt_token_ids` as the +source of truth for aligning all position-indexed factors and readouts. ## Result structure @@ -184,6 +186,7 @@ activation-editing hooks still affect the analyzed computation. ## References +- [TransformerLens Backward Lens demonstration](https://github.com/TransformerLensOrg/TransformerLens/blob/dev/demos/Backward_Lens_Demo.ipynb). - Shahar Katz, Yonatan Belinkov, Mor Geva, and Lior Wolf. 2024. [Backward Lens: Projecting Language Model Gradients into the Vocabulary Space](https://aclanthology.org/2024.emnlp-main.142/). *Proceedings of EMNLP 2024*, pages 2390–2422. diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index b9ba4739a..8fbe5c272 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -70,6 +70,8 @@ def test_real_gpt2_factors_reconstruct_both_mlp_weight_gradients( assert gradient_capture.prompt_token_ids.device.type == "cpu" expected_tokens = gpt2_bridge.to_tokens(PROMPT) assert torch.equal(gradient_capture.prompt_token_ids, expected_tokens) + assert gpt2_bridge.tokenizer is not None + assert gradient_capture.prompt_token_ids[0, 0].item() == gpt2_bridge.tokenizer.bos_token_id with torch.no_grad(): logits = gpt2_bridge(expected_tokens) expected_loss = F.cross_entropy( diff --git a/transformer_lens/tools/analysis/backward_lens.py b/transformer_lens/tools/analysis/backward_lens.py index 45ae9a5b4..419fb2fde 100644 --- a/transformer_lens/tools/analysis/backward_lens.py +++ b/transformer_lens/tools/analysis/backward_lens.py @@ -198,9 +198,10 @@ class BackwardLensResult: scalar cross-entropy of the final-position next-token prediction against the target; it preserves the ``d(loss)/d(...)`` sign convention and is not negated. ``prompt_token_ids`` is an owned CPU int64 tensor with shape ``[position]``; - position zero is the prepended BOS, and every residual-width factor in - ``layers`` is aligned to these same positions. ``layers`` preserves requested - order. Maximum errors summarize both matrices over every requested layer. + every residual-width factor in ``layers`` is aligned to these same positions. + Position zero is a prepended BOS only when the model and tokenizer configuration + requests one. ``layers`` preserves requested order. Maximum errors summarize + both matrices over every requested layer. ``includes_normalized_logits`` records whether the Normalized Logit Lens was computed. ``includes_full_logits`` records whether full vocabulary tensors were retained in addition to bounded rankings. No model or tokenizer reference From 55d70474007fd8b419094a7a7b63a3e6488036fd Mon Sep 17 00:00:00 2001 From: jlarson4 Date: Thu, 3 Sep 2026 10:18:15 -0500 Subject: [PATCH 16/17] fixing export ordering --- transformer_lens/tools/analysis/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transformer_lens/tools/analysis/__init__.py b/transformer_lens/tools/analysis/__init__.py index 071ae9411..bb77a4ec4 100644 --- a/transformer_lens/tools/analysis/__init__.py +++ b/transformer_lens/tools/analysis/__init__.py @@ -74,14 +74,14 @@ "JSpaceVarianceProfile", "JacobianLens", "JacobianLensReadout", + "LinearGradientFactors", + "ProjectedFactor", "ProjectionKernelResult", "RandomSubspaceReference", "SubspaceBasis", - "attention_head_subspace_affinity", - "LinearGradientFactors", - "ProjectedFactor", "VocabularyRanking", "WeightLayout", + "attention_head_subspace_affinity", "direct_logit_attribution", "estimate_occupancy", "get_act_patch_direct_path", From f8ea0685d290ae0bfb156ce681f18d62da4c268a Mon Sep 17 00:00:00 2001 From: janmenjayap Date: Fri, 4 Sep 2026 00:44:09 -0700 Subject: [PATCH 17/17] fix(backward_lens): restore weight via in-place copy instead of functional_call - swap torch.func.functional_call reparametrization for weight.copy_(updated_weight) under no_grad - restore original weight via try/finally so cleanup happens even if assertion fails - functional_call left layer 11 c_proj weight overridden in the bridge's original_model, breaking the three public_* fixtures later in this module (coverage-tier failure; Compatibility jobs don't run this tier) --- tests/integration/test_backward_lens.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/integration/test_backward_lens.py b/tests/integration/test_backward_lens.py index 8fbe5c272..918fe9d26 100644 --- a/tests/integration/test_backward_lens.py +++ b/tests/integration/test_backward_lens.py @@ -121,24 +121,16 @@ def test_gradient_descent_weight_update_increases_target_logit( weight_gradient = layer_result.output_projection.weight_gradient.to( device=weight.device, dtype=weight.dtype ) - parameter_name = next( - name - for name, parameter in gpt2_bridge.original_model.named_parameters() - if parameter is weight - ) updated_weight = original_weight - 1e-4 * weight_gradient + prompt_token_ids = gradient_capture.prompt_token_ids.to(weight.device) with torch.no_grad(): - baseline_output = torch.func.functional_call( - gpt2_bridge.original_model, - {parameter_name: original_weight}, - (gradient_capture.prompt_token_ids.to(weight.device),), - ) - updated_output = torch.func.functional_call( - gpt2_bridge.original_model, - {parameter_name: updated_weight}, - (gradient_capture.prompt_token_ids.to(weight.device),), - ) + baseline_output = gpt2_bridge.original_model(prompt_token_ids) + weight.copy_(updated_weight) + try: + updated_output = gpt2_bridge.original_model(prompt_token_ids) + finally: + weight.copy_(original_weight) baseline_target_logit = baseline_output.logits[0, -1, gradient_capture.target_token_id] updated_target_logit = updated_output.logits[0, -1, gradient_capture.target_token_id]