Task6:可解释性分析、显著性分析

图像显著性 CNN_深度学习

 

感想:愈发感受到自己基础的薄弱了,今天的任务涉及的算法较多且深,短时间内恐怕是只能当个调包侠了,如果有和我一样基础薄弱的朋友可以一同前往子豪大佬的B站视频学习:

 

 torch-cam工具包

torch-cam的安装方式如下:

# 下载安装 torch-cam
git clone https:///frgfm/torch-cam.git
pip install -e torch-cam/.

安装之后在jupyterlab中充气内核,导入torchcam检查是否安装成功。

torchcam有两种使用方式,一种是直接通过命令行使用,另一种是在代码中导入。

命令行

python torch-cam/scripts/cam_example.py --help

--help参数查看torch-cam的基本用法。

usage: cam_example.py [-h] [--arch ARCH] [--img IMG] [--class-idx CLASS_IDX]
                      [--device DEVICE] [--savefig SAVEFIG] [--method METHOD]
                      [--alpha ALPHA] [--rows ROWS] [--noblock]

Saliency Map comparison

optional arguments:
  -h, --help            show this help message and exit
  --arch ARCH           Name of the architecture (default: resnet18)
  --img IMG             The image to extract CAM from (default:
                        https://www.woopets.fr/assets/races/000/066/big-
                        portrait/border-collie.jpg)
  --class-idx CLASS_IDX
                        Index of the class to inspect (default: 232)
  --device DEVICE       Default device to perform computation on (default:
                        None)
  --savefig SAVEFIG     Path to save figure (default: None)
  --method METHOD       CAM method to use (default: None)
  --alpha ALPHA         Transparency of the heatmap (default: 0.5)
  --rows ROWS           Number of rows for the layout (default: 1)
  --noblock             Disables blocking visualization (default: False)

下面是通过命令行的使用方式

python torch-cam/scripts/cam_example.py \
        --img test_img/border-collie.jpg \
        --savefig output/B1_border_collie.jpg \
        --arch resnet18 \
        --class-idx 232 \
        --rows 2

如图参数如同字面意思好理解。运行torchcam中的CAM范例文件,读如一张图片(img),处理后保存到(savefig)路径下arch就是architecture,在此处表示针对哪种模型,class-idx表示针对的类别,rows表示行数。

“CAM会对网络最后的特征图进行加权求和,就可以得到一个注意力的机制(就是卷积神经网络更关注图片的什么地方)。”通过这个有助于我们人类研究人工智能(机器学习、深度学习)的思考方式,从而着手处理。

比较常见的例子是区分雪豹和阿拉伯豹的任务中,分类器的注意力实际上集中在背景上,通过生活的背景区分豹子而不是豹子本身。子豪大佬的视频中也提到了给熊猫图片加上噪声之后,被识别为长臂猿的例子。这些例子表明,我们有需要通过了解人工智能的思维方式来对人工智能算法进行进一步的优化。

代码导包

下面的步骤使得我们得到了一个CAM的矩阵。这里用的是SmoothGradCAMpp的算法,它的速度较慢,效果较好。而速度相对较快的是CAM和Grad_CAM算法。

from torchcam.methods import SmoothGradCAMpp 
# CAM GradCAM GradCAMpp ISCAM LayerCAM SSCAM ScoreCAM SmoothGradCAMpp XGradCAM

cam_extractor = SmoothGradCAMpp(model)

activation_map = cam_extractor(pred_id, pred_logits)
activation_map = activation_map[0][0].detach().cpu().numpy()

得到了矩阵之后若将他进行可视化,得到的是一个7*7的图像,得到的矩阵是多少更选择的模型有关,这里是7*7是由于resnet18这个模型的某一层(好像可解释性学习在去年12月份专门作为系列任务出现,那时我没有参加,现在想在一个任务中完成还是太勉强了,还是继续学习吧,悲)

图像显著性 CNN_分类_02

 然后我们通过torchcam库自带的一个函数就可以把它投射到原图上。

rom torchcam.utils import overlay_mask

result = overlay_mask(img_pil, Image.fromarray(activation_map), alpha=0.7)

图像显著性 CNN_分类_03

alpha的值仍然是透明度,在这里直观地表现为原图的亮暗程度。

pytorch-grad-cam工具包

pip install grad-cam torchcam
git clone https:///jacobgil/pytorch-grad-cam.git

还是通过import pytorch_grad_cam来验证安装成功。

Grad-CAM热力图

grad-CAM:利用梯度作为特征图的权重。

CAM最重要的是两部分,一是特征图,一是特征图所对应的权重。grad-CAM就是利用梯度的方法找权重。

这样,Grad-CAM就不像CAM算法那样需要网络上有一个全局平均池化层,具有更大的适用范围。

LayerCAM

由于Grad-CAM存在的一些缺点,如“浅层权重方差变化大,不能使用梯度均值为特征图每个像素赋予同等重要度”,衍生出了一系列算法,比如LayerCAM。

LayerCAM:基于元素的,每一张特征图中每一个元素都有一个对应的权重。

