Advanced Usage
TopKSAETrainer is the easy path, but it is a thin wrapper.
When you need a custom training loop, a different loss, a non-linear encoder, or
direct control over the sparsification, drop down to the building blocks. This
page covers the lower-level objects exported at the top level of compresso.
The raw model: TopKSAE
TopKSAE is a plain nn.Module. Its forward returns a
(reconstruction, codes, stats) triple, where codes already has exactly
k non-zeros per row and stats is a dict of monitoring metrics:
import torch
from compresso import TopKSAE
model = TopKSAE(input_dim=128, hidden_dim=512, k=32, tied=False)
x = torch.randn(256, 128)
reconstruction, codes, stats = model(x)
# stats keys: reconstruction_mse, cosine_similarity,
# active_count, activation_freq, dead_features
Writing your own training loop is then completely standard PyTorch:
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(50):
perm = torch.randperm(x.size(0))
for i in range(0, x.size(0), 128):
batch = x[perm[i : i + 128]]
_recon, _codes, stats = model(batch)
loss = stats["reconstruction_mse"]
opt.zero_grad()
loss.backward()
opt.step()
The stats dictionary
The metrics returned each forward pass are useful both as losses and as health checks:
reconstruction_mseMean squared error between input and reconstruction.
cosine_similarityMean per-row cosine similarity; the trainer optimizes a blend of
(1 - cosine_similarity)and MSE (seealpha_loss).active_countMean number of active features per row (equals
kfor a standard top-k SAE).activation_freqPer-feature firing rate over the batch, shape
(hidden_dim,).dead_featuresCount of features that never fired in the batch. A large value means part of your dictionary is wasted — lower
k, lowerhidden_dim, or train longer.
Encoder, decoder, and tied weights
By default the encoder and decoder are single nn.Linear layers, but you can
supply any modules — for example a deeper, non-linear encoder — as long as the
shapes line up:
import torch.nn as nn
from compresso import TopKSAE
encoder = nn.Sequential(
nn.Linear(784, 256), nn.GELU(), nn.Linear(256, 512),
)
model = TopKSAE(input_dim=784, hidden_dim=512, k=16, encoder=encoder)
Set tied=True to make the decoder reuse the encoder weight (transposed),
which halves the parameter count and is common for SAEs. Use
model.get_decoder_weight() to fetch the effective decoder matrix in either
case (this is what the A First Example: Seeing What an SAE Learns plots as dictionary atoms).
Controlling sparsification
The bottleneck is a reusable layer, TopKSparsify, backed by
the functional topk_ste(). You can drop either into any model:
from compresso import TopKSparsify, topk_ste
sparsify = TopKSparsify(k=8, score_mode="abs", ste_alpha=0.01)
z = sparsify(torch.randn(4, 64)) # exactly 8 non-zeros per row
z2 = topk_ste(torch.randn(4, 64), k=8, score_mode="abs", ste_alpha=0.01)
Two knobs matter:
score_modeselects which entries survive the top-k:"abs"keeps the largest-magnitude values (signed features; the default)."raw"keeps the largest signed values."relu"keeps the largest positive values and discards negatives.
ste_alphais the straight-through estimator leak. The forward pass is a hard top-k (non-differentiable), so the backward pass routes a fractionste_alphaof the gradient to the non-selected entries and full gradient to the selected ones.ste_alpha=0is a pure hard mask; a small value such as0.01keeps unused features learning and reduces dead features.
These are surfaced on the config as sparsify_score_mode /
sparsify_ste_alpha and srp_score_mode (the latter is used by
transform when packing into an SRPTensor).
Post-sparsification hooks
A post_sparsify module runs on the codes after the top-k. The built-in
L1Normalize and L2Normalize rescale each
code to unit L1/L2 norm, which is handy when codes feed a downstream similarity
or retrieval step:
from compresso import TopKSAEConfig, TopKSAETrainer, L1Normalize
cfg = TopKSAEConfig(hidden_dim=4096, k=128, post_sparsify=L1Normalize())
trainer = TopKSAETrainer(cfg)
Full config reference
Every trainer hyperparameter lives on TopKSAEConfig:
Field |
Default |
Meaning |
|---|---|---|
|
|
Number of dictionary features |
|
|
Active features kept per row. |
|
|
Add a bias to the default decoder. |
|
|
Module applied before sparsification. |
|
|
Module applied to codes after top-k. |
|
|
Custom modules (else linear layers). |
|
|
Top-k scoring: |
|
|
Straight-through leak for non-selected entries. |
|
|
Cosine/MSE mixture weight in the training loss. |
|
|
Extra L1 penalty on code activations. |
|
|
Rows per batch. |
|
|
Shuffle rows between epochs. |
|
|
Seed for shuffling and init. |
|
|
Training epochs. |
|
|
AdamW parameters. |
|
|
Cosine LR decay to zero over training. |
|
|
|
|
|
Training/transform device. |
|
|
tqdm progress bar when tqdm is installed. |
|
|
Score mode for |
Sparse parameters and pruning
Beyond representation learning, Compresso ships sparse parameter types for compressing model weights:
MaskedParam— a weight with a learned/scheduled binary mask for magnitude pruning.SRPParam— a parameter backed by the same fixed-k sparse layout asSRPTensor.SparsityController— a global dispatcher that advances and rewindsMaskedParammasks during training, andexponential_decay(), a helper for sparsity schedules.
Note
The pruning stack (and the broader compresso.layers package of sparse
Linear/Embedding/attention layers) is experimental and not part
of the stable first-release surface. The representation-learning API on this
page and in Input and Output is the supported path; expect the parameter/pruning
APIs to change.