File size: 14,958 Bytes
31246a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# SAE API

[SAE hub](README.md) · [Complete runnable example](examples/README.md) ·
[Pretrained loading without fitting](loading.md)

## Use existing weights

The snippets in this reference assume `probe` is a `Prober` bound to your loaded
backbone and processor, and `inputs` is a prepared input batch. The linked
[complete loading example](loading.md) constructs all three from scratch.

```python
# Native dictionary saved by this library; no fitting is needed.
method = probe.sae.load("outputs/dictionary", device="cpu")
result = method.run(inputs)

# Published GPT-2 layer-0 TopK recipe; downloads and converts the author weights.
# The already-loaded backbone/tokenizer must match this recipe exactly.
method = probe.sae.from_pretrained(
    "sae-6783018355728933dae6d812", cache_dir="outputs/sae_cache", device="cpu",
)
result = method.run(inputs)
```

The [complete inference-only example](loading.md) includes the matching
backbone, tokenizer, fresh input and visualization. `from_pretrained` accepts
only implemented conversion recipes. A catalog record alone does not imply
automatic source-format or backbone compatibility.

| Loading parameter | Default | Meaning |
| --- | --- | --- |
| `card_id` | Required | Published model ID with an implemented automatic loading recipe. |
| `cache_dir` | `None` | Source checkpoint download cache and converted native-artifact cache; defaults to the Hugging Face cache root. |
| `device` | `"cpu"` | Device for the loaded SAE dictionary; backbone placement remains caller-owned. |

## Configure and fit a new dictionary

Here `probe`, `train_inputs` and `validation_inputs` are constructed in a
method's complete example, such as [ReLU SAE](relu.md#complete-live-example).

```python
from vlmprobe.sae import SAETrainingConfig

method = probe.sae.topk(layer=0, tokens="visual", d_sae=1024, k=32)
history = method.fit(
    train_inputs, validation_inputs, output_dir="outputs/fit",
    training=SAETrainingConfig(max_steps=200, batch_size=128),
    preprocessing_id="my-independent-training-split-v1",
)
method.save("outputs/dictionary", model_card=None, registry_dir=None)
loaded = probe.sae.load("outputs/dictionary", tokens=None, binding=None,
                        registry_dir=None, artifact_dir=None, device="cpu")
result = loaded.run(validation_inputs[0])
```

Every named factory is also available through
`probe.sae.method("topk", layer=0, tokens="visual", d_sae=1024, k=32)`.

`.fit(...)` trains the SAE encoder/decoder and any method-specific learned
parameters on activations from the frozen backbone. It does not fine-tune that
backbone. Loading a trained dictionary and calling `.run(...)` performs
inference without optimizer updates. A pretrained backbone and a pretrained SAE
are separate sets of weights; one does not supply the other. See the
[inference-only guide](loading.md) for complete loading examples.

`layer` is one zero-based residual layer from `describe()`. `tokens` accepts the
common token selector (`all`, `visual`, `text`, `last_prompt`, positions or a
boolean mask). A saved batch-specific selector needs matching coordinates at
inference; it is not a dataset-independent image region description.
`d_sae` defaults to four times the inferred activation width;
`expansion_factor=4` controls that default. An omitted `k` is capped at the
inferred dictionary width; an explicitly invalid `k` raises.
`train_inputs` and `validation_inputs` are finite native model input batches or
ProbeInputs. Actual model forwards capture detached CPU float32 activations.
Each split has a configurable `max_cache_bytes` payload cap. It is not a hard
total-RAM limit: concatenation temporarily retains both chunks and their joined
tensor, plus the other split; the trainer also uses in-memory tensors. `train_ids` and `validation_ids`, when provided, must
be disjoint source groups. Exact repeated complete batches are independently
rejected; this does not infer patient/slide overlap for you.

Fit hashes loaded model tensors and processor configuration, records the exact
adapter hook and token selection, and derives a calibration fingerprint from
inputs/activations/source IDs. `preprocessing_id` must describe your actual
external transforms; its default explicitly records that they are unverified.
Load verifies actual model tensors, architecture/configuration, numerical-stack
versions and processor/tokenizer behavior when fingerprints are present.
The frozen backbone is assumed unchanged between method calls; verification
is performed at fit/load, rather than rehashing a large backbone on every run. Source artifacts
without automatic fingerprints require a caller-supplied verified binding;
width matching alone is rejected as an automatic compatibility claim.

Loading creates an inference method. Calling `.fit()` on that loaded object
is rejected: it would otherwise silently discard weights or recipe settings.
To resume training, recreate the original factory/configuration and call fit
with its matching output directory; warm-start fine-tuning is not yet provided.
Resaving modified dictionary weights invalidates inherited numerical/semantic
evidence and marks earlier training metrics as stale.

Torch and explicit loader/sampler RNG states are preserved during collection.
Arbitrary Python/NumPy transforms and persistent worker processes need their own
deterministic data or caching protocol.

## Dictionary hyperparameters

The model-bound factory forwards keyword arguments to `SAEConfig` after inferring
`d_in`. Methods share these fields, but only relevant algorithm options affect
the selected method. Explicit recipe defaults and mathematical scope appear in
the [method inventory](ALGORITHMS.md).

| Parameter | Default | Meaning |
| --- | --- | --- |
| `architecture` | `required` | Native algorithm name; see the method inventory. |
| `d_in` | `required` | Captured activation width; inferred by the model-bound fit workflow. |
| `d_sae` | `required` | Dictionary feature count; fixed for the artifact. |
| `k` | `32` | Positive per-vector TopK count, or batch budget as selected below; pursuit defaults to this many iterations. |
| `l1_coefficient` | `0.01` | Coefficient for L1 sparsity in ReLU/Gated or explicit JumpReLU L1 recipe. |
| `seed` | `0` | Local dictionary initialization seed; does not reset caller RNG. |
| `normalization` | `None` | Optional named normalization override: rms, none, layer_norm, constant_norm_rescale. |
| `input_normalization` | `'scalar'` | Internal coordinate recipe: scalar, none, layer_norm or constant_norm. |
| `normalization_eps` | `1e-05` | Positive stability constant for per-input normalization. |
| `normalization_std_correction` | `0` | Standard-deviation correction: 0 (population) or 1 (sample). |
| `apply_decoder_bias_to_input` | `True` | Subtract decoder bias before encoding normalized inputs. |
| `apply_b_dec_to_input` | `None` | Optional source-format alias of apply_decoder_bias_to_input. |
| `use_decoder_bias` | `True` | Whether reconstruction includes the learned decoder bias. |
| `normalize_decoder` | `True` | Apply per-atom decoder normalization after optimizer steps. |
| `rescale_acts_by_decoder_norm` | `False` | Explicit source recipe that rescales feature values using decoder norms. |
| `auxk_coefficient` | `0.0` | Optional dead-feature auxiliary reconstruction weight; 0 disables it. |
| `auxk_k` | `None` | Number of dead features considered by AuxK; None uses the documented method default. |
| `dead_feature_steps` | `100` | Number of training steps without firing before a feature is dead for AuxK. |
| `jumprelu_threshold` | `0.1` | Positive starting threshold for each JumpReLU feature. |
| `jumprelu_bandwidth` | `0.05` | Rectangle surrogate width for threshold derivatives. |
| `l0_coefficient` | `0.01` | Weight on JumpReLU L0 surrogate sparsity. |
| `jumprelu_sparsity` | `'l0'` | Explicit JumpReLU sparsity recipe: l0 or l1. |
| `jumprelu_ste_to_input` | `False` | Whether threshold surrogate gradients also reach encoder preactivations. |
| `batch_budget` | `'average'` | average: k times number of vectors; global: k across the entire minibatch. |
| `threshold_ema` | `0.99` | EMA decay for BatchTopK inference thresholds. |
| `threshold_init` | `'first_batch'` | Initialize inference threshold from first_batch or zero_ema. |
| `matryoshka_widths` | `()` | Increasing prefix widths ending at d_sae; empty selects half/full widths. |
| `matryoshka_weights` | `()` | Positive weights for each nested reconstruction loss; empty means equal weights. |
| `dictionary` | `'learned'` | learned or archetypal; archetypal method names select the latter. |
| `archetypal_candidates` | `128` | Maximum number of training-only anchor vectors. |
| `archetypal_delta` | `1.0` | Bound on the archetypal dictionary relaxation component. |
| `pursuit_iterations` | `None` | Maximum greedy support additions; None uses k. |
| `residual_tolerance` | `0.01` | Residual stopping tolerance in pursuit. |
| `stop_on_duplicate_support` | `True` | Stop matching pursuit when no new support is added. |
| `omp_nnls_iterations` | `32` | Projected-gradient refit steps for nonnegative OMP; finite approximation, not exact NNLS. |
| `quantization_levels` | `4` | Number of learned scalar code levels; QSAE is experimental. |
| `hard_quantization` | `False` | Use discrete quantization rather than the soft code path. |

## Training parameters

`SAETrainingConfig` is shared by all algorithms. The `normalization` field here
chooses whether the training-only RMS scale is fitted for scalar-normalized
models (`rms`) or disabled (`none`); per-input dictionary normalization takes
precedence. These are not different source-specific trainers.

