ViT的可解释化-深度解析GradCam源码(4)-GradCAM类的定义

关于特征图可视化的计算大多在class GradCAM的实例化中完成

    cam = GradCAM(model=model,
                  target_layers=target_layers,
                  use_cuda=False,
                  reshape_transform=ReshapeTransform(model))
    target_category = 281  #281  tabby, tabby cat
    # target_category = 254  # pug, pug-dog

    grayscale_cam = cam(input_tensor=input_tensor, target_category=target_category)

    grayscale_cam = grayscale_cam[0, :]

下面具体看一下GradCAM如何定义的

获取目标层中每个通道的权重向量。
        返回权重通道的方法,
        通常只需要实现此函数。

call方法

这段代码定义了一个 __call__ 方法,用于执行 Saliency 属性算法。

        if self.cuda:

            input_tensor = input_tensor.cuda()

首先,若需要使用 GPU 计算,则将输入的张量 input_tensor 移到 GPU 上。

        # 正向传播得到网络输出logits(未经过softmax)

        output = self.activations_and_grads(input_tensor)

然后,通过调用 activations_and_grads 方法对输入进行前向传播,得到网络输出的 logits(未经 softmax 处理)。模型的activations_and_grads方法就是 ActivationsAndGradients(self.model, target_layers, reshape_transform) ,用于注册hook,并前向运行model推理,从指定中间层提取激活值和注册对应的梯度。详见:(27条消息) ViT的可解释化-深度解析GradCam源码(2)-注册hook获取梯度_白菜c的博客-CSDN博客

        if isinstance(target_category, int):

            target_category = [target_category] * input_tensor.size(0)

        if target_category is None:

            target_category = np.argmax(output.cpu().data.numpy(), axis=-1)

            print(f"category id: {target_category}")

        else:

            assert (len(target_category) == input_tensor.size(0))

GradCAM是根据类别有关的注意力,target_category 是你想关注的输入类别,比如小狗对应的是281。

如果 target_category 是整数类型,则将其扩展为与输入样本数量相同的列表。

接下来,根据情况选择目标类别。如果 target_category 为 None,则通过在输出 logits 上取最大值确定目标类别,并打印出相应的类别 id。否则,检查传入的目标类别列表长度是否与输入样本数量相同。

        self.model.zero_grad()

        loss = self.get_loss(output, target_category)

        loss.backward(retain_graph=True)

然后,对模型的参数梯度进行清零,计算损失函数 loss,并执行反向传播以计算梯度。

        cam_per_layer = self.compute_cam_per_layer(input_tensor)

        return self.aggregate_multi_layers(cam_per_layer)

一般情况下,Saliency 属性的研究会选择单个目标层来计算显著图像,通常是最后一个卷积层。但这里支持传入多个目标层的列表,将为每个输入图像计算显著图,并对它们进行聚合(默认使用均值聚合)。最后,返回多层显著图的聚合结果。compute_cam_per_layer方法具体解释如下

compute_cam_per_layer

这段代码定义了一个名为 compute_cam_per_layer 的方法,用于计算每个目标层的 CAM(Class Activation Map)

        activations_list = [a.cpu().data.numpy()

                            for a in self.activations_and_grads.activations]

        grads_list = [g.cpu().data.numpy()

                      for g in self.activations_and_grads.gradients]

activations_and_grads.activationsactivations_and_grads.gradients 中获取激活值和梯度,并将它们转换为 numpy 数组。这些激活值和梯度是通过执行 self.activations_and_grads 方法获得的。

        target_size = self.get_target_width_height(input_tensor)

接下来,根据输入张量的大小确定目标尺寸 target_size

        cam_per_target_layer = []

        # Loop over the saliency image from every layer

        for layer_activations, layer_grads in zip(activations_list, grads_list):

            cam = self.get_cam_image(layer_activations, layer_grads)

            cam[cam < 0] = 0  # works like mute the min-max scale in the function of scale_cam_image

            scaled = self.scale_cam_image(cam, target_size)

            cam_per_target_layer.append(scaled[:, None, :])

然后,通过迭代每个目标层的激活值和梯度数组,调用 get_cam_image 方法计算 CAM 图像。对于计算得到的 CAM 图像,将小于 0 的值设为 0,类似于在 scale_cam_image 函数中进行最小-最大缩放。将 CAM 图像缩放到目标尺寸,并将结果添加到 cam_per_target_layer 列表中。

        return cam_per_target_layer

最终,返回每个目标层的 CAM 图像列表 cam_per_target_layer

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值