Make JAX scGen reproduce PyTorch scGen#1028
Merged
Merged
Conversation
The JAX scGen module diverged from the canonical PyTorch scGen (theislab/scgen) in three ways that prevented it from reproducing its results: - The reconstruction loss was summed over the whole minibatch (`jnp.sum((x - px) ** 2)`, a scalar) instead of per cell over genes (`.sum(dim=1)`). Combined with `mean(0.5 * rl + 0.5 * kl * kl_weight)`, this scaled the reconstruction term by the batch size, dwarfing the KL and making the objective batch-size dependent. - The decoder was built without batch norm, while canonical scGen runs with `use_batch_norm="both"` (scvi `FCLayers` defaults to batch norm). - The encoder did not add `var_eps` (1e-4) to the latent variance. After aligning these, the module computes the same objective as canonical scGen: with identical parameters and inputs the training loss matches to ~15 significant digits. Add regression tests pinning each fix: per-cell reconstruction loss, decoder batch norm under `use_batch_norm="both"`, and the encoder `var_eps`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1028 +/- ##
==========================================
+ Coverage 77.95% 78.00% +0.05%
==========================================
Files 50 50
Lines 6587 6557 -30
==========================================
- Hits 5135 5115 -20
+ Misses 1452 1442 -10
🚀 New features to boost your workflow:
|
This was referenced Jun 26, 2026
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.
Summary
pertpy's JAX scGen has never faithfully reproduced the canonical PyTorch scGen (theislab/scgen). Diffing the JAX module against the canonical source turned up three concrete divergences, all fixed here:
Reconstruction loss reduced over the wrong axis (the dominant bug).
With
loss = mean(0.5 * rl + 0.5 * kl * kl_weight), the old scalarrlscaled withbatch_size, dwarfing the KL term and making the objective batch-size dependent.Decoder was missing batch norm. Canonical
DecoderSCGENuses scviFCLayers(defaultuse_batch_norm=True) andSCGENVAEruns withuse_batch_norm="both". pertpy builtFlaxDecoderwithout passinguse_batch_norm, so it defaulted toFalse. Now the decoder honorsuse_batch_norm/use_layer_normlike the encoder.Encoder did not add
var_eps. scvi'sEncoderaddsvar_eps=1e-4to the latent variance; the Flax encoder now does too.The architecture otherwise already matched (n_hidden=800, n_latent=100, n_layers=2, dropout=0.2, LeakyReLU, BN momentum/eps).
Verification
6.5323769911546…vs6.5323769911546…, matching to ~15 significant digits.batch_removal→predict→ reg-mean/var pipeline still runs.Tests
Added three regression tests (no new dependency) pinning each fix:
test_scgen_reconstruction_loss_is_per_cell— reconstruction loss has shape(n_cells,).test_scgen_decoder_uses_batch_norm— the decoder hasBatchNormparams underuse_batch_norm="both".test_scgen_encoder_applies_var_eps— the encoder addsvar_epsto the variance.Note
This changes outputs for existing users — intentionally, since the prior behavior did not reproduce scGen.
🤖 Generated with Claude Code