yolov5热力图可视化grad-cam踩坑经验分享

最近在做热力图的可视化,网上搜了很多的资料,但是大部分都是需要在原网络结构上进行修改,非常的不方便。最后在网上找到一位博主分享的即插即用的模块,觉得效果还可以,但是中间有些细节,需要注意。

原博文地址:https://blog.youkuaiyun.com/qq_37706472/article/details/128714604

源码地址:yolo-gradcam

有同学想要不带目标框的图,可以参考这个链接:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_classification/grad_cam(链接转自https://blog.youkuaiyun.com/qq_37541097/article/details/123089851

为了方便,我也把Github上的代码复制在下方了。

import warnings
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')
import torch, yaml, cv2, os, shutil
import numpy as np
np.random.seed(0)
import matplotlib.pyplot as plt
from tqdm import trange
from PIL import Image
from models.yolo import Model
from utils.general import intersect_dicts
from utils.augmentations import letterbox
from utils.general import xywh2xyxy
from pytorch_grad_cam import GradCAMPlusPlus, GradCAM, XGradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image
from pytorch_grad_cam.activations_and_gradients import ActivationsAndGradients

class yolov5_heatmap:
    def __init__(self, weight, cfg, device, method, layer, backward_type, conf_threshold, ratio):
        device = torch.device(device)
        ckpt = torch.load(weight)
        model_names = ckpt['model'].names
        csd = ckpt['model'].float().state_dict()  # checkpoint state_dict as FP32
        model = Model(cfg, ch=3, nc=len(model_names)).to(device)
        csd = intersect_dicts(csd, model.state_dict(), exclude=['anchor'])  # intersect
        model.load_state_dict(csd, strict=False)  # load
        model.eval()
        print(f'Transferred {len(csd)}/{len(model.state_dict())} items')
        
        target_layers = [eval(layer)]
        method = eval(method)

        colors = np.random.uniform(0, 255, size=(len(model_names), 3)).astype(np.int)
        self.__dict__.update(locals())
    
    def post_process(self, result):
        logits_ = result[..., 4:]
        boxes_ = result[..., :4]
        sorted, indices = torch.sort(logits_[..., 0], descending=True)
        return logits_[0][indices[0]], xywh2xyxy(boxes_[0][indices[0]]).cpu().detach().numpy()

    def draw_detections(self, box, color, name, img):
        xmin, ymin, xmax, ymax = list(map(int, list(box)))
        cv2.rectangle(img, (xmin, ymin), (xmax, ymax), tuple(int(x) for x in color), 2)
        cv2.putText(img, str(name), (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, tuple(int(x) for x in color), 2, lineType=cv2.LINE_AA)
        return img

    def __call__(self, img_path, save_path):
        # remove dir if exist
        if os.path.exists(save_path):
            shutil.rmtree(save_path)
        # make dir if not exist
        os.makedirs(save_path, exist_ok=True)

        # img process
        img = cv2.imread(img_path)
        img = letterbox(img)[0]
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = np.float32(img) / 255.0
        tensor = torch.from_numpy(np.transpose(img, axes=[2, 0, 1])).unsqueeze(0).to(self.device)

        # init ActivationsAndGradients
        grads = ActivationsAndGradients(self.model, self.target_layers, reshape_transform=None)

        # get ActivationsAndResult
        result = grads(tensor)
        activations = grads.activations[0].cpu().detach().numpy()

        # postprocess to yolo output
        post_result, post_boxes = self.post_process(result[0])
        for i in trange(int(post_result.size(0) * self.ratio)):
            if post_result[i][0] < self.conf_threshold:
                break

            self.model.zero_grad()
            if self.backward_type == 'conf':
                post_result[i, 0].backward(retain_graph=True)
            else:
                # get max probability for this prediction
                score = post_result[i, 1:].max()
                score.backward(retain_graph=True)

            # process heatmap
            gradients = grads.gradients[0]
            b, k, u, v = gradients.size()
            weights = self.method.get_cam_weights(self.method, None, None, None, activations, gradients.detach().numpy())
            weights = weights.reshape((b, k, 1, 1))
            saliency_map = np.sum(weights * activations, axis=1)
            saliency_map = np.squeeze(np.maximum(saliency_map, 0))
            saliency_map = cv2.resize(saliency_map, (tensor.size(3), tensor.size(2)))
            saliency_map_min, saliency_map_max = saliency_map.min(), saliency_map.max()
            if (saliency_map_max - saliency_map_min) == 0:
                continue
            saliency_map = (saliency_map - saliency_map_min) / (saliency_map_max - saliency_map_min)

            # add heatmap and box to image
            cam_image = show_cam_on_image(img.copy(), saliency_map, use_rgb=True)
            cam_image = self.draw_detections(post_boxes[i], self.colors[int(post_result[i, 1:].argmax())], f'{self.model_names[int(post_result[i, 1:].argmax())]} {post_result[i][0]:.2f}', cam_image)
            cam_image = Image.fromarray(cam_image)
            cam_image.save(f'{save_path}/{i}.png')

def get_params():
    params = {
        'weight': 'runs/train/exp/weights/best.pt',
        'cfg': 'models/yolov5m.yaml',
        'device': 'cuda:0',
        'method': 'XGradCAM', # GradCAMPlusPlus, GradCAM, XGradCAM
        'layer': 'model.model[-2]',
        'backward_type': 'class', # class or conf
        'conf_threshold': 0.6, # 0.6
        'ratio': 0.02 # 0.02-0.1
    }
    return params

if __name__ == '__main__':
    model = yolov5_heatmap(**get_params())
    model(r'dataset\images\test\aircraft_1064.jpg', 'result')
  1. 需要安装pytorch_grad_cam库,可以直接pip install pytorch-grad-cam或者去 jacobgil/pytorch-grad-cam将源码下载下来,只需要下载这一个文件夹就可以,放入项目中,

  1. get_params中的参数:

  1. weight:模型权重文件

  1. cfg:模型文件

  1. device:选择使用GPU还是CPU

  1. method:选择grad-cam方法,这里是提供了几种,可能对效果有点不一样,可以都尝试一下

  1. layer: 选择需要可视化的那层

  1. backward_type:反向传播的方式,可以是以conf的loss传播,也可以class的loss传播

  1. conf_threshold置信度

  1. ratio 就是一个参数,用来筛选置信度高的结果,低的就舍弃

报错解决:

1.没有生成任何结果

解决方法:

将model.eval()改为 model.fuse().eval()

2.inplace 出错

解决方法:

将yolo.py文件中,detectionmodel()函数的forward()函数中,true改为false

附最后效果图(ps:这个新版编辑器我不知道怎么把两张图放同一排。。。):

### 使用 Grad-CAM 生成可视化力图 #### 安装依赖库 为了实现 Grad-CAM 的功能,需要安装一些必要的 Python 库。可以使用 pip 来完成这些操作。 ```bash pip install torch torchvision numpy matplotlib opencv-python pillow ``` #### 导入所需模块 接下来导入所需的 Python 模块来处理图像并运行模型: ```python import cv2 import numpy as np from PIL import Image import torch import torchvision.transforms as transforms from pytorch_grad_cam.utils.image import show_cam_on_image from pytorch_grad_cam import GradCAM ``` #### 加载预训练模型 这里以 ResNet50 为例加载一个已经过预训练的卷积神经网络模型,并设置其评估模式以便后续推理过程中的梯度计算能够正常工作[^1]。 ```python model = torch.hub.load('pytorch/vision:v0.6.0', 'resnet50', pretrained=True) model.eval() ``` #### 准备目标层 对于大多数情况下,默认选择最后一层卷积作为目标层来进行注意力机制分析是最合适的;而对于某些特定架构,则可能需要手动指定其他位置上的卷积层作为目标对象[^2]。 ```python target_layer = model.layer4[-1] ``` #### 初始化 Grad-CAM 实例 创建一个新的 `GradCAM` 对象实例并将之前定义好的参数传递给它用于初始化配置。 ```python cam = GradCAM(model=model, target_layers=[target_layer], use_cuda=torch.cuda.is_available()) ``` #### 图像前处理函数 编写一个辅助方法负责将原始图片转换成适合送入深度学习框架的形式——即张量(tensor),同时保留原尺寸副本供后期叠加显示时调用[^3]。 ```python def preprocess_image(img_path): img = Image.open(img_path).convert('RGB') transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) input_tensor = transform(img).unsqueeze(0) # Add batch dimension. return input_tensor, np.array(img) / 255. input_tensor, rgb_img = preprocess_image("path_to_your_image.jpg") ``` #### 获取类别预测结果 通过调用模型对输入数据做出分类决策,并获取最高概率对应的标签索引值。 ```python output = model(input_tensor) prediction = output.argmax(dim=1).item() print(f"The predicted class index is {prediction}.") ``` #### 构建 Grad-CAM 力图 最后一步就是实际构建 Grad-CAM 映射了。这会返回一个形状与输入一致但只含单通道灰阶数值的结果矩阵表示重要性权重分布情况。 ```python grayscale_cam = cam(input_tensor=input_tensor, targets=None)[0, :] visualization = show_cam_on_image(rgb_img, grayscale_cam, use_rgb=True) cv2.imshow('Visualization', visualization[:, :, ::-1]) # Convert RGB to BGR for OpenCV display compatibility cv2.waitKey(0) cv2.destroyAllWindows() ``` 上述代码片段展示了如何利用 PyTorch 和 grad-cam 库快速搭建一套基于 Grad-CAM 技术的视觉解释工具链路,帮助理解复杂深层结构内部运作原理及其对外界刺激响应特性。
评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值