From dfae9a0f6ecb7455ca61bf98bc9694592133c8ea Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Mon, 20 Jan 2025 17:06:12 +0000 Subject: [PATCH 01/10] Changing Numpy version limit to include 2.x but not a theoretical 3.x Signed-off-by: Eric Kerfoot --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e184322c13..8f59b6f34d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ torch>=1.9 -numpy>=1.24,<2.0 +numpy>=1.24,<3.0 From 3eabf202771f58ccb956d7a61614856ec62c5205 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Mon, 20 Jan 2025 17:23:03 +0000 Subject: [PATCH 02/10] Changing Numpy version limit to include 2.x but not a theoretical 3.x Signed-off-by: Eric Kerfoot --- environment-dev.yml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/environment-dev.yml b/environment-dev.yml index 4a1723e8a5..e5a330ad23 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -5,7 +5,7 @@ channels: - nvidia - conda-forge dependencies: - - numpy>=1.24,<2.0 + - numpy>=1.24,<3.0 - pytorch>=1.9 - torchio - torchvision diff --git a/setup.cfg b/setup.cfg index 0c69051218..6337526ca0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,7 +43,7 @@ setup_requires = packaging install_requires = torch>=1.9 - numpy>=1.24,<2.0 + numpy>=1.24,<3.0 [options.extras_require] all = From a7b615e77babbbfba456bde8a74fe28a9fd21b33 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 14:49:31 +0000 Subject: [PATCH 03/10] Cleaning up autocast usage Signed-off-by: Eric Kerfoot --- monai/apps/deepedit/interaction.py | 2 +- monai/apps/deepgrow/interaction.py | 2 +- monai/bundle/scripts.py | 3 +-- monai/engines/evaluator.py | 16 ++++++++-------- monai/engines/trainer.py | 18 +++++++++--------- monai/engines/workflow.py | 4 ++-- monai/networks/layers/vector_quantizer.py | 4 ++-- monai/networks/utils.py | 4 ++-- tests/config/test_cv2_dist.py | 3 +-- tests/data/meta_tensor/test_meta_tensor.py | 2 +- .../integration/test_integration_fast_train.py | 4 ++-- 11 files changed, 30 insertions(+), 32 deletions(-) diff --git a/monai/apps/deepedit/interaction.py b/monai/apps/deepedit/interaction.py index 07302575c6..33e50700ca 100644 --- a/monai/apps/deepedit/interaction.py +++ b/monai/apps/deepedit/interaction.py @@ -72,7 +72,7 @@ def __call__(self, engine: SupervisedTrainer | SupervisedEvaluator, batchdata: d with torch.no_grad(): if engine.amp: - with torch.cuda.amp.autocast(): + with torch.autocast("cuda"): predictions = engine.inferer(inputs, engine.network) else: predictions = engine.inferer(inputs, engine.network) diff --git a/monai/apps/deepgrow/interaction.py b/monai/apps/deepgrow/interaction.py index fa3a28bfef..287f2d607c 100644 --- a/monai/apps/deepgrow/interaction.py +++ b/monai/apps/deepgrow/interaction.py @@ -67,7 +67,7 @@ def __call__(self, engine: SupervisedTrainer | SupervisedEvaluator, batchdata: d engine.network.eval() with torch.no_grad(): if engine.amp: - with torch.cuda.amp.autocast(): + with torch.autocast("cuda"): predictions = engine.inferer(inputs, engine.network) else: predictions = engine.inferer(inputs, engine.network) diff --git a/monai/bundle/scripts.py b/monai/bundle/scripts.py index 5089f0c045..ccb8010e65 100644 --- a/monai/bundle/scripts.py +++ b/monai/bundle/scripts.py @@ -1256,9 +1256,8 @@ def verify_net_in_out( if input_dtype == torch.float16: # fp16 can only be executed in gpu mode net.to("cuda") - from torch.cuda.amp import autocast - with autocast(): + with torch.autocast("cuda"): output = net(test_data.cuda(), **extra_forward_args_) net.to(device_) else: diff --git a/monai/engines/evaluator.py b/monai/engines/evaluator.py index d70a39726b..b3bf3a9c30 100644 --- a/monai/engines/evaluator.py +++ b/monai/engines/evaluator.py @@ -82,8 +82,8 @@ class Evaluator(Workflow): default to `True`. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. """ @@ -214,8 +214,8 @@ class SupervisedEvaluator(Evaluator): default to `True`. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. compile: whether to use `torch.compile`, default is False. If True, MetaTensor inputs will be converted to `torch.Tensor` before forward pass, then converted back afterward with copied meta information. compile_kwargs: dict of the args for `torch.compile()` API, for more details: @@ -329,7 +329,7 @@ def _iteration(self, engine: SupervisedEvaluator, batchdata: dict[str, torch.Ten # execute forward computation with engine.mode(engine.network): if engine.amp: - with torch.cuda.amp.autocast(**engine.amp_kwargs): + with torch.autocast("cuda", **engine.amp_kwargs): engine.state.output[Keys.PRED] = engine.inferer(inputs, engine.network, *args, **kwargs) else: engine.state.output[Keys.PRED] = engine.inferer(inputs, engine.network, *args, **kwargs) @@ -399,8 +399,8 @@ class EnsembleEvaluator(Evaluator): default to `True`. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. """ @@ -492,7 +492,7 @@ def _iteration(self, engine: EnsembleEvaluator, batchdata: dict[str, torch.Tenso for idx, network in enumerate(engine.networks): with engine.mode(network): if engine.amp: - with torch.cuda.amp.autocast(**engine.amp_kwargs): + with torch.autocast("cuda",**engine.amp_kwargs): if isinstance(engine.state.output, dict): engine.state.output.update( {engine.pred_keys[idx]: engine.inferer(inputs, network, *args, **kwargs)} diff --git a/monai/engines/trainer.py b/monai/engines/trainer.py index a0be86bae5..303cd6ad54 100644 --- a/monai/engines/trainer.py +++ b/monai/engines/trainer.py @@ -126,8 +126,8 @@ class SupervisedTrainer(Trainer): more details: https://pytorch.org/docs/stable/generated/torch.optim.Optimizer.zero_grad.html. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. compile: whether to use `torch.compile`, default is False. If True, MetaTensor inputs will be converted to `torch.Tensor` before forward pass, then converted back afterward with copied meta information. compile_kwargs: dict of the args for `torch.compile()` API, for more details: @@ -255,7 +255,7 @@ def _compute_pred_loss(): engine.optimizer.zero_grad(set_to_none=engine.optim_set_to_none) if engine.amp and engine.scaler is not None: - with torch.cuda.amp.autocast(**engine.amp_kwargs): + with torch.autocast("cuda",**engine.amp_kwargs): _compute_pred_loss() engine.scaler.scale(engine.state.output[Keys.LOSS]).backward() engine.fire_event(IterationEvents.BACKWARD_COMPLETED) @@ -341,8 +341,8 @@ class GanTrainer(Trainer): more details: https://pytorch.org/docs/stable/generated/torch.optim.Optimizer.zero_grad.html. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. """ @@ -518,8 +518,8 @@ class AdversarialTrainer(Trainer): more details: https://pytorch.org/docs/stable/generated/torch.optim.Optimizer.zero_grad.html. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. """ def __init__( @@ -689,7 +689,7 @@ def _compute_generator_loss() -> None: engine.state.g_optimizer.zero_grad(set_to_none=engine.optim_set_to_none) if engine.amp and engine.state.g_scaler is not None: - with torch.cuda.amp.autocast(**engine.amp_kwargs): + with torch.autocast("cuda",**engine.amp_kwargs): _compute_generator_loss() engine.state.output[Keys.LOSS] = ( @@ -737,7 +737,7 @@ def _compute_discriminator_loss() -> None: engine.state.d_network.zero_grad(set_to_none=engine.optim_set_to_none) if engine.amp and engine.state.d_scaler is not None: - with torch.cuda.amp.autocast(**engine.amp_kwargs): + with torch.autocast("cuda",**engine.amp_kwargs): _compute_discriminator_loss() engine.state.d_scaler.scale(engine.state.output[AdversarialKeys.DISCRIMINATOR_LOSS]).backward() diff --git a/monai/engines/workflow.py b/monai/engines/workflow.py index 0c36da6d3d..ecb0c4a070 100644 --- a/monai/engines/workflow.py +++ b/monai/engines/workflow.py @@ -90,8 +90,8 @@ class Workflow(Engine): default to `True`. to_kwargs: dict of other args for `prepare_batch` API when converting the input data, except for `device`, `non_blocking`. - amp_kwargs: dict of the args for `torch.cuda.amp.autocast()` API, for more details: - https://pytorch.org/docs/stable/amp.html#torch.cuda.amp.autocast. + amp_kwargs: dict of the args for `torch.autocast("cuda")` API, for more details: + https://pytorch.org/docs/stable/amp.html#torch.autocast. Raises: TypeError: When ``data_loader`` is not a ``torch.utils.data.DataLoader``. diff --git a/monai/networks/layers/vector_quantizer.py b/monai/networks/layers/vector_quantizer.py index 9c354e1009..19b23a8e8e 100644 --- a/monai/networks/layers/vector_quantizer.py +++ b/monai/networks/layers/vector_quantizer.py @@ -100,7 +100,7 @@ def quantize(self, inputs: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, to torch.Tensor: Quantization indices of shape [B,H,W,D,1] """ - with torch.cuda.amp.autocast(enabled=False): + with torch.autocast("cuda",enabled=False): encoding_indices_view = list(inputs.shape) del encoding_indices_view[1] @@ -138,7 +138,7 @@ def embed(self, embedding_indices: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Quantize space representation of encoding_indices in channel first format. """ - with torch.cuda.amp.autocast(enabled=False): + with torch.autocast("cuda",enabled=False): embedding: torch.Tensor = ( self.embedding(embedding_indices).permute(self.quantization_permutation).contiguous() ) diff --git a/monai/networks/utils.py b/monai/networks/utils.py index 2279bed0b4..c29bc602f1 100644 --- a/monai/networks/utils.py +++ b/monai/networks/utils.py @@ -1238,7 +1238,7 @@ def __init__(self, mod): def forward(self, x): dtype = x.dtype - with torch.amp.autocast("cuda", enabled=False): + with torch.autocast("cuda", enabled=False): ret = self.mod.forward(x.to(torch.float32)).to(dtype) return ret @@ -1255,7 +1255,7 @@ def __init__(self, mod): def forward(self, *args): from_dtype = args[0].dtype - with torch.amp.autocast("cuda", enabled=False): + with torch.autocast("cuda", enabled=False): ret = self.mod.forward(*cast_all(args, from_dtype=from_dtype, to_dtype=torch.float32)) return cast_all(ret, from_dtype=torch.float32, to_dtype=from_dtype) diff --git a/tests/config/test_cv2_dist.py b/tests/config/test_cv2_dist.py index 2ef8e5b10f..3bcb68e553 100644 --- a/tests/config/test_cv2_dist.py +++ b/tests/config/test_cv2_dist.py @@ -16,7 +16,6 @@ import numpy as np import torch import torch.distributed as dist -from torch.cuda.amp import autocast # FIXME: test for the workaround of https://github.com/Project-MONAI/MONAI/issues/5291 from monai.config.deviceconfig import print_config @@ -33,7 +32,7 @@ def main_worker(rank, ngpus_per_node, port): model, device_ids=[rank], output_device=rank, find_unused_parameters=False ) x = torch.ones(1, 1, 12, 12, 12).to(rank) - with autocast(enabled=True): + with torch.autocast("cuda"): model(x) if dist.is_initialized(): diff --git a/tests/data/meta_tensor/test_meta_tensor.py b/tests/data/meta_tensor/test_meta_tensor.py index cd3def4de1..1c192257fe 100644 --- a/tests/data/meta_tensor/test_meta_tensor.py +++ b/tests/data/meta_tensor/test_meta_tensor.py @@ -256,7 +256,7 @@ def test_amp(self): conv = torch.nn.Conv2d(im.shape[1], 5, 3) conv.to(device) im_conv = conv(im) - with torch.cuda.amp.autocast(): + with torch.autocast("cuda"): im_conv2 = conv(im) self.check(im_conv2, im_conv, ids=False, rtol=1e-2, atol=1e-2) diff --git a/tests/integration/test_integration_fast_train.py b/tests/integration/test_integration_fast_train.py index f9beb5613d..814c4b182c 100644 --- a/tests/integration/test_integration_fast_train.py +++ b/tests/integration/test_integration_fast_train.py @@ -186,7 +186,7 @@ def test_train_timing(self): step += 1 optimizer.zero_grad() # set AMP for training - with torch.cuda.amp.autocast(): + with torch.autocast("cuda"): outputs = model(batch_data["image"]) loss = loss_function(outputs, batch_data["label"]) scaler.scale(loss).backward() @@ -207,7 +207,7 @@ def test_train_timing(self): roi_size = (96, 96, 96) sw_batch_size = 4 # set AMP for validation - with torch.cuda.amp.autocast(): + with torch.autocast("cuda"): val_outputs = sliding_window_inference(val_data["image"], roi_size, sw_batch_size, model) val_outputs = [post_pred(i) for i in decollate_batch(val_outputs)] From 4685ecaf41dd37fbc70a577ea75d29f8bed1b244 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 15:11:33 +0000 Subject: [PATCH 04/10] Update torch.load usage to eliminate complaint mesages Signed-off-by: Eric Kerfoot --- .../detection/networks/retinanet_detector.py | 2 +- monai/apps/mmars/mmars.py | 2 +- monai/bundle/scripts.py | 4 ++-- monai/data/dataset.py | 10 ++------- monai/fl/client/monai_algo.py | 2 +- monai/handlers/checkpoint_loader.py | 2 +- monai/losses/perceptual.py | 2 +- monai/networks/nets/hovernet.py | 9 ++++---- monai/networks/nets/resnet.py | 4 ++-- monai/networks/nets/senet.py | 2 +- monai/networks/nets/swin_unetr.py | 2 +- monai/networks/nets/transchex.py | 3 ++- monai/utils/state_cacher.py | 2 +- tests/bundle/test_bundle_download.py | 22 ++++++++++++++----- tests/data/meta_tensor/test_meta_tensor.py | 2 +- .../test_integration_classification_2d.py | 2 +- .../test_integration_segmentation_3d.py | 2 +- tests/networks/nets/test_autoencoderkl.py | 2 +- tests/networks/nets/test_controlnet.py | 2 +- .../nets/test_diffusion_model_unet.py | 2 +- .../networks/nets/test_network_consistency.py | 2 +- tests/networks/nets/test_swin_unetr.py | 2 +- tests/networks/nets/test_transformer.py | 2 +- tests/networks/test_save_state.py | 2 +- 24 files changed, 47 insertions(+), 41 deletions(-) diff --git a/monai/apps/detection/networks/retinanet_detector.py b/monai/apps/detection/networks/retinanet_detector.py index a0573d6cd1..e996ae81bc 100644 --- a/monai/apps/detection/networks/retinanet_detector.py +++ b/monai/apps/detection/networks/retinanet_detector.py @@ -180,7 +180,7 @@ def forward(self, images: torch.Tensor): nesterov=True, ) torch.save(detector.network.state_dict(), 'model.pt') # save model - detector.network.load_state_dict(torch.load('model.pt')) # load model + detector.network.load_state_dict(torch.load('model.pt', weights_only=True)) # load model """ def __init__( diff --git a/monai/apps/mmars/mmars.py b/monai/apps/mmars/mmars.py index 31c88a17be..1fc0690cc9 100644 --- a/monai/apps/mmars/mmars.py +++ b/monai/apps/mmars/mmars.py @@ -241,7 +241,7 @@ def load_from_mmar( return torch.jit.load(_model_file, map_location=map_location) # loading with `torch.load` - model_dict = torch.load(_model_file, map_location=map_location) + model_dict = torch.load(_model_file, map_location=map_location, weights_only=True) if weights_only: return model_dict.get(model_key, model_dict) # model_dict[model_key] or model_dict directly diff --git a/monai/bundle/scripts.py b/monai/bundle/scripts.py index ccb8010e65..4f84e91f54 100644 --- a/monai/bundle/scripts.py +++ b/monai/bundle/scripts.py @@ -737,7 +737,7 @@ def load( if load_ts_module is True: return load_net_with_metadata(full_path, map_location=torch.device(device), more_extra_files=config_files) # loading with `torch.load` - model_dict = torch.load(full_path, map_location=torch.device(device)) + model_dict = torch.load(full_path, map_location=torch.device(device), weights_only=True) if not isinstance(model_dict, Mapping): warnings.warn(f"the state dictionary from {full_path} should be a dictionary but got {type(model_dict)}.") @@ -1306,7 +1306,7 @@ def _export( # here we use ignite Checkpoint to support nested weights and be compatible with MONAI CheckpointSaver Checkpoint.load_objects(to_load={key_in_ckpt: net}, checkpoint=ckpt_file) else: - ckpt = torch.load(ckpt_file) + ckpt = torch.load(ckpt_file, weights_only=True) copy_model_state(dst=net, src=ckpt if key_in_ckpt == "" else ckpt[key_in_ckpt]) # Use the given converter to convert a model and save with metadata, config content diff --git a/monai/data/dataset.py b/monai/data/dataset.py index 8c53338d66..6e7ad8ba48 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -372,10 +372,7 @@ def _cachecheck(self, item_transformed): if hashfile is not None and hashfile.is_file(): # cache hit try: - if "weights_only" in signature(torch.load).parameters: - return torch.load(hashfile, weights_only=False) - else: - return torch.load(hashfile) + return torch.load(hashfile, weights_only=False) except PermissionError as e: if sys.platform != "win32": raise e @@ -1674,7 +1671,4 @@ def _load_meta_cache(self, meta_hash_file_name): if meta_hash_file_name in self._meta_cache: return self._meta_cache[meta_hash_file_name] else: - if "weights_only" in signature(torch.load).parameters: - return torch.load(self.cache_dir / meta_hash_file_name, weights_only=False) - else: - return torch.load(self.cache_dir / meta_hash_file_name) + return torch.load(self.cache_dir / meta_hash_file_name, weights_only=False) diff --git a/monai/fl/client/monai_algo.py b/monai/fl/client/monai_algo.py index a3ac58c221..6e9a6fd1fe 100644 --- a/monai/fl/client/monai_algo.py +++ b/monai/fl/client/monai_algo.py @@ -574,7 +574,7 @@ def get_weights(self, extra=None): model_path = os.path.join(self.bundle_root, cast(str, self.model_filepaths[model_type])) if not os.path.isfile(model_path): raise ValueError(f"No best model checkpoint exists at {model_path}") - weights = torch.load(model_path, map_location="cpu") + weights = torch.load(model_path, map_location="cpu", weights_only=True) # if weights contain several state dicts, use the one defined by `save_dict_key` if isinstance(weights, dict) and self.save_dict_key in weights: weights = weights.get(self.save_dict_key) diff --git a/monai/handlers/checkpoint_loader.py b/monai/handlers/checkpoint_loader.py index f48968ecfd..105b4f3a79 100644 --- a/monai/handlers/checkpoint_loader.py +++ b/monai/handlers/checkpoint_loader.py @@ -122,7 +122,7 @@ def __call__(self, engine: Engine) -> None: Args: engine: Ignite Engine, it can be a trainer, validator or evaluator. """ - checkpoint = torch.load(self.load_path, map_location=self.map_location) + checkpoint = torch.load(self.load_path, map_location=self.map_location, weights_only=True) k, _ = list(self.load_dict.items())[0] # single object and checkpoint is directly a state_dict diff --git a/monai/losses/perceptual.py b/monai/losses/perceptual.py index a8ae90993a..ee653fac9d 100644 --- a/monai/losses/perceptual.py +++ b/monai/losses/perceptual.py @@ -374,7 +374,7 @@ def __init__( else: network = torchvision.models.resnet50(weights=None) if pretrained is True: - state_dict = torch.load(pretrained_path) + state_dict = torch.load(pretrained_path, weights_only=True) if pretrained_state_dict_key is not None: state_dict = state_dict[pretrained_state_dict_key] network.load_state_dict(state_dict) diff --git a/monai/networks/nets/hovernet.py b/monai/networks/nets/hovernet.py index 3745b66bb5..b773af91d4 100644 --- a/monai/networks/nets/hovernet.py +++ b/monai/networks/nets/hovernet.py @@ -633,9 +633,9 @@ def _remap_preact_resnet_model(model_url: str): # download the pretrained weights into torch hub's default dir weights_dir = os.path.join(torch.hub.get_dir(), "preact-resnet50.pth") download_url(model_url, fuzzy=True, filepath=weights_dir, progress=False) - state_dict = torch.load(weights_dir, map_location=None if torch.cuda.is_available() else torch.device("cpu"))[ - "desc" - ] + map_location = None if torch.cuda.is_available() else torch.device("cpu") + state_dict = torch.load(weights_dir, map_location=map_location, weights_only=True)["desc"] + for key in list(state_dict.keys()): new_key = None if pattern_conv0.match(key): @@ -668,7 +668,8 @@ def _remap_standard_resnet_model(model_url: str, state_dict_key: str | None = No # download the pretrained weights into torch hub's default dir weights_dir = os.path.join(torch.hub.get_dir(), "resnet50.pth") download_url(model_url, fuzzy=True, filepath=weights_dir, progress=False) - state_dict = torch.load(weights_dir, map_location=None if torch.cuda.is_available() else torch.device("cpu")) + map_location = None if torch.cuda.is_available() else torch.device("cpu") + state_dict = torch.load(weights_dir, map_location=map_location, weights_only=True) if state_dict_key is not None: state_dict = state_dict[state_dict_key] diff --git a/monai/networks/nets/resnet.py b/monai/networks/nets/resnet.py index d62722478e..d24b86d27d 100644 --- a/monai/networks/nets/resnet.py +++ b/monai/networks/nets/resnet.py @@ -493,7 +493,7 @@ def _resnet( if isinstance(pretrained, str): if Path(pretrained).exists(): logger.info(f"Loading weights from {pretrained}...") - model_state_dict = torch.load(pretrained, map_location=device) + model_state_dict = torch.load(pretrained, map_location=device, weights_only=True) else: # Throw error raise FileNotFoundError("The pretrained checkpoint file is not found") @@ -665,7 +665,7 @@ def get_pretrained_resnet_medicalnet(resnet_depth: int, device: str = "cpu", dat raise EntryNotFoundError( f"{filename} not found on {medicalnet_huggingface_repo_basename}{resnet_depth}" ) from None - checkpoint = torch.load(pretrained_path, map_location=torch.device(device)) + checkpoint = torch.load(pretrained_path, map_location=torch.device(device), weights_only=True) else: raise NotImplementedError("Supported resnet_depth are: [10, 18, 34, 50, 101, 152, 200]") logger.info(f"{filename} downloaded") diff --git a/monai/networks/nets/senet.py b/monai/networks/nets/senet.py index 51435a9ea2..c14118ad20 100644 --- a/monai/networks/nets/senet.py +++ b/monai/networks/nets/senet.py @@ -302,7 +302,7 @@ def _load_state_dict(model: nn.Module, arch: str, progress: bool): if isinstance(model_url, dict): download_url(model_url["url"], filepath=model_url["filename"]) - state_dict = torch.load(model_url["filename"], map_location=None) + state_dict = torch.load(model_url["filename"], map_location=None, weights_only=True) else: state_dict = load_state_dict_from_url(model_url, progress=progress) for key in list(state_dict.keys()): diff --git a/monai/networks/nets/swin_unetr.py b/monai/networks/nets/swin_unetr.py index cfc5dda41f..525e64ab3f 100644 --- a/monai/networks/nets/swin_unetr.py +++ b/monai/networks/nets/swin_unetr.py @@ -1118,7 +1118,7 @@ def filter_swinunetr(key, value): ) ssl_weights_path = "./ssl_pretrained_weights.pth" download_url(resource, ssl_weights_path) - ssl_weights = torch.load(ssl_weights_path)["model"] + ssl_weights = torch.load(ssl_weights_path, weights_only=True)["model"] dst_dict, loaded, not_loaded = copy_model_state(model, ssl_weights, filter_func=filter_swinunetr) diff --git a/monai/networks/nets/transchex.py b/monai/networks/nets/transchex.py index 6bfff3c956..68d917534a 100644 --- a/monai/networks/nets/transchex.py +++ b/monai/networks/nets/transchex.py @@ -68,7 +68,8 @@ def from_pretrained( weights_path = cached_file(path_or_repo_id, filename, cache_dir=cache_dir) model = cls(num_language_layers, num_vision_layers, num_mixed_layers, bert_config, *inputs, **kwargs) if state_dict is None and not from_tf: - state_dict = torch.load(weights_path, map_location="cpu" if not torch.cuda.is_available() else None) + map_location = "cpu" if not torch.cuda.is_available() else None + state_dict = torch.load(weights_path, map_location=map_location, weights_only=True) if from_tf: return load_tf_weights_in_bert(model, weights_path) old_keys = [] diff --git a/monai/utils/state_cacher.py b/monai/utils/state_cacher.py index 60a074544b..726d59273b 100644 --- a/monai/utils/state_cacher.py +++ b/monai/utils/state_cacher.py @@ -124,7 +124,7 @@ def retrieve(self, key: Hashable) -> Any: fn = self.cached[key]["obj"] # pytype: disable=attribute-error if not os.path.exists(fn): # pytype: disable=wrong-arg-types raise RuntimeError(f"Failed to load state in {fn}. File doesn't exist anymore.") - data_obj = torch.load(fn, map_location=lambda storage, location: storage) + data_obj = torch.load(fn, map_location=lambda storage, location: storage, weights_only=True) # copy back to device if necessary if "device" in self.cached[key]: data_obj = data_obj.to(self.cached[key]["device"]) diff --git a/tests/bundle/test_bundle_download.py b/tests/bundle/test_bundle_download.py index 38620d98ff..da58a6313e 100644 --- a/tests/bundle/test_bundle_download.py +++ b/tests/bundle/test_bundle_download.py @@ -266,6 +266,7 @@ def test_load_weights(self, bundle_files, bundle_name, repo, device, model_file) with skip_if_downloading_fails(): # download bundle, and load weights from the downloaded path with tempfile.TemporaryDirectory() as tempdir: + bundle_root = os.path.join(tempdir, bundle_name) # load weights weights = load( name=bundle_name, @@ -278,7 +279,7 @@ def test_load_weights(self, bundle_files, bundle_name, repo, device, model_file) return_state_dict=True, ) # prepare network - with open(os.path.join(tempdir, bundle_name, bundle_files[2])) as f: + with open(os.path.join(bundle_root, bundle_files[2])) as f: net_args = json.load(f)["network_def"] model_name = net_args["_target_"] del net_args["_target_"] @@ -288,9 +289,13 @@ def test_load_weights(self, bundle_files, bundle_name, repo, device, model_file) model.eval() # prepare data and test - input_tensor = torch.load(os.path.join(tempdir, bundle_name, bundle_files[4]), map_location=device) + input_tensor = torch.load( + os.path.join(bundle_root, bundle_files[4]), map_location=device, weights_only=True + ) output = model.forward(input_tensor) - expected_output = torch.load(os.path.join(tempdir, bundle_name, bundle_files[3]), map_location=device) + expected_output = torch.load( + os.path.join(bundle_root, bundle_files[3]), map_location=device, weights_only=True + ) assert_allclose(output, expected_output, atol=1e-4, rtol=1e-4, type_test=False) # load instantiated model directly and test, since the bundle has been downloaded, @@ -350,7 +355,7 @@ def test_load_weights_with_net_override(self, bundle_name, device, net_override) config_file=f"{tempdir}/spleen_ct_segmentation/configs/train.json", workflow_type="train" ) expected_model = workflow.network_def.to(device) - expected_model.load_state_dict(torch.load(model_path)) + expected_model.load_state_dict(torch.load(model_path, weights_only=True)) expected_output = expected_model(input_tensor) assert_allclose(output, expected_output, atol=1e-4, rtol=1e-4, type_test=False) @@ -378,6 +383,7 @@ def test_load_ts_module(self, bundle_files, bundle_name, version, repo, device, with skip_if_downloading_fails(): # load ts module with tempfile.TemporaryDirectory() as tempdir: + bundle_root = os.path.join(tempdir, bundle_name) # load ts module model_ts, metadata, extra_file_dict = load( name=bundle_name, @@ -393,9 +399,13 @@ def test_load_ts_module(self, bundle_files, bundle_name, version, repo, device, ) # prepare and test ts - input_tensor = torch.load(os.path.join(tempdir, bundle_name, bundle_files[1]), map_location=device) + input_tensor = torch.load( + os.path.join(bundle_root, bundle_files[1]), map_location=device, weights_only=True + ) output = model_ts.forward(input_tensor) - expected_output = torch.load(os.path.join(tempdir, bundle_name, bundle_files[0]), map_location=device) + expected_output = torch.load( + os.path.join(bundle_root, bundle_files[0]), map_location=device, weights_only=True + ) assert_allclose(output, expected_output, atol=1e-4, rtol=1e-4, type_test=False) # test metadata self.assertTrue(metadata["pytorch_version"] == "1.7.1") diff --git a/tests/data/meta_tensor/test_meta_tensor.py b/tests/data/meta_tensor/test_meta_tensor.py index 1c192257fe..772ea992af 100644 --- a/tests/data/meta_tensor/test_meta_tensor.py +++ b/tests/data/meta_tensor/test_meta_tensor.py @@ -245,7 +245,7 @@ def test_pickling(self): with tempfile.TemporaryDirectory() as tmp_dir: fname = os.path.join(tmp_dir, "im.pt") torch.save(m, fname) - m2 = torch.load(fname) + m2 = torch.load(fname, weights_only=True) self.check(m2, m, ids=False) @skip_if_no_cuda diff --git a/tests/integration/test_integration_classification_2d.py b/tests/integration/test_integration_classification_2d.py index fd9e58aaf8..aecfa2efab 100644 --- a/tests/integration/test_integration_classification_2d.py +++ b/tests/integration/test_integration_classification_2d.py @@ -166,7 +166,7 @@ def run_inference_test(root_dir, test_x, test_y, device="cuda:0", num_workers=10 model = DenseNet121(spatial_dims=2, in_channels=1, out_channels=len(np.unique(test_y))).to(device) model_filename = os.path.join(root_dir, "best_metric_model.pth") - model.load_state_dict(torch.load(model_filename)) + model.load_state_dict(torch.load(model_filename, weights_only=True)) y_true = [] y_pred = [] with eval_mode(model): diff --git a/tests/integration/test_integration_segmentation_3d.py b/tests/integration/test_integration_segmentation_3d.py index fb2937739f..7c30150505 100644 --- a/tests/integration/test_integration_segmentation_3d.py +++ b/tests/integration/test_integration_segmentation_3d.py @@ -216,7 +216,7 @@ def run_inference_test(root_dir, device="cuda:0"): ).to(device) model_filename = os.path.join(root_dir, "best_metric_model.pth") - model.load_state_dict(torch.load(model_filename)) + model.load_state_dict(torch.load(model_filename, weights_only=True)) with eval_mode(model): # resampling with align_corners=True or dtype=float64 will generate # slight different results between PyTorch 1.5 an 1.6 diff --git a/tests/networks/nets/test_autoencoderkl.py b/tests/networks/nets/test_autoencoderkl.py index 0a3db60830..2d4c5b66ca 100644 --- a/tests/networks/nets/test_autoencoderkl.py +++ b/tests/networks/nets/test_autoencoderkl.py @@ -330,7 +330,7 @@ def test_compatibility_with_monai_generative(self): weight_path = os.path.join(tmpdir, filename) download_url(url=url, filepath=weight_path, hash_val=hash_val, hash_type=hash_type) - net.load_old_state_dict(torch.load(weight_path), verbose=False) + net.load_old_state_dict(torch.load(weight_path, weights_only=True), verbose=False) if __name__ == "__main__": diff --git a/tests/networks/nets/test_controlnet.py b/tests/networks/nets/test_controlnet.py index 9503518762..6158dc2eef 100644 --- a/tests/networks/nets/test_controlnet.py +++ b/tests/networks/nets/test_controlnet.py @@ -208,7 +208,7 @@ def test_compatibility_with_monai_generative(self): weight_path = os.path.join(tmpdir, filename) download_url(url=url, filepath=weight_path, hash_val=hash_val, hash_type=hash_type) - net.load_old_state_dict(torch.load(weight_path), verbose=False) + net.load_old_state_dict(torch.load(weight_path, weights_only=True), verbose=False) if __name__ == "__main__": diff --git a/tests/networks/nets/test_diffusion_model_unet.py b/tests/networks/nets/test_diffusion_model_unet.py index a7c823709d..3bca26882c 100644 --- a/tests/networks/nets/test_diffusion_model_unet.py +++ b/tests/networks/nets/test_diffusion_model_unet.py @@ -578,7 +578,7 @@ def test_compatibility_with_monai_generative(self): weight_path = os.path.join(tmpdir, filename) download_url(url=url, filepath=weight_path, hash_val=hash_val, hash_type=hash_type) - net.load_old_state_dict(torch.load(weight_path), verbose=False) + net.load_old_state_dict(torch.load(weight_path, weights_only=True), verbose=False) if __name__ == "__main__": diff --git a/tests/networks/nets/test_network_consistency.py b/tests/networks/nets/test_network_consistency.py index e09826de75..4ce198b92f 100644 --- a/tests/networks/nets/test_network_consistency.py +++ b/tests/networks/nets/test_network_consistency.py @@ -55,7 +55,7 @@ def test_network_consistency(self, net_name, data_path, json_path): print("JSON path: " + json_path) # Load data - loaded_data = torch.load(data_path) + loaded_data = torch.load(data_path, weights_only=True) # Load json from file json_file = open(json_path) diff --git a/tests/networks/nets/test_swin_unetr.py b/tests/networks/nets/test_swin_unetr.py index 4908907bfe..2c4532ecc4 100644 --- a/tests/networks/nets/test_swin_unetr.py +++ b/tests/networks/nets/test_swin_unetr.py @@ -128,7 +128,7 @@ def test_filter_swinunetr(self, input_param, key, value): data_spec["url"], weight_path, hash_val=data_spec["hash_val"], hash_type=data_spec["hash_type"] ) - ssl_weight = torch.load(weight_path)["model"] + ssl_weight = torch.load(weight_path, weights_only=True)["model"] net = SwinUNETR(**input_param) dst_dict, loaded, not_loaded = copy_model_state(net, ssl_weight, filter_func=filter_swinunetr) assert_allclose(dst_dict[key][:8], value, atol=1e-4, rtol=1e-4, type_test=False) diff --git a/tests/networks/nets/test_transformer.py b/tests/networks/nets/test_transformer.py index f9264ba153..daf424c174 100644 --- a/tests/networks/nets/test_transformer.py +++ b/tests/networks/nets/test_transformer.py @@ -101,7 +101,7 @@ def test_compatibility_with_monai_generative(self): weight_path = os.path.join(tmpdir, filename) download_url(url=url, filepath=weight_path, hash_val=hash_val, hash_type=hash_type) - net.load_old_state_dict(torch.load(weight_path), verbose=False) + net.load_old_state_dict(torch.load(weight_path, weights_only=True), verbose=False) if __name__ == "__main__": diff --git a/tests/networks/test_save_state.py b/tests/networks/test_save_state.py index 0581a3ce1f..329065da2b 100644 --- a/tests/networks/test_save_state.py +++ b/tests/networks/test_save_state.py @@ -64,7 +64,7 @@ def test_file(self, src, expected_keys, create_dir=True, atomic=True, func=None, if kwargs is None: kwargs = {} save_state(src=src, path=path, create_dir=create_dir, atomic=atomic, func=func, **kwargs) - ckpt = dict(torch.load(path)) + ckpt = dict(torch.load(path, weights_only=True)) for k in ckpt.keys(): self.assertIn(k, expected_keys) From 77ff9b73c876a218a9e086f490e98ee6f9e8108b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 15:12:06 +0000 Subject: [PATCH 05/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- monai/data/dataset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monai/data/dataset.py b/monai/data/dataset.py index 6e7ad8ba48..691425994d 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -22,7 +22,6 @@ import warnings from collections.abc import Callable, Sequence from copy import copy, deepcopy -from inspect import signature from multiprocessing.managers import ListProxy from multiprocessing.pool import ThreadPool from pathlib import Path From 7ee3bf123da40ccd36661b55e458dce62e763ace Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 15:14:22 +0000 Subject: [PATCH 06/10] Formatting Signed-off-by: Eric Kerfoot --- monai/engines/evaluator.py | 2 +- monai/engines/trainer.py | 6 +++--- monai/networks/layers/vector_quantizer.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monai/engines/evaluator.py b/monai/engines/evaluator.py index b3bf3a9c30..c39a00604c 100644 --- a/monai/engines/evaluator.py +++ b/monai/engines/evaluator.py @@ -492,7 +492,7 @@ def _iteration(self, engine: EnsembleEvaluator, batchdata: dict[str, torch.Tenso for idx, network in enumerate(engine.networks): with engine.mode(network): if engine.amp: - with torch.autocast("cuda",**engine.amp_kwargs): + with torch.autocast("cuda", **engine.amp_kwargs): if isinstance(engine.state.output, dict): engine.state.output.update( {engine.pred_keys[idx]: engine.inferer(inputs, network, *args, **kwargs)} diff --git a/monai/engines/trainer.py b/monai/engines/trainer.py index 303cd6ad54..64c66b064b 100644 --- a/monai/engines/trainer.py +++ b/monai/engines/trainer.py @@ -255,7 +255,7 @@ def _compute_pred_loss(): engine.optimizer.zero_grad(set_to_none=engine.optim_set_to_none) if engine.amp and engine.scaler is not None: - with torch.autocast("cuda",**engine.amp_kwargs): + with torch.autocast("cuda", **engine.amp_kwargs): _compute_pred_loss() engine.scaler.scale(engine.state.output[Keys.LOSS]).backward() engine.fire_event(IterationEvents.BACKWARD_COMPLETED) @@ -689,7 +689,7 @@ def _compute_generator_loss() -> None: engine.state.g_optimizer.zero_grad(set_to_none=engine.optim_set_to_none) if engine.amp and engine.state.g_scaler is not None: - with torch.autocast("cuda",**engine.amp_kwargs): + with torch.autocast("cuda", **engine.amp_kwargs): _compute_generator_loss() engine.state.output[Keys.LOSS] = ( @@ -737,7 +737,7 @@ def _compute_discriminator_loss() -> None: engine.state.d_network.zero_grad(set_to_none=engine.optim_set_to_none) if engine.amp and engine.state.d_scaler is not None: - with torch.autocast("cuda",**engine.amp_kwargs): + with torch.autocast("cuda", **engine.amp_kwargs): _compute_discriminator_loss() engine.state.d_scaler.scale(engine.state.output[AdversarialKeys.DISCRIMINATOR_LOSS]).backward() diff --git a/monai/networks/layers/vector_quantizer.py b/monai/networks/layers/vector_quantizer.py index 19b23a8e8e..0ff7143b69 100644 --- a/monai/networks/layers/vector_quantizer.py +++ b/monai/networks/layers/vector_quantizer.py @@ -100,7 +100,7 @@ def quantize(self, inputs: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, to torch.Tensor: Quantization indices of shape [B,H,W,D,1] """ - with torch.autocast("cuda",enabled=False): + with torch.autocast("cuda", enabled=False): encoding_indices_view = list(inputs.shape) del encoding_indices_view[1] @@ -138,7 +138,7 @@ def embed(self, embedding_indices: torch.Tensor) -> torch.Tensor: Returns: torch.Tensor: Quantize space representation of encoding_indices in channel first format. """ - with torch.autocast("cuda",enabled=False): + with torch.autocast("cuda", enabled=False): embedding: torch.Tensor = ( self.embedding(embedding_indices).permute(self.quantization_permutation).contiguous() ) From f69ab502ce9a87eb0bd699ca19bdb1bc9f568689 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 15:26:38 +0000 Subject: [PATCH 07/10] Fix Signed-off-by: Eric Kerfoot --- monai/utils/state_cacher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/utils/state_cacher.py b/monai/utils/state_cacher.py index 726d59273b..c59436525c 100644 --- a/monai/utils/state_cacher.py +++ b/monai/utils/state_cacher.py @@ -124,7 +124,7 @@ def retrieve(self, key: Hashable) -> Any: fn = self.cached[key]["obj"] # pytype: disable=attribute-error if not os.path.exists(fn): # pytype: disable=wrong-arg-types raise RuntimeError(f"Failed to load state in {fn}. File doesn't exist anymore.") - data_obj = torch.load(fn, map_location=lambda storage, location: storage, weights_only=True) + data_obj = torch.load(fn, map_location=lambda storage, location: storage, weights_only=False) # copy back to device if necessary if "device" in self.cached[key]: data_obj = data_obj.to(self.cached[key]["device"]) From 3e0b5175c8ad80dccf0cd8bfddf84453e4125374 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 15:32:47 +0000 Subject: [PATCH 08/10] Fix requirements Signed-off-by: Eric Kerfoot --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8f59b6f34d..571aae051d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -torch>=1.9 +torch>=1.13.1 numpy>=1.24,<3.0 From 5db7a56a2263df1074bc938853cd8c534ddfe4fb Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 16:00:14 +0000 Subject: [PATCH 09/10] Change one torch.load Signed-off-by: Eric Kerfoot --- tests/data/meta_tensor/test_meta_tensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/meta_tensor/test_meta_tensor.py b/tests/data/meta_tensor/test_meta_tensor.py index 772ea992af..f52d70e7b6 100644 --- a/tests/data/meta_tensor/test_meta_tensor.py +++ b/tests/data/meta_tensor/test_meta_tensor.py @@ -245,7 +245,7 @@ def test_pickling(self): with tempfile.TemporaryDirectory() as tmp_dir: fname = os.path.join(tmp_dir, "im.pt") torch.save(m, fname) - m2 = torch.load(fname, weights_only=True) + m2 = torch.load(fname, weights_only=False) self.check(m2, m, ids=False) @skip_if_no_cuda From 3dc4fe17330268cb42a39d68f87a2a601a50bbb8 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot Date: Tue, 25 Feb 2025 16:56:53 +0000 Subject: [PATCH 10/10] Change one torch.load and otrher fixes Signed-off-by: Eric Kerfoot --- .github/workflows/pythonapp-min.yml | 2 +- monai/handlers/checkpoint_loader.py | 2 +- runtests.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pythonapp-min.yml b/.github/workflows/pythonapp-min.yml index 19e30f86bb..868285b2ed 100644 --- a/.github/workflows/pythonapp-min.yml +++ b/.github/workflows/pythonapp-min.yml @@ -124,7 +124,7 @@ jobs: strategy: fail-fast: false matrix: - pytorch-version: ['1.13.1', '2.0.1', '2.2.2', '2.3.1', '2.4.1', 'latest'] + pytorch-version: ['1.13.1', '2.0.1', '2.2.2', '2.3.1', '2.4.1', '2.5.1', 'latest'] timeout-minutes: 40 steps: - uses: actions/checkout@v4 diff --git a/monai/handlers/checkpoint_loader.py b/monai/handlers/checkpoint_loader.py index 105b4f3a79..16cb875d03 100644 --- a/monai/handlers/checkpoint_loader.py +++ b/monai/handlers/checkpoint_loader.py @@ -122,7 +122,7 @@ def __call__(self, engine: Engine) -> None: Args: engine: Ignite Engine, it can be a trainer, validator or evaluator. """ - checkpoint = torch.load(self.load_path, map_location=self.map_location, weights_only=True) + checkpoint = torch.load(self.load_path, map_location=self.map_location, weights_only=False) k, _ = list(self.load_dict.items())[0] # single object and checkpoint is directly a state_dict diff --git a/runtests.sh b/runtests.sh index 2a399d5c3a..fd7df79722 100755 --- a/runtests.sh +++ b/runtests.sh @@ -120,7 +120,7 @@ function print_usage { # FIXME: https://github.com/Project-MONAI/MONAI/issues/4354 protobuf_major_version=$("${PY_EXE}" -m pip list | grep '^protobuf ' | tr -s ' ' | cut -d' ' -f2 | cut -d'.' -f1) -if [ "$protobuf_major_version" -ge "4" ] +if [ ! -z "$protobuf_major_version" ] && [ "$protobuf_major_version" -ge "4" ] then export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python fi