Skip to content

fix: AuxK loss carries no gradient, now backprop combined loss, not reconstruction only#3

Open
SohrabTa wants to merge 1 commit into
oclivegriffin:mainfrom
SohrabTa:fix/auxk-loss-carries-no-gradient
Open

fix: AuxK loss carries no gradient, now backprop combined loss, not reconstruction only#3
SohrabTa wants to merge 1 commit into
oclivegriffin:mainfrom
SohrabTa:fix/auxk-loss-carries-no-gradient

Conversation

@SohrabTa

@SohrabTa SohrabTa commented Jun 7, 2026

Copy link
Copy Markdown

The TopK / BatchTopK / GroupMax trainer computes the combined loss loss = reconstruction_loss + lambda_aux * aux_loss, but _calculate_loss_and_log returns reconstruction_loss (not loss) in both the logging and non-logging branches. The returned tensor is the one that gets .backward() called on it (base_acausal_trainer.run_batchbase_trainer's loss.div(grad_accum).backward()), so the AuxK dead-latent revival term never contributes any gradient. The auxiliary loss is logged to W&B but is effectively inert, since it does nothing to revive dead latents.

reconstruction_loss = calculate_reconstruction_loss_summed_norm_MSEs(...)
aux_loss = self.aux_loss(batch_BMPD, train_res)
loss = reconstruction_loss + self.cfg.lambda_aux * aux_loss   # combined loss built...

if log:
    log_dict = {... "train/aux_loss": aux_loss.item() ...}
    return reconstruction_loss, log_dict   # ...but reconstruction_loss returned for backprop
return reconstruction_loss, None

Fix

Return loss (the combined reconstruction + AuxK loss) in both branches so the auxiliary term is actually optimized. Two-line change and logging is unaffected.

How we found it

We forked this repo to train Anthropic-style sparse crosscoders on the residual-stream activations of ProtT5 (a protein language model) for a research project on disentangling and interpreting its internal representations. When we inspected our dead-latent counts, they stayed stubbornly high throughout training even though train/aux_loss was being logged, but it kept increasing. Tracing the loss that reaches .backward() showed the AuxK term was logged but never backpropped and the returned tensor was reconstruction_loss, so lambda_aux * aux_loss was dropped from the gradient. The symptom (AuxK appears active in logs but has zero effect on dead latents) makes this easy to miss without following the exact tensor that gets differentiated.

Testing

Verified the combined loss now flows to backprop and that train/loss, train/reconstruction_loss, and train/aux_loss logging is unchanged. We are re-running our full crosscoder training pipeline with this fix.

…on only

_calculate_loss_and_log builds loss = reconstruction_loss + lambda_aux * aux_loss
but returns reconstruction_loss for backprop in both branches, so the AuxK
dead-latent revival term never contributes a gradient. Return the combined loss
so the auxiliary loss is actually optimized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@SohrabTa SohrabTa changed the title fix: AuxK loss carries no gradient — backprop combined loss, not reconstruction only fix: AuxK loss carries no gradient, now backprop combined loss, not reconstruction only Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant