Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ to [Semantic Versioning]. The full commit history is available in the [commit lo
enabling memory-efficient training on large-scale datasets stored as sharded Zarr collections,
with support for batch covariates, {pr}`3620`.
- Add support for rapids-singlecell, {pr}`3811`.
- Add {class}`scvi.external.JointEmbeddingSCVI`, a self-supervised SCVI variant using binomial
thinning and a cross-correlation objective (CCO) for robust embeddings, {pr}`3883`.
- Add {class}`scvi.external.CYTOVI` KNN imputation backend option to be cuML, {pr}`3821`.

#### Fixed
Expand Down
2 changes: 1 addition & 1 deletion docs/api/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Module classes in the external API with respective generative and inference proc
external.scviva.NicheLossOutput
external.sysvi.SysVAE
external.diagvi.DIAGVAE

external.JointEmbeddingVAE
```

## Module (Base)
Expand Down
1 change: 1 addition & 0 deletions docs/api/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import scvi
external.SysVI
external.SCVIVA
external.DIAGVI
external.JointEmbeddingSCVI
external.Tangram
```

Expand Down
9 changes: 9 additions & 0 deletions docs/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,12 @@ @article{Weinberger26
month = apr,
publisher={Nature Publishing Group}
}

@article{Svensson26,
title={Improving SCVI for low-count cells through self-supervised augmentation},
author={Valentine Svensson},
journal={bioRxiv},
year={2026},
publisher={Cold Spring Harbor Laboratory},
doi = {10.64898/2026.02.11.705441}
}
8 changes: 8 additions & 0 deletions docs/tutorials/index_scrna.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ notebooks/scrna/AutoZI_tutorial
notebooks/scrna/sysVI
notebooks/scrna/decipher_tutorial
notebooks/scrna/velovi
notebooks/scrna/JointEmbeddingSCVI_tutorial
notebooks/scrna/Tahoe100_mrVI
```

Expand Down Expand Up @@ -126,6 +127,13 @@ Use Decipher to jointly analyze samples from distinct conditions.
Use VeloVI to estimate RNA velocity.
```

```{customcard}
:path: notebooks/scrna/JointEmbeddingSCVI_tutorial
:tags: Analysis

Use JointEmbeddingSCVI for improving SCVI for low-count cells through self-supervised augmentation
```

```{customcard}
:path: notebooks/scrna/Tahoe100_mrVI
:tags: Analysis, Differential-comparison, Dimensionality-reduction, Removal-of-variance
Expand Down
3 changes: 3 additions & 0 deletions docs/user_guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ scvi-tools is composed of models that can perform one or many analysis tasks. In
* - :doc:`/user_guide/models/velovi`
- Deep generative modeling of transcriptional dynamics for RNA velocity analysis in single cells
- :cite:p:`GayosoWeiler23`
* - :doc:`/user_guide/models/jointembeddingscvi`
- Improving SCVI for low-count cells through self-supervised augmentation
- :cite:p:`Svensson26`
```

## ATAC-seq analysis
Expand Down
1 change: 1 addition & 0 deletions docs/user_guide/models/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ decipher
destvi
diagvi
gimvi
jointembeddingscvi
linearscvi
methylanvi
methylvi
Expand Down
20 changes: 20 additions & 0 deletions docs/user_guide/models/jointembeddingscvi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Joint-Embedding SCVI

:::{note}
This page is under construction.
:::

**JointEmbeddingSCVI** {cite:p}`Svensson26` (Python class {class}`~scvi.external.JointEmbeddingSCVI`) is a ...

The advantages of JointEmbeddingSCVI are:

- ...

The limitations of JointEmbeddingSCVI include:

- ...

```{topic} Tutorials:

- {doc}`/tutorials/notebooks/scrna/JointEmbeddingSCVI_tutorial`
```
3 changes: 3 additions & 0 deletions src/scvi/external/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .decipher import Decipher
from .diagvi import DIAGVI
from .gimvi import GIMVI
from .joint_embedding_scvi import JointEmbeddingSCVI, JointEmbeddingVAE
from .methylvi import METHYLANVI, METHYLVI
from .mrvi import MRVI
from .poissonvi import POISSONVI
Expand All @@ -22,6 +23,8 @@
"SCAR",
"SOLO",
"GIMVI",
"JointEmbeddingSCVI",
"JointEmbeddingVAE",
"Decipher",
"RNAStereoscope",
"SpatialStereoscope",
Expand Down
4 changes: 4 additions & 0 deletions src/scvi/external/joint_embedding_scvi/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from ._model import JointEmbeddingSCVI
from ._module import JointEmbeddingVAE

__all__ = ["JointEmbeddingSCVI", "JointEmbeddingVAE"]
157 changes: 157 additions & 0 deletions src/scvi/external/joint_embedding_scvi/_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""Joint Embedding SCVI model."""

from __future__ import annotations

from typing import TYPE_CHECKING

from scvi.external.joint_embedding_scvi._module import JointEmbeddingVAE
from scvi.model._scvi import SCVI

if TYPE_CHECKING:
from typing import Literal

from anndata import AnnData


class JointEmbeddingSCVI(SCVI):
"""SCVI with joint embedding loss using binomial thinning and CCO :cite:p:`Svensson26`.

This model extends the standard SCVI with a cross-correlation objective (CCO)
loss that encourages the embedding of a thinned view to match the embedding
of the original data. This promotes robustness to count dropout/noise.

Thinning probabilities are dynamically sampled per cell to produce target
library sizes that are log-uniform between min_library_size and the observed
library size. This matches realistic library size variation in single-cell data.

Parameters
----------
adata
AnnData object that has been registered via :meth:`~scvi.model.SCVI.setup_anndata`.
registry
Registry of the datamodule used to train the model, passed through when training
without an :class:`~anndata.AnnData` object (see :class:`~scvi.model.SCVI`).
joint_embedding_weight
Weight for the CCO loss. Default is 100.0.
lambda_off_diag
Off-diagonal penalty in CCO loss. Default is 0.01.
min_library_size
Minimum target library size for thinning. Default is 10.
Thinned library sizes are sampled log-uniformly between this
value and the observed library size.
reconstruction_weight
Weight for reconstruction loss. Default is 1.0.
Set to 0.0 for pure self-supervised training with only CCO loss.
variance_weight
Weight for variance regularization loss (VICReg-style). Default is 0.0.
Set to positive value (e.g., 1.0) to prevent dimension collapse
in self-supervised training.
use_joint_embedding
Whether to use joint embedding loss. Default is True.
Set to False to train as standard SCVI.
n_hidden
Number of nodes per hidden layer.
n_latent
Dimensionality of the latent space.
n_layers
Number of hidden layers used for encoder and decoder NNs.
dropout_rate
Dropout rate for neural networks.
dispersion
One of the following:

* ``'gene'`` - dispersion parameter of NB is constant per gene across cells
* ``'gene-batch'`` - dispersion can differ between different batches
* ``'gene-label'`` - dispersion can differ between different labels
* ``'gene-cell'`` - dispersion can differ for every gene in every cell
gene_likelihood
One of:

* ``'nb'`` - Negative binomial distribution
* ``'zinb'`` - Zero-inflated negative binomial distribution
* ``'poisson'`` - Poisson distribution
* ``'normal'`` - ``EXPERIMENTAL`` Normal distribution
use_observed_lib_size
If ``True``, use the observed library size for RNA as the scaling factor in the mean of the
conditional distribution.
latent_distribution
One of:

* ``'normal'`` - Normal distribution
* ``'ln'`` - Logistic normal distribution (Normal(0, I) transformed by softmax)
**kwargs
Additional keyword arguments for :class:`~scvi.external.JointEmbeddingVAE`.

Examples
--------
>>> adata = anndata.read_h5ad(path_to_anndata)
>>> scvi.external.JointEmbeddingSCVI.setup_anndata(adata, batch_key="batch")
>>> model = scvi.external.JointEmbeddingSCVI(
... adata,
... joint_embedding_weight=1.0,
... lambda_off_diag=0.01,
... )
>>> model.train()
>>> latent = model.get_latent_representation()

See Also
--------
:class:`~scvi.model.SCVI`
:class:`~scvi.external.JointEmbeddingVAE`
"""

_module_cls = JointEmbeddingVAE

def __init__(
self,
adata: AnnData | None = None,
registry: dict | None = None,
joint_embedding_weight: float = 100.0,
lambda_off_diag: float = 0.01,
min_library_size: float = 10.0,
reconstruction_weight: float = 1.0,
variance_weight: float = 0.0,
use_joint_embedding: bool = True,
n_hidden: int = 128,
n_latent: int = 10,
n_layers: int = 1,
dropout_rate: float = 0.1,
dispersion: Literal["gene", "gene-batch", "gene-label", "gene-cell"] = "gene",
gene_likelihood: Literal["zinb", "nb", "poisson", "normal"] = "zinb",
use_observed_lib_size: bool = True,
latent_distribution: Literal["normal", "ln"] = "normal",
**kwargs,
):
# Pass joint embedding params through kwargs to module
super().__init__(
adata=adata,
registry=registry,
n_hidden=n_hidden,
n_latent=n_latent,
n_layers=n_layers,
dropout_rate=dropout_rate,
dispersion=dispersion,
gene_likelihood=gene_likelihood,
use_observed_lib_size=use_observed_lib_size,
latent_distribution=latent_distribution,
joint_embedding_weight=joint_embedding_weight,
lambda_off_diag=lambda_off_diag,
min_library_size=min_library_size,
reconstruction_weight=reconstruction_weight,
variance_weight=variance_weight,
use_joint_embedding=use_joint_embedding,
**kwargs,
)

# Update model summary string
self._model_summary_string = (
"JointEmbeddingSCVI model with the following parameters: \n"
f"n_hidden: {n_hidden}, n_latent: {n_latent}, n_layers: {n_layers}, "
f"dropout_rate: {dropout_rate}, dispersion: {dispersion}, "
f"gene_likelihood: {gene_likelihood}, latent_distribution: {latent_distribution}, "
f"joint_embedding_weight: {joint_embedding_weight}, "
f"lambda_off_diag: {lambda_off_diag}, min_library_size: {min_library_size}, "
f"reconstruction_weight: {reconstruction_weight}, "
f"variance_weight: {variance_weight}, "
f"use_joint_embedding: {use_joint_embedding}."
)
Loading
Loading