|
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 |
|
|
|
|
|
|
|
|
|
class FullGrad(BaseCAM): |
|
def __init__(self, model, target_layers, use_cuda=False, |
|
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, |
|
use_cuda, |
|
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): |
|
|
|
|
|
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) |
|
|
|
|
|
assert(len(self.bias_data) == len(grads_list)) |
|
for bias, grads in zip(self.bias_data, grads_list): |
|
bias = bias[None, :, None, None] |
|
|
|
|
|
|
|
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: |
|
|
|
|
|
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) |
|
|