scunveil is the inference package for scUNVEIL, a pretrained model for
single-cell RNA-seq measurement enrichment. It produces reusable cell
embeddings and predicts a depth-enriched transcriptome composition from raw UMI
counts.
The model operates on one cell-level count vector at a time. It does not require cell-type, tissue, donor, batch, or dataset labels, and it does not perform dataset-specific training during inference.
scUNVEIL supports Python 3.9 through 3.12.
python -m pip install scunveilOn supported Linux x86-64 systems, TensorFlow's CUDA dependencies can be requested with:
python -m pip install "scunveil[cuda]"The first scUnveil() initialization downloads the selected pretrained
checkpoint from the
scUNVEIL model repository. Later
initializations reuse the Hugging Face cache.
process_anndata accepts an anndata.AnnData containing raw, nonnegative,
integer-like UMI counts in .X. Dense NumPy arrays, SciPy sparse matrices, and
backed sparse H5AD matrices are supported.
Do not pass normalized or log-transformed expression. If raw counts are stored in a layer, select them explicitly before inference:
adata_for_scunveil = adata.copy()
adata_for_scunveil.X = adata.layers["counts"].copy()The .var index or one of its columns must contain human gene symbols or
Ensembl gene IDs. Versioned Ensembl IDs such as ENSG00000141510.18 are
accepted. The package compares all candidate identifier columns with the full
model vocabulary, preferring Ensembl IDs when mappings are equivalent.
At least half of the input features and half of the nonzero UMI mass must map to the model vocabulary. Ambiguous symbols are not assigned arbitrarily; use their Ensembl IDs instead. Mapping information is available after processing:
print(result.gene_mapping_summary)import anndata as ad
from scunveil import scUnveil
adata = ad.read_h5ad("my_raw_counts.h5ad")
scu_model = scUnveil()
result = scu_model.process_anndata(adata, batch_size=256)
# Leading PCA-ordered components, shape: (cells, 512)
embeddings = result.get_embeddings(n_features=512)
# Selected-gene depth-enriched expression
markers = result.get_specific_genes_imputation(
["CD4", "CD8A", "ENSG00000163599"],
batch_size=256,
)process_anndata returns a ScUnveilResult for that dataset. Results retain a
reference to their originating model and input AnnData; processing another
dataset produces a separate result without changing existing ones. The input
is not copied. Keep a backed H5AD file open if you need to access it through
result.input_anndata later.
other_result = scu_model.process_anndata(other_adata)
assert result.sc_unveil is other_result.sc_unveil is scu_modelPass verbose=False to suppress package messages and progress bars:
scu_model = scUnveil(verbose=False)An explicit checkpoint can be selected with
scUnveil(model_version="VERSION"). The default model version is selected by
this package release.
# Default: the leading 512 PCA components
x_512 = result.get_embeddings()
# All 2048 PCA components
x_pca_full = result.get_embeddings(n_features=None)
# Original, unrotated 2048-dimensional hidden state
x_raw = result.get_raw_embeddings()The PCA projection was fitted after pretraining so that the leading columns are ordered by explained variation. A full PCA rotation preserves the complete embedding space; truncation selects its leading components.
selected = result.get_specific_genes_imputation(["CD4", "CD8A"])
all_genes = result.get_all_genes_imputation()Both methods return an AnnData. Its .X contains:
log10(predicted gene probability) + 6 = log10(CPM)
These values are log10 counts per million, not raw counts, probabilities, or ordinary CPM. Selected-gene predictions are normalized against the complete 60,000-gene output vocabulary and preserve request order.
All-gene imputation allocates a dense (n_cells, 60_000) float16 matrix. Its
approximate output size is:
n_cells × 60,000 × 2 bytes
Use selected-gene imputation when the complete matrix is unnecessary.
gene_embeddings = scu_model.get_genes_embeddings(normalize=True)This returns an AnnData with genes in .obs and shared gene embeddings in
.X, shaped (n_genes, embedding_dimension). The same matrix reads observed
genes into the model and scores genes from cell embeddings.
With normalize=True, the complete matrix is divided by its global standard
deviation. Relative vector lengths and cosine relationships are preserved.
input_genes = adata.var["feature_id"].tolist()
finetuning_model = scu_model.get_model_clone_for_finetuning(
output_dim=3,
list_of_input_genes=input_genes,
)This returns a separate, trainable Keras model. It copies the pretrained
backbone through the final cell embedding, selects and orders the initial gene
projection according to input_genes, then adds a residual block with a
zero-initialized final branch and a linear output_dim head. Training the clone
does not change scu_model. Gene names and Ensembl IDs are accepted; unknown,
ambiguous, or duplicate mappings raise an error.
The returned Keras model takes raw UMI counts with columns in input_genes
order and applies log1p internally. It does not accept an AnnData object
directly. Omitting list_of_input_genes uses the full model gene order instead.
Its final layer outputs raw scores, so compile it with a loss that expects
logits when doing classification.
generated = scu_model.generate_cells(
n_cells=128,
sampling_depth=1_024,
batch_size=128,
seed=7,
)Generation starts with empty cells and repeatedly samples the next UMI from the
model's categorical prediction before adding it to the count vector. Every
returned row contains exactly sampling_depth UMIs.
enriched = result.get_fully_enriched_h5ad(
batch_size=256,
list_of_genes=["CD4", "CD8A"],
n_embedding_features=512,
)The result contains:
- Imputed log10(CPM) in
.X. - PCA cell embeddings in
.obsm["X_scunveil"]. - Shared input/output gene embeddings in
.varm["scunveil_gene_embeddings"].
Omit list_of_genes to include all model genes. Omit
n_embedding_features to include all PCA components.
scUNVEIL is distributed under the Apache License 2.0.
