Skip to content

refactor(losses): replace standalone VAE loss functions with nn.Module subclasses - #71

Open
kellrott with Copilot wants to merge 13 commits into
developfrom
copilot/featuremore-encoding
Open

refactor(losses): replace standalone VAE loss functions with nn.Module subclasses#71
kellrott with Copilot wants to merge 13 commits into
developfrom
copilot/featuremore-encoding

Conversation

Copilot AI commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

embkit.losses used stateless free functions with inconsistent signatures, pushing beta warmup state and kl_weight management into every training loop. Replace them with a proper nn.Module hierarchy.

New API

  • VAELoss — abstract base (nn.Module) with forward(recon, x, mu, logvar) → (total, recon_loss, kl_loss), mutable beta attribute, and step_beta(kappa, max_beta) warmup helper
  • Concrete classes: MSELoss, BCELoss, BCEWithLogitsLoss, BCEKLWeightedLoss
  • LOSS_REGISTRY + get_loss(name, **kwargs) factory — eliminates duplicated if-else dispatch
# Before — beta threaded through every call site
total, recon, kl = bce_kl_weighted(recon, x, mu, logvar, beta=beta, kl_weight=5.0)

# After — state lives in the object
loss_fn = BCEKLWeightedLoss(beta=0.0, kl_weight=5.0)
loss_fn.step_beta(kappa=0.01)          # call once per epoch
total, recon, kl = loss_fn(recon, x, mu, logvar)

Caller updates

  • rna_vae.py — removes the manual beta variable; delegates warmup to loss_fn.step_beta()
  • optimize.fit_vae — sets loss.beta per phase when given a VAELoss; still accepts legacy callables
  • optimize.fit_net_vae — allocates a single BCEWithLogitsLoss instance before the epoch loop instead of constructing one per batch
  • commands/model.py — replaces duplicated if-else blocks with get_loss(loss)
  • vae_estimator.py — fixes broken imports; uses BCEWithLogitsLoss

Backward compatibility

All previous free functions (bce, mse, bce_with_logits, bce_kl_weighted, net_vae_loss) are retained as deprecated thin wrappers emitting DeprecationWarning. CLI interface (--loss mse|bce|bce-logit) is unchanged.

Copilot AI and others added 2 commits August 12, 2026 23:35
… classes

- Add src/embkit/losses/base.py: VAELoss abstract base with step_beta() and _kl_divergence()
- Rewrite vae_loss.py: MSELoss, BCELoss, BCEWithLogitsLoss, BCEKLWeightedLoss classes
- Add LOSS_REGISTRY + get_loss() factory in vae_loss.py
- Keep deprecated free-function wrappers for backward compatibility
- Update losses/__init__.py to export all new symbols
- Update optimize/__init__.py: fit_vae accepts VAELoss instances; fit_net_vae uses BCELoss/BCEWithLogitsLoss directly
- Update rna_vae.py: use BCEKLWeightedLoss + step_beta() instead of manual beta warmup
- Update vae_estimator.py: fix broken imports; use BCEWithLogitsLoss
- Update commands/model.py: use get_loss() registry instead of if-else dispatch
- Update tests: cover new class API; update command assertions to use isinstance checks

Co-authored-by: kellrott <113868+kellrott@users.noreply.github.com>
…n fit_net_vae

Co-authored-by: kellrott <113868+kellrott@users.noreply.github.com>
Copilot AI changed the title refactor(losses): replace free functions with nn.Module-based VAELoss classes refactor(losses): replace standalone VAE loss functions with nn.Module subclasses Aug 12, 2026
Copilot AI requested a review from kellrott August 12, 2026 23:39

@kellrott kellrott left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These loss functions should probably be mentioned in the documentation.

Comment thread src/embkit/losses/vae_loss.py Outdated
def bce(recon_x, x, mu, logvar, beta=1.0) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""Calculate the VAE loss.
Used for classification of class probabilities (e.g., MNIST).
class MSELoss(VAELoss):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be confused with torch.nn.MSELoss, maybe a name like MSEVAELoss

Comment thread src/embkit/losses/vae_loss.py Outdated
Returns:
tuple: Total loss, reconstruction loss and KL divergence loss respectively as floats.

class BCELoss(VAELoss):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to MSELoss, this could be mistaken for torch.nn.BCELoss. Maybe a naming pattern like BCEVAELoss would help to differentiate.

Comment thread src/embkit/losses/vae_loss.py Outdated
# Registry + factory helper
# ---------------------------------------------------------------------------

LOSS_REGISTRY: dict = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are only VAE losses

# Legacy free-function API (deprecated)
# ---------------------------------------------------------------------------

def _deprecated(fn_name: str) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you verified that all of the deprecated methods have been removed from the unit tests?

@kellrott

Copy link
Copy Markdown
Contributor

@copilot respond to PR review

Co-authored-by: kellrott <113868+kellrott@users.noreply.github.com>

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot respond to PR review

Addressed the PR review feedback in 025dd7e: VAE loss class names now use explicit *VAELoss suffixes, registry/factory are VAE-scoped (VAE_LOSS_REGISTRY/get_vae_loss with compatibility aliases), docs were updated, and unit tests were switched off deprecated free-function losses.

Copilot AI requested a review from kellrott August 13, 2026 02:01
@kellrott
kellrott marked this pull request as ready for review August 13, 2026 02:06
- encode command: use result[2] (z sample) instead of result[0] (mu)
  from the encoder's (mu, logvar, z) output tuple
- train_netvae: change --min-group-size CLI default from 0 to 2 to match
  feature_map_intersect's own default, so groups with no overlapping
  members are filtered out and NetVAE correctly raises ValueError when
  there is no overlap between RNA features and pathway genes

Co-authored-by: kellrott <113868+kellrott@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
2881 2392 83% 0% 🟢

New Files

File Coverage Status
src/embkit/losses/base.py 100% 🟢
TOTAL 100% 🟢

Modified Files

File Coverage Status
src/embkit/commands/matrix.py 100% 🟢
src/embkit/commands/model.py 87% 🟢
src/embkit/constraints/pathway_constraint.py 89% 🟢
src/embkit/factory/_init_.py 100% 🟢
src/embkit/factory/core.py 50% 🟢
src/embkit/factory/layers.py 83% 🟢
src/embkit/losses/_init_.py 100% 🟢
src/embkit/losses/vae_loss.py 76% 🟢
src/embkit/models/vae/_init_.py 100% 🟢
src/embkit/models/vae/encoder.py 78% 🟢
src/embkit/models/vae/net_vae.py 68% 🟢
src/embkit/models/vae/rna_vae.py 72% 🟢
src/embkit/models/vae/vae.py 53% 🟢
src/embkit/optimize/_init_.py 69% 🟢
src/embkit/pathway.py 92% 🟢
TOTAL 81% 🟢

updated for commit: abf9c58 by action🐍

Co-authored-by: kellrott <113868+kellrott@users.noreply.github.com>
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.

2 participants