From 5274a23a18a8776368b0fc65ea0800c997db7f3d Mon Sep 17 00:00:00 2001 From: Caleb Robinson Date: Wed, 21 Jul 2021 02:15:32 +0000 Subject: [PATCH 1/6] Update Deprecation message to 1.5, move model_size helper function to utilities/memory.py --- CHANGELOG.md | 2 +- pytorch_lightning/core/lightning.py | 6 +++++- pytorch_lightning/utilities/memory.py | 19 ++++++++++++++++- tests/callbacks/test_quantization.py | 5 +++-- tests/deprecated_api/test_remove_1-7.py | 26 ++++++++++++++++++++++++ tests/utilities/test_memory.py | 27 ++++++++++++++++++++++++- 6 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 tests/deprecated_api/test_remove_1-7.py diff --git a/CHANGELOG.md b/CHANGELOG.md index a2806602291e7..37af018309841 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Deprecated -- +- Deprecated `LightningModule.model_size` ([#8343](https://github.com/PyTorchLightning/pytorch-lightning/pull/8343)) - diff --git a/pytorch_lightning/core/lightning.py b/pytorch_lightning/core/lightning.py index cbe4b4e86ff26..71d3ec186b7c0 100644 --- a/pytorch_lightning/core/lightning.py +++ b/pytorch_lightning/core/lightning.py @@ -1959,7 +1959,11 @@ def model_size(self) -> float: The model's size in megabytes. The computation includes everything in the :meth:`~torch.nn.Module.state_dict`, i.e., by default the parameteters and buffers. """ - # todo: think about better way without need to dump model to drive + rank_zero_deprecation( + "The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7. Please " + "use the get_model_size_mb method under utilities/memory.py" + ) + tmp_name = f"{uuid.uuid4().hex}.pt" torch.save(self.state_dict(), tmp_name) size_mb = os.path.getsize(tmp_name) / 1e6 diff --git a/pytorch_lightning/utilities/memory.py b/pytorch_lightning/utilities/memory.py index 81fecfc9f5531..81666ebbe9b87 100644 --- a/pytorch_lightning/utilities/memory.py +++ b/pytorch_lightning/utilities/memory.py @@ -13,9 +13,11 @@ # limitations under the License. import gc +import os +import uuid import torch - +from torch.nn import Module def recursive_detach(in_dict: dict, to_cpu: bool = False) -> dict: """Detach all tensors in `in_dict`. @@ -87,3 +89,18 @@ def garbage_collection_cuda(): if not is_oom_error(exception): # Only handle OOM errors raise + + +def get_model_size_mb(model: Module) -> float: + """ + Calculates the size of a Module in megabytes by saving the model to a temporary file + and reading in the size. + Returns: + Number of megabytes in the parameters of the input module + """ + # TODO: Implement a method without needing to download the model + tmp_name = f"{uuid.uuid4().hex}.pt" + torch.save(model.state_dict(), tmp_name) + size_mb = os.path.getsize(tmp_name) / 1e6 + os.remove(tmp_name) + return size_mb diff --git a/tests/callbacks/test_quantization.py b/tests/callbacks/test_quantization.py index 6de9063b6a694..092eb108cbd8b 100644 --- a/tests/callbacks/test_quantization.py +++ b/tests/callbacks/test_quantization.py @@ -21,6 +21,7 @@ from pytorch_lightning import seed_everything, Trainer from pytorch_lightning.callbacks import QuantizationAwareTraining from pytorch_lightning.utilities.exceptions import MisconfigurationException +from pytorch_lightning.utilities.memory import get_model_size_mb from tests.helpers.datamodules import RegressDataModule from tests.helpers.runif import RunIf from tests.helpers.simple_models import RegressionModel @@ -40,7 +41,7 @@ def test_quantization(tmpdir, observe: str, fuse: bool, convert: bool): trainer = Trainer(**trainer_args) trainer.fit(model, datamodule=dm) - org_size = model.model_size + org_size = get_model_size_mb(model) org_score = torch.mean(torch.tensor([mean_relative_error(model(x), y) for x, y in dm.test_dataloader()])) fusing_layers = [(f"layer_{i}", f"layer_{i}a") for i in range(3)] if fuse else None @@ -62,7 +63,7 @@ def test_quantization(tmpdir, observe: str, fuse: bool, convert: bool): qmodel.eval() torch.quantization.convert(qmodel, inplace=True) - quant_size = qmodel.model_size + quant_size = get_model_size_mb(qmodel) # test that the trained model is smaller then initial size_ratio = quant_size / org_size assert size_ratio < 0.65 diff --git a/tests/deprecated_api/test_remove_1-7.py b/tests/deprecated_api/test_remove_1-7.py new file mode 100644 index 0000000000000..a789493e7113b --- /dev/null +++ b/tests/deprecated_api/test_remove_1-7.py @@ -0,0 +1,26 @@ +# Copyright The PyTorch Lightning team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" Test deprecated functionality which will be removed in v1.7.0 """ +import pytest + +from tests.helpers import BoringModel + + +def test_v1_7_0_deprecated_model_size(): + model = BoringModel() + with pytest.deprecated_call( + match="The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7. " + "Please use the get_model_size_mb method under utilities/memory.py" + ): + _ = model.model_size diff --git a/tests/utilities/test_memory.py b/tests/utilities/test_memory.py index 1c90423a27c83..b486157480877 100644 --- a/tests/utilities/test_memory.py +++ b/tests/utilities/test_memory.py @@ -11,9 +11,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import math + import torch +import torch.nn as nn -from pytorch_lightning.utilities.memory import recursive_detach +from pytorch_lightning.utilities.memory import get_model_size_mb, recursive_detach +from tests.helpers import BoringModel def test_recursive_detach(): @@ -28,3 +32,24 @@ def test_recursive_detach(): assert y["foo"].device.type == "cpu" assert y["bar"]["baz"].device.type == "cpu" assert not y["bar"]["baz"].requires_grad + + +def test_get_model_size_mb(): + model = BoringModel() + + size_bytes = get_model_size_mb(model) + + # Size will be python version dependent. + assert math.isclose(size_bytes, 0.001319, rel_tol=0.1) + + +def test_get_sparse_model_size_mb(): + class BoringSparseModel(BoringModel): + def __init__(self): + super().__init__() + self.layer = nn.Parameter(torch.ones(32).to_sparse()) + + model = BoringSparseModel() + size_bytes = get_model_size_mb(model) + + assert math.isclose(size_bytes, 0.001511, rel_tol=0.1) From ae89e2dcb088923e66470c071f44e651285b13a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 29 Jul 2021 01:04:49 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pytorch_lightning/utilities/memory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytorch_lightning/utilities/memory.py b/pytorch_lightning/utilities/memory.py index 81666ebbe9b87..06dd8f13cbb00 100644 --- a/pytorch_lightning/utilities/memory.py +++ b/pytorch_lightning/utilities/memory.py @@ -19,6 +19,7 @@ import torch from torch.nn import Module + def recursive_detach(in_dict: dict, to_cpu: bool = False) -> dict: """Detach all tensors in `in_dict`. From 390c2c1e0269f5085d27d193bcbd6bdc214d0445 Mon Sep 17 00:00:00 2001 From: Jirka Borovec Date: Thu, 29 Jul 2021 23:00:52 +0200 Subject: [PATCH 3/6] Apply suggestions from code review --- pytorch_lightning/core/lightning.py | 2 +- tests/deprecated_api/test_remove_1-7.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytorch_lightning/core/lightning.py b/pytorch_lightning/core/lightning.py index 71d3ec186b7c0..c6bd26cce869a 100644 --- a/pytorch_lightning/core/lightning.py +++ b/pytorch_lightning/core/lightning.py @@ -1961,7 +1961,7 @@ def model_size(self) -> float: """ rank_zero_deprecation( "The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7. Please " - "use the get_model_size_mb method under utilities/memory.py" + "use the `utilities.memory.get_model_size_mb` method under utilities/memory.py" ) tmp_name = f"{uuid.uuid4().hex}.pt" diff --git a/tests/deprecated_api/test_remove_1-7.py b/tests/deprecated_api/test_remove_1-7.py index a789493e7113b..f2ed31114f5f2 100644 --- a/tests/deprecated_api/test_remove_1-7.py +++ b/tests/deprecated_api/test_remove_1-7.py @@ -21,6 +21,6 @@ def test_v1_7_0_deprecated_model_size(): model = BoringModel() with pytest.deprecated_call( match="The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7. " - "Please use the get_model_size_mb method under utilities/memory.py" + "Please use the `utilities.memory.get_model_size_mb` method under utilities/memory.py" ): _ = model.model_size From cd02905eadb98195b597ce15bb6bda8183ae9fa0 Mon Sep 17 00:00:00 2001 From: Carlos Mocholi Date: Fri, 30 Jul 2021 14:56:19 +0200 Subject: [PATCH 4/6] Call the new function --- pytorch_lightning/core/lightning.py | 17 ++++------------- pytorch_lightning/utilities/memory.py | 9 ++++++--- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/pytorch_lightning/core/lightning.py b/pytorch_lightning/core/lightning.py index 63c5d3a29cc1f..529ee0b07ce05 100644 --- a/pytorch_lightning/core/lightning.py +++ b/pytorch_lightning/core/lightning.py @@ -19,7 +19,6 @@ import numbers import os import tempfile -import uuid from abc import ABC from contextlib import contextmanager from pathlib import Path @@ -44,6 +43,7 @@ from pytorch_lightning.utilities.cloud_io import get_filesystem from pytorch_lightning.utilities.distributed import distributed_available, sync_ddp from pytorch_lightning.utilities.exceptions import MisconfigurationException +from pytorch_lightning.utilities.memory import get_model_size_mb from pytorch_lightning.utilities.parsing import collect_init_args from pytorch_lightning.utilities.signature_utils import is_param_in_hook_signature from pytorch_lightning.utilities.types import _METRIC_COLLECTION, EPOCH_OUTPUT, STEP_OUTPUT @@ -1980,20 +1980,11 @@ def to_torchscript( @property def model_size(self) -> float: - """ - The model's size in megabytes. The computation includes everything in the - :meth:`~torch.nn.Module.state_dict`, i.e., by default the parameteters and buffers. - """ rank_zero_deprecation( - "The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7. Please " - "use the `utilities.memory.get_model_size_mb` method under utilities/memory.py" + "The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7." + " Please use the `pytorch_lightning.utilities.memory.get_model_size_mb`." ) - - tmp_name = f"{uuid.uuid4().hex}.pt" - torch.save(self.state_dict(), tmp_name) - size_mb = os.path.getsize(tmp_name) / 1e6 - os.remove(tmp_name) - return size_mb + return get_model_size_mb(self) def add_to_queue(self, queue: torch.multiprocessing.SimpleQueue) -> None: """ diff --git a/pytorch_lightning/utilities/memory.py b/pytorch_lightning/utilities/memory.py index 06dd8f13cbb00..e6edb6c925ae2 100644 --- a/pytorch_lightning/utilities/memory.py +++ b/pytorch_lightning/utilities/memory.py @@ -94,10 +94,13 @@ def garbage_collection_cuda(): def get_model_size_mb(model: Module) -> float: """ - Calculates the size of a Module in megabytes by saving the model to a temporary file - and reading in the size. + Calculates the size of a Module in megabytes by saving the model to a temporary file and reading its size. + + The computation includes everything in the :meth:`~torch.nn.Module.state_dict`, + i.e., by default the parameteters and buffers. + Returns: - Number of megabytes in the parameters of the input module + Number of megabytes in the parameters of the input module. """ # TODO: Implement a method without needing to download the model tmp_name = f"{uuid.uuid4().hex}.pt" From b9b9fa28ad85aac096d669adfa927391a788a222 Mon Sep 17 00:00:00 2001 From: Carlos Mocholi Date: Fri, 30 Jul 2021 15:15:04 +0200 Subject: [PATCH 5/6] Forgot to fix the test --- tests/deprecated_api/test_remove_1-7.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/deprecated_api/test_remove_1-7.py b/tests/deprecated_api/test_remove_1-7.py index f2ed31114f5f2..a4d92e8a8d5ed 100644 --- a/tests/deprecated_api/test_remove_1-7.py +++ b/tests/deprecated_api/test_remove_1-7.py @@ -20,7 +20,6 @@ def test_v1_7_0_deprecated_model_size(): model = BoringModel() with pytest.deprecated_call( - match="The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7. " - "Please use the `utilities.memory.get_model_size_mb` method under utilities/memory.py" + match="LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7" ): _ = model.model_size From f8a6182f2b7aaa7a49d49391c8e5f58e5a2a3613 Mon Sep 17 00:00:00 2001 From: Carlos Mocholi Date: Fri, 30 Jul 2021 15:19:28 +0200 Subject: [PATCH 6/6] Set stacklevel for property --- pytorch_lightning/core/lightning.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytorch_lightning/core/lightning.py b/pytorch_lightning/core/lightning.py index 529ee0b07ce05..7606bce0fc0c6 100644 --- a/pytorch_lightning/core/lightning.py +++ b/pytorch_lightning/core/lightning.py @@ -1982,7 +1982,8 @@ def to_torchscript( def model_size(self) -> float: rank_zero_deprecation( "The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7." - " Please use the `pytorch_lightning.utilities.memory.get_model_size_mb`." + " Please use the `pytorch_lightning.utilities.memory.get_model_size_mb`.", + stacklevel=5, ) return get_model_size_mb(self)