From 5f510c3f1bc165cfde3a153d64904ea224690f30 Mon Sep 17 00:00:00 2001 From: F-G Fernandez Date: Sun, 31 Oct 2021 18:14:17 +0100 Subject: [PATCH] refactor: Reorganized package hierarchy (#106) * refactor: Moved torchcam.cams to torchcam.methods * test: Renamed unittests * refactor: Updated scripts * refactor: Updated demo app * chore: Updated conda recipe * docs: Udpated documentation and README * docs: Updated docstrings --- .conda/meta.yaml | 2 +- README.md | 44 +++++++++---------- demo/app.py | 7 +-- docs/source/index.rst | 2 +- docs/source/{cams.rst => methods.rst} | 6 +-- docs/source/notebooks/quicktour.rst | 2 +- scripts/cam_example.py | 8 ++-- scripts/eval_latency.py | 2 +- ...cams_cam.py => test_methods_activation.py} | 12 ++--- ...test_cams_core.py => test_methods_core.py} | 2 +- ...ms_gradcam.py => test_methods_gradient.py} | 20 ++++----- ...st_cams_utils.py => test_methods_utils.py} | 10 ++--- torchcam/__init__.py | 2 +- torchcam/cams/__init__.py | 3 -- torchcam/methods/__init__.py | 2 + torchcam/{cams/utils.py => methods/_utils.py} | 0 .../{cams/cam.py => methods/activation.py} | 10 ++--- torchcam/{cams => methods}/core.py | 2 +- .../{cams/gradcam.py => methods/gradient.py} | 10 ++--- 19 files changed, 73 insertions(+), 73 deletions(-) rename docs/source/{cams.rst => methods.rst} (93%) rename test/{test_cams_cam.py => test_methods_activation.py} (89%) rename test/{test_cams_core.py => test_methods_core.py} (99%) rename test/{test_cams_gradcam.py => test_methods_gradient.py} (85%) rename test/{test_cams_utils.py => test_methods_utils.py} (72%) delete mode 100644 torchcam/cams/__init__.py create mode 100644 torchcam/methods/__init__.py rename torchcam/{cams/utils.py => methods/_utils.py} (100%) rename torchcam/{cams/cam.py => methods/activation.py} (98%) rename torchcam/{cams => methods}/core.py (99%) rename torchcam/{cams/gradcam.py => methods/gradient.py} (98%) diff --git a/.conda/meta.yaml b/.conda/meta.yaml index a6290d0a..d25dd0b1 100644 --- a/.conda/meta.yaml +++ b/.conda/meta.yaml @@ -28,7 +28,7 @@ test: # Python imports imports: - torchcam - - torchcam.cams + - torchcam.methods - torchcam.utils requires: - python diff --git a/README.md b/README.md index 36a4dc55..4a7f1431 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Simple way to leverage the class-specific activation of convolutional layers in TorchCAM leverages [PyTorch hooking mechanisms](https://pytorch.org/tutorials/beginner/former_torchies/nnft_tutorial.html#forward-and-backward-function-hooks) to seamlessly retrieve all required information to produce the class activation without additional efforts from the user. Each CAM object acts as a wrapper around your model. -You can find the exhaustive list of supported CAM methods in the [documentation](https://frgfm.github.io/torch-cam/cams.html), then use it as follows: +You can find the exhaustive list of supported CAM methods in the [documentation](https://frgfm.github.io/torch-cam/methods.html), then use it as follows: ```python # Define your model @@ -28,7 +28,7 @@ from torchvision.models import resnet18 model = resnet18(pretrained=True).eval() # Set your CAM extractor -from torchcam.cams import SmoothGradCAMpp +from torchcam.methods import SmoothGradCAMpp cam_extractor = SmoothGradCAMpp(model) ``` @@ -44,7 +44,7 @@ Once your CAM extractor is set, you only need to use your model to infer on your from torchvision.io.image import read_image from torchvision.transforms.functional import normalize, resize, to_pil_image from torchvision.models import resnet18 -from torchcam.cams import SmoothGradCAMpp +from torchcam.methods import SmoothGradCAMpp model = resnet18(pretrained=True).eval() cam_extractor = SmoothGradCAMpp(model) @@ -131,7 +131,7 @@ This project is developed and maintained by the repo owner, but the implementati

- Source: YouTube video (activation maps created by Layer-CAM with a pretrained ResNet-18) + Source: YouTube video (activation maps created by Layer-CAM with a pretrained ResNet-18)

@@ -182,24 +182,24 @@ In the table below, you will find a latency benchmark (forward pass not included | CAM method | Arch | GPU mean (std) | CPU mean (std) | | ------------------------------------------------------------ | ------------------ | ------------------ | -------------------- | -| [CAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.CAM) | resnet18 | 0.11ms (0.02ms) | 0.14ms (0.03ms) | -| [GradCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.GradCAM) | resnet18 | 3.71ms (1.11ms) | 40.66ms (1.82ms) | -| [GradCAMpp](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.GradCAMpp) | resnet18 | 5.21ms (1.22ms) | 41.61ms (3.24ms) | -| [SmoothGradCAMpp](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.SmoothGradCAMpp) | resnet18 | 33.67ms (2.51ms) | 239.27ms (7.85ms) | -| [ScoreCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.ScoreCAM) | resnet18 | 304.74ms (11.54ms) | 6796.89ms (415.14ms) | -| [SSCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.SSCAM) | resnet18 | | | -| [ISCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.ISCAM) | resnet18 | | | -| [XGradCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.XGradCAM) | resnet18 | 3.78ms (0.96ms) | 40.63ms (2.03ms) | -| [LayerCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.LayerCAM) | resnet18 | 3.65ms (1.04ms) | 40.91ms (1.79ms) | -| [CAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.CAM) | mobilenet_v3_large | N/A* | N/A* | -| [GradCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.GradCAM) | mobilenet_v3_large | 8.61ms (1.04ms) | 26.64ms (3.46ms) | -| [GradCAMpp](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.GradCAMpp) | mobilenet_v3_large | 8.83ms (1.29ms) | 25.50ms (3.10ms) | -| [SmoothGradCAMpp](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.SmoothGradCAMpp) | mobilenet_v3_large | 77.38ms (3.83ms) | 156.25ms (4.89ms) | -| [ScoreCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.ScoreCAM) | mobilenet_v3_large | 35.19ms (2.11ms) | 679.16ms (55.04ms) | -| [SSCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.SSCAM) | mobilenet_v3_large | | | -| [ISCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.ISCAM) | mobilenet_v3_large | | | -| [XGradCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.XGradCAM) | mobilenet_v3_large | 8.41ms (0.98ms) | 24.21ms (2.94ms) | -| [LayerCAM](https://frgfm.github.io/torch-cam/latest/cams.html#torchcam.cams.LayerCAM) | mobilenet_v3_large | 8.02ms (0.95ms) | 25.14ms (3.17ms) | +| [CAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.CAM) | resnet18 | 0.11ms (0.02ms) | 0.14ms (0.03ms) | +| [GradCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.GradCAM) | resnet18 | 3.71ms (1.11ms) | 40.66ms (1.82ms) | +| [GradCAMpp](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.GradCAMpp) | resnet18 | 5.21ms (1.22ms) | 41.61ms (3.24ms) | +| [SmoothGradCAMpp](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.SmoothGradCAMpp) | resnet18 | 33.67ms (2.51ms) | 239.27ms (7.85ms) | +| [ScoreCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.ScoreCAM) | resnet18 | 304.74ms (11.54ms) | 6796.89ms (415.14ms) | +| [SSCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.SSCAM) | resnet18 | | | +| [ISCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.ISCAM) | resnet18 | | | +| [XGradCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.XGradCAM) | resnet18 | 3.78ms (0.96ms) | 40.63ms (2.03ms) | +| [LayerCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.LayerCAM) | resnet18 | 3.65ms (1.04ms) | 40.91ms (1.79ms) | +| [CAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.CAM) | mobilenet_v3_large | N/A* | N/A* | +| [GradCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.GradCAM) | mobilenet_v3_large | 8.61ms (1.04ms) | 26.64ms (3.46ms) | +| [GradCAMpp](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.GradCAMpp) | mobilenet_v3_large | 8.83ms (1.29ms) | 25.50ms (3.10ms) | +| [SmoothGradCAMpp](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.SmoothGradCAMpp) | mobilenet_v3_large | 77.38ms (3.83ms) | 156.25ms (4.89ms) | +| [ScoreCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.ScoreCAM) | mobilenet_v3_large | 35.19ms (2.11ms) | 679.16ms (55.04ms) | +| [SSCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.SSCAM) | mobilenet_v3_large | | | +| [ISCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.ISCAM) | mobilenet_v3_large | | | +| [XGradCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.XGradCAM) | mobilenet_v3_large | 8.41ms (0.98ms) | 24.21ms (2.94ms) | +| [LayerCAM](https://frgfm.github.io/torch-cam/latest/methods.html#torchcam.methods.LayerCAM) | mobilenet_v3_large | 8.02ms (0.95ms) | 25.14ms (3.17ms) | **The base CAM method cannot work with architectures that have multiple fully-connected layers* diff --git a/demo/app.py b/demo/app.py index e38a2297..7c5e42f3 100644 --- a/demo/app.py +++ b/demo/app.py @@ -12,7 +12,8 @@ from torchvision import models from torchvision.transforms.functional import normalize, resize, to_pil_image, to_tensor -from torchcam import cams +from torchcam import methods +from torchcam.methods._utils import locate_candidate_layer from torchcam.utils import overlay_mask CAM_METHODS = ["CAM", "GradCAM", "GradCAMpp", "SmoothGradCAMpp", "ScoreCAM", "SSCAM", "ISCAM", "XGradCAM", "LayerCAM"] @@ -56,12 +57,12 @@ def main(): if tv_model is not None: with st.spinner('Loading model...'): model = models.__dict__[tv_model](pretrained=True).eval() - default_layer = cams.utils.locate_candidate_layer(model, (3, 224, 224)) + default_layer = locate_candidate_layer(model, (3, 224, 224)) target_layer = st.sidebar.text_input("Target layer", default_layer) cam_method = st.sidebar.selectbox("CAM method", CAM_METHODS) if cam_method is not None: - cam_extractor = cams.__dict__[cam_method]( + cam_extractor = methods.__dict__[cam_method]( model, target_layer=target_layer.split("+") if len(target_layer) > 0 else None ) diff --git a/docs/source/index.rst b/docs/source/index.rst index faab10fb..74674f6c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -49,7 +49,7 @@ Gradient-based methods :caption: Package Reference :hidden: - cams + methods utils diff --git a/docs/source/cams.rst b/docs/source/methods.rst similarity index 93% rename from docs/source/cams.rst rename to docs/source/methods.rst index 20372b9d..e4d2fec6 100644 --- a/docs/source/cams.rst +++ b/docs/source/methods.rst @@ -1,8 +1,8 @@ -torchcam.cams -============= +torchcam.methods +================ -.. currentmodule:: torchcam.cams +.. currentmodule:: torchcam.methods Class activation map diff --git a/docs/source/notebooks/quicktour.rst b/docs/source/notebooks/quicktour.rst index d267b501..ab7f46c5 100644 --- a/docs/source/notebooks/quicktour.rst +++ b/docs/source/notebooks/quicktour.rst @@ -40,7 +40,7 @@ Basic usage >>> from torchvision.models import resnet18 >>> from torchvision.transforms.functional import normalize, resize, to_pil_image >>> - >>> from torchcam.cams import SmoothGradCAMpp, LayerCAM + >>> from torchcam.methods import SmoothGradCAMpp, LayerCAM >>> from torchcam.utils import overlay_mask .. code-block:: python diff --git a/scripts/cam_example.py b/scripts/cam_example.py index fc0b9e1b..9eb9097c 100644 --- a/scripts/cam_example.py +++ b/scripts/cam_example.py @@ -18,7 +18,7 @@ from torchvision import models from torchvision.transforms.functional import normalize, resize, to_pil_image, to_tensor -from torchcam import cams +from torchcam import methods from torchcam.utils import overlay_mask @@ -44,16 +44,16 @@ def main(args): [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]).to(device=device) if isinstance(args.method, str): - methods = [args.method] + cam_methods = [args.method] else: - methods = [ + cam_methods = [ 'CAM', 'GradCAM', 'GradCAMpp', 'SmoothGradCAMpp', 'ScoreCAM', 'SSCAM', 'ISCAM', 'XGradCAM', 'LayerCAM' ] # Hook the corresponding layer in the model - cam_extractors = [cams.__dict__[name](model, enable_hooks=False) for name in methods] + cam_extractors = [methods.__dict__[name](model, enable_hooks=False) for name in cam_methods] # Homogenize number of elements in each row num_cols = math.ceil((len(cam_extractors) + 1) / args.rows) diff --git a/scripts/eval_latency.py b/scripts/eval_latency.py index 8f73de74..116a84a4 100644 --- a/scripts/eval_latency.py +++ b/scripts/eval_latency.py @@ -14,7 +14,7 @@ import torch from torchvision import models -from torchcam import cams as methods +from torchcam import methods def main(args): diff --git a/test/test_cams_cam.py b/test/test_methods_activation.py similarity index 89% rename from test/test_cams_cam.py rename to test/test_methods_activation.py index b0a66a8f..782be392 100644 --- a/test/test_cams_cam.py +++ b/test/test_methods_activation.py @@ -7,18 +7,18 @@ import torch from torchvision.models import mobilenet_v2 -from torchcam.cams import cam +from torchcam.methods import activation def test_base_cam_constructor(mock_img_model): model = mobilenet_v2(pretrained=False).eval() # Check that multiple target layers is disabled for base CAM with pytest.raises(ValueError): - _ = cam.CAM(model, ['classifier.1', 'classifier.2']) + _ = activation.CAM(model, ['classifier.1', 'classifier.2']) # FC layer checks with pytest.raises(TypeError): - _ = cam.CAM(model, fc_layer=3) + _ = activation.CAM(model, fc_layer=3) def _verify_cam(activation_map, output_size): @@ -52,7 +52,7 @@ def test_img_cams(cam_name, target_layer, fc_layer, num_samples, output_size, mo target_layer = target_layer(model) if callable(target_layer) else target_layer # Hook the corresponding layer in the model - extractor = cam.__dict__[cam_name](model, target_layer, **kwargs) + extractor = activation.__dict__[cam_name](model, target_layer, **kwargs) with torch.no_grad(): scores = model(mock_img_tensor) @@ -61,7 +61,7 @@ def test_img_cams(cam_name, target_layer, fc_layer, num_samples, output_size, mo def test_cam_conv1x1(mock_fullyconv_model): - extractor = cam.CAM(mock_fullyconv_model, fc_layer='1') + extractor = activation.CAM(mock_fullyconv_model, fc_layer='1') with torch.no_grad(): scores = mock_fullyconv_model(torch.rand((1, 3, 32, 32))) # Use the hooked data to compute activation map @@ -85,7 +85,7 @@ def test_video_cams(cam_name, target_layer, num_samples, output_size, mock_video kwargs['num_samples'] = num_samples # Hook the corresponding layer in the model - extractor = cam.__dict__[cam_name](model, target_layer, **kwargs) + extractor = activation.__dict__[cam_name](model, target_layer, **kwargs) with torch.no_grad(): scores = model(mock_video_tensor) diff --git a/test/test_cams_core.py b/test/test_methods_core.py similarity index 99% rename from test/test_cams_core.py rename to test/test_methods_core.py index 362771c1..c0338a0e 100644 --- a/test/test_cams_core.py +++ b/test/test_methods_core.py @@ -6,7 +6,7 @@ import pytest import torch -from torchcam.cams import core +from torchcam.methods import core def test_cam_constructor(mock_img_model): diff --git a/test/test_cams_gradcam.py b/test/test_methods_gradient.py similarity index 85% rename from test/test_cams_gradcam.py rename to test/test_methods_gradient.py index d80c77d7..e4e9f9b8 100644 --- a/test/test_cams_gradcam.py +++ b/test/test_methods_gradient.py @@ -8,7 +8,7 @@ from torch import nn from torchvision.models import mobilenet_v2 -from torchcam.cams import gradcam +from torchcam.methods import gradient def _verify_cam(activation_map, output_size): @@ -34,7 +34,7 @@ def test_img_cams(cam_name, target_layer, output_size, mock_img_tensor): target_layer = target_layer(model) if callable(target_layer) else target_layer # Hook the corresponding layer in the model - extractor = gradcam.__dict__[cam_name](model, target_layer) + extractor = gradient.__dict__[cam_name](model, target_layer) scores = model(mock_img_tensor) # Use the hooked data to compute activation map @@ -52,7 +52,7 @@ def test_img_cams(cam_name, target_layer, output_size, mock_img_tensor): ) # Hook before the inplace ops - extractor = gradcam.__dict__[cam_name](model, '2') + extractor = gradient.__dict__[cam_name](model, '2') scores = model(mock_img_tensor) # Use the hooked data to compute activation map _verify_cam(extractor(scores[0].argmax().item(), scores)[0], (224, 224)) @@ -71,7 +71,7 @@ def test_img_cams(cam_name, target_layer, output_size, mock_img_tensor): def test_video_cams(cam_name, target_layer, output_size, mock_video_model, mock_video_tensor): model = mock_video_model.eval() # Hook the corresponding layer in the model - extractor = gradcam.__dict__[cam_name](model, target_layer) + extractor = gradient.__dict__[cam_name](model, target_layer) scores = model(mock_video_tensor) # Use the hooked data to compute activation map @@ -82,7 +82,7 @@ def test_smoothgradcampp_repr(): model = mobilenet_v2(pretrained=False).eval() # Hook the corresponding layer in the model - extractor = gradcam.SmoothGradCAMpp(model, 'features.18.0') + extractor = gradient.SmoothGradCAMpp(model, 'features.18.0') assert repr(extractor) == "SmoothGradCAMpp(target_layer=['features.18.0'], num_samples=4, std=0.3)" @@ -90,24 +90,24 @@ def test_smoothgradcampp_repr(): def test_layercam_fuse_cams(mock_img_model): with pytest.raises(TypeError): - gradcam.LayerCAM.fuse_cams(torch.zeros((3, 32, 32))) + gradient.LayerCAM.fuse_cams(torch.zeros((3, 32, 32))) with pytest.raises(ValueError): - gradcam.LayerCAM.fuse_cams([]) + gradient.LayerCAM.fuse_cams([]) cams = [torch.rand((32, 32)), torch.rand((16, 16))] # Single CAM - assert torch.equal(cams[0], gradcam.LayerCAM.fuse_cams(cams[:1])) + assert torch.equal(cams[0], gradient.LayerCAM.fuse_cams(cams[:1])) # Fusion - cam = gradcam.LayerCAM.fuse_cams(cams) + cam = gradient.LayerCAM.fuse_cams(cams) assert isinstance(cam, torch.Tensor) assert cam.ndim == cams[0].ndim assert cam.shape == (32, 32) # Specify target shape - cam = gradcam.LayerCAM.fuse_cams(cams, (16, 16)) + cam = gradient.LayerCAM.fuse_cams(cams, (16, 16)) assert isinstance(cam, torch.Tensor) assert cam.ndim == cams[0].ndim assert cam.shape == (16, 16) diff --git a/test/test_cams_utils.py b/test/test_methods_utils.py similarity index 72% rename from test/test_cams_utils.py rename to test/test_methods_utils.py index 49f4ad9c..6fcfa9e9 100644 --- a/test/test_cams_utils.py +++ b/test/test_methods_utils.py @@ -5,18 +5,18 @@ from torchvision.models import resnet18 -from torchcam.cams import utils +from torchcam.methods import _utils def test_locate_candidate_layer(mock_img_model): # ResNet-18 mod = resnet18().eval() - assert utils.locate_candidate_layer(mod) == 'layer4' + assert _utils.locate_candidate_layer(mod) == 'layer4' # Custom model mod = mock_img_model.train() - assert utils.locate_candidate_layer(mod) == '0.3' + assert _utils.locate_candidate_layer(mod) == '0.3' # Check that the model is switched back to its origin mode afterwards assert mod.training @@ -25,8 +25,8 @@ def test_locate_linear_layer(mock_img_model): # ResNet-18 mod = resnet18().eval() - assert utils.locate_linear_layer(mod) == 'fc' + assert _utils.locate_linear_layer(mod) == 'fc' # Custom model mod = mock_img_model - assert utils.locate_linear_layer(mod) == '2' + assert _utils.locate_linear_layer(mod) == '2' diff --git a/torchcam/__init__.py b/torchcam/__init__.py index baf30605..7019db88 100644 --- a/torchcam/__init__.py +++ b/torchcam/__init__.py @@ -1,4 +1,4 @@ -from torchcam import cams, utils +from torchcam import methods, utils try: from .version import __version__ # noqa: F401 diff --git a/torchcam/cams/__init__.py b/torchcam/cams/__init__.py deleted file mode 100644 index ad26e96e..00000000 --- a/torchcam/cams/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .cam import * -from .gradcam import * -from .utils import * diff --git a/torchcam/methods/__init__.py b/torchcam/methods/__init__.py new file mode 100644 index 00000000..d9af8075 --- /dev/null +++ b/torchcam/methods/__init__.py @@ -0,0 +1,2 @@ +from .activation import * +from .gradient import * diff --git a/torchcam/cams/utils.py b/torchcam/methods/_utils.py similarity index 100% rename from torchcam/cams/utils.py rename to torchcam/methods/_utils.py diff --git a/torchcam/cams/cam.py b/torchcam/methods/activation.py similarity index 98% rename from torchcam/cams/cam.py rename to torchcam/methods/activation.py index ddb5b758..111f188d 100644 --- a/torchcam/cams/cam.py +++ b/torchcam/methods/activation.py @@ -12,7 +12,7 @@ from torch import Tensor, nn from .core import _CAM -from .utils import locate_linear_layer +from ._utils import locate_linear_layer __all__ = ['CAM', 'ScoreCAM', 'SSCAM', 'ISCAM'] @@ -34,7 +34,7 @@ class CAM(_CAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import CAM + >>> from torchcam.methods import CAM >>> model = resnet18(pretrained=True).eval() >>> cam = CAM(model, 'layer4', 'fc') >>> with torch.no_grad(): out = model(input_tensor) @@ -116,7 +116,7 @@ class ScoreCAM(_CAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import ScoreCAM + >>> from torchcam.methods import ScoreCAM >>> model = resnet18(pretrained=True).eval() >>> cam = ScoreCAM(model, 'layer4') >>> with torch.no_grad(): out = model(input_tensor) @@ -239,7 +239,7 @@ class SSCAM(ScoreCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import SSCAM + >>> from torchcam.methods import SSCAM >>> model = resnet18(pretrained=True).eval() >>> cam = SSCAM(model, 'layer4') >>> with torch.no_grad(): out = model(input_tensor) @@ -329,7 +329,7 @@ class ISCAM(ScoreCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import ISSCAM + >>> from torchcam.methods import ISSCAM >>> model = resnet18(pretrained=True).eval() >>> cam = ISCAM(model, 'layer4') >>> with torch.no_grad(): out = model(input_tensor) diff --git a/torchcam/cams/core.py b/torchcam/methods/core.py similarity index 99% rename from torchcam/cams/core.py rename to torchcam/methods/core.py index 8e4afc77..79d75e8b 100644 --- a/torchcam/cams/core.py +++ b/torchcam/methods/core.py @@ -11,7 +11,7 @@ import torch.nn.functional as F from torch import Tensor, nn -from .utils import locate_candidate_layer +from ._utils import locate_candidate_layer __all__ = ['_CAM'] diff --git a/torchcam/cams/gradcam.py b/torchcam/methods/gradient.py similarity index 98% rename from torchcam/cams/gradcam.py rename to torchcam/methods/gradient.py index e8abd493..729906d6 100644 --- a/torchcam/cams/gradcam.py +++ b/torchcam/methods/gradient.py @@ -86,7 +86,7 @@ class GradCAM(_GradCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import GradCAM + >>> from torchcam.methods import GradCAM >>> model = resnet18(pretrained=True).eval() >>> cam = GradCAM(model, 'layer4') >>> scores = model(input_tensor) @@ -137,7 +137,7 @@ class GradCAMpp(_GradCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import GradCAMpp + >>> from torchcam.methods import GradCAMpp >>> model = resnet18(pretrained=True).eval() >>> cam = GradCAMpp(model, 'layer4') >>> scores = model(input_tensor) @@ -214,7 +214,7 @@ class SmoothGradCAMpp(_GradCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import SmoothGradCAMpp + >>> from torchcam.methods import SmoothGradCAMpp >>> model = resnet18(pretrained=True).eval() >>> cam = SmoothGradCAMpp(model, 'layer4') >>> scores = model(input_tensor) @@ -326,7 +326,7 @@ class XGradCAM(_GradCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import XGradCAM + >>> from torchcam.methods import XGradCAM >>> model = resnet18(pretrained=True).eval() >>> cam = XGradCAM(model, 'layer4') >>> scores = model(input_tensor) @@ -370,7 +370,7 @@ class LayerCAM(_GradCAM): Example:: >>> from torchvision.models import resnet18 - >>> from torchcam.cams import LayerCAM + >>> from torchcam.methods import LayerCAM >>> model = resnet18(pretrained=True).eval() >>> extractor = LayerCAM(model, 'layer4') >>> scores = model(input_tensor)