此外还有 Grad-CAMpp,ScoreCAM等衍生算法。

基于Guided Grad-CAM的高分辨率细粒度可解释性分析

对单张图像,进行Guided Grad-CAM可解释性分析,绘制既具有类别判别性(Class-Discriminative),又具有高分辨率的细粒度热力图。

效果展示:

图像显著性 CNN_图像分类_04

如果去看了源码的话可以知道实际上我们期望的是只显示出狗头,而这个Guided Backpropagation算法还显示出了猫头,所以采用了该图像和Grad-CAM热力图逐元素相乘的方式,结果如下:

图像显著性 CNN_分类_05

这里子豪大佬给出了一个思考题:

Guided Backpropagation确实兼顾了高分辨率和class-discriminative,唯一美中不足的就是可视化效果太不锐利

是否可以进行进一步的图像处理,更适合人眼来看

说实话,我感到这个图像已经很适合人眼来看了,也不太明白可视化效果不太锐利是什么意思。是增加锐度的话可以做个卷积核,但显然不是这个意思。如果是要更突出的话,那么是不是要把狗头着一块(颜色波动明显)用原图覆盖呢。

基于DFF的图像子区域可解释性分析

对单张图像,进行Deep Feature Factorization可解释性分析,展示Concept Discovery概念发现图。参考阅 

图像显著性 CNN_图像显著性 CNN_06

通过获取每个concept对应的类别来做到这张图像。

我是越来越觉得可解释性分析和语义分割有关系了,似乎都是弱监督,但我很菜没学过,我还是表达以下期待吧。

封装函数如下:

def dff_show(img_path='test_img/cat_dog.jpg', n_components=5, top_k=2, hstack=False):
    img, rgb_img_float, input_tensor = get_image_from_path(img_path)
    dff = DeepFeatureFactorization(model=model, 
                                   target_layer=model.layer4, 
                                   computation_on_concepts=classifier)
    concepts, batch_explanations, concept_outputs = dff(input_tensor, n_components)
    concept_outputs = torch.softmax(torch.from_numpy(concept_outputs), axis=-1).numpy()
    concept_label_strings = create_labels(concept_outputs, top_k=top_k)
    visualization = show_factorization_on_image(rgb_img_float, 
                                                batch_explanations[0],
                                                image_weight=0.3, # 原始图像透明度
                                                concept_labels=concept_label_strings)
    if hstack:
        result = np.hstack((img, visualization))
    else:
        result = visualization
    display(Image.fromarray(result))

captum工具包

直接使用pip安装这个包即可。

遮挡可解释性分析-ImageNet图像分类

遮挡可解释性分析

在输入图像上,用遮挡滑块,滑动遮挡不同区域,探索哪些区域被遮挡后会显著影响模型的分类决策。

提示:因为每次遮挡都需要分别单独预测,因此代码运行可能需要较长时间。

我们可以改变遮挡滑块的大小。

图像显著性 CNN_图像显著性 CNN_07

下面代码以小滑块为例。

# 更改遮挡滑块的尺寸
attributions_occ = occlusion.attribute(input_tensor,
                                       strides = (3, 2, 2), # 遮挡滑动移动步长
                                       target=pred_id, # 目标类别
                                       sliding_window_shapes=(3, 4, 4), # 遮挡滑块尺寸
                                       baselines=0)

# 转为 224 x 224 x 3的数据维度
attributions_occ_norm = np.transpose(attributions_occ.detach().cpu().squeeze().numpy(), (1,2,0))

viz.visualize_image_attr_multiple(attributions_occ_norm, # 224 224 3
                                  rc_img_norm,           # 224 224 3
                                  ["original_image", "heat_map"],
                                  ["all", "positive"],
                                  show_colorbar=True,
                                  outlier_perc=2)
plt.show()

图像显著性 CNN_深度学习_08

Integrated Gradients可解释性分析

Integrated Gradients 原理

输入图像像素由空白变为输入图像像素的过程中,模型预测为某一特定类别的概率相对于输入图像像素的梯度积分。

图像显著性 CNN_分类_09

noise_tunnel = NoiseTunnel(integrated_gradients)

# 获得输入图像每个像素的 IG 值
attributions_ig_nt = noise_tunnel.attribute(input_tensor, nt_samples=12, nt_type='smoothgrad_sq', target=pred_id)

# 转为 224 x 224 x 3的数据维度
attributions_ig_nt_norm = np.transpose(attributions_ig_nt.squeeze().cpu().detach().numpy(), (1,2,0))

# 设置配色方案
default_cmap = LinearSegmentedColormap.from_list('custom blue', 
                                                 [(0, '#ffffff'),
                                                  (0.25, '#000000'),
                                                  (1, '#000000')], N=256)

viz.visualize_image_attr_multiple(attributions_ig_nt_norm, # 224 224 3
                                  rc_img_norm, # 224 224 3
                                  ["original_image", "heat_map"],
                                  ["all", "positive"],
                                  cmap=default_cmap,
                                  show_colorbar=True)
plt.show()