feat(sparse_probing): add leakage-safe k-sparse probing over activation tensors - #1774
Open
janmenjayap wants to merge 4 commits into
Open
feat(sparse_probing): add leakage-safe k-sparse probing over activation tensors#1774janmenjayap wants to merge 4 commits into
janmenjayap wants to merge 4 commits into
Conversation
Add fit_sparse_probe: a stratified train/test split is computed before any learned statistic, features are selected by train-only mean difference, and a CPU-float64 LBFGS logistic fit reports explicit objective/gradient-norm convergence diagnostics instead of assuming success. Report held-out accuracy, precision, recall, and F1 so callers can judge decodability without the result implying causal model use, neuron monosemanticity, or superposition. Cover exact score/index selection, leakage isolation, deterministic ties/RNG, optimizer convergence and failure, constant columns, and invalid-input rejection. Sweep, controls, exports, and docs land in a follow-up commit.
Add sweep_sparse_probe: fits a shared-split k-grid and reports raw random-coordinate and shuffled-training-label control distributions alongside each k, so callers can judge coordinate concentration without the result implying an automatic significance test. Export fit_sparse_probe, sweep_sparse_probe, and the result dataclasses from transformer_lens.tools.analysis, and add the sparse_probing guide to the docs toctree, documenting the leakage-safe contract and a run_with_cache composition example.
fit_sparse_probe accepts feature matrices on any device, including MPS, where float64 is unsupported. Add a regression test that fits a probe over MPS-resident features and asserts the selected train/test tensors land on CPU with float64 dtype instead of raising when the float64 cast is attempted while still on the Metal device.
…type policy Rebasing onto origin/dev pulled in jaxtyping>=0.3 (TransformerLensOrg#1732), which re-raises type-check violations as jaxtyping.TypeCheckError instead of letting BeartypeCallHintParamViolation propagate, and now classifies float8_e4m3fn as a valid Float dtype at the annotation level. Use the project's tests/typecheck_errors.TYPECHECK_ERRORS convention for the annotation-level cases, and move the float8_e4m3fn case to the explicit ValueError dtype-rejection table, where the function's own dtype guard now catches it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds
transformer_lens/tools/analysis/sparse_probing.py, a dependency-free, model-free module for fitting binary k sparse probes to a supplied activation matrixXand label vectory, following the method in Gurnee et al., "Finding Neurons in a Haystack: Case Studies with Sparse Probing" (TMLR 2023).TransformerLens already exposes activations via
run_with_cache, but has no maintained probing primitive: users currently rebuild splitting, feature selection, fitting, and control logic themselves, and it is easy to leak test information into that process. This PR adds a leakage-safe, dependency-free core for that workflow. Activation extraction, model wrappers, plotting, notebooks, multiclass probing, and optimal (MIP) selection are deliberately deferred to follow-up work.Fixes #1728
What's included
fit_sparse_probe(X, y, k, ...)— deterministic stratified train/test split computed before any learned statistic, train-only raw mean-difference feature selection, and a CPU-float64 Torch LBFGS balanced-logistic fit with explicit objective/gradient-norm convergence diagnostics (raises rather than silently returning an unconverged fit).sweep_sparse_probe(X, y, ks, ...)— reuses one fixed split across a k-grid and reports raw random-coordinate and shuffled-training-label control distributions alongside eachk, without assigning an automatic "significance" label.SparseProbeResult/SparseProbeSweepresult dataclasses reporting held-out accuracy, precision, recall, and F1 (F1 primary), selected indices/scores, coefficients/intercept, preprocessing metadata, split indices/class counts, and control distributions.jaxtyping>=0.3compatibility fix: the rebase ontoorigin/devpulled in CI Warnings Cleanup #1732, which now raisesjaxtyping.TypeCheckErrorinstead of lettingBeartypeCallHintParamViolationpropagate, and reclassifiesfloat8_e4m3fnas a validFloatdtype at the annotation level. Updatedtest_runtime_typecheck_rejects_invalid_tensor_contractsto usetests/typecheck_errors.TYPECHECK_ERRORS, and moved the float8 case to this module's ownValueErrordtype-rejection table, where it's now actually caught.transformer_lens.tools.analysis, plusdocs/source/content/sparse_probing.md(contracts, claim boundaries, and arun_with_cachecomposition example) linked fromdocs/source/index.md.Design decisions
Issue #1728 asked maintainers to weigh in on five open questions before implementation. This PR takes the issue's own recommended position on each:
sklearndependency), and reports final gradient/objective metadata, raising on non-convergence."none"(matching the paper's reference code); optional train-only"standardize"is documented as intentionally changing the L2 objective.[example, k]matrices move to CPU float64 for the deterministic LBFGS fit.Non-goals (tracked as follow-ups)
Activation extraction and position-reduction helpers, model wrappers/downloads, multiclass/one-vs-rest probing, plotting and notebooks, an optimal/MIP selector, feature batteries, SAE-latent composition, and causal validation are all explicitly out of scope for this PR.
Claim boundaries
A high held-out F1 means the labeled feature is linearly decodable from the supplied activations — it does not by itself establish that the model uses that feature, that a selected coordinate is monosemantic, or that a smooth k-sweep curve is evidence of superposition. The docs and result contracts call this out explicitly, and the module exposes no
.plot()method or automatic "significant" label.Test plan
uv run pytest tests/unit/tools/test_sparse_probing.py tests/mps/test_mps_basic.py -q— 68 passed.make test-pr(unit + docstring + acceptance + integration): unit 5,827 passed, docstring 18 passed, acceptance 209 passed, integration 1,459 passed / 1 failed. The one failure,test_granite_eager_scan_device_correctness[mps], is an unrelated Granite MoE Hybrid eager/fused-scan divergence — reproduced identically on a cleanorigin/devcheckout in an isolated worktree, confirming it's pre-existing on trunk and not introduced by this branch.make formatanduv run mypy .clean.docs/source/index.mdincludes the new guide exactly once; docs build succeeds.Type of change
Checklist: