diff --git a/.github/workflows/cron-mmar.yml b/.github/workflows/cron-ngc-bundle.yml
similarity index 77%
rename from .github/workflows/cron-mmar.yml
rename to .github/workflows/cron-ngc-bundle.yml
index ae65388a8b..2b16d05ce7 100644
--- a/.github/workflows/cron-mmar.yml
+++ b/.github/workflows/cron-ngc-bundle.yml
@@ -1,15 +1,15 @@
-# daily tests for clara mmar models
-name: cron-mmar
+# daily tests for ngc bundles
+name: cron-ngc-bundle
on:
- # schedule:
- # - cron: "0 2 * * *" # at 02:00 UTC
+ schedule:
+ - cron: "0 2 * * *" # at 02:00 UTC
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
concurrency:
# automatically cancel the previously triggered workflows when there's a newer version
- group: mmar-tests-${{ github.event.pull_request.number || github.ref }}
+ group: bundle-tests-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
@@ -33,12 +33,12 @@ jobs:
key: ${{ runner.os }}-pip-${{ steps.pip-cache.outputs.datew }}
- name: Install dependencies
run: |
- rm -rf /github/home/.cache/torch/hub/mmars/
+ rm -rf /github/home/.cache/torch/hub/bundle/
python -m pip install --upgrade pip wheel
python -m pip install -r requirements-dev.txt
- - name: Loading MMARs
+ - name: Loading Bundles
run: |
# clean up temporary files
$(pwd)/runtests.sh --build --clean
# run tests
- python -m tests.ngc_mmar_loading
+ python -m tests.ngc_bundle_download
diff --git a/.github/workflows/pythonapp-gpu.yml b/.github/workflows/pythonapp-gpu.yml
index 9541bd1caa..47259be9aa 100644
--- a/.github/workflows/pythonapp-gpu.yml
+++ b/.github/workflows/pythonapp-gpu.yml
@@ -44,7 +44,7 @@ jobs:
pytorch: "-h" # we explicitly set pytorch to -h to avoid pip install error
base: "nvcr.io/nvidia/pytorch:22.09-py3"
- environment: PT113+CUDA116
- pytorch: "torch==1.13.0 torchvision==0.14.0"
+ pytorch: "torch==1.13.1 torchvision==0.14.1"
base: "nvcr.io/nvidia/cuda:11.6.1-devel-ubuntu18.04"
container:
image: ${{ matrix.base }}
diff --git a/.github/workflows/pythonapp-min.yml b/.github/workflows/pythonapp-min.yml
index 317fcbbcd2..a5e24639bf 100644
--- a/.github/workflows/pythonapp-min.yml
+++ b/.github/workflows/pythonapp-min.yml
@@ -52,11 +52,11 @@ jobs:
- if: runner.os == 'windows'
name: Install torch cpu from pytorch.org (Windows only)
run: |
- python -m pip install torch==1.13+cpu -f https://download.pytorch.org/whl/torch_stable.html
+ python -m pip install torch==1.13.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
- name: Install the dependencies
run: |
# min. requirements
- python -m pip install torch==1.13
+ python -m pip install torch==1.13.1
python -m pip install -r requirements-min.txt
python -m pip list
BUILD_MONAI=0 python setup.py develop # no compile of extensions
diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml
index fb68e04b44..a603deb8a2 100644
--- a/.github/workflows/pythonapp.yml
+++ b/.github/workflows/pythonapp.yml
@@ -88,14 +88,14 @@ jobs:
- if: runner.os == 'windows'
name: Install torch cpu from pytorch.org (Windows only)
run: |
- python -m pip install torch==1.13.0+cpu torchvision==0.14.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
+ python -m pip install torch==1.13.1+cpu torchvision==0.14.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
- if: runner.os == 'Linux'
name: Install itk pre-release (Linux only)
run: |
python -m pip install --pre -U itk
- name: Install the dependencies
run: |
- python -m pip install torch==1.13.0 torchvision==0.14.0
+ python -m pip install torch==1.13.1 torchvision==0.14.1
cat "requirements-dev.txt"
python -m pip install -r requirements-dev.txt
python -m pip list
diff --git a/.github/workflows/setupapp.yml b/.github/workflows/setupapp.yml
index 7aa2649918..b8efda471e 100644
--- a/.github/workflows/setupapp.yml
+++ b/.github/workflows/setupapp.yml
@@ -100,7 +100,7 @@ jobs:
- name: Install the dependencies
run: |
python -m pip install --upgrade pip wheel
- python -m pip install torch==1.13.0 torchvision==0.14.0
+ python -m pip install torch==1.13.1 torchvision==0.14.1
python -m pip install -r requirements-dev.txt
- name: Run quick tests CPU ubuntu
run: |
diff --git a/CITATION.cff b/CITATION.cff
index 9b8a9f6ec5..d00c8a364a 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -48,8 +48,6 @@ preferred-citation:
family-names: "Xu"
- given-names: "Ali"
family-names: "Hatamizadeh"
- - given-names: "Andriy"
- family-names: "Myronenko"
- given-names: "Wentao"
family-names: "Zhu"
- given-names: "Yun"
diff --git a/README.md b/README.md
index c3fd93f522..d54aa7e1a7 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,17 @@
**M**edical **O**pen **N**etwork for **AI**
+![Supported Python versions](https://raw.githubusercontent.com/Project-MONAI/MONAI/dev/docs/images/python.svg)
[![License](https://img.shields.io/badge/license-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)
-[![CI Build](https://github.com/Project-MONAI/MONAI/workflows/build/badge.svg?branch=dev)](https://github.com/Project-MONAI/MONAI/commits/dev)
-[![Documentation Status](https://readthedocs.org/projects/monai/badge/?version=latest)](https://docs.monai.io/en/latest/?badge=latest)
-[![codecov](https://codecov.io/gh/Project-MONAI/MONAI/branch/dev/graph/badge.svg?token=6FTC7U1JJ4)](https://codecov.io/gh/Project-MONAI/MONAI)
[![PyPI version](https://badge.fury.io/py/monai.svg)](https://badge.fury.io/py/monai)
-[![conda](https://img.shields.io/conda/vn/conda-forge/monai)](https://anaconda.org/conda-forge/monai)
+[![docker](https://img.shields.io/badge/docker-pull-green.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/projectmonai/monai)
+[![conda](https://img.shields.io/conda/vn/conda-forge/monai?color=green)](https://anaconda.org/conda-forge/monai)
+
+[![premerge](https://github.com/Project-MONAI/MONAI/actions/workflows/pythonapp.yml/badge.svg?branch=dev)](https://github.com/Project-MONAI/MONAI/actions/workflows/pythonapp.yml)
+[![postmerge](https://img.shields.io/github/checks-status/project-monai/monai/dev?label=postmerge)](https://github.com/Project-MONAI/MONAI/actions?query=branch%3Adev)
+[![docker](https://github.com/Project-MONAI/MONAI/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/Project-MONAI/MONAI/actions/workflows/docker.yml)
+[![Documentation Status](https://readthedocs.org/projects/monai/badge/?version=latest)](https://docs.monai.io/en/latest/)
+[![codecov](https://codecov.io/gh/Project-MONAI/MONAI/branch/dev/graph/badge.svg?token=6FTC7U1JJ4)](https://codecov.io/gh/Project-MONAI/MONAI)
MONAI is a [PyTorch](https://pytorch.org/)-based, [open-source](https://github.com/Project-MONAI/MONAI/blob/dev/LICENSE) framework for deep learning in healthcare imaging, part of [PyTorch Ecosystem](https://pytorch.org/ecosystem/).
Its ambitions are:
diff --git a/docs/images/exp_mgmt.png b/docs/images/exp_mgmt.png
new file mode 100644
index 0000000000..f3faf4c09f
Binary files /dev/null and b/docs/images/exp_mgmt.png differ
diff --git a/docs/images/hovernet_diagram.png b/docs/images/hovernet_diagram.png
new file mode 100644
index 0000000000..aa7adcbdcf
Binary files /dev/null and b/docs/images/hovernet_diagram.png differ
diff --git a/docs/images/python.svg b/docs/images/python.svg
new file mode 100644
index 0000000000..2d24bd007f
--- /dev/null
+++ b/docs/images/python.svg
@@ -0,0 +1 @@
+
diff --git a/docs/source/whatsnew.rst b/docs/source/whatsnew.rst
index bb6665e621..9dd00bcd1e 100644
--- a/docs/source/whatsnew.rst
+++ b/docs/source/whatsnew.rst
@@ -6,6 +6,7 @@ What's New
.. toctree::
:maxdepth: 1
+ whatsnew_1_1.md
whatsnew_1_0.md
whatsnew_0_9.md
whatsnew_0_8.md
diff --git a/docs/source/whatsnew_1_0.md b/docs/source/whatsnew_1_0.md
index 36ab393af1..e8e0b031c1 100644
--- a/docs/source/whatsnew_1_0.md
+++ b/docs/source/whatsnew_1_0.md
@@ -1,4 +1,4 @@
-# What's new in 1.0 🎉🎉
+# What's new in 1.0
- Model Zoo
- Auto3DSeg
diff --git a/docs/source/whatsnew_1_1.md b/docs/source/whatsnew_1_1.md
new file mode 100644
index 0000000000..261af460fc
--- /dev/null
+++ b/docs/source/whatsnew_1_1.md
@@ -0,0 +1,75 @@
+# What's new in 1.1 🎉🎉
+
+- Digital pathology workflows
+- Experiment management for MONAI bundle
+- Auto3dSeg enhancements
+- New models in MONAI Model Zoo
+- State-of-the-art SurgToolLoc solution
+
+## Digital pathology workflows
+
+![hovernet](../images/hovernet_diagram.png)
+
+Hover-Net is a model for simultaneous segmentation and classification of nuclei in multi-tissue histology images (Graham et al. Medical Image Analysis, 2019).
+We have added support for this model in MONAI by implementing several new components, enhancing existing ones and providing pipelines and examples for training, validation and inference.
+
+Along with the modules release, new digital pathology analysis tutorials are made available:
+
+- [HoVerNet pipelines](https://github.com/Project-MONAI/tutorials/tree/main/pathology/hovernet) based on MONAI workflows for training, validation and inference
+- [HoVerNet tutorial](https://github.com/Project-MONAI/tutorials/blob/main/pathology/hovernet/hovernet_torch.ipynb) for training, validation and inference
+- NuClick (Interactive Annotation for Pathology) tutorials for [training](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclick_training_notebook.ipynb)
+and [inference](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclick_infer.ipynb)
+- Nuclei classification tutorials for [training](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclei_classification_training_notebook.ipynb)
+and [inference](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclei_classification_infer.ipynb)
+
+## Experiment management for MONAI bundle
+
+![exp_mgmt](../images/exp_mgmt.png)
+
+In this release, experiment management features are integrated with MONAI bundle.
+It provides essential APIs for managing the end-to-end model bundle lifecycle.
+Users can start tracking experiments by, for example, appending `--tracking "mlflow"` to the training or inference commands to enable the MLFlow-based management.
+By default, MLFlow will track the executed bundle config, model quality measurements, and source code versioning.
+For more details, please refer to the [tutorial](https://github.com/Project-MONAI/tutorials/blob/main/experiment_management/bundle_integrate_mlflow.ipynb).
+
+## Auto3dSeg enhancements
+
+Multiple improvements have been added in `Auto3DSeg` both in terms of
+usability and performance.
+- Multi-modality support is added and applied for
+automated segmentation of the HECKTOR22 challenge dataset, which includes input 3D
+CT and PET images of various resolutions and sizes. A tutorial example of
+running Auto3DSeg on the HECKTOR22 challenge dataset is available in MONAI
+Tutorials. The tutorial is based on [the HECKTOR22 challenge 1st place solution](https://arxiv.org/abs/2209.10809).
+- A new improved version of `Segresnet` Algo is now available in `AutoRunner`.
+In this version, data caching is more efficient and the preprocessing transforms are more flexible.
+The workflow progresses including the timings of steps are written to console output as well as a YAML file.
+- Automatic customization and optimization of the model training configuration
+can be achieved according to the GPU devices used. The feature
+focuses on determining parameters including batch size of model
+training and sliding-window inference, allocated devices for
+data in sliding-window inference. For more details about how to enable it, please see [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/auto3dseg).
+
+## New models in MONAI Model Zoo
+
+New pretrained models are being created and released [in the Model Zoo](https://monai.io/model-zoo.html).
+Notably,
+
+- The `mednist_reg` model demonstrates how to build image registration workflows in MONAI bundle
+format. The model uses a ResNet and spatial transformer for hand X-ray image registration based on
+[the registration_mednist tutorial](https://github.com/Project-MONAI/tutorials/blob/main/2d_registration/registration_mednist.ipynb),
+- `pathology_nuclei_segmentation_and_classification`,
+ `pathology_nuclick_annotation`, and `pathology_nuclei_classification` bundles
+ are built for [digital pathology image
+ analysis](https://github.com/Project-MONAI/model-zoo/tree/dev/models/pathology_nuclei_segmentation_classification).
+
+For more details about how to use the models, please see [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/model_zoo).
+
+## State-of-the-art SurgToolLoc solution
+
+[SurgToolLoc](https://surgtoolloc.grand-challenge.org/Home/) is a part of the
+[EndoVis](https://endovis.grand-challenge.org/) challenge at [MICCAI 2022](https://conferences.miccai.org/2022/en/).
+The challenge focuses on endoscopic video analysis and is divided into (1) fully supervised tool classification
+and (2) weakly supervised tool classification/localization.
+Team NVIDIA won prizes by finishing [third](https://surgtoolloc.grand-challenge.org/results/) in both categories.
+The core components of the solutions [are released in MONAI](https://github.com/Project-MONAI/tutorials/tree/main/competitions/MICCAI/surgtoolloc).
diff --git a/monai/apps/auto3dseg/bundle_gen.py b/monai/apps/auto3dseg/bundle_gen.py
index efbecb2061..59e90b795b 100644
--- a/monai/apps/auto3dseg/bundle_gen.py
+++ b/monai/apps/auto3dseg/bundle_gen.py
@@ -32,7 +32,7 @@
from monai.utils import ensure_tuple
logger = get_logger(module_name=__name__)
-ALGO_HASH = os.environ.get("MONAI_ALGO_HASH", "c812e5f")
+ALGO_HASH = os.environ.get("MONAI_ALGO_HASH", "1dde7a1")
__all__ = ["BundleAlgo", "BundleGen"]
diff --git a/monai/bundle/scripts.py b/monai/bundle/scripts.py
index cf9be1f98d..b625578049 100644
--- a/monai/bundle/scripts.py
+++ b/monai/bundle/scripts.py
@@ -25,6 +25,7 @@
import torch
from torch.cuda import is_available
+from monai.apps.mmars.mmars import _get_all_ngc_models
from monai.apps.utils import _basename, download_url, extractall, get_logger
from monai.bundle.config_item import ConfigComponent
from monai.bundle.config_parser import ConfigParser
@@ -42,6 +43,9 @@
logger = get_logger(module_name=__name__)
+# set BUNDLE_DOWNLOAD_SRC="ngc" to use NGC source in default for bundle download
+download_source = os.environ.get("BUNDLE_DOWNLOAD_SRC", "github")
+
def _update_args(args: Optional[Union[str, Dict]] = None, ignore_none: bool = True, **kwargs) -> Dict:
"""
@@ -130,9 +134,11 @@ def _get_git_release_url(repo_owner: str, repo_name: str, tag_name: str, filenam
return f"https://github.com/{repo_owner}/{repo_name}/releases/download/{tag_name}/{filename}"
+def _get_ngc_bundle_url(model_name: str, version: str):
+ return f"https://api.ngc.nvidia.com/v2/models/nvidia/monaitoolkit/{model_name}/versions/{version}/zip"
+
+
def _download_from_github(repo: str, download_path: Path, filename: str, progress: bool = True):
- if len(repo.split("/")) != 3:
- raise ValueError("if source is `github`, repo should be in the form of `repo_owner/repo_name/release_tag`.")
repo_owner, repo_name, tag_name = repo.split("/")
if ".zip" not in filename:
filename += ".zip"
@@ -142,6 +148,45 @@ def _download_from_github(repo: str, download_path: Path, filename: str, progres
extractall(filepath=filepath, output_dir=download_path, has_base=True)
+def _add_ngc_prefix(name: str, prefix: str = "monai_"):
+ if name.startswith(prefix):
+ return name
+ return f"{prefix}{name}"
+
+
+def _remove_ngc_prefix(name: str, prefix: str = "monai_"):
+ if name.startswith(prefix):
+ return name[len(prefix) :]
+ return name
+
+
+def _download_from_ngc(download_path: Path, filename: str, version: str, remove_prefix: Optional[str], progress: bool):
+ # ensure prefix is contained
+ filename = _add_ngc_prefix(filename)
+ url = _get_ngc_bundle_url(model_name=filename, version=version)
+ filepath = download_path / f"{filename}_v{version}.zip"
+ if remove_prefix:
+ filename = _remove_ngc_prefix(filename, prefix=remove_prefix)
+ extract_path = download_path / f"{filename}"
+ download_url(url=url, filepath=filepath, hash_val=None, progress=progress)
+ extractall(filepath=filepath, output_dir=extract_path, has_base=True)
+
+
+def _get_latest_bundle_version(source: str, name: str, repo: str):
+ if source == "ngc":
+ name = _add_ngc_prefix(name)
+ model_dict = _get_all_ngc_models(name)
+ for v in model_dict.values():
+ if v["name"] == name:
+ return v["latest"]
+ return None
+ elif source == "github":
+ repo_owner, repo_name, tag_name = repo.split("/")
+ return get_bundle_versions(name, repo=os.path.join(repo_owner, repo_name), tag=tag_name)["latest_version"]
+ else:
+ raise ValueError(f"To get the latest bundle version, source should be 'github' or 'ngc', got {source}.")
+
+
def _process_bundle_dir(bundle_dir: Optional[PathLike] = None):
if bundle_dir is None:
get_dir, has_home = optional_import("torch.hub", name="get_dir")
@@ -156,9 +201,10 @@ def download(
name: Optional[str] = None,
version: Optional[str] = None,
bundle_dir: Optional[PathLike] = None,
- source: str = "github",
- repo: str = "Project-MONAI/model-zoo/hosting_storage_v1",
+ source: str = download_source,
+ repo: Optional[str] = None,
url: Optional[str] = None,
+ remove_prefix: Optional[str] = "monai_",
progress: bool = True,
args_file: Optional[str] = None,
):
@@ -175,9 +221,12 @@ def download(
# Execute this module as a CLI entry, and download bundle from the model-zoo repo:
python -m monai.bundle download --name --version "0.1.0" --bundle_dir "./"
- # Execute this module as a CLI entry, and download bundle:
+ # Execute this module as a CLI entry, and download bundle from specified github repo:
python -m monai.bundle download --name --source "github" --repo "repo_owner/repo_name/release_tag"
+ # Execute this module as a CLI entry, and download bundle from ngc with latest version:
+ python -m monai.bundle download --name --source "ngc" --bundle_dir "./"
+
# Execute this module as a CLI entry, and download bundle via URL:
python -m monai.bundle download --name --url
@@ -190,18 +239,27 @@ def download(
Args:
name: bundle name. If `None` and `url` is `None`, it must be provided in `args_file`.
- for example: "spleen_ct_segmentation", "prostate_mri_anatomy" in the model-zoo:
+ for example:
+ "spleen_ct_segmentation", "prostate_mri_anatomy" in model-zoo:
https://github.com/Project-MONAI/model-zoo/releases/tag/hosting_storage_v1.
- version: version name of the target bundle to download, like: "0.1.0".
+ "monai_brats_mri_segmentation" in ngc:
+ https://catalog.ngc.nvidia.com/models?filters=&orderBy=scoreDESC&query=monai.
+ version: version name of the target bundle to download, like: "0.1.0". If `None`, will download
+ the latest version.
bundle_dir: target directory to store the downloaded data.
Default is `bundle` subfolder under `torch.hub.get_dir()`.
source: storage location name. This argument is used when `url` is `None`.
- "github" is currently the only supported value.
- repo: repo name. This argument is used when `url` is `None`.
- If `source` is "github", it should be in the form of "repo_owner/repo_name/release_tag".
+ In default, the value is achieved from the environment variable BUNDLE_DOWNLOAD_SRC, and
+ it should be "ngc" or "github".
+ repo: repo name. This argument is used when `url` is `None` and `source` is "github".
+ If used, it should be in the form of "repo_owner/repo_name/release_tag".
url: url to download the data. If not `None`, data will be downloaded directly
and `source` will not be checked.
If `name` is `None`, filename is determined by `monai.apps.utils._basename(url)`.
+ remove_prefix: This argument is used when `source` is "ngc". Currently, all ngc bundles
+ have the ``monai_`` prefix, which is not existing in their model zoo contrasts. In order to
+ maintain the consistency between these two sources, remove prefix is necessary.
+ Therefore, if specified, downloaded folder name will remove the prefix.
progress: whether to display a progress bar.
args_file: a JSON or YAML file to provide default values for all the args in this function.
so that the command line inputs can be simplified.
@@ -215,17 +273,20 @@ def download(
source=source,
repo=repo,
url=url,
+ remove_prefix=remove_prefix,
progress=progress,
)
_log_input_summary(tag="download", args=_args)
- source_, repo_, progress_, name_, version_, bundle_dir_, url_ = _pop_args(
- _args, "source", "repo", "progress", name=None, version=None, bundle_dir=None, url=None
+ source_, progress_, remove_prefix_, repo_, name_, version_, bundle_dir_, url_ = _pop_args(
+ _args, "source", "progress", remove_prefix=None, repo=None, name=None, version=None, bundle_dir=None, url=None
)
bundle_dir_ = _process_bundle_dir(bundle_dir_)
- if name_ is not None and version_ is not None:
- name_ = "_v".join([name_, version_])
+ if repo_ is None:
+ repo_ = "Project-MONAI/model-zoo/hosting_storage_v1"
+ if len(repo_.split("/")) != 3:
+ raise ValueError("repo should be in the form of `repo_owner/repo_name/release_tag`.")
if url_ is not None:
if name_ is not None:
@@ -234,14 +295,27 @@ def download(
filepath = bundle_dir_ / f"{_basename(url_)}"
download_url(url=url_, filepath=filepath, hash_val=None, progress=progress_)
extractall(filepath=filepath, output_dir=bundle_dir_, has_base=True)
- elif source_ == "github":
- if name_ is None:
- raise ValueError(f"To download from source: Github, `name` must be provided, got {name_}.")
- _download_from_github(repo=repo_, download_path=bundle_dir_, filename=name_, progress=progress_)
else:
- raise NotImplementedError(
- f"Currently only download from provided URL in `url` or Github is implemented, got source: {source_}."
- )
+ if name_ is None:
+ raise ValueError(f"To download from source: {source_}, `name` must be provided.")
+ if version_ is None:
+ version_ = _get_latest_bundle_version(source=source_, name=name_, repo=repo_)
+ if source_ == "github":
+ if version_ is not None:
+ name_ = "_v".join([name_, version_])
+ _download_from_github(repo=repo_, download_path=bundle_dir_, filename=name_, progress=progress_)
+ elif source_ == "ngc":
+ _download_from_ngc(
+ download_path=bundle_dir_,
+ filename=name_,
+ version=version_,
+ remove_prefix=remove_prefix_,
+ progress=progress_,
+ )
+ else:
+ raise NotImplementedError(
+ f"Currently only download from `url`, source 'github' or 'ngc' are implemented, got source: {source_}."
+ )
def load(
@@ -250,8 +324,9 @@ def load(
model_file: Optional[str] = None,
load_ts_module: bool = False,
bundle_dir: Optional[PathLike] = None,
- source: str = "github",
- repo: str = "Project-MONAI/model-zoo/hosting_storage_v1",
+ source: str = download_source,
+ repo: Optional[str] = None,
+ remove_prefix: Optional[str] = "monai_",
progress: bool = True,
device: Optional[str] = None,
key_in_ckpt: Optional[str] = None,
@@ -263,18 +338,29 @@ def load(
Load model weights or TorchScript module of a bundle.
Args:
- name: bundle name, for example: "spleen_ct_segmentation", "prostate_mri_anatomy" in the model-zoo:
+ name: bundle name. If `None` and `url` is `None`, it must be provided in `args_file`.
+ for example:
+ "spleen_ct_segmentation", "prostate_mri_anatomy" in model-zoo:
https://github.com/Project-MONAI/model-zoo/releases/tag/hosting_storage_v1.
- version: version name of the target bundle to download, like: "0.1.0".
+ "monai_brats_mri_segmentation" in ngc:
+ https://catalog.ngc.nvidia.com/models?filters=&orderBy=scoreDESC&query=monai.
+ version: version name of the target bundle to download, like: "0.1.0". If `None`, will download
+ the latest version.
model_file: the relative path of the model weights or TorchScript module within bundle.
If `None`, "models/model.pt" or "models/model.ts" will be used.
load_ts_module: a flag to specify if loading the TorchScript module.
bundle_dir: directory the weights/TorchScript module will be loaded from.
Default is `bundle` subfolder under `torch.hub.get_dir()`.
source: storage location name. This argument is used when `model_file` is not existing locally and need to be
- downloaded first. "github" is currently the only supported value.
- repo: repo name. This argument is used when `model_file` is not existing locally and need to be
- downloaded first. If `source` is "github", it should be in the form of "repo_owner/repo_name/release_tag".
+ downloaded first.
+ In default, the value is achieved from the environment variable BUNDLE_DOWNLOAD_SRC, and
+ it should be "ngc" or "github".
+ repo: repo name. This argument is used when `url` is `None` and `source` is "github".
+ If used, it should be in the form of "repo_owner/repo_name/release_tag".
+ remove_prefix: This argument is used when `source` is "ngc". Currently, all ngc bundles
+ have the ``monai_`` prefix, which is not existing in their model zoo contrasts. In order to
+ maintain the consistency between these two sources, remove prefix is necessary.
+ Therefore, if specified, downloaded folder name will remove the prefix.
progress: whether to display a progress bar when downloading.
device: target device of returned weights or module, if `None`, prefer to "cuda" if existing.
key_in_ckpt: for nested checkpoint like `{"model": XXX, "optimizer": XXX, ...}`, specify the key of model
@@ -298,9 +384,21 @@ def load(
if model_file is None:
model_file = os.path.join("models", "model.ts" if load_ts_module is True else "model.pt")
+ if source == "ngc":
+ name = _add_ngc_prefix(name)
+ if remove_prefix:
+ name = _remove_ngc_prefix(name, prefix=remove_prefix)
full_path = os.path.join(bundle_dir_, name, model_file)
if not os.path.exists(full_path):
- download(name=name, version=version, bundle_dir=bundle_dir_, source=source, repo=repo, progress=progress)
+ download(
+ name=name,
+ version=version,
+ bundle_dir=bundle_dir_,
+ source=source,
+ repo=repo,
+ remove_prefix=remove_prefix,
+ progress=progress,
+ )
if device is None:
device = "cuda:0" if is_available() else "cpu"
@@ -421,7 +519,7 @@ def get_bundle_versions(
bundles_info = _get_all_bundles_info(repo=repo, tag=tag, auth_token=auth_token)
if bundle_name not in bundles_info:
- raise ValueError(f"bundle: {bundle_name} is not existing.")
+ raise ValueError(f"bundle: {bundle_name} is not existing in repo: {repo}.")
bundle_info = bundles_info[bundle_name]
all_versions = sorted(bundle_info.keys())
diff --git a/setup.cfg b/setup.cfg
index d14b7fe30b..30352d50db 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -14,6 +14,24 @@ project_urls =
Documentation=https://docs.monai.io/
Bug Tracker=https://github.com/Project-MONAI/MONAI/issues
Source Code=https://github.com/Project-MONAI/MONAI
+classifiers =
+ Intended Audience :: Developers
+ Intended Audience :: Education
+ Intended Audience :: Science/Research
+ Intended Audience :: Healthcare Industry
+ Programming Language :: C++
+ Programming Language :: Python :: 3
+ Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
+ Programming Language :: Python :: 3.9
+ Programming Language :: Python :: 3.10
+ Topic :: Scientific/Engineering
+ Topic :: Scientific/Engineering :: Artificial Intelligence
+ Topic :: Scientific/Engineering :: Medical Science Apps.
+ Topic :: Scientific/Engineering :: Information Analysis
+ Topic :: Software Development
+ Topic :: Software Development :: Libraries
+ Typing :: Typed
[options]
python_requires = >= 3.7
diff --git a/tests/ngc_mmar_loading.py b/tests/ngc_bundle_download.py
similarity index 57%
rename from tests/ngc_mmar_loading.py
rename to tests/ngc_bundle_download.py
index 8dca4f72c0..2b376c3c2d 100644
--- a/tests/ngc_mmar_loading.py
+++ b/tests/ngc_bundle_download.py
@@ -11,14 +11,81 @@
import os
import sys
+import tempfile
import unittest
import torch
from parameterized import parameterized
+from monai.apps import check_hash
from monai.apps.mmars import MODEL_DESC, load_from_mmar
+from monai.bundle import download, load
from monai.config import print_debug_info
from monai.networks.utils import copy_model_state
+from tests.utils import assert_allclose, skip_if_downloading_fails, skip_if_quick, skip_if_windows
+
+TEST_CASE_NGC_1 = [
+ "spleen_ct_segmentation",
+ "0.3.7",
+ None,
+ "monai_spleen_ct_segmentation",
+ "models/model.pt",
+ "b418a2dc8672ce2fd98dc255036e7a3d",
+]
+TEST_CASE_NGC_2 = [
+ "monai_spleen_ct_segmentation",
+ "0.3.7",
+ "monai_",
+ "spleen_ct_segmentation",
+ "models/model.pt",
+ "b418a2dc8672ce2fd98dc255036e7a3d",
+]
+
+TESTCASE_WEIGHTS = {
+ "key": "model.0.conv.unit0.adn.N.bias",
+ "value": torch.tensor(
+ [
+ -0.0705,
+ -0.0937,
+ -0.0422,
+ -0.2068,
+ 0.1023,
+ -0.2007,
+ -0.0883,
+ 0.0018,
+ -0.1719,
+ 0.0116,
+ 0.0285,
+ -0.0044,
+ 0.1223,
+ -0.1287,
+ -0.1858,
+ 0.0460,
+ ]
+ ),
+}
+
+
+@skip_if_windows
+class TestNgcBundleDownload(unittest.TestCase):
+ @parameterized.expand([TEST_CASE_NGC_1, TEST_CASE_NGC_2])
+ @skip_if_quick
+ def test_ngc_download_bundle(self, bundle_name, version, remove_prefix, download_name, file_path, hash_val):
+ with skip_if_downloading_fails():
+ with tempfile.TemporaryDirectory() as tempdir:
+ download(
+ name=bundle_name, source="ngc", version=version, bundle_dir=tempdir, remove_prefix=remove_prefix
+ )
+ full_file_path = os.path.join(tempdir, download_name, file_path)
+ self.assertTrue(os.path.exists(full_file_path))
+ self.assertTrue(check_hash(filepath=full_file_path, val=hash_val))
+
+ weights = load(
+ name=bundle_name, source="ngc", version=version, bundle_dir=tempdir, remove_prefix=remove_prefix
+ )
+ assert_allclose(
+ weights[TESTCASE_WEIGHTS["key"]], TESTCASE_WEIGHTS["value"], atol=1e-4, rtol=1e-4, type_test=False
+ )
@unittest.skip("deprecating mmar tests")
diff --git a/tests/test_auto3dseg_ensemble.py b/tests/test_auto3dseg_ensemble.py
index 514844ff07..24cf37201e 100644
--- a/tests/test_auto3dseg_ensemble.py
+++ b/tests/test_auto3dseg_ensemble.py
@@ -49,11 +49,10 @@
train_param = (
{
"CUDA_VISIBLE_DEVICES": list(range(num_gpus)),
- "num_iterations": int(4 / num_gpus),
- "num_iterations_per_validation": int(4 / num_gpus),
"num_images_per_batch": 2,
- "num_epochs": 1,
- "num_warmup_iterations": int(4 / num_gpus),
+ "num_epochs": 2,
+ "num_epochs_per_validation": 1,
+ "num_warmup_epochs": 1,
"use_pretrain": False,
"pretrained_path": "",
}
diff --git a/tests/test_auto3dseg_hpo.py b/tests/test_auto3dseg_hpo.py
index acb2721732..30c2361ef9 100644
--- a/tests/test_auto3dseg_hpo.py
+++ b/tests/test_auto3dseg_hpo.py
@@ -33,11 +33,10 @@
override_param = (
{
"CUDA_VISIBLE_DEVICES": list(range(num_gpus)),
- "num_iterations": int(4 / num_gpus),
- "num_iterations_per_validation": int(4 / num_gpus),
"num_images_per_batch": 2,
- "num_epochs": 1,
- "num_warmup_iterations": int(4 / num_gpus),
+ "num_epochs": 2,
+ "num_epochs_per_validation": 1,
+ "num_warmup_epochs": 1,
"use_pretrain": False,
"pretrained_path": "",
}
diff --git a/tests/test_bundle_download.py b/tests/test_bundle_download.py
index 0bb7834dac..09cd0128f9 100644
--- a/tests/test_bundle_download.py
+++ b/tests/test_bundle_download.py
@@ -31,18 +31,16 @@
TEST_CASE_1 = ["test_bundle", None]
-TEST_CASE_2 = ["test_bundle_v0.1.1", None]
+TEST_CASE_2 = ["test_bundle", "0.1.1"]
-TEST_CASE_3 = ["test_bundle", "0.1.1"]
-
-TEST_CASE_4 = [
+TEST_CASE_3 = [
["model.pt", "model.ts", "network.json", "test_output.pt", "test_input.pt"],
"test_bundle",
"https://github.com/Project-MONAI/MONAI-extra-test-data/releases/download/0.8.1/test_bundle.zip",
"a131d39a0af717af32d19e565b434928",
]
-TEST_CASE_5 = [
+TEST_CASE_4 = [
["model.pt", "model.ts", "network.json", "test_output.pt", "test_input.pt"],
"test_bundle",
"Project-MONAI/MONAI-extra-test-data/0.8.1",
@@ -50,7 +48,7 @@
"model.pt",
]
-TEST_CASE_6 = [
+TEST_CASE_5 = [
["test_output.pt", "test_input.pt"],
"test_bundle",
"0.1.1",
@@ -62,9 +60,9 @@
@skip_if_windows
class TestDownload(unittest.TestCase):
- @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
+ @parameterized.expand([TEST_CASE_1, TEST_CASE_2])
@skip_if_quick
- def test_download_bundle(self, bundle_name, version):
+ def test_github_download_bundle(self, bundle_name, version):
bundle_files = ["model.pt", "model.ts", "network.json", "test_output.pt", "test_input.pt"]
repo = "Project-MONAI/MONAI-extra-test-data/0.8.1"
hash_val = "a131d39a0af717af32d19e565b434928"
@@ -72,7 +70,7 @@ def test_download_bundle(self, bundle_name, version):
# download a whole bundle from github releases
with tempfile.TemporaryDirectory() as tempdir:
cmd = ["coverage", "run", "-m", "monai.bundle", "download", "--name", bundle_name, "--source", "github"]
- cmd += ["--bundle_dir", tempdir, "--repo", repo, "--progress", "False"]
+ cmd += ["--bundle_dir", tempdir, "--repo", repo]
if version is not None:
cmd += ["--version", version]
command_line_tests(cmd)
@@ -82,7 +80,7 @@ def test_download_bundle(self, bundle_name, version):
if file == "network.json":
self.assertTrue(check_hash(filepath=file_path, val=hash_val))
- @parameterized.expand([TEST_CASE_4])
+ @parameterized.expand([TEST_CASE_3])
@skip_if_quick
def test_url_download_bundle(self, bundle_files, bundle_name, url, hash_val):
with skip_if_downloading_fails():
@@ -103,7 +101,7 @@ def test_url_download_bundle(self, bundle_files, bundle_name, url, hash_val):
class TestLoad(unittest.TestCase):
- @parameterized.expand([TEST_CASE_5])
+ @parameterized.expand([TEST_CASE_4])
@skip_if_quick
def test_load_weights(self, bundle_files, bundle_name, repo, device, model_file):
with skip_if_downloading_fails():
@@ -150,7 +148,7 @@ def test_load_weights(self, bundle_files, bundle_name, repo, device, model_file)
output_2 = model_2.forward(input_tensor)
assert_allclose(output_2, expected_output, atol=1e-4, rtol=1e-4, type_test=False)
- @parameterized.expand([TEST_CASE_6])
+ @parameterized.expand([TEST_CASE_5])
@skip_if_quick
@SkipIfBeforePyTorchVersion((1, 7, 1))
def test_load_ts_module(self, bundle_files, bundle_name, version, repo, device, model_file):
diff --git a/tests/test_integration_autorunner.py b/tests/test_integration_autorunner.py
index 4067aa1c6d..237045fda7 100644
--- a/tests/test_integration_autorunner.py
+++ b/tests/test_integration_autorunner.py
@@ -49,11 +49,10 @@
train_param = (
{
"CUDA_VISIBLE_DEVICES": list(range(num_gpus)),
- "num_iterations": int(4 / num_gpus),
- "num_iterations_per_validation": int(4 / num_gpus),
"num_images_per_batch": 2,
- "num_epochs": 1,
- "num_warmup_iterations": int(4 / num_gpus),
+ "num_epochs": 2,
+ "num_epochs_per_validation": 1,
+ "num_warmup_epochs": 1,
"use_pretrain": False,
"pretrained_path": "",
}
@@ -145,24 +144,21 @@ def test_autorunner_hpo(self) -> None:
runner = AutoRunner(work_dir=work_dir, input=self.data_src_cfg, hpo=True, ensemble=False)
hpo_param = {
"CUDA_VISIBLE_DEVICES": train_param["CUDA_VISIBLE_DEVICES"],
- "num_iterations": train_param["num_iterations"],
- "num_iterations_per_validation": train_param["num_iterations_per_validation"],
+ "num_epochs_per_validation": train_param["num_epochs_per_validation"],
"num_images_per_batch": train_param["num_images_per_batch"],
"num_epochs": train_param["num_epochs"],
- "num_warmup_iterations": train_param["num_warmup_iterations"],
+ "num_warmup_epochs": train_param["num_warmup_epochs"],
"use_pretrain": train_param["use_pretrain"],
"pretrained_path": train_param["pretrained_path"],
# below are to shorten the time for dints
- "training#num_iterations": train_param["num_iterations"],
- "training#num_iterations_per_validation": train_param["num_iterations_per_validation"],
+ "training#num_epochs_per_validation": train_param["num_epochs_per_validation"],
"training#num_images_per_batch": train_param["num_images_per_batch"],
"training#num_epochs": train_param["num_epochs"],
- "training#num_warmup_iterations": train_param["num_warmup_iterations"],
- "searching#num_iterations": train_param["num_iterations"],
- "searching#num_iterations_per_validation": train_param["num_iterations_per_validation"],
+ "training#num_warmup_epochs": train_param["num_warmup_epochs"],
+ "searching#num_epochs_per_validation": train_param["num_epochs_per_validation"],
"searching#num_images_per_batch": train_param["num_images_per_batch"],
"searching#num_epochs": train_param["num_epochs"],
- "searching#num_warmup_iterations": train_param["num_warmup_iterations"],
+ "searching#num_warmup_epochs": train_param["num_warmup_epochs"],
"nni_dry_run": True,
}
search_space = {"learning_rate": {"_type": "choice", "_value": [0.0001, 0.001, 0.01, 0.1]}}
diff --git a/tests/test_integration_gpu_customization.py b/tests/test_integration_gpu_customization.py
index c9e2cfec08..787c222c9f 100644
--- a/tests/test_integration_gpu_customization.py
+++ b/tests/test_integration_gpu_customization.py
@@ -49,11 +49,10 @@
train_param = (
{
"CUDA_VISIBLE_DEVICES": list(range(num_gpus)),
- "num_iterations": int(4 / num_gpus),
- "num_iterations_per_validation": int(4 / num_gpus),
"num_images_per_batch": 2,
- "num_epochs": 1,
- "num_warmup_iterations": int(4 / num_gpus),
+ "num_epochs": 2,
+ "num_epochs_per_validation": 1,
+ "num_warmup_epochs": 1,
"use_pretrain": False,
"pretrained_path": "",
}