Spaces:
Sleeping
Sleeping
| import numpy as np | |
| import torch | |
| from pytorch_grad_cam.base_cam import BaseCAM | |
| from pytorch_grad_cam.utils.find_layers import find_layer_predicate_recursive | |
| from pytorch_grad_cam.utils.svd_on_activations import get_2d_projection | |
| from pytorch_grad_cam.utils.image import scale_accross_batch_and_channels, scale_cam_image | |
| # https://arxiv.org/abs/1905.00780 | |
| class FullGrad(BaseCAM): | |
| def __init__(self, model, target_layers, | |
| reshape_transform=None): | |
| if len(target_layers) > 0: | |
| print( | |
| "Warning: target_layers is ignored in FullGrad. All bias layers will be used instead") | |
| def layer_with_2D_bias(layer): | |
| bias_target_layers = [torch.nn.Conv2d, torch.nn.BatchNorm2d] | |
| if type(layer) in bias_target_layers and layer.bias is not None: | |
| return True | |
| return False | |
| target_layers = find_layer_predicate_recursive( | |
| model, layer_with_2D_bias) | |
| super( | |
| FullGrad, | |
| self).__init__( | |
| model, | |
| target_layers, | |
| reshape_transform, | |
| compute_input_gradient=True) | |
| self.bias_data = [self.get_bias_data( | |
| layer).cpu().numpy() for layer in target_layers] | |
| def get_bias_data(self, layer): | |
| # Borrowed from official paper impl: | |
| # https://github.com/idiap/fullgrad-saliency/blob/master/saliency/tensor_extractor.py#L47 | |
| if isinstance(layer, torch.nn.BatchNorm2d): | |
| bias = - (layer.running_mean * layer.weight | |
| / torch.sqrt(layer.running_var + layer.eps)) + layer.bias | |
| return bias.data | |
| else: | |
| return layer.bias.data | |
| def compute_cam_per_layer( | |
| self, | |
| input_tensor, | |
| target_category, | |
| eigen_smooth): | |
| input_grad = input_tensor.grad.data.cpu().numpy() | |
| grads_list = [g.cpu().data.numpy() for g in | |
| self.activations_and_grads.gradients] | |
| cam_per_target_layer = [] | |
| target_size = self.get_target_width_height(input_tensor) | |
| gradient_multiplied_input = input_grad * input_tensor.data.cpu().numpy() | |
| gradient_multiplied_input = np.abs(gradient_multiplied_input) | |
| gradient_multiplied_input = scale_accross_batch_and_channels( | |
| gradient_multiplied_input, | |
| target_size) | |
| cam_per_target_layer.append(gradient_multiplied_input) | |
| # Loop over the saliency image from every layer | |
| assert(len(self.bias_data) == len(grads_list)) | |
| for bias, grads in zip(self.bias_data, grads_list): | |
| bias = bias[None, :, None, None] | |
| # In the paper they take the absolute value, | |
| # but possibily taking only the positive gradients will work | |
| # better. | |
| bias_grad = np.abs(bias * grads) | |
| result = scale_accross_batch_and_channels( | |
| bias_grad, target_size) | |
| result = np.sum(result, axis=1) | |
| cam_per_target_layer.append(result[:, None, :]) | |
| cam_per_target_layer = np.concatenate(cam_per_target_layer, axis=1) | |
| if eigen_smooth: | |
| # Resize to a smaller image, since this method typically has a very large number of channels, | |
| # and then consumes a lot of memory | |
| cam_per_target_layer = scale_accross_batch_and_channels( | |
| cam_per_target_layer, (target_size[0] // 8, target_size[1] // 8)) | |
| cam_per_target_layer = get_2d_projection(cam_per_target_layer) | |
| cam_per_target_layer = cam_per_target_layer[:, None, :, :] | |
| cam_per_target_layer = scale_accross_batch_and_channels( | |
| cam_per_target_layer, | |
| target_size) | |
| else: | |
| cam_per_target_layer = np.sum( | |
| cam_per_target_layer, axis=1)[:, None, :] | |
| return cam_per_target_layer | |
| def aggregate_multi_layers(self, cam_per_target_layer): | |
| result = np.sum(cam_per_target_layer, axis=1) | |
| return scale_cam_image(result) | |