Repository: Project-MONAI/MONAI

Issue:
Documentation missing for `VanillaGrad` etc interpretability.
Documentation missing.
Guided back propogation (also combined with GradCAM)
Would be good to include guided back propagation to our list of interpretability classes. As seen in the screenshot below, Guided-GradCAM can then be given by combining the two together. 

A lot of the functionality is already there, thanks to the `ModelWithHooks` class, but I couldn't quite figure out how to modify it to our needs.

![image](https://user-images.githubusercontent.com/33289025/147246159-11a5f46b-1faf-4bb0-aabf-3b192979c9af.png)



Discussion:

Resolving patch:
diff --git a/monai/visualize/gradient_based.py b/monai/visualize/gradient_based.py
--- a/monai/visualize/gradient_based.py
+++ b/monai/visualize/gradient_based.py
@@ -45,12 +45,29 @@ def backward(ctx, grad_output):
 
 
 class _GradReLU(torch.nn.Module):
+    """
+    A customized ReLU with the backward pass imputed for guided backpropagation (https://arxiv.org/abs/1412.6806).
+    """
+
     def forward(self, x: torch.Tensor) -> torch.Tensor:
         out: torch.Tensor = _AutoGradReLU.apply(x)
         return out
 
 
 class VanillaGrad:
+    """
+    Given an input image ``x``, calling this class will perform the forward pass, then set to zero
+    all activations except one (defined by ``index``) and propagate back to the image to achieve a gradient-based
+    saliency map.
+
+    If ``index`` is None, argmax of the output logits will be used.
+
+    See also:
+
+        - Simonyan et al. Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps
+          (https://arxiv.org/abs/1312.6034)
+    """
+
     def __init__(self, model: torch.nn.Module) -> None:
         if not isinstance(model, ModelWithHooks):  # Convert to model with hooks if necessary
             self._model = ModelWithHooks(model, target_layer_names=(), register_backward=True)
@@ -83,7 +100,11 @@ def __call__(self, x: torch.Tensor, index: torch.Tensor | int | None = None, **k
 
 class SmoothGrad(VanillaGrad):
     """
+    Compute averaged sensitivity map based on ``n_samples`` (Gaussian additive) of noisy versions
+    of the input image ``x``.
+
     See also:
+
         - Smilkov et al. SmoothGrad: removing noise by adding noise https://arxiv.org/abs/1706.03825
     """
 
@@ -126,12 +147,26 @@ def __call__(self, x: torch.Tensor, index: torch.Tensor | int | None = None, **k
 
 
 class GuidedBackpropGrad(VanillaGrad):
+    """
+    Based on Springenberg and Dosovitskiy et al. https://arxiv.org/abs/1412.6806,
+    compute gradient-based saliency maps by backpropagating positive graidents and inputs (see ``_AutoGradReLU``).
+
+    See also:
+
+        - Springenberg and Dosovitskiy et al. Striving for Simplicity: The All Convolutional Net
+          (https://arxiv.org/abs/1412.6806)
+    """
+
     def __call__(self, x: torch.Tensor, index: torch.Tensor | int | None = None, **kwargs) -> torch.Tensor:
         with replace_modules_temp(self.model, "relu", _GradReLU(), strict_match=False):
             return super().__call__(x, index, **kwargs)
 
 
 class GuidedBackpropSmoothGrad(SmoothGrad):
+    """
+    Compute gradient-based saliency maps based on both ``GuidedBackpropGrad`` and ``SmoothGrad``.
+    """
+
     def __call__(self, x: torch.Tensor, index: torch.Tensor | int | None = None, **kwargs) -> torch.Tensor:
         with replace_modules_temp(self.model, "relu", _GradReLU(), strict_match=False):
             return super().__call__(x, index, **kwargs)


Test patch: