diff --git a/tuning/autotune_new_model.ipynb b/tuning/autotune_new_model.ipynb
index c9875b73..b1458d6f 100644
--- a/tuning/autotune_new_model.ipynb
+++ b/tuning/autotune_new_model.ipynb
@@ -42,33 +42,30 @@
"## Installing required packages"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Uncomment the following lines in Google Colab in order to install `scvi-tools`:"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
- "!pip install --quiet hyperopt\n",
- "!pip install --quiet \"ray[tune]\"\n",
- "!pip install --quiet scvi-colab\n",
- "from scvi_colab import install\n",
+ "# !pip install --quiet scvi-colab\n",
+ "# from scvi_colab import install\n",
"\n",
- "install()"
+ "# install()"
]
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 2,
"metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Global seed set to 0\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"import jax\n",
"import jax.numpy as jnp\n",
@@ -81,9 +78,17 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Last run with scvi-tools version: 1.0.3\n"
+ ]
+ }
+ ],
"source": [
"scvi.settings.seed = 0\n",
"print(\"Last run with scvi-tools version:\", scvi.__version__)"
@@ -107,7 +112,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@@ -176,7 +181,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -217,15 +222,15 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
- "/home/martin/dev/scvi-tools/scvi/autotune/_manager.py:74: UserWarning: No default search space available for LassoTunable.\n",
- " warnings.warn(\n"
+ "/home/martin/dev/scvi-tools/scvi/autotune/_manager.py:60: UserWarning: No default search space available for LassoTunable.\n",
+ " self._defaults = self._get_defaults(self._model_cls)\n"
]
},
{
@@ -334,7 +339,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@@ -385,7 +390,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
@@ -496,7 +501,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
@@ -526,15 +531,15 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
- "/home/martin/dev/scvi-tools/scvi/autotune/_manager.py:74: UserWarning: No default search space available for LassoModel.\n",
- " warnings.warn(\n"
+ "/home/martin/dev/scvi-tools/scvi/autotune/_manager.py:60: UserWarning: No default search space available for LassoModel.\n",
+ " self._defaults = self._get_defaults(self._model_cls)\n"
]
},
{
@@ -649,7 +654,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.8"
+ "version": "3.11.4"
},
"orig_nbformat": 4,
"vscode": {
diff --git a/tuning/autotune_scvi.ipynb b/tuning/autotune_scvi.ipynb
index c53912ea..10c2f6eb 100644
--- a/tuning/autotune_scvi.ipynb
+++ b/tuning/autotune_scvi.ipynb
@@ -1,615 +1,661 @@
{
- "cells": [
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Model hyperparameter tuning with scVI"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "```{warning}\n",
- "`scvi.autotune` development is still in progress. The API is subject to change.\n",
- "```"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Finding an effective set of model hyperparameters (e.g. learning rate, number of hidden layers, etc.) is an important component in training a model as its performance can be highly dependent on these non-trainable parameters. Manually tuning a model often involves picking a set of hyperparameters to search over and then evaluating different configurations over a validation set for a desired metric. This process can be time consuming and can require some prior intuition about a model and dataset pair, which is not always feasible.\n",
- "\n",
- "In this tutorial, we show how to use `scvi`'s [`autotune`](https://docs.scvi-tools.org/en/latest/api/user.html#model-hyperparameter-autotuning) module, which allows us to automatically find a good set of model hyperparameters using [Ray Tune](https://docs.ray.io/en/latest/tune/index.html). We will use `SCVI` and a subsample of the [heart cell atlas](https://www.heartcellatlas.org/#DataSources) for the task of batch correction, but the principles outlined here can be applied to any model and dataset. In particular, we will go through the following steps:\n",
- "\n",
- "1. Installing required packages\n",
- "1. Loading and preprocessing the dataset\n",
- "1. Defining the tuner and discovering hyperparameters\n",
- "1. Running the tuner\n",
- "1. Comparing latent spaces\n",
- "1. Optional: Monitoring progress with Tensorboard\n",
- "1. Optional: Tuning over integration metrics with `scib-metrics`"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Installing required packages"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install --quiet hyperopt\n",
- "!pip install --quiet \"ray[tune]\"\n",
- "!pip install --quiet scvi-colab\n",
- "from scvi_colab import install\n",
- "\n",
- "install()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Global seed set to 0\n"
- ]
- }
- ],
- "source": [
- "import ray\n",
- "import scanpy as sc\n",
- "import scvi\n",
- "from ray import tune\n",
- "from scvi import autotune"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "scvi.settings.seed = 0\n",
- "print(\"Last run with scvi-tools version:\", scvi.__version__)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Loading and preprocessing the dataset"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\u001b[34mINFO \u001b[0m File data/hca_subsampled_20k.h5ad already downloaded \n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "AnnData object with n_obs × n_vars = 18641 × 26662\n",
- " obs: 'NRP', 'age_group', 'cell_source', 'cell_type', 'donor', 'gender', 'n_counts', 'n_genes', 'percent_mito', 'percent_ribo', 'region', 'sample', 'scrublet_score', 'source', 'type', 'version', 'cell_states', 'Used'\n",
- " var: 'gene_ids-Harvard-Nuclei', 'feature_types-Harvard-Nuclei', 'gene_ids-Sanger-Nuclei', 'feature_types-Sanger-Nuclei', 'gene_ids-Sanger-Cells', 'feature_types-Sanger-Cells', 'gene_ids-Sanger-CD45', 'feature_types-Sanger-CD45', 'n_counts'\n",
- " uns: 'cell_type_colors'"
- ]
- },
- "execution_count": 2,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "adata = scvi.data.heart_cell_atlas_subsampled()\n",
- "adata"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The only preprocessing step we will perform in this case will be to subsample the dataset for 2000 highly variable genes using `scanpy` for faster model training."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "AnnData object with n_obs × n_vars = 18641 × 2000\n",
- " obs: 'NRP', 'age_group', 'cell_source', 'cell_type', 'donor', 'gender', 'n_counts', 'n_genes', 'percent_mito', 'percent_ribo', 'region', 'sample', 'scrublet_score', 'source', 'type', 'version', 'cell_states', 'Used'\n",
- " var: 'gene_ids-Harvard-Nuclei', 'feature_types-Harvard-Nuclei', 'gene_ids-Sanger-Nuclei', 'feature_types-Sanger-Nuclei', 'gene_ids-Sanger-Cells', 'feature_types-Sanger-Cells', 'gene_ids-Sanger-CD45', 'feature_types-Sanger-CD45', 'n_counts', 'highly_variable', 'highly_variable_rank', 'means', 'variances', 'variances_norm'\n",
- " uns: 'cell_type_colors', 'hvg'"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "sc.pp.highly_variable_genes(adata, n_top_genes=2000, flavor=\"seurat_v3\", subset=True)\n",
- "adata"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Defining the tuner and discovering hyperparameters"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The first part of our workflow is the same as the standard `scvi-tools` workflow: we start with our desired model class, and we register our dataset with it using `setup_anndata`. All datasets must be registered prior to hyperparameter tuning."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "model_cls = scvi.model.SCVI\n",
- "model_cls.setup_anndata(adata)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Our main entry point to the `autotune` module is the `ModelTuner` class, a wrapper around [`ray.tune.Tuner`](https://docs.ray.io/en/latest/tune/api_docs/execution.html#tuner) with additional functionality specific to `scvi-tools`. We can define a new `ModelTuner` by providing it with our model class."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "scvi_tuner = autotune.ModelTuner(model_cls)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "`ModelTuner` will register all tunable hyperparameters in `SCVI` -- these can be viewed by calling `info()`. By default, this method will display three tables:\n",
- "\n",
- "1. **Tunable hyperparameters**: The names of hyperparameters that can be tuned, their default values, and the internal classes they are defined in.\n",
- "1. **Available metrics**: The metrics that can be used to evaluate the performance of the model. One of these must be provided when running the tuner.\n",
- "1. **Default search space**: The default search space for the model class, which will be used if no search space is provided by the user."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "
ModelTuner registry for SCVI\n",
- "
\n"
- ],
- "text/plain": [
- "ModelTuner registry for SCVI\n"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "text/html": [
- " Tunable hyperparameters \n",
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
- "┃ Hyperparameter ┃ Default value ┃ Source ┃\n",
- "┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
- "│ n_hidden │ 128 │ VAE │\n",
- "│ n_latent │ 10 │ VAE │\n",
- "│ n_layers │ 1 │ VAE │\n",
- "│ dropout_rate │ 0.1 │ VAE │\n",
- "│ dispersion │ gene │ VAE │\n",
- "│ gene_likelihood │ zinb │ VAE │\n",
- "│ latent_distribution │ normal │ VAE │\n",
- "│ encode_covariates │ False │ VAE │\n",
- "│ deeply_inject_covariates │ True │ VAE │\n",
- "│ use_batch_norm │ both │ VAE │\n",
- "│ use_layer_norm │ none │ VAE │\n",
- "│ optimizer │ Adam │ TrainingPlan │\n",
- "│ lr │ 0.001 │ TrainingPlan │\n",
- "│ weight_decay │ 1e-06 │ TrainingPlan │\n",
- "│ eps │ 0.01 │ TrainingPlan │\n",
- "│ n_steps_kl_warmup │ None │ TrainingPlan │\n",
- "│ n_epochs_kl_warmup │ 400 │ TrainingPlan │\n",
- "│ reduce_lr_on_plateau │ False │ TrainingPlan │\n",
- "│ lr_factor │ 0.6 │ TrainingPlan │\n",
- "│ lr_patience │ 30 │ TrainingPlan │\n",
- "│ lr_threshold │ 0.0 │ TrainingPlan │\n",
- "│ lr_min │ 0 │ TrainingPlan │\n",
- "│ max_kl_weight │ 1.0 │ TrainingPlan │\n",
- "│ min_kl_weight │ 0.0 │ TrainingPlan │\n",
- "└──────────────────────────┴───────────────┴──────────────┘\n",
- "
\n"
- ],
- "text/plain": [
- "\u001b[3m Tunable hyperparameters \u001b[0m\n",
- "┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
- "┃\u001b[1m \u001b[0m\u001b[1m Hyperparameter \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mDefault value\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Source \u001b[0m\u001b[1m \u001b[0m┃\n",
- "┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_hidden \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 128 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_latent \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 10 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_layers \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 1 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m dropout_rate \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.1 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m dispersion \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m gene \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m gene_likelihood \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m zinb \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m latent_distribution \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m normal \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m encode_covariates \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m False \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33mdeeply_inject_covariates\u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m True \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m use_batch_norm \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m both \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m use_layer_norm \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m none \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m optimizer \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m Adam \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.001 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m weight_decay \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 1e-06 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m eps \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.01 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_steps_kl_warmup \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m None \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_epochs_kl_warmup \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 400 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m reduce_lr_on_plateau \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m False \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_factor \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.6 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_patience \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 30 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_threshold \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_min \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m max_kl_weight \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 1.0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m min_kl_weight \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
- "└──────────────────────────┴───────────────┴──────────────┘\n"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "text/html": [
- " Available metrics \n",
- "┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
- "┃ Metric ┃ Mode ┃\n",
- "┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
- "│ validation_loss │ min │\n",
- "└─────────────────┴────────────┘\n",
- "
\n"
- ],
- "text/plain": [
- "\u001b[3m Available metrics \u001b[0m\n",
- "┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
- "┃\u001b[1m \u001b[0m\u001b[1m Metric \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Mode \u001b[0m\u001b[1m \u001b[0m┃\n",
- "┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33mvalidation_loss\u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m min \u001b[0m\u001b[38;5;128m \u001b[0m│\n",
- "└─────────────────┴────────────┘\n"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "text/html": [
- " Default search space \n",
- "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n",
- "┃ Hyperparameter ┃ Sample function ┃ Arguments ┃ Keyword arguments ┃\n",
- "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n",
- "│ n_hidden │ choice │ [[64, 128]] │ {} │\n",
- "└────────────────┴─────────────────┴─────────────┴───────────────────┘\n",
- "\n"
- ],
- "text/plain": [
- "\u001b[3m Default search space \u001b[0m\n",
- "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n",
- "┃\u001b[1m \u001b[0m\u001b[1mHyperparameter\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mSample function\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Arguments \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mKeyword arguments\u001b[0m\u001b[1m \u001b[0m┃\n",
- "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n",
- "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_hidden \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m choice \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m[[64, 128]]\u001b[0m\u001b[32m \u001b[0m│\u001b[38;5;208m \u001b[0m\u001b[38;5;208m {} \u001b[0m\u001b[38;5;208m \u001b[0m│\n",
- "└────────────────┴─────────────────┴─────────────┴───────────────────┘\n"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "scvi_tuner.info()"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Running the tuner"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now that we know what hyperparameters are available to us, we can define a search space using the [search space API](https://docs.ray.io/en/latest/tune/api/search_space.html) in `ray.tune`. For this tutorial, we choose a simple search space with two model hyperparameters and one training plan hyperparameter. These can all be combined into a single dictionary that we pass into the `fit` method."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "search_space = {\n",
- " \"n_hidden\": tune.choice([64, 128, 256]),\n",
- " \"n_layers\": tune.choice([1, 2, 3]),\n",
- " \"lr\": tune.loguniform(1e-4, 1e-2),\n",
- "}"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "There are a couple more arguments we should be aware of before fitting the tuner:\n",
- "\n",
- "- `num_samples`: The total number of hyperparameter sets to sample from `search_space`. This is the total number of models that will be trained.\n",
- "\n",
- " For example, if we set `num_samples=2`, we might sample two models with the following hyperparameter configurations:\n",
- "\n",
- " ```python\n",
- " model1 = {\n",
- " \"n_hidden\": 64,\n",
- " \"n_layers\": 1,\n",
- " \"lr\": 0.001,\n",
- " }\n",
- " model2 = {\n",
- " \"n_hidden\": 64,\n",
- " \"n_layers\": 3,\n",
- " \"lr\": 0.0001,\n",
- " }\n",
- " ```\n",
- "\n",
- "- `max_epochs`: The maximum number of epochs to train each model for.\n",
- "\n",
- " Note: This does not mean that each model will be trained for `max_epochs`. Depending on the scheduler used, some trials are likely to be stopped early.\n",
- "\n",
- "- `resources`: A dictionary of maximum resources to allocate for the whole experiment. This allows us to run concurrent trials on limited hardware.\n",
- "\n",
- "Now, we can call `fit` on the tuner to start the hyperparameter sweep. This will return a `TuneAnalysis` dataclass, which will contain the best set of hyperparameters, as well as other information."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "
\n",
- "
\n",
- "
Tune Status
\n",
- "
\n",
- "\n",
- "| Current time: | 2023-01-30 13:44:04 |
\n",
- "| Running for: | 00:03:01.04 |
\n",
- "| Memory: | 10.9/125.7 GiB |
\n",
- "\n",
- "
\n",
- "
\n",
- "
\n",
- "
\n",
- "
System Info
\n",
- " Using AsyncHyperBand: num_stopped=5
Bracket: Iter 64.000: -466.8729553222656 | Iter 32.000: -467.76768493652344 | Iter 16.000: -473.09934997558594 | Iter 8.000: -481.9038391113281 | Iter 4.000: -493.9166717529297 | Iter 2.000: -516.9609985351562 | Iter 1.000: -559.62353515625
Resources requested: 0/20 CPUs, 0/1 GPUs, 0.0/74.12 GiB heap, 0.0/35.76 GiB objects (0.0/1.0 accelerator_type:G)\n",
- " \n",
- " \n",
- "
\n",
- "
\n",
- "
\n",
- "
Trial Status
\n",
- "
\n",
- "\n",
- "| Trial name | status | loc | n_hidden | n_layers | lr | validation_loss |
\n",
- "\n",
- "\n",
- "| _trainable_cb47e_00000 | TERMINATED | 128.32.142.133:515229 | 128 | 1 | 0.000109758 | 468.995 |
\n",
- "| _trainable_cb47e_00001 | TERMINATED | 128.32.142.133:515229 | 256 | 1 | 0.00906226 | 469.203 |
\n",
- "| _trainable_cb47e_00002 | TERMINATED | 128.32.142.133:515229 | 128 | 3 | 0.00276226 | 616.49 |
\n",
- "| _trainable_cb47e_00003 | TERMINATED | 128.32.142.133:515229 | 128 | 1 | 0.00110585 | 516.961 |
\n",
- "| _trainable_cb47e_00004 | TERMINATED | 128.32.142.133:515229 | 256 | 3 | 0.000817148 | 595.537 |
\n",
- "\n",
- "
\n",
- "
\n",
- "
\n",
- "\n"
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "2023-01-30 13:44:04,458\tINFO tune.py:762 -- Total run time: 181.26 seconds (181.03 seconds for the tuning loop).\n"
- ]
- }
- ],
- "source": [
- "ray.init(log_to_driver=False)\n",
- "results = scvi_tuner.fit(\n",
- " adata,\n",
- " metric=\"validation_loss\",\n",
- " search_space=search_space,\n",
- " num_samples=5,\n",
- " max_epochs=100,\n",
- " resources={\"cpu\": 20, \"gpu\": 1},\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'n_hidden': 128, 'n_layers': 1}\n",
- "{'plan_kwargs': {}}\n"
- ]
- }
- ],
- "source": [
- "print(results.model_kwargs)\n",
- "print(results.train_kwargs)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Comparing latent spaces"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Work in progress: please check back in the next release!"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Optional: Monitoring progress with Tensorboard"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Work in progress: please check back in the next release!"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Optional: Tuning over integration metrics with `scib-metrics`"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Work in progress: please check back in the next release!"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "scvi-gpu",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.10.8"
- },
- "orig_nbformat": 4,
- "vscode": {
- "interpreter": {
- "hash": "2f978838050607ec9770689d8200902a4128a2ce208b502e911dd714d57e924e"
- }
- }
+ "cells": [
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Model hyperparameter tuning with scVI"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```{warning}\n",
+ "`scvi.autotune` development is still in progress. The API is subject to change.\n",
+ "```"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finding an effective set of model hyperparameters (e.g. learning rate, number of hidden layers, etc.) is an important component in training a model as its performance can be highly dependent on these non-trainable parameters. Manually tuning a model often involves picking a set of hyperparameters to search over and then evaluating different configurations over a validation set for a desired metric. This process can be time consuming and can require some prior intuition about a model and dataset pair, which is not always feasible.\n",
+ "\n",
+ "In this tutorial, we show how to use `scvi`'s [`autotune`](https://docs.scvi-tools.org/en/latest/api/user.html#model-hyperparameter-autotuning) module, which allows us to automatically find a good set of model hyperparameters using [Ray Tune](https://docs.ray.io/en/latest/tune/index.html). We will use `SCVI` and a subsample of the [heart cell atlas](https://www.heartcellatlas.org/#DataSources) for the task of batch correction, but the principles outlined here can be applied to any model and dataset. In particular, we will go through the following steps:\n",
+ "\n",
+ "1. Installing required packages\n",
+ "1. Loading and preprocessing the dataset\n",
+ "1. Defining the tuner and discovering hyperparameters\n",
+ "1. Running the tuner\n",
+ "1. Comparing latent spaces\n",
+ "1. Optional: Monitoring progress with Tensorboard\n",
+ "1. Optional: Tuning over integration metrics with `scib-metrics`"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Installing required packages"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Uncomment the following lines in Google Colab in order to install `scvi-tools`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# !pip install --quiet scvi-colab\n",
+ "# from scvi_colab import install\n",
+ "\n",
+ "# install()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import tempfile\n",
+ "\n",
+ "import ray\n",
+ "import scanpy as sc\n",
+ "import scvi\n",
+ "import torch\n",
+ "from ray import tune\n",
+ "from scvi import autotune"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Global seed set to 0\n"
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 2
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Last run with scvi-tools version: 1.0.3\n"
+ ]
+ }
+ ],
+ "source": [
+ "scvi.settings.seed = 0\n",
+ "print(\"Last run with scvi-tools version:\", scvi.__version__)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can modify `save_dir` below to change where the data files for this tutorial are saved."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sc.set_figure_params(figsize=(4, 4), frameon=False)\n",
+ "torch.set_float32_matmul_precision(\"high\")\n",
+ "save_dir = tempfile.TemporaryDirectory()\n",
+ "\n",
+ "%config InlineBackend.print_figure_kwargs={\"facecolor\" : \"w\"}\n",
+ "%config InlineBackend.figure_format=\"retina\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Loading and preprocessing the dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[34mINFO \u001b[0m Downloading file at \u001b[35m/tmp/tmp51dvndw4/\u001b[0m\u001b[95mhca_subsampled_20k.h5ad\u001b[0m \n",
+ "Downloading...: 100%|██████████| 65714/65714.0 [00:00<00:00, 110628.59it/s]\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "AnnData object with n_obs × n_vars = 18641 × 26662\n",
+ " obs: 'NRP', 'age_group', 'cell_source', 'cell_type', 'donor', 'gender', 'n_counts', 'n_genes', 'percent_mito', 'percent_ribo', 'region', 'sample', 'scrublet_score', 'source', 'type', 'version', 'cell_states', 'Used'\n",
+ " var: 'gene_ids-Harvard-Nuclei', 'feature_types-Harvard-Nuclei', 'gene_ids-Sanger-Nuclei', 'feature_types-Sanger-Nuclei', 'gene_ids-Sanger-Cells', 'feature_types-Sanger-Cells', 'gene_ids-Sanger-CD45', 'feature_types-Sanger-CD45', 'n_counts'\n",
+ " uns: 'cell_type_colors'"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "adata = scvi.data.heart_cell_atlas_subsampled(save_path=save_dir.name)\n",
+ "adata"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The only preprocessing step we will perform in this case will be to subsample the dataset for 2000 highly variable genes using `scanpy` for faster model training."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "AnnData object with n_obs × n_vars = 18641 × 2000\n",
+ " obs: 'NRP', 'age_group', 'cell_source', 'cell_type', 'donor', 'gender', 'n_counts', 'n_genes', 'percent_mito', 'percent_ribo', 'region', 'sample', 'scrublet_score', 'source', 'type', 'version', 'cell_states', 'Used'\n",
+ " var: 'gene_ids-Harvard-Nuclei', 'feature_types-Harvard-Nuclei', 'gene_ids-Sanger-Nuclei', 'feature_types-Sanger-Nuclei', 'gene_ids-Sanger-Cells', 'feature_types-Sanger-Cells', 'gene_ids-Sanger-CD45', 'feature_types-Sanger-CD45', 'n_counts', 'highly_variable', 'highly_variable_rank', 'means', 'variances', 'variances_norm'\n",
+ " uns: 'cell_type_colors', 'hvg'"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sc.pp.highly_variable_genes(adata, n_top_genes=2000, flavor=\"seurat_v3\", subset=True)\n",
+ "adata"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Defining the tuner and discovering hyperparameters"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The first part of our workflow is the same as the standard `scvi-tools` workflow: we start with our desired model class, and we register our dataset with it using `setup_anndata`. All datasets must be registered prior to hyperparameter tuning."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model_cls = scvi.model.SCVI\n",
+ "model_cls.setup_anndata(adata)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Our main entry point to the `autotune` module is the `ModelTuner` class, a wrapper around [`ray.tune.Tuner`](https://docs.ray.io/en/latest/tune/api_docs/execution.html#tuner) with additional functionality specific to `scvi-tools`. We can define a new `ModelTuner` by providing it with our model class."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "scvi_tuner = autotune.ModelTuner(model_cls)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`ModelTuner` will register all tunable hyperparameters in `SCVI` -- these can be viewed by calling `info()`. By default, this method will display three tables:\n",
+ "\n",
+ "1. **Tunable hyperparameters**: The names of hyperparameters that can be tuned, their default values, and the internal classes they are defined in.\n",
+ "1. **Available metrics**: The metrics that can be used to evaluate the performance of the model. One of these must be provided when running the tuner.\n",
+ "1. **Default search space**: The default search space for the model class, which will be used if no search space is provided by the user."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "ModelTuner registry for SCVI\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "ModelTuner registry for SCVI\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ " Tunable hyperparameters \n",
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
+ "┃ Hyperparameter ┃ Default value ┃ Source ┃\n",
+ "┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
+ "│ n_hidden │ 128 │ VAE │\n",
+ "│ n_latent │ 10 │ VAE │\n",
+ "│ n_layers │ 1 │ VAE │\n",
+ "│ dropout_rate │ 0.1 │ VAE │\n",
+ "│ dispersion │ gene │ VAE │\n",
+ "│ log_variational │ True │ VAE │\n",
+ "│ gene_likelihood │ zinb │ VAE │\n",
+ "│ latent_distribution │ normal │ VAE │\n",
+ "│ encode_covariates │ False │ VAE │\n",
+ "│ deeply_inject_covariates │ True │ VAE │\n",
+ "│ use_batch_norm │ both │ VAE │\n",
+ "│ use_layer_norm │ none │ VAE │\n",
+ "│ use_observed_lib_size │ True │ VAE │\n",
+ "│ var_activation │ None │ VAE │\n",
+ "│ optimizer │ Adam │ TrainingPlan │\n",
+ "│ lr │ 0.001 │ TrainingPlan │\n",
+ "│ weight_decay │ 1e-06 │ TrainingPlan │\n",
+ "│ eps │ 0.01 │ TrainingPlan │\n",
+ "│ n_steps_kl_warmup │ None │ TrainingPlan │\n",
+ "│ n_epochs_kl_warmup │ 400 │ TrainingPlan │\n",
+ "│ reduce_lr_on_plateau │ False │ TrainingPlan │\n",
+ "│ lr_factor │ 0.6 │ TrainingPlan │\n",
+ "│ lr_patience │ 30 │ TrainingPlan │\n",
+ "│ lr_threshold │ 0.0 │ TrainingPlan │\n",
+ "│ lr_min │ 0 │ TrainingPlan │\n",
+ "│ max_kl_weight │ 1.0 │ TrainingPlan │\n",
+ "│ min_kl_weight │ 0.0 │ TrainingPlan │\n",
+ "└──────────────────────────┴───────────────┴──────────────┘\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "\u001b[3m Tunable hyperparameters \u001b[0m\n",
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n",
+ "┃\u001b[1m \u001b[0m\u001b[1m Hyperparameter \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mDefault value\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Source \u001b[0m\u001b[1m \u001b[0m┃\n",
+ "┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_hidden \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 128 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_latent \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 10 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_layers \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 1 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m dropout_rate \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.1 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m dispersion \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m gene \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m log_variational \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m True \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m gene_likelihood \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m zinb \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m latent_distribution \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m normal \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m encode_covariates \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m False \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33mdeeply_inject_covariates\u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m True \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m use_batch_norm \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m both \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m use_layer_norm \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m none \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m use_observed_lib_size \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m True \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m var_activation \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m None \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m VAE \u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m optimizer \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m Adam \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.001 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m weight_decay \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 1e-06 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m eps \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.01 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_steps_kl_warmup \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m None \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_epochs_kl_warmup \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 400 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m reduce_lr_on_plateau \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m False \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_factor \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.6 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_patience \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 30 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_threshold \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m lr_min \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m max_kl_weight \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 1.0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m min_kl_weight \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m 0.0 \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32mTrainingPlan\u001b[0m\u001b[32m \u001b[0m│\n",
+ "└──────────────────────────┴───────────────┴──────────────┘\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ " Available metrics \n",
+ "┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
+ "┃ Metric ┃ Mode ┃\n",
+ "┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
+ "│ validation_loss │ min │\n",
+ "└─────────────────┴────────────┘\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "\u001b[3m Available metrics \u001b[0m\n",
+ "┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
+ "┃\u001b[1m \u001b[0m\u001b[1m Metric \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Mode \u001b[0m\u001b[1m \u001b[0m┃\n",
+ "┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33mvalidation_loss\u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m min \u001b[0m\u001b[38;5;128m \u001b[0m│\n",
+ "└─────────────────┴────────────┘\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ " Default search space \n",
+ "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃ Hyperparameter ┃ Sample function ┃ Arguments ┃ Keyword arguments ┃\n",
+ "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n",
+ "│ n_hidden │ choice │ [[64, 128]] │ {} │\n",
+ "└────────────────┴─────────────────┴─────────────┴───────────────────┘\n",
+ "\n"
+ ],
+ "text/plain": [
+ "\u001b[3m Default search space \u001b[0m\n",
+ "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n",
+ "┃\u001b[1m \u001b[0m\u001b[1mHyperparameter\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mSample function\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Arguments \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mKeyword arguments\u001b[0m\u001b[1m \u001b[0m┃\n",
+ "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n",
+ "│\u001b[38;5;33m \u001b[0m\u001b[38;5;33m n_hidden \u001b[0m\u001b[38;5;33m \u001b[0m│\u001b[38;5;128m \u001b[0m\u001b[38;5;128m choice \u001b[0m\u001b[38;5;128m \u001b[0m│\u001b[32m \u001b[0m\u001b[32m[[64, 128]]\u001b[0m\u001b[32m \u001b[0m│\u001b[38;5;208m \u001b[0m\u001b[38;5;208m {} \u001b[0m\u001b[38;5;208m \u001b[0m│\n",
+ "└────────────────┴─────────────────┴─────────────┴───────────────────┘\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "scvi_tuner.info()"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Running the tuner"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that we know what hyperparameters are available to us, we can define a search space using the [search space API](https://docs.ray.io/en/latest/tune/api/search_space.html) in `ray.tune`. For this tutorial, we choose a simple search space with two model hyperparameters and one training plan hyperparameter. These can all be combined into a single dictionary that we pass into the `fit` method."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "search_space = {\n",
+ " \"n_hidden\": tune.choice([64, 128, 256]),\n",
+ " \"n_layers\": tune.choice([1, 2, 3]),\n",
+ " \"lr\": tune.loguniform(1e-4, 1e-2),\n",
+ "}"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are a couple more arguments we should be aware of before fitting the tuner:\n",
+ "\n",
+ "- `num_samples`: The total number of hyperparameter sets to sample from `search_space`. This is the total number of models that will be trained.\n",
+ "\n",
+ " For example, if we set `num_samples=2`, we might sample two models with the following hyperparameter configurations:\n",
+ "\n",
+ " ```python\n",
+ " model1 = {\n",
+ " \"n_hidden\": 64,\n",
+ " \"n_layers\": 1,\n",
+ " \"lr\": 0.001,\n",
+ " }\n",
+ " model2 = {\n",
+ " \"n_hidden\": 64,\n",
+ " \"n_layers\": 3,\n",
+ " \"lr\": 0.0001,\n",
+ " }\n",
+ " ```\n",
+ "\n",
+ "- `max_epochs`: The maximum number of epochs to train each model for.\n",
+ "\n",
+ " Note: This does not mean that each model will be trained for `max_epochs`. Depending on the scheduler used, some trials are likely to be stopped early.\n",
+ "\n",
+ "- `resources`: A dictionary of maximum resources to allocate for the whole experiment. This allows us to run concurrent trials on limited hardware.\n",
+ "\n",
+ "Now, we can call `fit` on the tuner to start the hyperparameter sweep. This will return a `TuneAnalysis` dataclass, which will contain the best set of hyperparameters, as well as other information."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "
\n",
+ "
\n",
+ "
Tune Status
\n",
+ "
\n",
+ "\n",
+ "| Current time: | 2023-07-27 03:16:53 |
\n",
+ "| Running for: | 00:02:40.69 |
\n",
+ "| Memory: | 34.8/125.7 GiB |
\n",
+ "\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
System Info
\n",
+ " Using AsyncHyperBand: num_stopped=5
Bracket: Iter 64.000: -451.93446350097656 | Iter 32.000: -453.1311798095703 | Iter 16.000: -459.6698303222656 | Iter 8.000: -469.3856506347656 | Iter 4.000: -481.6686248779297 | Iter 2.000: -517.1959228515625 | Iter 1.000: -642.4600219726562
Logical resource usage: 10.0/20 CPUs, 1.0/1 GPUs (0.0/1.0 accelerator_type:G)\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
Trial Status
\n",
+ "
\n",
+ "\n",
+ "| Trial name | status | loc | n_hidden | n_layers | lr | validation_loss |
\n",
+ "\n",
+ "\n",
+ "| _trainable_9ca2fa7c | TERMINATED | 128.32.142.133:965309 | 256 | 3 | 0.000589003 | 455.212 |
\n",
+ "| _trainable_7d98a8b2 | TERMINATED | 128.32.142.133:965309 | 64 | 2 | 0.000350049 | 737.187 |
\n",
+ "| _trainable_ff1772f6 | TERMINATED | 128.32.142.133:965309 | 64 | 3 | 0.000918661 | 556.365 |
\n",
+ "| _trainable_5fa3a6f6 | TERMINATED | 128.32.142.133:965309 | 256 | 3 | 0.000161977 | 770.872 |
\n",
+ "| _trainable_53fa3aa1 | TERMINATED | 128.32.142.133:965309 | 128 | 1 | 0.00122786 | 452.142 |
\n",
+ "\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/martin/bin/mambaforge/envs/scvi-tools-dev/lib/python3.11/site-packages/ray/air/config.py:803: UserWarning: Setting a `RunConfig.local_dir` is deprecated and will be removed in the future. If you are not using remote storage,set the `RunConfig.storage_path` instead. Otherwise, set the`RAY_AIR_LOCAL_CACHE_DIR` environment variable to control the local cache location.\n",
+ " warnings.warn(\n",
+ "2023-07-27 03:14:12,825\tINFO tune.py:657 -- [output] This uses the legacy output and progress reporter, as Jupyter notebooks are not supported by the new engine, yet. For more information, please see https://github.com/ray-project/ray/issues/36949\n",
+ "2023-07-27 03:16:53,535\tINFO tune.py:1148 -- Total run time: 160.71 seconds (160.69 seconds for the tuning loop).\n"
+ ]
+ }
+ ],
+ "source": [
+ "ray.init(log_to_driver=False)\n",
+ "results = scvi_tuner.fit(\n",
+ " adata,\n",
+ " metric=\"validation_loss\",\n",
+ " search_space=search_space,\n",
+ " num_samples=5,\n",
+ " max_epochs=100,\n",
+ " resources={\"cpu\": 10, \"gpu\": 1},\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'n_hidden': 128, 'n_layers': 1}\n",
+ "{'plan_kwargs': {'lr': 0.001227857394187722}}\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(results.model_kwargs)\n",
+ "print(results.train_kwargs)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Comparing latent spaces"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Work in progress: please check back in the next release!"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Optional: Monitoring progress with Tensorboard"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Work in progress: please check back in the next release!"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Optional: Tuning over integration metrics with `scib-metrics`"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Work in progress: please check back in the next release!"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "scvi-gpu",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.11.4"
+ },
+ "orig_nbformat": 4,
+ "vscode": {
+ "interpreter": {
+ "hash": "2f978838050607ec9770689d8200902a4128a2ce208b502e911dd714d57e924e"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
}