From 86f56849bd39311f041e4c5dfdb51be0cb5eef22 Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Wed, 17 Nov 2021 12:05:23 +0000 Subject: [PATCH 1/3] drop pytorch 1.5.x Signed-off-by: Wenqi Li --- .github/workflows/cron.yml | 6 +++--- .github/workflows/pythonapp-min.yml | 6 +++--- .github/workflows/pythonapp.yml | 2 +- docs/requirements.txt | 2 +- monai/networks/layers/simplelayers.py | 3 +-- monai/visualize/class_activation_maps.py | 8 ++++---- pyproject.toml | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- tests/test_cachedataset.py | 4 ++-- tests/test_integration_workflows.py | 5 ++--- tests/test_invertd.py | 3 +-- tests/test_map_label_value.py | 18 ++++++++---------- tests/test_mmar_download.py | 8 +------- 14 files changed, 30 insertions(+), 41 deletions(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index bd4e51b16e..835069c833 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -15,7 +15,7 @@ jobs: runs-on: [self-hosted, linux, x64, common] strategy: matrix: - pytorch-version: [1.5.1, 1.6.0, 1.7.1, 1.8.1, latest] + pytorch-version: [1.6.0, 1.7.1, 1.8.1, 1.9.1, latest] steps: - uses: actions/checkout@v2 - name: Install the dependencies @@ -25,14 +25,14 @@ jobs: python -m pip uninstall -y torch torchvision if [ ${{ matrix.pytorch-version }} == "latest" ]; then python -m pip install torch torchvision - elif [ ${{ matrix.pytorch-version }} == "1.5.1" ]; then - python -m pip install torch==1.5.1 torchvision==0.6.1 elif [ ${{ matrix.pytorch-version }} == "1.6.0" ]; then python -m pip install torch==1.6.0 torchvision==0.7.0 elif [ ${{ matrix.pytorch-version }} == "1.7.1" ]; then python -m pip install torch==1.7.1 torchvision==0.8.2 elif [ ${{ matrix.pytorch-version }} == "1.8.1" ]; then python -m pip install torch==1.8.1 torchvision==0.9.1 + elif [ ${{ matrix.pytorch-version }} == "1.9.1" ]; then + python -m pip install torch==1.9.1 torchvision==0.10.1 fi python -m pip install -r requirements-dev.txt python -m pip list diff --git a/.github/workflows/pythonapp-min.yml b/.github/workflows/pythonapp-min.yml index 002701c5ad..e2ed6c7513 100644 --- a/.github/workflows/pythonapp-min.yml +++ b/.github/workflows/pythonapp-min.yml @@ -119,7 +119,7 @@ jobs: strategy: fail-fast: false matrix: - pytorch-version: [1.5.1, 1.6.0, 1.7.1, 1.8.1, latest] + pytorch-version: [1.6.0, 1.7.1, 1.8.1, 1.9.1, latest] timeout-minutes: 40 steps: - uses: actions/checkout@v2 @@ -148,14 +148,14 @@ jobs: # min. requirements if [ ${{ matrix.pytorch-version }} == "latest" ]; then python -m pip install torch - elif [ ${{ matrix.pytorch-version }} == "1.5.1" ]; then - python -m pip install torch==1.5.1 elif [ ${{ matrix.pytorch-version }} == "1.6.0" ]; then python -m pip install torch==1.6.0 elif [ ${{ matrix.pytorch-version }} == "1.7.1" ]; then python -m pip install torch==1.7.1 elif [ ${{ matrix.pytorch-version }} == "1.8.1" ]; then python -m pip install torch==1.8.1 + elif [ ${{ matrix.pytorch-version }} == "1.9.1" ]; then + python -m pip install torch==1.9.1 fi python -m pip install -r requirements-min.txt python -m pip list diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 4d69206247..2e2318c8a8 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -137,7 +137,7 @@ jobs: # install the latest pytorch for testing # however, "pip install monai*.tar.gz" will build cpp/cuda with an isolated # fresh torch installation according to pyproject.toml - python -m pip install torch>=1.5 torchvision + python -m pip install torch>=1.6 torchvision - name: Check packages run: | pip uninstall monai diff --git a/docs/requirements.txt b/docs/requirements.txt index 55bb8f0cb0..e3b7cb6136 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ -f https://download.pytorch.org/whl/cpu/torch-1.6.0%2Bcpu-cp37-cp37m-linux_x86_64.whl -torch>=1.5 +torch>=1.6 pytorch-ignite==0.4.6 numpy>=1.17 itk>=5.2 diff --git a/monai/networks/layers/simplelayers.py b/monai/networks/layers/simplelayers.py index 4f365d169e..0c0e68c32e 100644 --- a/monai/networks/layers/simplelayers.py +++ b/monai/networks/layers/simplelayers.py @@ -215,8 +215,7 @@ def separable_filtering(x: torch.Tensor, kernels: List[torch.Tensor], mode: str could be a single kernel (duplicated for all spatial dimensions), or a list of `spatial_dims` number of kernels. mode (string, optional): padding mode passed to convolution class. ``'zeros'``, ``'reflect'``, ``'replicate'`` - or ``'circular'``. Default: ``'zeros'``. Modes other than ``'zeros'`` require PyTorch version >= 1.5.1. See - torch.nn.Conv1d() for more information. + or ``'circular'``. Default: ``'zeros'``. See ``torch.nn.Conv1d()`` for more information. Raises: TypeError: When ``x`` is not a ``torch.Tensor``. diff --git a/monai/visualize/class_activation_maps.py b/monai/visualize/class_activation_maps.py index 6109d76a8a..cddd0b1270 100644 --- a/monai/visualize/class_activation_maps.py +++ b/monai/visualize/class_activation_maps.py @@ -19,7 +19,7 @@ from monai.config import NdarrayTensor from monai.transforms import ScaleIntensity -from monai.utils import ensure_tuple, get_torch_version_tuple +from monai.utils import ensure_tuple, pytorch_after from monai.visualize.visualizer import default_upsampler __all__ = ["CAM", "GradCAM", "GradCAMpp", "ModelWithHooks", "default_normalizer"] @@ -80,13 +80,13 @@ def __init__( continue _registered.append(name) if self.register_backward: - if get_torch_version_tuple() < (1, 8): - mod.register_backward_hook(self.backward_hook(name)) - else: + if pytorch_after(1, 8): if "inplace" in mod.__dict__ and mod.__dict__["inplace"]: # inplace=True causes errors for register_full_backward_hook mod.__dict__["inplace"] = False mod.register_full_backward_hook(self.backward_hook(name)) + else: + mod.register_backward_hook(self.backward_hook(name)) if self.register_forward: mod.register_forward_hook(self.forward_hook(name)) if len(_registered) != len(self.target_layers): diff --git a/pyproject.toml b/pyproject.toml index 008af45f97..b53a7640b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires = [ "wheel", "setuptools", - "torch>=1.5", + "torch>=1.6", "ninja", ] diff --git a/requirements.txt b/requirements.txt index 5d96284307..e4ea34b5d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -torch>=1.5 +torch>=1.6 numpy>=1.17 diff --git a/setup.cfg b/setup.cfg index 1a87d0d91a..03ae724657 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,7 @@ setup_requires = torch ninja install_requires = - torch>=1.5 + torch>=1.6 numpy>=1.17 [options.extras_require] diff --git a/tests/test_cachedataset.py b/tests/test_cachedataset.py index b7d2e97590..7f35b99ad5 100644 --- a/tests/test_cachedataset.py +++ b/tests/test_cachedataset.py @@ -20,7 +20,7 @@ from monai.data import CacheDataset, DataLoader, PersistentDataset, SmartCacheDataset from monai.transforms import Compose, Lambda, LoadImaged, RandLambda, ThreadUnsafe, Transform -from monai.utils import get_torch_version_tuple +from monai.utils.module import pytorch_after TEST_CASE_1 = [Compose([LoadImaged(keys=["image", "label", "extra"])]), (128, 128, 128)] @@ -134,7 +134,7 @@ class TestCacheThread(unittest.TestCase): @parameterized.expand(TEST_DS) def test_thread_safe(self, persistent_workers, cache_workers, loader_workers): expected = [102, 202, 302, 402, 502, 602, 702, 802, 902, 1002] - _kwg = {"persistent_workers": persistent_workers} if get_torch_version_tuple() > (1, 7) else {} + _kwg = {"persistent_workers": persistent_workers} if pytorch_after(1, 8) else {} data_list = list(range(1, 11)) dataset = CacheDataset( data=data_list, transform=_StatefulTransform(), cache_rate=1.0, num_workers=cache_workers, progress=False diff --git a/tests/test_integration_workflows.py b/tests/test_integration_workflows.py index 0ffef2935b..4d37d51e83 100644 --- a/tests/test_integration_workflows.py +++ b/tests/test_integration_workflows.py @@ -346,7 +346,7 @@ def _test_saved_files(postfix): def test_training(self): repeated = [] - test_rounds = 3 if monai.utils.module.get_torch_version_tuple() >= (1, 6) else 2 + test_rounds = 3 for i in range(test_rounds): results = self.train_and_infer(idx=i) repeated.append(results) @@ -354,8 +354,7 @@ def test_training(self): @TimedCall(seconds=300, skip_timing=not torch.cuda.is_available(), daemon=False) def test_timing(self): - if monai.utils.module.get_torch_version_tuple() >= (1, 6): - self.train_and_infer(idx=2) + self.train_and_infer(idx=2) if __name__ == "__main__": diff --git a/tests/test_invertd.py b/tests/test_invertd.py index a04064c315..c0a949dd9a 100644 --- a/tests/test_invertd.py +++ b/tests/test_invertd.py @@ -161,9 +161,8 @@ def test_invert(self): print("invert diff", reverted.size - n_good) # 25300: 2 workers (cpu, non-macos) # 1812: 0 workers (gpu or macos) - # 1824: torch 1.5.1 # 1821: windows torch 1.10.0 - self.assertTrue((reverted.size - n_good) in (34007, 1812, 1824, 1821), f"diff. {reverted.size - n_good}") + self.assertTrue((reverted.size - n_good) in (34007, 1812, 1821), f"diff. {reverted.size - n_good}") set_determinism(seed=None) diff --git a/tests/test_map_label_value.py b/tests/test_map_label_value.py index 009dbd4281..5705074c06 100644 --- a/tests/test_map_label_value.py +++ b/tests/test_map_label_value.py @@ -16,7 +16,6 @@ from parameterized import parameterized from monai.transforms import MapLabelValue -from monai.utils import pytorch_after from tests.utils import TEST_NDARRAYS TESTS = [] @@ -33,15 +32,14 @@ [{"orig_labels": [1, 2, 3], "target_labels": [0.5, 1.5, 2.5]}, p([3, 1, 1, 2]), p([2.5, 0.5, 0.5, 1.5])], ] ) - # PyTorch 1.5.1 doesn't support rich dtypes - if pytorch_after(1, 7): - TESTS.append( - [ - {"orig_labels": [1.5, 2.5, 3.5], "target_labels": [0, 1, 2], "dtype": np.int8}, - p([3.5, 1.5, 1.5, 2.5]), - p([2, 0, 0, 1]), - ] - ) + # note: PyTorch 1.5.1 doesn't support rich dtypes + TESTS.append( + [ + {"orig_labels": [1.5, 2.5, 3.5], "target_labels": [0, 1, 2], "dtype": np.int8}, + p([3.5, 1.5, 1.5, 2.5]), + p([2, 0, 0, 1]), + ] + ) TESTS.extend( [ [ diff --git a/tests/test_mmar_download.py b/tests/test_mmar_download.py index bc098d74c4..7e778fa334 100644 --- a/tests/test_mmar_download.py +++ b/tests/test_mmar_download.py @@ -22,7 +22,7 @@ from monai.apps import RemoteMMARKeys, download_mmar, get_model_spec, load_from_mmar from monai.apps.mmars import MODEL_DESC from monai.apps.mmars.mmars import _get_val -from tests.utils import SkipIfAtLeastPyTorchVersion, SkipIfBeforePyTorchVersion, skip_if_quick +from tests.utils import SkipIfBeforePyTorchVersion, skip_if_quick TEST_CASES = [["clara_pt_prostate_mri_segmentation_1"], ["clara_pt_covid19_ct_lesion_segmentation_1"]] TEST_EXTRACT_CASES = [ @@ -125,7 +125,6 @@ def test_download(self, idx): @parameterized.expand(TEST_EXTRACT_CASES) @skip_if_quick - @SkipIfBeforePyTorchVersion((1, 6)) def test_load_ckpt(self, input_args, expected_name, expected_val): try: output = load_from_mmar(**input_args) @@ -143,11 +142,6 @@ def test_unique(self): keys = sorted(m["id"] for m in MODEL_DESC) self.assertTrue(keys == sorted(set(keys))) - @SkipIfAtLeastPyTorchVersion((1, 6)) - def test_no_default(self): - with self.assertRaises(ValueError): - download_mmar(0) - def test_search(self): self.assertEqual(_get_val({"a": 1, "b": 2}, key="b"), 2) self.assertEqual(_get_val({"a": {"c": {"c": 4}}, "b": {"c": 2}}, key="b"), {"c": 2}) From 1ad89be946dc7a595b39fb75a077d5119d981eab Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Wed, 17 Nov 2021 12:44:47 +0000 Subject: [PATCH 2/3] update premerge gpu Signed-off-by: Wenqi Li --- .github/workflows/pythonapp-gpu.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pythonapp-gpu.yml b/.github/workflows/pythonapp-gpu.yml index e290bcfd99..6e460a2ade 100644 --- a/.github/workflows/pythonapp-gpu.yml +++ b/.github/workflows/pythonapp-gpu.yml @@ -19,17 +19,13 @@ jobs: strategy: matrix: environment: - - "PT16+CUDA110" - "PT17+CUDA102" - "PT17+CUDA110" - "PT18+CUDA102" + - "PT18+CUDA110" - "PT19+CUDA114" - "PT110+CUDA102" include: - - environment: PT16+CUDA110 - # we explicitly set pytorch to -h to avoid pip install error - pytorch: "-h" - base: "nvcr.io/nvidia/pytorch:20.07-py3" - environment: PT17+CUDA102 pytorch: "torch==1.7.1 torchvision==0.8.2" base: "nvcr.io/nvidia/cuda:10.2-devel-ubuntu18.04" @@ -40,6 +36,10 @@ jobs: - environment: PT18+CUDA102 pytorch: "torch==1.8.1 torchvision==0.9.1" base: "nvcr.io/nvidia/cuda:10.2-devel-ubuntu18.04" + - environment: PT18+CUDA110 + # we explicitly set pytorch to -h to avoid pip install error + pytorch: "-h" + base: "nvcr.io/nvidia/pytorch:21.02-py3" - environment: PT19+CUDA114 # we explicitly set pytorch to -h to avoid pip install error # https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes From ee8fd2d5525e399121ce2abca943387588f20b40 Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 18 Nov 2021 09:52:39 +0000 Subject: [PATCH 3/3] update based on comments Signed-off-by: Wenqi Li --- monai/transforms/utils_pytorch_numpy_unification.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/monai/transforms/utils_pytorch_numpy_unification.py b/monai/transforms/utils_pytorch_numpy_unification.py index 00ac8ab566..0ae08775ae 100644 --- a/monai/transforms/utils_pytorch_numpy_unification.py +++ b/monai/transforms/utils_pytorch_numpy_unification.py @@ -297,12 +297,7 @@ def searchsorted(a: NdarrayOrTensor, v: NdarrayOrTensor, right=False, sorter=Non side = "right" if right else "left" if isinstance(a, np.ndarray): return np.searchsorted(a, v, side, sorter) # type: ignore - if hasattr(torch, "searchsorted"): - return torch.searchsorted(a, v, right=right) # type: ignore - # if using old PyTorch, will convert to numpy array then compute - ret = np.searchsorted(a.cpu().numpy(), v.cpu().numpy(), side, sorter) # type: ignore - ret, *_ = convert_to_dst_type(ret, a) - return ret + return torch.searchsorted(a, v, right=right) # type: ignore def repeat(a: NdarrayOrTensor, repeats: int, axis: Optional[int] = None):