| Parameter | Default | Meaning |
| --- | --- | --- |
| `epochs` | `10` | Maximum epochs unless a step/vector budget is chosen. |
| `lr` | `0.001` | Adam learning rate. |
| `batch_size` | `256` | Number of activation vectors per optimizer minibatch. |
| `patience` | `3` | Stop after this many epochs without min_delta validation-loss improvement. |
| `min_delta` | `0.0001` | Minimum validation-loss improvement for best-model selection. |
| `seed` | `0` | Local dictionary initialization seed; does not reset caller RNG. |
| `normalization` | `'rms'` | Fit train-only scalar RMS (rms) or disable it (none); per-input model normalization takes precedence. |
| `max_steps` | `None` | Optional exact optimizer-step budget; mutually exclusive with max_vectors. |
| `max_vectors` | `None` | Optional exact cumulative training-vector budget; mutually exclusive with max_steps. |
| `device` | `'cpu'` | Dictionary training device (cpu or cuda); the caller owns backbone placement. |
| `resume` | `True` | Resume matching optimizer/RNG/cursor/method state from output_dir. |
| `grad_clip` | `5.0` | Positive maximum gradient norm applied before Adam updates. |
| `progress_every` | `20` | Progress/checkpoint write interval in optimizer steps. |

## Probe operations and outputs

| Call | Parameters | Tensors |
| --- | --- | --- |
| `method.run(inputs)` | Native input batch | `features[N,F]`, `activation[N,D]`, `reconstruction[N,D]`, `reconstruction_error[N,D]`, `l0[N]`, `positions[N,2]` |
| `method.profile(top_k=10).run(inputs)` | Positive top count per feature; ranks absolute values and retains signs | Feature mean/variance/firing frequency/min/max; `top_activation_values[K,F]`, `top_positions[K,F,2]` |
| `method.reconstruct().run(inputs, metric=None)` | Optional task metric | Baseline/edited logits or representation outputs; optional baseline/edited score and score change |
| `method.edit(feature_ids=[...], scale=0., mode="error_preserving").run(inputs, metric=None)` | Unique valid IDs; finite scaling; error_preserving or reconstruction | Same output/score comparison as above, with explicit intervention metadata |

`N` packs selected batch/token positions, `F=d_sae`, `D=d_in`; the two position
columns are batch and native expanded-token index. Signed features count as
active when nonzero. Profiles rank absolute activation, retaining negative
values. Representation outputs remain vectors; they are not fabricated logits.

Default feature editing uses `x + decode(z_edited) - decode(z)` with the same
per-input normalization context reused for both decodes. `scale=1` returns an
exact no-op, including native bf16 precision. Reconstruction is deliberately a
separate control. Generic low-level operations remain available as
`probe.sae.features/profile/reconstruct/edit(layer=..., sae=..., binding=...)`.

## Lower-level training, artifacts and discovery

```python
from vlmprobe.sae import (SAEConfig, SparseAutoencoder, SAETrainingConfig,
                         fit_sae, list_methods, list_models, get_model_card,
                         load_artifact, save_artifact, import_checkpoint)
```

`fit_sae(model, train_vectors, validation_vectors, output_dir, training=..., provenance=...)`
uses the same engine as model-bound fitting. Vectors must be finite floating
`[N,D]` in the bound native coordinate system. Legacy `train_sae(...)` is only a
compatibility adapter to this engine.

`save_artifact(model, path, model_card=None, registry_dir=None)` writes either
config.json/weights.pt/model_card.json or the same schema in a safe `.pt`
envelope. `load_artifact(path, expected_binding=None, device="cpu")` validates
checksums, types, exact state, config and optional binding. Inference weights
and optimizer resume state are separate. Browse `list_models(...)` and `get_model_card(id, ...)`;
see [model cards](models/README.md) for local registration, published pretrained
cards and explicit source checkpoint conversion.


### Model discovery parameters

```text
list_models(*, modality=None, method=None, status=None, query=None,
            artifact_kind="sae", registry_dir=None, include_builtin=True, limit=None)
get_model_card(card_id, *, registry_dir=None)
```

| Parameter | Meaning |
| --- | --- |
| `modality` | Filter language, vision or multimodal resources. Unreported source metadata remains unspecified. |
| `method` | Filter an algorithm name such as relu, topk or jumprelu. |
| `query` | Case-insensitive search over model, hook, release and other card metadata. |
| `artifact_kind` | `"sae"` selects dictionary weights; `"backbone"` selects activation-extraction models; `None` includes both. |
| `registry_dir` | Optional directory containing your saved artifact/model cards. |
| `include_builtin` | Include the packaged author checkpoint catalog; use False to browse only local runs. |
| `status` | Optional artifact metadata filter, for example published or trained. |
| `limit` | Optional positive maximum result count. |

Cards provide source model/hook/configuration, dimensions when reported, direct
checkpoint links, and a link to our model information page. `load_model(...)`
restores a registered native artifact; source weights use the explicit
checkpoint conversion interface before model-bound probing.