From fb6d0f8003773eea31a82d5ec06bc6f570f40256 Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 10 Dec 2020 17:38:15 +0000 Subject: [PATCH 1/4] ci/cd with 1.7.1 Signed-off-by: Wenqi Li --- .github/workflows/cron.yml | 3 +++ .github/workflows/integration.yml | 2 +- .github/workflows/pythonapp.yml | 13 +++++++------ .github/workflows/setupapp.yml | 5 +++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index fa68be8c68..9a22a0a3d7 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -3,6 +3,9 @@ name: crons on: schedule: - cron: "0 2 * * *" # at 02:00 UTC + push: + branches: + - 1344-torch-1-7-1 jobs: cron-gpu: diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 1dcc7675f0..003a746de4 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -34,7 +34,7 @@ jobs: which python python -m pip install --upgrade pip wheel python -m pip uninstall -y torch torchvision - python -m pip install torch==1.7.0 torchvision==0.8.1 + python -m pip install torch==1.7.1 torchvision==0.8.2 python -m pip install -r requirements-dev.txt - name: Run integration tests run: | diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 4db50b2723..d55ade5422 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - 1344-torch-1-7-1 pull_request: jobs: @@ -80,13 +81,13 @@ jobs: - if: runner.os == 'windows' name: Install torch cpu from pytorch.org (Windows only) run: | - python -m pip install torch==1.7.0+cpu torchvision==0.8.1+cpu -f https://download.pytorch.org/whl/torch_stable.html + python -m pip install torch==1.7.1+cpu torchvision==0.8.2+cpu -f https://download.pytorch.org/whl/torch_stable.html # min. requirements for windows instances python -c "f=open('requirements-dev.txt', 'r'); txt=f.readlines(); f.close(); print(txt); f=open('requirements-dev.txt', 'w'); f.writelines(txt[1:12]); f.close()" - name: Install the dependencies run: | - python -m pip install torch==1.7.0 - python -m pip install torchvision==0.8.1 + python -m pip install torch==1.7.1 + python -m pip install torchvision==0.8.2 cat "requirements-dev.txt" python -m pip install -r requirements-dev.txt python -m pip list @@ -134,11 +135,11 @@ jobs: - if: runner.os == 'windows' name: Install torch cpu from pytorch.org (Windows only) run: | - python -m pip install torch==1.7.0+cpu -f https://download.pytorch.org/whl/torch_stable.html + python -m pip install torch==1.7.1+cpu -f https://download.pytorch.org/whl/torch_stable.html - name: Install the dependencies run: | # min. requirements - python -m pip install torch==1.7.0 + python -m pip install torch==1.7.1 python -m pip install -r requirements-min.txt python -m pip list BUILD_MONAI=0 python setup.py develop # no compile of extensions @@ -173,7 +174,7 @@ jobs: pytorch: "-h" base: "nvcr.io/nvidia/pytorch:20.07-py3" - environment: PT17+CUDA102 - pytorch: "torch==1.7.0 torchvision==0.8.1" + pytorch: "torch==1.7.1 torchvision==0.8.2" base: "nvcr.io/nvidia/cuda:10.2-devel-ubuntu18.04" - environment: PT17+CUDA110 # we explicitly set pytorch to -h to avoid pip install error diff --git a/.github/workflows/setupapp.yml b/.github/workflows/setupapp.yml index a3a1745977..8516a7e2ec 100644 --- a/.github/workflows/setupapp.yml +++ b/.github/workflows/setupapp.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - 1344-torch-1-7-1 jobs: # caching of these jobs: @@ -36,7 +37,7 @@ jobs: which python python -m pip install --upgrade pip wheel python -m pip uninstall -y torch torchvision - python -m pip install torch==1.7.0 torchvision==0.8.1 + python -m pip install torch==1.7.1 torchvision==0.8.2 python -m pip install -r requirements-dev.txt - name: Run unit tests report coverage run: | @@ -82,7 +83,7 @@ jobs: - name: Install the dependencies run: | python -m pip install --upgrade pip wheel - python -m pip install torch==1.7.0 torchvision==0.8.1 + python -m pip install torch==1.7.1 torchvision==0.8.2 python -m pip install -r requirements-dev.txt - name: Run quick tests CPU ubuntu run: | From 83270da3099eed693f8a2ef5542b3e2ac3418ebf Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 10 Dec 2020 20:30:24 +0000 Subject: [PATCH 2/4] revise various tests, fixes docs Signed-off-by: Wenqi Li --- .github/workflows/pythonapp.yml | 2 +- docs/source/utils.rst | 5 +++++ monai/config/deviceconfig.py | 8 -------- monai/networks/layers/simplelayers.py | 7 +++---- monai/transforms/intensity/array.py | 5 ++--- monai/utils/misc.py | 21 --------------------- monai/utils/module.py | 23 +++++++++++++++++++++++ monai/utils/profiling.py | 22 +++++++++++++++++++++- tests/min_tests.py | 2 ++ tests/test_detect_envelope.py | 3 +-- tests/test_highresnet.py | 2 +- tests/test_hilbert_transform.py | 3 +-- tests/test_integration_workflows.py | 4 ++-- tests/utils.py | 13 ++++++++++--- 14 files changed, 72 insertions(+), 48 deletions(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 4db50b2723..10942e3100 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -108,7 +108,7 @@ jobs: fail-fast: false matrix: os: [windows-latest, macOS-latest, ubuntu-latest] - timeout-minutes: 20 + timeout-minutes: 40 steps: - uses: actions/checkout@v2 - name: Set up Python 3.8 diff --git a/docs/source/utils.rst b/docs/source/utils.rst index 6a03529c30..e0b993da60 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -26,3 +26,8 @@ Misc ---- .. automodule:: monai.utils.misc :members: + +Profiling +--------- +.. automodule:: monai.utils.profiling + :members: diff --git a/monai/config/deviceconfig.py b/monai/config/deviceconfig.py index 1bb2bb7907..355069f941 100644 --- a/monai/config/deviceconfig.py +++ b/monai/config/deviceconfig.py @@ -100,14 +100,6 @@ def set_visible_devices(*dev_inds): os.environ["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, dev_inds)) -def get_torch_version_tuple(): - """ - Returns: - tuple of ints represents the pytorch major/minor version. - """ - return tuple((int(x) for x in torch.__version__.split(".")[:2])) - - def _dict_append(in_dict, key, fn): try: in_dict[key] = fn() diff --git a/monai/networks/layers/simplelayers.py b/monai/networks/layers/simplelayers.py index bd800a7c91..4860b2862c 100644 --- a/monai/networks/layers/simplelayers.py +++ b/monai/networks/layers/simplelayers.py @@ -17,12 +17,11 @@ from torch import nn from torch.autograd import Function -from monai.config import get_torch_version_tuple from monai.networks.layers.convutils import gaussian_1d, same_padding -from monai.utils import InvalidPyTorchVersionError, SkipMode, ensure_tuple_rep, optional_import +from monai.utils import PT_BEFORE_1_7, InvalidPyTorchVersionError, SkipMode, ensure_tuple_rep, optional_import _C, _ = optional_import("monai._C") -if tuple(int(s) for s in torch.__version__.split(".")[0:2]) >= (1, 7): +if not PT_BEFORE_1_7: fft, _ = optional_import("torch.fft") __all__ = ["SkipConnection", "Flatten", "GaussianFilter", "LLTM", "Reshape", "separable_filtering", "HilbertTransform"] @@ -145,7 +144,7 @@ class HilbertTransform(nn.Module): def __init__(self, axis: int = 2, n: Union[int, None] = None) -> None: - if get_torch_version_tuple() < (1, 7): + if PT_BEFORE_1_7: raise InvalidPyTorchVersionError("1.7.0", self.__class__.__name__) super().__init__() diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index b1b11fe6f4..58eb0da2a7 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -20,11 +20,10 @@ import numpy as np import torch -from monai.config import get_torch_version_tuple from monai.networks.layers import GaussianFilter, HilbertTransform from monai.transforms.compose import Randomizable, Transform from monai.transforms.utils import rescale_array -from monai.utils import InvalidPyTorchVersionError, dtype_torch_to_numpy, ensure_tuple_size +from monai.utils import PT_BEFORE_1_7, InvalidPyTorchVersionError, dtype_torch_to_numpy, ensure_tuple_size class RandGaussianNoise(Randomizable, Transform): @@ -524,7 +523,7 @@ class DetectEnvelope(Transform): def __init__(self, axis: int = 1, n: Union[int, None] = None) -> None: - if get_torch_version_tuple() < (1, 7): + if PT_BEFORE_1_7: raise InvalidPyTorchVersionError("1.7.0", self.__class__.__name__) if axis < 0: diff --git a/monai/utils/misc.py b/monai/utils/misc.py index ef688174f1..020884bbcc 100644 --- a/monai/utils/misc.py +++ b/monai/utils/misc.py @@ -12,7 +12,6 @@ import collections.abc import itertools import random -import time from ast import literal_eval from distutils.util import strtobool from typing import Any, Callable, Optional, Sequence, Tuple, Union @@ -287,23 +286,3 @@ def dtype_torch_to_numpy(dtype): def dtype_numpy_to_torch(dtype): """Convert a numpy dtype to its torch equivalent.""" return _np_to_torch_dtype[dtype] - - -class PerfContext: - """ - Context manager for tracking how much time is spent within context blocks. This uses `time.perf_counter` to - accumulate the total amount of time in seconds in the attribute `total_time` over however many context blocks - the object is used in. - """ - - def __init__(self): - self.total_time = 0 - self.start_time = None - - def __enter__(self): - self.start_time = time.perf_counter() - return self - - def __exit__(self, exc_type, exc_value, exc_traceback): - self.total_time += time.perf_counter() - self.start_time - self.start_time = None diff --git a/monai/utils/module.py b/monai/utils/module.py index 039c4f9fb5..4bc9a6d63b 100644 --- a/monai/utils/module.py +++ b/monai/utils/module.py @@ -16,6 +16,8 @@ from re import match from typing import Any, Callable, List, Sequence, Tuple, Union +import torch + from .misc import ensure_tuple OPTIONAL_IMPORT_MSG_FMT = "{}" @@ -31,6 +33,8 @@ "get_full_type_name", "has_option", "get_package_version", + "get_torch_version_tuple", + "PT_BEFORE_1_7", ] @@ -264,3 +268,22 @@ def get_package_version(dep_name, default="NOT INSTALLED or UNKNOWN VERSION."): del dep del sys.modules[dep_name] return dep_ver + + +def get_torch_version_tuple(): + """ + Returns: + tuple of ints represents the pytorch major/minor version. + """ + return tuple((int(x) for x in torch.__version__.split(".")[:2])) + + +PT_BEFORE_1_7 = True +ver, has_ver = optional_import("pkg_resources", name="parse_version") +try: + if has_ver: + PT_BEFORE_1_7 = ver(torch.__version__) < ver("1.7") + else: + PT_BEFORE_1_7 = get_torch_version_tuple() < (1, 7) +except (AttributeError, TypeError): + pass diff --git a/monai/utils/profiling.py b/monai/utils/profiling.py index a8d6e2c630..bcdc0357c4 100644 --- a/monai/utils/profiling.py +++ b/monai/utils/profiling.py @@ -14,7 +14,7 @@ import torch -__all__ = ["torch_profiler_full", "torch_profiler_time_cpu_gpu", "torch_profiler_time_end_to_end"] +__all__ = ["torch_profiler_full", "torch_profiler_time_cpu_gpu", "torch_profiler_time_end_to_end", "PerfContext"] def torch_profiler_full(func): @@ -88,3 +88,23 @@ def wrapper(*args, **kwargs): return result return wrapper + + +class PerfContext: + """ + Context manager for tracking how much time is spent within context blocks. This uses `time.perf_counter` to + accumulate the total amount of time in seconds in the attribute `total_time` over however many context blocks + the object is used in. + """ + + def __init__(self): + self.total_time = 0 + self.start_time = None + + def __enter__(self): + self.start_time = time.perf_counter() + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.total_time += time.perf_counter() - self.start_time + self.start_time = None diff --git a/tests/min_tests.py b/tests/min_tests.py index e22d94bc57..cd855aa543 100644 --- a/tests/min_tests.py +++ b/tests/min_tests.py @@ -32,6 +32,7 @@ def run_testsuit(): "test_cachedataset", "test_cachedataset_parallel", "test_dataset", + "test_detect_envelope", "test_iterable_dataset", "test_ensemble_evaluator", "test_handler_checkpoint_loader", @@ -51,6 +52,7 @@ def run_testsuit(): "test_handler_validation", "test_hausdorff_distance", "test_header_correct", + "test_hilbert_transform", "test_img2tensorboard", "test_integration_segmentation_3d", "test_integration_sliding_window", diff --git a/tests/test_detect_envelope.py b/tests/test_detect_envelope.py index aec014731b..cbd281f6e8 100644 --- a/tests/test_detect_envelope.py +++ b/tests/test_detect_envelope.py @@ -156,9 +156,8 @@ def test_no_fft_module_error(self): @SkipIfAtLeastPyTorchVersion((1, 7)) class TestDetectEnvelopeInvalidPyTorch(unittest.TestCase): def test_invalid_pytorch_error(self): - with self.assertRaises(InvalidPyTorchVersionError) as cm: + with self.assertRaisesRegexp(InvalidPyTorchVersionError, "version"): DetectEnvelope() - self.assertEqual("DetectEnvelope requires PyTorch version 1.7.0 or later", str(cm.exception)) if __name__ == "__main__": diff --git a/tests/test_highresnet.py b/tests/test_highresnet.py index 6a4b129588..0afa4b1a9b 100644 --- a/tests/test_highresnet.py +++ b/tests/test_highresnet.py @@ -53,7 +53,7 @@ def test_shape(self, input_param, input_shape, expected_shape): result = net.forward(torch.randn(input_shape).to(device)) self.assertEqual(result.shape, expected_shape) - @TimedCall(seconds=100, force_quit=True) + @TimedCall(seconds=200, force_quit=True) def test_script(self): input_param, input_shape, expected_shape = TEST_CASE_1 net = HighResNet(**input_param) diff --git a/tests/test_hilbert_transform.py b/tests/test_hilbert_transform.py index 1e9e0e660f..1099468102 100644 --- a/tests/test_hilbert_transform.py +++ b/tests/test_hilbert_transform.py @@ -217,9 +217,8 @@ def test_no_fft_module_error(self): @SkipIfAtLeastPyTorchVersion((1, 7)) class TestHilbertTransformInvalidPyTorch(unittest.TestCase): def test_invalid_pytorch_error(self): - with self.assertRaises(InvalidPyTorchVersionError) as cm: + with self.assertRaisesRegex(InvalidPyTorchVersionError, "version"): HilbertTransform() - self.assertEqual("HilbertTransform requires PyTorch version 1.7.0 or later", str(cm.exception)) if __name__ == "__main__": diff --git a/tests/test_integration_workflows.py b/tests/test_integration_workflows.py index 2a0b5d5d86..8e96947ccb 100644 --- a/tests/test_integration_workflows.py +++ b/tests/test_integration_workflows.py @@ -296,7 +296,7 @@ def train_and_infer(self, idx=0): def test_training(self): repeated = [] - test_rounds = 3 if monai.config.get_torch_version_tuple() >= (1, 6) else 2 + test_rounds = 3 if monai.utils.module.get_torch_version_tuple() >= (1, 6) else 2 for i in range(test_rounds): results = self.train_and_infer(idx=i) repeated.append(results) @@ -308,7 +308,7 @@ def test_training(self): daemon=False, ) def test_timing(self): - if monai.config.get_torch_version_tuple() >= (1, 6): + if monai.utils.module.get_torch_version_tuple() >= (1, 6): self.train_and_infer(idx=2) diff --git a/tests/utils.py b/tests/utils.py index 763ddaa9b2..50c159053e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -28,11 +28,12 @@ import torch import torch.distributed as dist -from monai.config import get_torch_version_tuple from monai.data import create_test_image_2d, create_test_image_3d from monai.utils import ensure_tuple, optional_import, set_determinism +from monai.utils.module import get_torch_version_tuple nib, _ = optional_import("nibabel") +ver, has_pkg_res = optional_import("pkg_resources", name="parse_version") quick_test_var = "QUICKTEST" @@ -99,7 +100,10 @@ class SkipIfBeforePyTorchVersion(object): def __init__(self, pytorch_version_tuple): self.min_version = pytorch_version_tuple - self.version_too_old = get_torch_version_tuple() < self.min_version + if has_pkg_res: + self.version_too_old = ver(torch.__version__) < ver(".".join(map(str, self.min_version))) + else: + self.version_too_old = get_torch_version_tuple() < self.min_version def __call__(self, obj): return unittest.skipIf( @@ -113,7 +117,10 @@ class SkipIfAtLeastPyTorchVersion(object): def __init__(self, pytorch_version_tuple): self.max_version = pytorch_version_tuple - self.version_too_new = get_torch_version_tuple() >= self.max_version + if has_pkg_res: + self.version_too_new = ver(torch.__version__) >= ver(".".join(map(str, self.max_version))) + else: + self.version_too_new = get_torch_version_tuple() >= self.max_version def __call__(self, obj): return unittest.skipIf( From 04345693e55cde7621831914a1e0f3971c6325da Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 10 Dec 2020 20:31:09 +0000 Subject: [PATCH 3/4] temp full tests Signed-off-by: Wenqi Li --- .github/workflows/cron.yml | 3 +++ .github/workflows/pythonapp.yml | 1 + .github/workflows/setupapp.yml | 1 + 3 files changed, 5 insertions(+) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index fa68be8c68..c828560aa0 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -3,6 +3,9 @@ name: crons on: schedule: - cron: "0 2 * * *" # at 02:00 UTC + push: + branches: + - minor-fixes jobs: cron-gpu: diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 10942e3100..eb39ec20c3 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - minor-fixes pull_request: jobs: diff --git a/.github/workflows/setupapp.yml b/.github/workflows/setupapp.yml index a3a1745977..d156f66574 100644 --- a/.github/workflows/setupapp.yml +++ b/.github/workflows/setupapp.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - minor-fixes jobs: # caching of these jobs: From 1ed99d3d47918b1d21a40d82d8767c68265a5f23 Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 10 Dec 2020 22:16:51 +0000 Subject: [PATCH 4/4] remove temp. tests Signed-off-by: Wenqi Li --- .github/workflows/cron.yml | 3 --- .github/workflows/pythonapp.yml | 1 - .github/workflows/setupapp.yml | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 9a22a0a3d7..fa68be8c68 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -3,9 +3,6 @@ name: crons on: schedule: - cron: "0 2 * * *" # at 02:00 UTC - push: - branches: - - 1344-torch-1-7-1 jobs: cron-gpu: diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index d9930e9464..8e92ea0ed7 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -5,7 +5,6 @@ on: push: branches: - master - - 1344-torch-1-7-1 pull_request: jobs: diff --git a/.github/workflows/setupapp.yml b/.github/workflows/setupapp.yml index 8516a7e2ec..ed5d560861 100644 --- a/.github/workflows/setupapp.yml +++ b/.github/workflows/setupapp.yml @@ -5,7 +5,6 @@ on: push: branches: - master - - 1344-torch-1-7-1 jobs: # caching of these jobs: @@ -152,7 +151,7 @@ jobs: run: | docker build -t localhost:5000/local_monai:latest -f Dockerfile . docker push localhost:5000/local_monai:latest - sed -i '/flake/d' requirements-dev.txt + sed -i '/flake/d' requirements-dev.txt docker build -t projectmonai/monai:latest -f Dockerfile . docker login -u projectmonai -p ${{ secrets.DOCKER_PW }} docker push projectmonai/monai:latest