Managing large and complex training datasets#
Large and complex datasets can be difficult to use for model training because of computational constraints; simply loading such datasets may require substantial amounts of RAM.
In addition, the time required to select genes for model training increases as dataset size grows.
However, using more training data does not always lead to a substantial improvement in model performance.
This tutorial presents a method for overcoming these computational limitations when training scAdam models on large, complex datasets with many samples.
[1]:
# Python packages
import warnings
warnings.simplefilter('ignore')
import scanpy as sc
import scparadise
import numpy as np
import pandas as pd
import os
sc.set_figure_params(dpi = 120)
[2]:
# Create folder to save files
# single nuclear RNA-seq of human retina
os.makedirs('snRNAseq_human_retina', exist_ok=True)
[3]:
# Download CELLxGENE dataset (snRNA-seq of human retina):
# https://cellxgene.cziscience.com/collections/4c6eaf5c-6d57-4c76-b1e9-60df8c655f1e
!wget https://datasets.cellxgene.cziscience.com/2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad
--2025-02-08 13:49:05-- https://datasets.cellxgene.cziscience.com/2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad
Resolving datasets.cellxgene.cziscience.com (datasets.cellxgene.cziscience.com)... 52.85.49.24, 52.85.49.28, 52.85.49.17, ...
Connecting to datasets.cellxgene.cziscience.com (datasets.cellxgene.cziscience.com)|52.85.49.24|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 37946797973 (35G) [binary/octet-stream]
Saving to: ‘2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad’
2e910e62-7eaf-4c06- 100%[===================>] 35.34G 25.1MB/s in 27m 20s
2025-02-08 14:13:58 (22.1 MB/s) - ‘2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad’ saved [37946797973/37946797973]
[4]:
# Open the dataset in backed mode to inspect the cell-type annotations
# without loading the full expression matrix into memory.
adata = sc.read_h5ad('2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad', backed = 'r')
adata
[4]:
AnnData object with n_obs × n_vars = 3177310 × 36406 backed at '2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad'
obs: 'reference_genome', 'gene_annotation_version', 'alignment_software', 'intronic_reads_counted', 'donor_id', 'donor_age', 'self_reported_ethnicity_ontology_term_id', 'donor_cause_of_death', 'donor_living_at_sample_collection', 'organism_ontology_term_id', 'sample_id', 'sample_preservation_method', 'tissue_ontology_term_id', 'development_stage_ontology_term_id', 'sample_collection_method', 'tissue_source', 'tissue_type', 'suspension_derivation_process', 'suspension_dissociation_reagent', 'suspension_enriched_cell_types', 'suspension_enrichment_factors', 'suspension_uuid', 'suspension_type', 'tissue_handling_interval', 'library_id', 'assay_ontology_term_id', 'sequenced_fragment', 'institute', 'library_id_repository', 'sequencing_platform', 'is_primary_data', 'cell_type_ontology_term_id', 'author_cell_type', 'disease_ontology_term_id', 'sex_ontology_term_id', 'majorclass', 'AC_subclass', 'AC_cluster', 'AC_celltype_number', 'BC_subclass', 'RGC_cluster', 'RGC_celltype_number', 'study_name', 'nCount_RNA', 'nFeature_RNA', 'percent.mt', 'sampleid', 'cell_type', 'assay', 'disease', 'organism', 'sex', 'tissue', 'self_reported_ethnicity', 'development_stage', 'observation_joinid'
var: 'feature_is_filtered', 'feature_name', 'feature_reference', 'feature_biotype', 'feature_length', 'feature_type'
uns: 'citation', 'default_embedding', 'schema_reference', 'schema_version', 'title'
obsm: 'X_scVI', 'X_umap'
[5]:
# Count cells for each cell type in the dataset
adata.obs['cell_type'].value_counts()
[5]:
cell_type
retinal rod cell 1066056
GABAergic amacrine cell 362866
Mueller cell 221612
OFF midget ganglion cell 200402
ON midget ganglion cell 151685
flat midget bipolar cell 144086
glycinergic amacrine cell 131207
retinal cone cell 118559
invaginating midget bipolar cell 106152
rod bipolar cell 101199
H1 horizontal cell 68583
diffuse bipolar 2 cell 68176
amacrine cell 57236
retinal bipolar neuron 52358
diffuse bipolar 1 cell 50429
diffuse bipolar 4 cell 49157
diffuse bipolar 3b cell 35342
retinal ganglion cell 31230
giant bipolar cell 25269
diffuse bipolar 3a cell 21827
starburst amacrine cell 20270
diffuse bipolar 6 cell 18587
OFFx cell 15495
astrocyte 14085
H2 horizontal cell 11965
OFF parasol ganglion cell 10104
S cone cell 8501
ON parasol ganglion cell 6184
microglial cell 4894
ON-blue cone bipolar cell 2931
retinal pigment epithelial cell 863
Name: count, dtype: int64
Train a scAdam model using a fraction of the dataset#
The entire dataset contains 3,177,310 cells and 36,406 genes and occupies 35.34 GB on disk, making it impractical to load into memory on a standard computer. Large single-cell datasets may require substantially more RAM than their file size suggests once loaded, depending on the matrix representation and associated metadata.
In addition, gene selection and model training on a dataset of this size require substantial computational time and resources.
Therefore, the scParadise team recommends extracting a smaller, representative subset of the dataset for scAdam model training.
[6]:
# Obtain 25000 cells from dataset
adata_fraction = scparadise.scnoah.get_frac(
path = '2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad',
fraction = 25000,
stratify = 'cell_type', # Use to get the same ratio as in the full dataset
random_state = 0
)
[7]:
# Count cells for each cell type in the fraction of dataset
adata_fraction.obs['cell_type'].value_counts()
[7]:
cell_type
retinal rod cell 8388
GABAergic amacrine cell 2855
Mueller cell 1744
OFF midget ganglion cell 1577
ON midget ganglion cell 1193
flat midget bipolar cell 1134
glycinergic amacrine cell 1032
retinal cone cell 933
invaginating midget bipolar cell 835
rod bipolar cell 796
H1 horizontal cell 540
diffuse bipolar 2 cell 536
amacrine cell 450
retinal bipolar neuron 412
diffuse bipolar 1 cell 397
diffuse bipolar 4 cell 387
diffuse bipolar 3b cell 278
retinal ganglion cell 246
giant bipolar cell 199
diffuse bipolar 3a cell 172
starburst amacrine cell 159
diffuse bipolar 6 cell 146
OFFx cell 122
astrocyte 111
H2 horizontal cell 94
OFF parasol ganglion cell 79
S cone cell 67
ON parasol ganglion cell 49
microglial cell 39
ON-blue cone bipolar cell 23
retinal pigment epithelial cell 7
Name: count, dtype: int64
[8]:
# Get raw counts from adata_fraction.raw
adata_fraction = adata_fraction.raw.to_adata()
# Replace variable names with gene names
adata_fraction.var.set_index('feature_name', inplace = True)
adata_fraction.var_names = adata_fraction.var_names.astype('str')
adata_fraction.var_names_make_unique()
# Normalize data
sc.pp.normalize_total(adata_fraction, target_sum = None)
sc.pp.log1p(adata_fraction)
[9]:
# Select genes for model training (marker genes of cell types)
lst_genes = []
lst_annotations = ['majorclass', 'cell_type'] # annotation levels
for annotation in lst_annotations:
sc.tl.rank_genes_groups(adata_fraction,
groupby = annotation,
method = 't-test_overestim_var', pts = True)
# Filter marker genes of cell types
sc.tl.filter_rank_genes_groups(adata_fraction,
min_fold_change = 1.0,
min_in_group_fraction = 0.4,
key_added = 'filtered_rank_genes_groups')
# Create list of genes for model training
for i in adata_fraction.obs[annotation].unique():
df = sc.get.rank_genes_groups_df(adata_fraction, group = i, key = 'filtered_rank_genes_groups', pval_cutoff = 0.05)
df['pts_comparizon'] = df['pct_nz_group']/df['pct_nz_reference']
lst_genes.extend(df.sort_values(by = 'logfoldchanges', ascending = False).head(20)['names'].tolist())
lst_genes.extend(df.sort_values(by = 'pts_comparizon', ascending = False).head(20)['names'].tolist())
# Remove duplicates
lst_genes = np.unique(lst_genes).tolist()
print(f'Number of selected features: {len(lst_genes)}')
Number of selected features: 595
[10]:
# Subset genes for model training
adata_fraction = adata_fraction[:, lst_genes]
[11]:
# Alternative way to select genes for model training
# sc.pp.highly_variable_genes(adata_fraction,
# n_top_genes = 1000,
# subset = True)
# lst_genes = adata_fraction.var_names.tolist()
[12]:
adata_balanced = scparadise.scnoah.balance(
adata_fraction,
celltype_keys = lst_annotations
)
[13]:
# Train scadam model using adata_fraction dataset
scparadise.scadam.train(
adata_balanced,
path = 'snRNAseq_human_retina', # path to save model
model_name = 'Human_Retina_scAdam', # folder name with model
celltype_keys = lst_annotations,
eval_metric = ['accuracy', 'balanced_accuracy']
)
Device: cuda
Number of features: 595
Label hierarchy: majorclass → cell_type
Annotation levels weights using strategy 'linear_offset':
majorclass: 10 cell types, 0.4 relative weight
cell_type: 31 cell types, 0.6 relative weight
Dataset split:
Train dataset contains: 20717 cells, it is 80.0 % of input dataset
Validation dataset contains: 5180 cells, it is 20.0 % of input dataset
Unsupervised pretraining: 100%|████████████████| 50/50 [04:09<00:00, 4.98s/it]
Training scAdam model: 10%|█▊ | 20/200 [01:31<13:39, 4.56s/it]
Early stopping triggered! Best score: 0.9943
Training completed!
Fitting unknown cells detector
UnknownCellDetector fitted successfully!
Model saved to snRNAseq_human_retina/Human_Retina_scAdam
Evaluation of model quality#
For model evaluation, we use another subset of 25,000 cells generated using a different random state.
[14]:
# Get test dataset for model quality evaluation
adata_test = scparadise.scnoah.get_frac(
path = '2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad',
fraction = 25000,
stratify = 'cell_type',
random_state = 42
)
[15]:
# Check common cells between test and training datasets
lst_train = adata_fraction.obs_names.tolist()
lst_test = adata_test.obs_names.tolist()
lst_train.extend(lst_test)
lst_train = np.unique(lst_train)
percent = round((2 * len(lst_test) - len(lst_train))/len(lst_test)*100, 5)
print(f"There are {percent} % common cells ({2 * len(lst_test) - len(lst_train)} cells) between the test and training datasets")
There are 0.836 % common cells (209 cells) between the test and training datasets
Fewer than 1% of cells overlap between the training and test datasets. Although this overlap is minimal and is unlikely to materially affect the evaluation, it should be acknowledged when interpreting the model’s performance.
[16]:
# Apply the same preprocessing steps to the test dataset as used for training
# Get raw counts from adata_fraction.raw
adata_test = adata_test.raw.to_adata()
# Replace variable names with gene names
adata_test.var.set_index('feature_name', inplace = True)
adata_test.var_names = adata_test.var_names.astype('str')
adata_test.var_names_make_unique()
# Normalize data
sc.pp.normalize_total(adata_test, target_sum = None)
sc.pp.log1p(adata_test)
adata_test.raw = adata_test
[17]:
# Predict cell types using trained model
adata_test = scparadise.scadam.predict(
adata_test,
path_model = 'snRNAseq_human_retina/Human_Retina_scAdam'
)
Cell type annotation using the 2nd version of the scAdam model
scAdam model with unknown detector loaded from snRNAseq_human_retina/Human_Retina_scAdam
Gene alignment:
Model features: 595
Matched features: 595 (100.0%)
Predicting: 100%|██████████████████████████████| 98/98 [00:01<00:00, 69.80it/s]
Added cell type column: pred_celltype_l1
Added probabilities column: pred_celltype_l1_probability
Added cell type column: pred_celltype_l2
Added probabilities column: pred_celltype_l2_probability
[18]:
## Check model quality
df_l1 = scparadise.scnoah.report_classif_full(
adata_test,
celltype = 'majorclass',
pred_celltype = 'pred_celltype_l1'
)
df_l1
[18]:
| precision | recall/sensitivity | specificity | f1-score | geometric mean | index balanced accuracy | number of cells | |
|---|---|---|---|---|---|---|---|
| AC | 0.9991 | 0.9996 | 0.9998 | 0.9993 | 0.9997 | 0.9993 | 4496 |
| Astrocyte | 1.0000 | 0.982 | 1.0 | 0.9909 | 0.991 | 0.9802 | 111 |
| BC | 0.9998 | 0.9991 | 0.9999 | 0.9994 | 0.9995 | 0.9989 | 5437 |
| Cone | 1.0000 | 0.999 | 1.0 | 0.9995 | 0.9995 | 0.9989 | 1000 |
| HC | 0.9969 | 1.0 | 0.9999 | 0.9984 | 1.0 | 0.9999 | 634 |
| MG | 1.0000 | 0.9994 | 1.0 | 0.9997 | 0.9997 | 0.9994 | 1744 |
| Microglia | 1.0000 | 0.9487 | 1.0 | 0.9737 | 0.974 | 0.9439 | 39 |
| RGC | 0.9987 | 1.0 | 0.9998 | 0.9994 | 0.9999 | 0.9998 | 3144 |
| RPE | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 7 |
| Rod | 0.9995 | 0.9998 | 0.9998 | 0.9996 | 0.9998 | 0.9995 | 8388 |
| macro avg | 0.9994 | 0.9928 | 0.9999 | 0.996 | 0.9963 | 0.992 | |
| weighted avg | 0.9994 | 0.9994 | 0.9998 | 0.9994 | 0.9996 | 0.9992 | |
| Accuracy | 0.9994 | ||||||
| Balanced accuracy | 0.9928 |
[19]:
## Check model quality
df_l2 = scparadise.scnoah.report_classif_full(
adata_test,
celltype = 'cell_type',
pred_celltype = 'pred_celltype_l2'
)
df_l2
[19]:
| precision | recall/sensitivity | specificity | f1-score | geometric mean | index balanced accuracy | number of cells | |
|---|---|---|---|---|---|---|---|
| GABAergic amacrine cell | 0.9916 | 0.9982 | 0.9989 | 0.9949 | 0.9986 | 0.9971 | 2855 |
| H1 horizontal cell | 0.9836 | 0.9981 | 0.9996 | 0.9908 | 0.9989 | 0.9976 | 540 |
| H2 horizontal cell | 0.9888 | 0.9362 | 1.0 | 0.9617 | 0.9675 | 0.9302 | 94 |
| Mueller cell | 1.0000 | 0.9994 | 1.0 | 0.9997 | 0.9997 | 0.9994 | 1744 |
| OFF midget ganglion cell | 0.9343 | 0.9112 | 0.9957 | 0.9226 | 0.9525 | 0.8996 | 1577 |
| OFF parasol ganglion cell | 0.9744 | 0.962 | 0.9999 | 0.9682 | 0.9808 | 0.9583 | 79 |
| OFFx cell | 1.0000 | 0.9754 | 1.0 | 0.9876 | 0.9876 | 0.973 | 122 |
| ON midget ganglion cell | 0.9411 | 0.9103 | 0.9971 | 0.9254 | 0.9527 | 0.8998 | 1193 |
| ON parasol ganglion cell | 0.9796 | 0.9796 | 1.0 | 0.9796 | 0.9897 | 0.9776 | 49 |
| ON-blue cone bipolar cell | 0.9524 | 0.8696 | 1.0 | 0.9091 | 0.9325 | 0.8582 | 23 |
| S cone cell | 0.9710 | 1.0 | 0.9999 | 0.9853 | 1.0 | 0.9999 | 67 |
| amacrine cell | 0.9907 | 0.9489 | 0.9998 | 0.9694 | 0.974 | 0.9439 | 450 |
| astrocyte | 1.0000 | 0.982 | 1.0 | 0.9909 | 0.991 | 0.9802 | 111 |
| diffuse bipolar 1 cell | 0.9875 | 0.9975 | 0.9998 | 0.9925 | 0.9986 | 0.997 | 397 |
| diffuse bipolar 2 cell | 0.9962 | 0.9869 | 0.9999 | 0.9916 | 0.9934 | 0.9856 | 536 |
| diffuse bipolar 3a cell | 1.0000 | 0.9942 | 1.0 | 0.9971 | 0.9971 | 0.9936 | 172 |
| diffuse bipolar 3b cell | 0.9964 | 0.9964 | 1.0 | 0.9964 | 0.9982 | 0.996 | 278 |
| diffuse bipolar 4 cell | 0.9948 | 0.9948 | 0.9999 | 0.9948 | 0.9974 | 0.9942 | 387 |
| diffuse bipolar 6 cell | 0.9932 | 0.9932 | 1.0 | 0.9932 | 0.9965 | 0.9924 | 146 |
| flat midget bipolar cell | 0.9912 | 0.9974 | 0.9996 | 0.9943 | 0.9985 | 0.9967 | 1134 |
| giant bipolar cell | 0.9899 | 0.9899 | 0.9999 | 0.9899 | 0.9949 | 0.9889 | 199 |
| glycinergic amacrine cell | 0.9836 | 0.9874 | 0.9993 | 0.9855 | 0.9933 | 0.9855 | 1032 |
| invaginating midget bipolar cell | 1.0000 | 0.9976 | 1.0 | 0.9988 | 0.9988 | 0.9974 | 835 |
| microglial cell | 1.0000 | 0.9231 | 1.0 | 0.96 | 0.9608 | 0.916 | 39 |
| retinal bipolar neuron | 0.9903 | 0.9951 | 0.9998 | 0.9927 | 0.9975 | 0.9945 | 412 |
| retinal cone cell | 1.0000 | 0.9957 | 1.0 | 0.9979 | 0.9979 | 0.9953 | 933 |
| retinal ganglion cell | 0.4695 | 0.626 | 0.993 | 0.5366 | 0.7884 | 0.5988 | 246 |
| retinal pigment epithelial cell | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 7 |
| retinal rod cell | 0.9995 | 0.9999 | 0.9998 | 0.9997 | 0.9998 | 0.9997 | 8388 |
| rod bipolar cell | 0.9987 | 0.9962 | 1.0 | 0.9975 | 0.9981 | 0.9958 | 796 |
| starburst amacrine cell | 1.0000 | 0.9811 | 1.0 | 0.9905 | 0.9905 | 0.9793 | 159 |
| macro avg | 0.9709 | 0.9653 | 0.9994 | 0.9676 | 0.9815 | 0.962 | |
| weighted avg | 0.9841 | 0.9826 | 0.9992 | 0.9832 | 0.9906 | 0.9804 | |
| Accuracy | 0.9826 | ||||||
| Balanced accuracy | 0.9653 |
The model performs well except for the retinal ganglion cell.
You could try using a different random state to generate another test dataset.
Iterative warm start training (optional)#
You may use another subset of the whole dataset to increase model generalization.
[20]:
# Do not set the lower bound of the range to 0, which was used for the primary training of the model
for i in range(1, 4):
# Obtain 25000 cells randomly
adata_fraction = scparadise.scnoah.get_frac(
path = '2e910e62-7eaf-4c06-80cb-8918e3eea16e.h5ad',
fraction = 25000,
stratify = 'cell_type',
random_state = i
)
# Get raw counts from adata_fraction.raw
adata_fraction = adata_fraction.raw.to_adata()
# Replace variable names with gene names
adata_fraction.var.set_index('feature_name', inplace=True)
adata_fraction.var_names = adata_fraction.var_names.astype('str')
adata_fraction.var_names_make_unique()
# Normalize data
sc.pp.normalize_total(adata_fraction, target_sum = None)
sc.pp.log1p(adata_fraction)
adata_fraction.raw = adata_fraction
# Subset genes for model training
adata_fraction = adata_fraction[:, lst_genes]
# Balance dataset
adata_balanced = scparadise.scnoah.balance(
adata_fraction,
celltype_keys = lst_annotations
)
adata_balanced.raw = adata_balanced
# Warm start requires second training dataset and path to pretrained model
scparadise.scadam.warm_start(
adata_balanced,
path = 'snRNAseq_human_retina', # path to save model
path_model = 'snRNAseq_human_retina/Human_Retina_scAdam', # folder name with pretrained model
model_name = 'Human_Retina_scAdam',
celltype_keys = lst_annotations,
eval_metric = ['accuracy', 'balanced_accuracy']
)
scAdam model with unknown detector loaded from snRNAseq_human_retina/Human_Retina_scAdam
Device: cuda
Gene alignment:
Number of features: 595
Matched features: 595 (100.0%)
Label hierarchy: majorclass → cell_type
Annotation levels weights using strategy 'linear_offset':
majorclass: 10 cell types, 0.4 relative weight
cell_type: 31 cell types, 0.6 relative weight
Dataset split:
Train dataset contains: 20717 cells, it is 80.0 % of input dataset
Validation dataset contains: 5180 cells, it is 20.0 % of input dataset
Warm-start model fine-tuning: 29%|███▏ | 29/100 [02:35<06:20, 5.35s/it]
Early stopping triggered! Best score: 0.9943
Fitting unknown cells detector
UnknownCellDetector fitted successfully!
Model saved to snRNAseq_human_retina/Human_Retina_scAdam
scAdam model with unknown detector loaded from snRNAseq_human_retina/Human_Retina_scAdam
Device: cuda
Gene alignment:
Number of features: 595
Matched features: 595 (100.0%)
Label hierarchy: majorclass → cell_type
Annotation levels weights using strategy 'linear_offset':
majorclass: 10 cell types, 0.4 relative weight
cell_type: 31 cell types, 0.6 relative weight
Dataset split:
Train dataset contains: 20717 cells, it is 80.0 % of input dataset
Validation dataset contains: 5180 cells, it is 20.0 % of input dataset
Warm-start model fine-tuning: 22%|██▍ | 22/100 [02:13<07:52, 6.05s/it]
Early stopping triggered! Best score: 0.9941
Fitting unknown cells detector
UnknownCellDetector fitted successfully!
Model saved to snRNAseq_human_retina/Human_Retina_scAdam
scAdam model with unknown detector loaded from snRNAseq_human_retina/Human_Retina_scAdam
Device: cuda
Gene alignment:
Number of features: 595
Matched features: 595 (100.0%)
Label hierarchy: majorclass → cell_type
Annotation levels weights using strategy 'linear_offset':
majorclass: 10 cell types, 0.4 relative weight
cell_type: 31 cell types, 0.6 relative weight
Dataset split:
Train dataset contains: 20717 cells, it is 80.0 % of input dataset
Validation dataset contains: 5180 cells, it is 20.0 % of input dataset
Warm-start model fine-tuning: 16%|█▊ | 16/100 [01:28<07:42, 5.50s/it]
Early stopping triggered! Best score: 0.9937
Fitting unknown cells detector
UnknownCellDetector fitted successfully!
Model saved to snRNAseq_human_retina/Human_Retina_scAdam
[21]:
# Predict cell types using trained model
adata_test = scparadise.scadam.predict(
adata_test,
path_model = 'snRNAseq_human_retina/Human_Retina_scAdam'
)
Cell type annotation using the 2nd version of the scAdam model
scAdam model with unknown detector loaded from snRNAseq_human_retina/Human_Retina_scAdam
Gene alignment:
Model features: 595
Matched features: 595 (100.0%)
Predicting: 100%|██████████████████████████████| 98/98 [00:01<00:00, 91.31it/s]
Added cell type column: pred_celltype_l1
Added probabilities column: pred_celltype_l1_probability
Added cell type column: pred_celltype_l2
Added probabilities column: pred_celltype_l2_probability
[22]:
## Check model quality
df_warm_start_l1 = scparadise.scnoah.report_classif_full(
adata_test,
celltype='majorclass',
pred_celltype='pred_celltype_l1'
)
df_warm_start_l1
[22]:
| precision | recall/sensitivity | specificity | f1-score | geometric mean | index balanced accuracy | number of cells | |
|---|---|---|---|---|---|---|---|
| AC | 0.9982 | 0.9996 | 0.9996 | 0.9989 | 0.9996 | 0.9992 | 4496 |
| Astrocyte | 1.0000 | 0.982 | 1.0 | 0.9909 | 0.991 | 0.9802 | 111 |
| BC | 0.9993 | 1.0 | 0.9998 | 0.9996 | 0.9999 | 0.9998 | 5437 |
| Cone | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1000 |
| HC | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 634 |
| MG | 0.9989 | 0.9994 | 0.9999 | 0.9991 | 0.9997 | 0.9993 | 1744 |
| Microglia | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 39 |
| RGC | 1.0000 | 0.9987 | 1.0 | 0.9994 | 0.9994 | 0.9986 | 3144 |
| RPE | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 7 |
| Rod | 0.9999 | 0.9993 | 0.9999 | 0.9996 | 0.9996 | 0.9992 | 8388 |
| macro avg | 0.9996 | 0.9979 | 0.9999 | 0.9988 | 0.9989 | 0.9976 | |
| weighted avg | 0.9994 | 0.9994 | 0.9999 | 0.9994 | 0.9996 | 0.9992 | |
| Accuracy | 0.9994 | ||||||
| Balanced accuracy | 0.9979 |
[23]:
## Check model quality
df_warm_start_l2 = scparadise.scnoah.report_classif_full(
adata_test,
celltype='cell_type',
pred_celltype='pred_celltype_l2'
)
df_warm_start_l2
[23]:
| precision | recall/sensitivity | specificity | f1-score | geometric mean | index balanced accuracy | number of cells | |
|---|---|---|---|---|---|---|---|
| GABAergic amacrine cell | 0.9951 | 0.9975 | 0.9994 | 0.9963 | 0.9985 | 0.9967 | 2855 |
| H1 horizontal cell | 0.9944 | 0.9944 | 0.9999 | 0.9944 | 0.9972 | 0.9938 | 540 |
| H2 horizontal cell | 0.9684 | 0.9787 | 0.9999 | 0.9735 | 0.9892 | 0.9765 | 94 |
| Mueller cell | 0.9994 | 0.9994 | 1.0 | 0.9994 | 0.9997 | 0.9993 | 1744 |
| OFF midget ganglion cell | 0.9263 | 0.9569 | 0.9949 | 0.9414 | 0.9757 | 0.9484 | 1577 |
| OFF parasol ganglion cell | 0.9870 | 0.962 | 1.0 | 0.9744 | 0.9808 | 0.9583 | 79 |
| OFFx cell | 0.9836 | 0.9836 | 0.9999 | 0.9836 | 0.9917 | 0.9819 | 122 |
| ON midget ganglion cell | 0.9412 | 0.9522 | 0.997 | 0.9467 | 0.9744 | 0.9451 | 1193 |
| ON parasol ganglion cell | 1.0000 | 0.9796 | 1.0 | 0.9897 | 0.9897 | 0.9776 | 49 |
| ON-blue cone bipolar cell | 0.9524 | 0.8696 | 1.0 | 0.9091 | 0.9325 | 0.8582 | 23 |
| S cone cell | 0.8933 | 1.0 | 0.9997 | 0.9437 | 0.9998 | 0.9997 | 67 |
| amacrine cell | 0.9844 | 0.9822 | 0.9997 | 0.9833 | 0.9909 | 0.9802 | 450 |
| astrocyte | 1.0000 | 0.982 | 1.0 | 0.9909 | 0.991 | 0.9802 | 111 |
| diffuse bipolar 1 cell | 0.9900 | 0.995 | 0.9998 | 0.9925 | 0.9974 | 0.9943 | 397 |
| diffuse bipolar 2 cell | 0.9944 | 0.9925 | 0.9999 | 0.9935 | 0.9962 | 0.9917 | 536 |
| diffuse bipolar 3a cell | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 172 |
| diffuse bipolar 3b cell | 0.9964 | 0.9928 | 1.0 | 0.9946 | 0.9964 | 0.9921 | 278 |
| diffuse bipolar 4 cell | 0.9949 | 1.0 | 0.9999 | 0.9974 | 1.0 | 0.9999 | 387 |
| diffuse bipolar 6 cell | 0.9931 | 0.9795 | 1.0 | 0.9862 | 0.9897 | 0.9774 | 146 |
| flat midget bipolar cell | 0.9947 | 0.9982 | 0.9997 | 0.9965 | 0.999 | 0.9978 | 1134 |
| giant bipolar cell | 0.9801 | 0.9899 | 0.9998 | 0.985 | 0.9949 | 0.9888 | 199 |
| glycinergic amacrine cell | 0.9874 | 0.9864 | 0.9995 | 0.9869 | 0.9929 | 0.9846 | 1032 |
| invaginating midget bipolar cell | 0.9988 | 1.0 | 1.0 | 0.9994 | 1.0 | 1.0 | 835 |
| microglial cell | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 39 |
| retinal bipolar neuron | 0.9976 | 0.9927 | 1.0 | 0.9951 | 0.9963 | 0.992 | 412 |
| retinal cone cell | 1.0000 | 0.9914 | 1.0 | 0.9957 | 0.9957 | 0.9906 | 933 |
| retinal ganglion cell | 0.6444 | 0.4715 | 0.9974 | 0.5446 | 0.6858 | 0.4456 | 246 |
| retinal pigment epithelial cell | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | 7 |
| retinal rod cell | 0.9999 | 0.9994 | 0.9999 | 0.9996 | 0.9997 | 0.9993 | 8388 |
| rod bipolar cell | 0.9987 | 1.0 | 1.0 | 0.9994 | 1.0 | 1.0 | 796 |
| starburst amacrine cell | 0.9937 | 0.9874 | 1.0 | 0.9905 | 0.9937 | 0.9861 | 159 |
| macro avg | 0.9739 | 0.9682 | 0.9995 | 0.9704 | 0.9822 | 0.9657 | |
| weighted avg | 0.9859 | 0.9866 | 0.9994 | 0.9861 | 0.9925 | 0.9851 | |
| Accuracy | 0.9866 | ||||||
| Balanced accuracy | 0.9682 |
[24]:
pd.set_option('display.max_rows', 100)
df_l2.compare(df_warm_start_l2, keep_equal=True, align_axis = 0, result_names=('default', 'warm start'))
[24]:
| precision | recall/sensitivity | specificity | f1-score | geometric mean | index balanced accuracy | ||
|---|---|---|---|---|---|---|---|
| GABAergic amacrine cell | default | 0.9916 | 0.9982 | 0.9989 | 0.9949 | 0.9986 | 0.9971 |
| warm start | 0.9951 | 0.9975 | 0.9994 | 0.9963 | 0.9985 | 0.9967 | |
| H1 horizontal cell | default | 0.9836 | 0.9981 | 0.9996 | 0.9908 | 0.9989 | 0.9976 |
| warm start | 0.9944 | 0.9944 | 0.9999 | 0.9944 | 0.9972 | 0.9938 | |
| H2 horizontal cell | default | 0.9888 | 0.9362 | 1.0 | 0.9617 | 0.9675 | 0.9302 |
| warm start | 0.9684 | 0.9787 | 0.9999 | 0.9735 | 0.9892 | 0.9765 | |
| Mueller cell | default | 1.0000 | 0.9994 | 1.0 | 0.9997 | 0.9997 | 0.9994 |
| warm start | 0.9994 | 0.9994 | 1.0 | 0.9994 | 0.9997 | 0.9993 | |
| OFF midget ganglion cell | default | 0.9343 | 0.9112 | 0.9957 | 0.9226 | 0.9525 | 0.8996 |
| warm start | 0.9263 | 0.9569 | 0.9949 | 0.9414 | 0.9757 | 0.9484 | |
| OFF parasol ganglion cell | default | 0.9744 | 0.962 | 0.9999 | 0.9682 | 0.9808 | 0.9583 |
| warm start | 0.9870 | 0.962 | 1.0 | 0.9744 | 0.9808 | 0.9583 | |
| OFFx cell | default | 1.0000 | 0.9754 | 1.0 | 0.9876 | 0.9876 | 0.973 |
| warm start | 0.9836 | 0.9836 | 0.9999 | 0.9836 | 0.9917 | 0.9819 | |
| ON midget ganglion cell | default | 0.9411 | 0.9103 | 0.9971 | 0.9254 | 0.9527 | 0.8998 |
| warm start | 0.9412 | 0.9522 | 0.997 | 0.9467 | 0.9744 | 0.9451 | |
| ON parasol ganglion cell | default | 0.9796 | 0.9796 | 1.0 | 0.9796 | 0.9897 | 0.9776 |
| warm start | 1.0000 | 0.9796 | 1.0 | 0.9897 | 0.9897 | 0.9776 | |
| S cone cell | default | 0.9710 | 1.0 | 0.9999 | 0.9853 | 1.0 | 0.9999 |
| warm start | 0.8933 | 1.0 | 0.9997 | 0.9437 | 0.9998 | 0.9997 | |
| amacrine cell | default | 0.9907 | 0.9489 | 0.9998 | 0.9694 | 0.974 | 0.9439 |
| warm start | 0.9844 | 0.9822 | 0.9997 | 0.9833 | 0.9909 | 0.9802 | |
| diffuse bipolar 1 cell | default | 0.9875 | 0.9975 | 0.9998 | 0.9925 | 0.9986 | 0.997 |
| warm start | 0.9900 | 0.995 | 0.9998 | 0.9925 | 0.9974 | 0.9943 | |
| diffuse bipolar 2 cell | default | 0.9962 | 0.9869 | 0.9999 | 0.9916 | 0.9934 | 0.9856 |
| warm start | 0.9944 | 0.9925 | 0.9999 | 0.9935 | 0.9962 | 0.9917 | |
| diffuse bipolar 3a cell | default | 1.0000 | 0.9942 | 1.0 | 0.9971 | 0.9971 | 0.9936 |
| warm start | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | |
| diffuse bipolar 3b cell | default | 0.9964 | 0.9964 | 1.0 | 0.9964 | 0.9982 | 0.996 |
| warm start | 0.9964 | 0.9928 | 1.0 | 0.9946 | 0.9964 | 0.9921 | |
| diffuse bipolar 4 cell | default | 0.9948 | 0.9948 | 0.9999 | 0.9948 | 0.9974 | 0.9942 |
| warm start | 0.9949 | 1.0 | 0.9999 | 0.9974 | 1.0 | 0.9999 | |
| diffuse bipolar 6 cell | default | 0.9932 | 0.9932 | 1.0 | 0.9932 | 0.9965 | 0.9924 |
| warm start | 0.9931 | 0.9795 | 1.0 | 0.9862 | 0.9897 | 0.9774 | |
| flat midget bipolar cell | default | 0.9912 | 0.9974 | 0.9996 | 0.9943 | 0.9985 | 0.9967 |
| warm start | 0.9947 | 0.9982 | 0.9997 | 0.9965 | 0.999 | 0.9978 | |
| giant bipolar cell | default | 0.9899 | 0.9899 | 0.9999 | 0.9899 | 0.9949 | 0.9889 |
| warm start | 0.9801 | 0.9899 | 0.9998 | 0.985 | 0.9949 | 0.9888 | |
| glycinergic amacrine cell | default | 0.9836 | 0.9874 | 0.9993 | 0.9855 | 0.9933 | 0.9855 |
| warm start | 0.9874 | 0.9864 | 0.9995 | 0.9869 | 0.9929 | 0.9846 | |
| invaginating midget bipolar cell | default | 1.0000 | 0.9976 | 1.0 | 0.9988 | 0.9988 | 0.9974 |
| warm start | 0.9988 | 1.0 | 1.0 | 0.9994 | 1.0 | 1.0 | |
| microglial cell | default | 1.0000 | 0.9231 | 1.0 | 0.96 | 0.9608 | 0.916 |
| warm start | 1.0000 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 | |
| retinal bipolar neuron | default | 0.9903 | 0.9951 | 0.9998 | 0.9927 | 0.9975 | 0.9945 |
| warm start | 0.9976 | 0.9927 | 1.0 | 0.9951 | 0.9963 | 0.992 | |
| retinal cone cell | default | 1.0000 | 0.9957 | 1.0 | 0.9979 | 0.9979 | 0.9953 |
| warm start | 1.0000 | 0.9914 | 1.0 | 0.9957 | 0.9957 | 0.9906 | |
| retinal ganglion cell | default | 0.4695 | 0.626 | 0.993 | 0.5366 | 0.7884 | 0.5988 |
| warm start | 0.6444 | 0.4715 | 0.9974 | 0.5446 | 0.6858 | 0.4456 | |
| retinal rod cell | default | 0.9995 | 0.9999 | 0.9998 | 0.9997 | 0.9998 | 0.9997 |
| warm start | 0.9999 | 0.9994 | 0.9999 | 0.9996 | 0.9997 | 0.9993 | |
| rod bipolar cell | default | 0.9987 | 0.9962 | 1.0 | 0.9975 | 0.9981 | 0.9958 |
| warm start | 0.9987 | 1.0 | 1.0 | 0.9994 | 1.0 | 1.0 | |
| starburst amacrine cell | default | 1.0000 | 0.9811 | 1.0 | 0.9905 | 0.9905 | 0.9793 |
| warm start | 0.9937 | 0.9874 | 1.0 | 0.9905 | 0.9937 | 0.9861 | |
| macro avg | default | 0.9709 | 0.9653 | 0.9994 | 0.9676 | 0.9815 | 0.962 |
| warm start | 0.9739 | 0.9682 | 0.9995 | 0.9704 | 0.9822 | 0.9657 | |
| weighted avg | default | 0.9841 | 0.9826 | 0.9992 | 0.9832 | 0.9906 | 0.9804 |
| warm start | 0.9859 | 0.9866 | 0.9994 | 0.9861 | 0.9925 | 0.9851 | |
| Accuracy | default | 0.9826 | |||||
| warm start | 0.9866 | ||||||
| Balanced accuracy | default | 0.9653 | |||||
| warm start | 0.9682 |
Iterative warm start training led to an increase in all model quality metrics (rows - macro average, weighted average, accuracy, and balanced accuracy).
[25]:
import session_info2
session_info2.session_info()
[25]:
| Package | Version |
|---|---|
| pandas | 2.3.3 |
| scanpy | 1.11.5 |
| scparadise | 1.2.0 |
| numpy | 2.4.6 |
| anndata | 0.12.16 |
| Component | Info |
| Python | 3.11.15 (main, Jun 11 2026, 15:20:16) [GCC 14.3.0] |
| OS | Linux-6.8.0-138-generic-x86_64-with-glibc2.39 |
| Updated | 2026-09-04 00:04 |
Dependencies
| Dependency | Version |
|---|---|
| stack-data | 0.6.3 |
| tqdm | 4.68.2 |
| packaging | 26.0 |
| numcodecs | 0.16.5 |
| prompt_toolkit | 3.0.52 |
| ipykernel | 7.3.0 |
| wcwidth | 0.8.1 |
| fonttools | 4.63.0 |
| h5py | 3.16.0 |
| statsmodels | 0.14.6 |
| pillow | 12.2.0 |
| shap | 0.49.1 |
| python-dateutil | 2.9.0.post0 |
| typing_extensions | 4.15.0 |
| jupyter_client | 8.9.1 |
| comm | 0.2.3 |
| pyzmq | 27.1.0 |
| donfig | 0.8.1.post1 |
| ipython | 9.14.1 |
| scikit-learn | 1.9.0 |
| mpmath | 1.3.0 |
| cloudpickle | 3.1.2 |
| sparse | 0.18.0 |
| pure_eval | 0.2.3 |
| rich | 15.0.0 |
| triton | 3.7.0 |
| typing | 3.7.4.3 |
| executing | 2.2.1 |
| imbalanced-learn | 0.14.2 |
| debugpy | 1.8.21 |
| parso | 0.8.7 |
| Pygments | 2.20.0 |
| cuda-bindings | 13.3.1 |
| asttokens | 3.0.1 |
| matplotlib | 3.11.0 |
| natsort | 8.4.0 |
| pytorch-tabnet | 4.1.0 |
| leidenalg | 0.12.0 |
| PyYAML | 6.0.3 |
| scikit-image | 0.26.0 |
| decorator | 5.3.1 |
| charset-normalizer | 3.4.7 |
| muon | 0.1.7 |
| fsspec | 2026.4.0 |
| kiwisolver | 1.5.0 |
| optuna | 4.9.0 |
| cffi | 2.0.0 |
| cuda-pathfinder | 1.5.5 |
| torch | 2.12.0 (2.12.0+cu130) |
| legacy-api-wrap | 1.5 |
| dataclasses | 0.6 |
| session-info2 | 0.4.1 |
| sklearn-compat | 0.1.6 |
| numba | 0.65.1 |
| pynndescent | 0.6.0 |
| llvmlite | 0.47.0 |
| jedi | 0.20.0 |
| six | 1.17.0 |
| lazy-loader | 0.5 |
| pyarrow | 24.0.0 |
| pyparsing | 3.3.2 |
| google-crc32c | 1.8.0 |
| igraph | 1.0.0 |
| threadpoolctl | 3.6.0 |
| scverse-misc | 0.0.3 |
| jupyter_core | 5.9.1 |
| plottable | 0.1.5 |
| cycler | 0.12.1 |
| Cython | 3.3.0 |
| zarr | 3.1.6 |
| sympy | 1.14.0 |
| colorlog | 6.10.1 |
| patsy | 1.0.2 |
| psutil | 7.2.2 |
| opt_einsum | 3.4.0 |
| pycparser | 3.0 (3.00) |
| joblib | 1.5.3 |
| umap-learn | 0.5.12 |
| xarray | 2026.4.0 |
| pathlib | 1.0.1 |
| tornado | 6.5.7 |
| platformdirs | 4.10.0 |
| defusedxml | 0.7.1 |
| mudata | 0.3.8 |
| matplotlib-inline | 0.2.2 |
| argparse | 1.4.0 (1.1) |
| texttable | 1.7.0 |
| pytz | 2026.2 |
| seaborn | 0.13.2 |
| narwhals | 2.22.1 |
| setuptools | 81.0.0 |
| traitlets | 5.15.1 |
| scipy | 1.17.1 |
| slicer | 0.0.8 |
Copyable Markdown
| Package | Version | | ---------- | ------- | | pandas | 2.3.3 | | scanpy | 1.11.5 | | scparadise | 1.2.0 | | numpy | 2.4.6 | | anndata | 0.12.16 | | Dependency | Version | | ------------------ | --------------------- | | stack-data | 0.6.3 | | tqdm | 4.68.2 | | packaging | 26.0 | | numcodecs | 0.16.5 | | prompt_toolkit | 3.0.52 | | ipykernel | 7.3.0 | | wcwidth | 0.8.1 | | fonttools | 4.63.0 | | h5py | 3.16.0 | | statsmodels | 0.14.6 | | pillow | 12.2.0 | | shap | 0.49.1 | | python-dateutil | 2.9.0.post0 | | typing_extensions | 4.15.0 | | jupyter_client | 8.9.1 | | comm | 0.2.3 | | pyzmq | 27.1.0 | | donfig | 0.8.1.post1 | | ipython | 9.14.1 | | scikit-learn | 1.9.0 | | mpmath | 1.3.0 | | cloudpickle | 3.1.2 | | sparse | 0.18.0 | | pure_eval | 0.2.3 | | rich | 15.0.0 | | triton | 3.7.0 | | typing | 3.7.4.3 | | executing | 2.2.1 | | imbalanced-learn | 0.14.2 | | debugpy | 1.8.21 | | parso | 0.8.7 | | Pygments | 2.20.0 | | cuda-bindings | 13.3.1 | | asttokens | 3.0.1 | | matplotlib | 3.11.0 | | natsort | 8.4.0 | | pytorch-tabnet | 4.1.0 | | leidenalg | 0.12.0 | | PyYAML | 6.0.3 | | scikit-image | 0.26.0 | | decorator | 5.3.1 | | charset-normalizer | 3.4.7 | | muon | 0.1.7 | | fsspec | 2026.4.0 | | kiwisolver | 1.5.0 | | optuna | 4.9.0 | | cffi | 2.0.0 | | cuda-pathfinder | 1.5.5 | | torch | 2.12.0 (2.12.0+cu130) | | legacy-api-wrap | 1.5 | | dataclasses | 0.6 | | session-info2 | 0.4.1 | | sklearn-compat | 0.1.6 | | numba | 0.65.1 | | pynndescent | 0.6.0 | | llvmlite | 0.47.0 | | jedi | 0.20.0 | | six | 1.17.0 | | lazy-loader | 0.5 | | pyarrow | 24.0.0 | | pyparsing | 3.3.2 | | google-crc32c | 1.8.0 | | igraph | 1.0.0 | | threadpoolctl | 3.6.0 | | scverse-misc | 0.0.3 | | jupyter_core | 5.9.1 | | plottable | 0.1.5 | | cycler | 0.12.1 | | Cython | 3.3.0 | | zarr | 3.1.6 | | sympy | 1.14.0 | | colorlog | 6.10.1 | | patsy | 1.0.2 | | psutil | 7.2.2 | | opt_einsum | 3.4.0 | | pycparser | 3.0 (3.00) | | joblib | 1.5.3 | | umap-learn | 0.5.12 | | xarray | 2026.4.0 | | pathlib | 1.0.1 | | tornado | 6.5.7 | | platformdirs | 4.10.0 | | defusedxml | 0.7.1 | | mudata | 0.3.8 | | matplotlib-inline | 0.2.2 | | argparse | 1.4.0 (1.1) | | texttable | 1.7.0 | | pytz | 2026.2 | | seaborn | 0.13.2 | | narwhals | 2.22.1 | | setuptools | 81.0.0 | | traitlets | 5.15.1 | | scipy | 1.17.1 | | slicer | 0.0.8 | | Component | Info | | --------- | -------------------------------------------------- | | Python | 3.11.15 (main, Jun 11 2026, 15:20:16) [GCC 14.3.0] | | OS | Linux-6.8.0-138-generic-x86_64-with-glibc2.39 | | Updated | 2026-09-04 00:04 |