当前位置:   article > 正文

使用grad_cam生成自己的模型的热力图_如何在自己模型上添加热力图

如何在自己模型上添加热力图
  1. import os
  2. import numpy as np
  3. import torch
  4. from PIL import Image
  5. import matplotlib.pyplot as plt
  6. from torchvision import models
  7. from torchvision import transforms
  8. from utils import GradCAM, show_cam_on_image, center_crop_img
  9. from resnet1 import Mymodel
  10. def main():
  11. model = Mymodel(num_classes=7) #导入自己的模型,num_classes数为自己数据集的类别数
  12. weights_dict = torch.load("自己模型的训练权重", map_location='cpu')
  13. model.load_state_dict(weights_dict, strict=False) #加载自己模型的权重
  14. # target_layers = [model.backbone.layer4]
  15. target_layers = [model.backbone.layer4[-1]] #定义目标层
  16. # model = models.mobilenet_v3_large(pretrained=True)
  17. # target_layers = [model.features[-1]]
  18. # model = models.vgg16(pretrained=True)
  19. # target_layers = [model.features]
  20. # model = models.resnet34(pretrained=True)
  21. # target_layers = [model.layer4]
  22. # model = models.regnet_y_800mf(pretrained=True)
  23. # target_layers = [model.trunk_output]
  24. # model = models.efficientnet_b0(pretrained=True)
  25. # target_layers = [model.features]
  26. data_transform = transforms.Compose([
  27. transforms.ToTensor(),
  28. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
  29. # load image
  30. img_path = "导入图片路径"
  31. assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
  32. img = Image.open(img_path).convert('RGB')
  33. img = np.array(img, dtype=np.uint8)
  34. img = center_crop_img(img, 448) #将导入的图片reshape到自己想要的尺寸
  35. # [C, H, W]
  36. img_tensor = data_transform(img)
  37. # expand batch dimension
  38. # [C, H, W] -> [N, C, H, W]
  39. input_tensor = torch.unsqueeze(img_tensor, dim=0)
  40. cam = GradCAM(model=model, target_layers=target_layers, use_cuda=False)
  41. # target_category = 281 # tabby, tabby cat
  42. # target_category = 254 # pug, pug-dog
  43. target_category = 4
  44. grayscale_cam = cam(input_tensor=input_tensor, target_category=target_category)
  45. grayscale_cam = grayscale_cam[0, :]
  46. visualization = show_cam_on_image(img.astype(dtype=np.float32) / 255.,
  47. grayscale_cam,
  48. use_rgb=True)
  49. plt.imshow(visualization)
  50. plt.show()
  51. if __name__ == '__main__':
  52. main()

下面是grad_cam的代码,注意:如果自己的模型是多输出的,要选择模型的指定输出。

  1. import cv2
  2. import numpy as np
  3. class ActivationsAndGradients:
  4. """ Class for extracting activations and
  5. registering gradients from targeted intermediate layers """
  6. def __init__(self, model, target_layers, reshape_transform):
  7. self.model = model
  8. self.gradients = []
  9. self.activations = []
  10. self.reshape_transform = reshape_transform
  11. self.handles = []
  12. for target_layer in target_layers:
  13. self.handles.append(
  14. target_layer.register_forward_hook(
  15. self.save_activation))
  16. # Backward compatibility with older pytorch versions:
  17. if hasattr(target_layer, 'register_full_backward_hook'):
  18. self.handles.append(
  19. target_layer.register_full_backward_hook(
  20. self.save_gradient))
  21. else:
  22. self.handles.append(
  23. target_layer.register_backward_hook(
  24. self.save_gradient))
  25. def save_activation(self, module, input, output):
  26. activation = output
  27. if self.reshape_transform is not None:
  28. activation = self.reshape_transform(activation)
  29. self.activations.append(activation.cpu().detach())
  30. def save_gradient(self, module, grad_input, grad_output):
  31. # Gradients are computed in reverse order
  32. grad = grad_output[0]
  33. if self.reshape_transform is not None:
  34. grad = self.reshape_transform(grad)
  35. self.gradients = [grad.cpu().detach()] + self.gradients
  36. def __call__(self, x):
  37. self.gradients = []
  38. self.activations = []
  39. return self.model(x)
  40. def release(self):
  41. for handle in self.handles:
  42. handle.remove()
  43. class GradCAM:
  44. def __init__(self,
  45. model,
  46. target_layers,
  47. reshape_transform=None,
  48. use_cuda=False):
  49. self.model = model.eval()
  50. self.target_layers = target_layers
  51. self.reshape_transform = reshape_transform
  52. self.cuda = use_cuda
  53. if self.cuda:
  54. self.model = model.cuda()
  55. self.activations_and_grads = ActivationsAndGradients(
  56. self.model, target_layers, reshape_transform)
  57. """ Get a vector of weights for every channel in the target layer.
  58. Methods that return weights channels,
  59. will typically need to only implement this function. """
  60. @staticmethod
  61. def get_cam_weights(grads):
  62. return np.mean(grads, axis=(2, 3), keepdims=True)
  63. @staticmethod
  64. def get_loss(output, target_category):
  65. loss = 0
  66. output = output[2] #注意:如果模型是多输出,需要选择自己想要的输出
  67. for i in range(len(target_category)):
  68. loss = loss + output[i, target_category[i]]
  69. return loss
  70. def get_cam_image(self, activations, grads):
  71. weights = self.get_cam_weights(grads)
  72. weighted_activations = weights * activations
  73. cam = weighted_activations.sum(axis=1)
  74. return cam
  75. @staticmethod
  76. def get_target_width_height(input_tensor):
  77. width, height = input_tensor.size(-1), input_tensor.size(-2)
  78. return width, height
  79. def compute_cam_per_layer(self, input_tensor):
  80. activations_list = [a.cpu().data.numpy()
  81. for a in self.activations_and_grads.activations]
  82. grads_list = [g.cpu().data.numpy()
  83. for g in self.activations_and_grads.gradients]
  84. target_size = self.get_target_width_height(input_tensor)
  85. cam_per_target_layer = []
  86. # Loop over the saliency image from every layer
  87. for layer_activations, layer_grads in zip(activations_list, grads_list):
  88. cam = self.get_cam_image(layer_activations, layer_grads)
  89. cam[cam < 0] = 0 # works like mute the min-max scale in the function of scale_cam_image
  90. scaled = self.scale_cam_image(cam, target_size)
  91. cam_per_target_layer.append(scaled[:, None, :])
  92. return cam_per_target_layer
  93. def aggregate_multi_layers(self, cam_per_target_layer):
  94. cam_per_target_layer = np.concatenate(cam_per_target_layer, axis=1)
  95. cam_per_target_layer = np.maximum(cam_per_target_layer, 0)
  96. result = np.mean(cam_per_target_layer, axis=1)
  97. return self.scale_cam_image(result)
  98. @staticmethod
  99. def scale_cam_image(cam, target_size=None):
  100. result = []
  101. for img in cam:
  102. img = img - np.min(img)
  103. img = img / (1e-7 + np.max(img))
  104. if target_size is not None:
  105. img = cv2.resize(img, target_size)
  106. result.append(img)
  107. result = np.float32(result)
  108. return result
  109. def __call__(self, input_tensor, target_category=None):
  110. if self.cuda:
  111. input_tensor = input_tensor.cuda()
  112. # 正向传播得到网络输出logits(未经过softmax)
  113. output = self.activations_and_grads(input_tensor)
  114. if isinstance(target_category, int):
  115. target_category = [target_category] * input_tensor.size(0)
  116. if target_category is None:
  117. target_category = np.argmax(output.cpu().data.numpy(), axis=-1)
  118. print(f"category id: {target_category}")
  119. else:
  120. assert (len(target_category) == input_tensor.size(0))
  121. self.model.zero_grad()
  122. loss = self.get_loss(output, target_category)
  123. loss.backward(retain_graph=True)
  124. # In most of the saliency attribution papers, the saliency is
  125. # computed with a single target layer.
  126. # Commonly it is the last convolutional layer.
  127. # Here we support passing a list with multiple target layers.
  128. # It will compute the saliency image for every image,
  129. # and then aggregate them (with a default mean aggregation).
  130. # This gives you more flexibility in case you just want to
  131. # use all conv layers for example, all Batchnorm layers,
  132. # or something else.
  133. cam_per_layer = self.compute_cam_per_layer(input_tensor)
  134. return self.aggregate_multi_layers(cam_per_layer)
  135. def __del__(self):
  136. self.activations_and_grads.release()
  137. def __enter__(self):
  138. return self
  139. def __exit__(self, exc_type, exc_value, exc_tb):
  140. self.activations_and_grads.release()
  141. if isinstance(exc_value, IndexError):
  142. # Handle IndexError here...
  143. print(
  144. f"An exception occurred in CAM with block: {exc_type}. Message: {exc_value}")
  145. return True
  146. def show_cam_on_image(img: np.ndarray,
  147. mask: np.ndarray,
  148. use_rgb: bool = False,
  149. colormap: int = cv2.COLORMAP_JET) -> np.ndarray:
  150. """ This function overlays the cam mask on the image as an heatmap.
  151. By default the heatmap is in BGR format.
  152. :param img: The base image in RGB or BGR format.
  153. :param mask: The cam mask.
  154. :param use_rgb: Whether to use an RGB or BGR heatmap, this should be set to True if 'img' is in RGB format.
  155. :param colormap: The OpenCV colormap to be used.
  156. :returns: The default image with the cam overlay.
  157. """
  158. heatmap = cv2.applyColorMap(np.uint8(255 * mask), colormap)
  159. if use_rgb:
  160. heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB)
  161. heatmap = np.float32(heatmap) / 255
  162. if np.max(img) > 1:
  163. raise Exception(
  164. "The input image should np.float32 in the range [0, 1]")
  165. cam = heatmap + img
  166. cam = cam / np.max(cam)
  167. return np.uint8(255 * cam)
  168. def center_crop_img(img: np.ndarray, size: int):
  169. h, w, c = img.shape
  170. if w == h == size:
  171. return img
  172. if w < h:
  173. ratio = size / w
  174. new_w = size
  175. new_h = int(h * ratio)
  176. else:
  177. ratio = size / h
  178. new_h = size
  179. new_w = int(w * ratio)
  180. img = cv2.resize(img, dsize=(new_w, new_h))
  181. if new_w == size:
  182. h = (new_h - size) // 2
  183. img = img[h: h+size]
  184. else:
  185. w = (new_w - size) // 2
  186. img = img[:, w: w+size]
  187. return img

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/340946
推荐阅读
相关标签
  

闽ICP备14008679号