.. _sec_tutorial_simple_experiment_python:
.. automodule:: dacapo
.. contents::
:depth: 1
:local:
Tutorial: A Simple Experiment in Python
---------------------------------------
This tutorial goes through all the necessary steps from installation
to getting an experiment running with dacapo. As an example we will learn
neuron segmentation on the `cremi dataset ` using
a *3D U-Net*.
Installation
^^^^^^^^^^^^
First, follow the :ref:`installation guide`.
Data Storage
^^^^^^^^^^^^
Next you much choose where you want to store your data. We have 2 supported
modes of saving data.
1. | Save to disk: For particularly large data such as model weights or image
| volumes, it doesn't make sense to store your data in the cloud. In These
| cases we store to disk.
2. | Save to MongoDB: For dense data that can be nicely indexed, we encourage
| saving to a mongodb. This includes data such as loss and validation scores
| during training. This will allow us to quickly fetch specific scores for a
| range of experiments for comparison. Note: this option requires some set up,
| you need a mongodb accessible and you need to configure `DaCapo` to know
| where to find it. If you just want to get started quickly, you can save
| all data to disk.
`DaCapo` has a couple main data storage components:
1. | Loss stats: We store the loss per iteration, and include a couple other
| statistics such as how long that iteration took to compute. These will
| be stored in the MongoDB if available.
2. | Validation scores: For each `:ref:Run` we will evaluate on every held out
| validation dataset every `n` iterations where `n` is defined as the
| validation interval on the `:ref:RunConfig`. These
| will be stored in the MongoDB if available.
3. | Validation volumes: For qualitative inspection, we also store the results
| of validation in zarr datasets. This allows you to view the best predictions on
| your held out data according to the validation metric of your choice.
| This data will be stored on disk.
4. | Checkpoints: These are copies of your model at various intervals during
| training. Storing checkpoints lets you retrieve the best performing model
| according to the validation metric of your choice.
| This data will be stored on disk.
5. | Training Snapshots: Every `n` iterations where `n` corresponds to the
| `snapshot_interval` defined in the `:ref:TrainerConfig`,
| we store a snapshot that includes the inputs and outputs of the model at that
| iteration, and also some extra results that can be very helpful for debugging.
| Saved arrays include: Ground Truth, Target (Ground Truth transformed by Task),
| Raw, Prediction, Gradient, and Weights (for modifying the loss)
| This data will be stored on disk.
6. | Configs: To make our runs easily reproducible, we save our configuration
| files and then use them to execute our experiments. This way other people
| can use the exact same configuration files or change single parameters and
| get comparable results. This data will be stored in the MongoDB if available.
To define where this data goes, create a `dacapo.yaml` configuration file.
Here is a template:
.. code-block:: yaml
mongodbhost: mongodb://dbuser:dbpass@dburl:dbport/
mongodbname: dacapo
runs_base_dir: /path/to/my/data/storage
The `runs_base_dir` defines where your on-disk data will be stored.
The `mongodbhost` and `mongodbname` define the mongodb host and database
that will store your cloud data.
If you want to store everything on disk, replace `mongodbhost` and `mongodbname`
with a single `type: files` and everything will be saved to disk.
Configs
^^^^^^^
Next you need to create your configuration files for your experiments.
This can all be done in python. There is also a web based gui: the
`dacapo-dashboard `, see the
:ref:`Simple Experiment using the Dashboard`
for a tutorial stepping through the use of the dashboard.
First lets handle some basics, importing, setting logging, etc.
.. code-block:: python
import dacapo
import logging
logging.basicConfig(level=logging.INFO)
Now lets create some configs.
#. DataSplit
.. code-block:: python
# TODO: create datasplit config
datasplit_config = ...
#. Architecture
.. code-block:: python
from dacapo.experiments.architectures import CNNectomeUNetConfig
architecture_config = CNNectomeUNetConfig(
name="small_unet",
input_shape=Coordinate(212, 212, 212),
eval_shape_increase=Coordinate(72, 72, 72),
fmaps_in=1,
num_fmaps=8,
fmaps_out=32,
fmap_inc_factor=4,
downsample_factors=[(2, 2, 2), (2, 2, 2), (2, 2, 2)],
constant_upsample=True,
)
#. Task
.. code-block:: python
from dacapo.experiments.tasks import AffinitiesTaskConfig
task_config = AffinitiesTaskConfig(
name="AffinitiesPrediction",
neighborhood=[(0,0,1),(0,1,0),(1,0,0)]
)
#. Trainer
.. code-block:: python
from dacapo.experiments.trainers import GunpowderTrainerConfig
from dacapo.experiments.trainers.gp_augments import (
SimpleAugmentConfig,
ElasticAugmentConfig,
IntensityAugmentConfig,
)
trainer_config = GunpowderTrainerConfig(
name="gunpowder",
batch_size=2,
learning_rate=0.0001,
augments=[
SimpleAugmentConfig(),
ElasticAugmentConfig(
control_point_spacing=(100, 100, 100),
control_point_displacement_sigma=(10.0, 10.0, 10.0),
rotation_interval=(0, math.pi / 2.0),
subsample=8,
uniform_3d_rotation=True,
),
IntensityAugmentConfig(
scale=(0.25, 1.75),
shift=(-0.5, 0.35),
clip=False,
)
],
num_data_fetchers=20,
snapshot_interval=10000,
min_masked=0.15,
min_labelled=0.1,
)
Create a Run
^^^^^^^^^^^^
Now that we have our components configured, we just need to
combine them into a run and start training.
.. code-block:: python
:caption: Create a run
from funlib.geometry import Coordinate
from dacapo.experiments.run_config import RunConfig
from dacapo.experiments.run import Run
from torchsummary import summary
run_config = RunConfig(
name="tutorial_run",
task_config=task_config,
architecture_config=architecture_config,
trainer_config=trainer_config,
datasplit_config=datasplit_config,
repetition=0,
num_iterations=100000,
validation_interval=1000,
)
run = Run(run_config)
# if you want a summary of the model you can print that here
print(summary(run.model, (1, 212, 212, 212)))
Start the Run
^^^^^^^^^^^^^
You have 2 options for starting the run:
#. Simple case:
.. code-block:: python
from dacapo.train import train_run
train_run(run)
Your job will run but you haven't stored your configuration files, so
it may be difficult to reproduce your work without copy pasting everything
you've written so far.
#. Store your configs
.. code-block:: python
from dacapo.store.create_store import create_config_store
from dacapo.train import train
config_store = create_config_store()
config_store.store_datasplit_config(datasplit_config)
config_store.store_architecture_config(architecture_config)
config_store.store_task_config(task_config)
config_store.store_trainer_config(trainer_config)
config_store.store_run_config(run_config)
# Optional start training by config name:
train(run_config.name)
Once you have stored all your configs, you can start your Run just with
the name of the config you want to start. If you want to start your
run on some compute cluster, you might want to use the command line
interface: :code:`dacapo train -r {run_config.name}`.
This makes it particularly convenient to run on compute nodes where
you can specify specific compute requirements.
Finally your job should be running. The full script to run this tutorial is
provided here:
`dacapo.yaml`:
.. code-block:: yaml
mongodbhost: mongodb://dbuser:dbpass@dburl:dbport/
mongodbname: dacapo
runs_base_dir: /path/to/my/data/storage
`train.py`:
.. code-block:: python
import dacapo
import logging
from dacapo.experiments.architectures import CNNectomeUNetConfig
from dacapo.experiments.tasks import AffinitiesTaskConfig
from dacapo.experiments.trainers import GunpowderTrainerConfig
from dacapo.experiments.trainers.gp_augments import (
SimpleAugmentConfig,
ElasticAugmentConfig,
IntensityAugmentConfig,
)
from funlib.geometry import Coordinate
from dacapo.experiments.run_config import RunConfig
from dacapo.experiments.run import Run
from dacapo.store.create_store import create_config_store
from dacapo.train import train
logging.basicConfig(level=logging.INFO)
# TODO: create datasplit config
train_array
datasplit_config = ...
# Create Architecture Config
architecture_config = CNNectomeUNetConfig(
name="small_unet",
input_shape=Coordinate(212, 212, 212),
eval_shape_increase=Coordinate(72, 72, 72),
fmaps_in=1,
num_fmaps=8,
fmaps_out=32,
fmap_inc_factor=4,
downsample_factors=[(2, 2, 2), (2, 2, 2), (2, 2, 2)],
constant_upsample=True,
)
# Create Task Config
task_config = AffinitiesTaskConfig(
name="AffinitiesPrediction",
neighborhood=[(0,0,1),(0,1,0),(1,0,0)]
)
# Create Trainer Config
trainer_config = GunpowderTrainerConfig(
name="gunpowder",
batch_size=2,
learning_rate=0.0001,
augments=[
SimpleAugmentConfig(),
ElasticAugmentConfig(
control_point_spacing=(100, 100, 100),
control_point_displacement_sigma=(10.0, 10.0, 10.0),
rotation_interval=(0, math.pi / 2.0),
subsample=8,
uniform_3d_rotation=True,
),
IntensityAugmentConfig(
scale=(0.25, 1.75),
shift=(-0.5, 0.35),
clip=False,
)
],
num_data_fetchers=20,
snapshot_interval=10000,
min_masked=0.15,
min_labelled=0.1,
)
# Create Run Config
run_config = RunConfig(
name="tutorial_run",
task_config=task_config,
architecture_config=architecture_config,
trainer_config=trainer_config,
datasplit_config=datasplit_config,
repetition=0,
num_iterations=100000,
validation_interval=1000,
)
run = Run(run_config)
# Store configs
config_store = create_config_store()
config_store.store_datasplit_config(datasplit_config)
config_store.store_architecture_config(architecture_config)
config_store.store_task_config(task_config)
config_store.store_trainer_config(trainer_config)
config_store.store_run_config(run_config)
# Optional start training by config name:
train(run_config.name)