1.Captum工具包
https://github.com/frgfm/torch-cam
#安装配置环境
pip install numpy pandas matplotlib requests tqdm opencv-python pillow -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu113/torch1.10.0/index.html
#下载中文字体文件
wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf
#Image Net1000
wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/meta_data/imagenet_class_index.csv
#创建目录
import os
# 存放测试图片
os.mkdir('test_img')
# 存放结果文件
os.mkdir('output')
# 存放训练得到的模型权重
os.mkdir('checkpoint')
# 下载样例模型文件
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/checkpoints/fruit30_pytorch_20220814.pth -P checkpoint
# 下载 类别名称 和 ID索引号 的映射字典
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/labels_to_idx.npy
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/idx_to_labels.npy
# 下载测试图像文件 至 test_img 文件夹
# 边牧犬,来源:https://www.woopets.fr/assets/races/000/066/big-portrait/border-collie.jpg
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/border-collie.jpg -P test_img
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/cat_dog.jpg -P test_img
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0818/room_video.mp4 -P test_img
# 草莓图像,来源:https://www.pexels.com/zh-cn/photo/4828489/
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0818/test_草莓.jpg -P test_img
# # 下载测试视频文件 至 test_img 文件夹
# !wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/fruits_video.mp4 -P test_img
#安装配置环境
pip install numpy pandas matplotlib requests tqdm opencv-python pillow captum -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
#下载中文字体文字
wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf
#加载Image Net1000类别信息
wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/meta_data/imagenet_class_index.csv
#创建目录
import os
# 存放测试图片
os.mkdir('test_img')
# 存放结果文件
os.mkdir('output')
# 存放训练得到的模型权重
os.mkdir('checkpoints')
#下载模型权重矩阵
# 下载样例模型文件
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/checkpoints/fruit30_pytorch_20220814.pth -P checkpoints
# 下载 类别名称 和 ID索引号 的映射字典
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/labels_to_idx.npy
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/idx_to_labels.npy
# 下载测试图像文件 至 test_img 文件夹
# 边牧犬,来源:https://www.woopets.fr/assets/races/000/066/big-portrait/border-collie.jpg
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/border-collie.jpg -P test_img
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/cat_dog.jpg -P test_img
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0818/room_video.mp4 -P test_img
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/swan-3299528_1280.jpg -P test_img
# 草莓图像,来源:https://www.pexels.com/zh-cn/photo/4828489/
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0818/test_草莓.jpg -P test_img
!wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_lemon.jpg -P test_img
# 来自 VOC 2012 数据集两张图像
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0820/2007_002953_mask.png -P test_img
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0820/2007_002953.jpg -P test_img
!wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_fruits.jpg -P test_img
!wget https://zihao-openmmlab.obs.myhuaweicloud.com/20220716-mmclassification/test/0818/test_orange_2.jpg -P test_img
#下载matplotlib中文字体
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
matplotlib.rc("font",family='SimHei') # 中文字体
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.plot([1,2,3], [100,500,300])
plt.title('matplotlib中文字体测试', fontsize=25)
plt.xlabel('X轴', fontsize=15)
plt.ylabel('Y轴', fontsize=15)
plt.show()
遮挡可解释性分析
#导入模块
import os
import json
import numpy as np
import pandas as pd
from PIL import Image
import torch
import torch.nn.functional as F
import torchvision
from torchvision import models
from torchvision import transforms
# from captum.attr import IntegratedGradients
# from captum.attr import GradientShap
from captum.attr import Occlusion
# from captum.attr import NoiseTunnel
from captum.attr import visualization as viz
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
%matplotlib inline
# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)
#载入预训练模型
model = models.resnet18(pretrained=True)
model = model.eval().to(device)
#ImageNet 1000图像分类标签
import pandas as pd
df = pd.read_csv('imagenet_class_index.csv')
idx_to_labels = {}
idx_to_labels_cn = {}
for idx, row in df.iterrows():
idx_to_labels[row['ID']] = row['class']
idx_to_labels_cn[row['ID']] = row['Chinese']
#图像预处理
from torchvision import transforms
# 缩放、裁剪、转 Tensor、归一化
transform_A = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor()
])
transform_B = transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
#载入测试图片
img_path = 'test_img/swan-3299528_1280.jpg'
img_pil = Image.open(img_path)
img_pil
#图像预处理
# 缩放、裁剪
rc_img = transform_A(img_pil)
rc_img.shape
# 调整数据维度
rc_img_norm = np.transpose(rc_img.squeeze().cpu().detach().numpy(), (1,2,0))
rc_img_norm.shape
# 色彩归一化
input_tensor = transform_B(rc_img).unsqueeze(0).to(device)
input_tensor.shape
#向前预测
pred_logits = model(input_tensor)
pred_softmax = F.softmax(pred_logits, dim=1) # 对 logit 分数做 softmax 运算
pred_softmax.shape
pred_conf, pred_id = torch.topk(pred_softmax, 1)
pred_conf = pred_conf.detach().cpu().numpy().squeeze().item()
pred_id = pred_id.detach().cpu().numpy().squeeze().item()
pred_id
pred_conf
pred_label = idx_to_labels[pred_id]
pred_label
print('预测类别的ID {} 名称 {} 置信度 {:.2f}'.format(pred_id, pred_label, pred_conf))
在输入图像上,用遮挡滑块,滑动遮挡不同区域,探索哪些区域被遮挡后会显著影响模型的分类决策。
occlusion = Occlusion(model)
#中等遮挡滑块
# 获得输入图像每个像素的 occ 值
attributions_occ = occlusion.attribute(input_tensor,
strides = (3, 8, 8), # 遮挡滑动移动步长
target=pred_id, # 目标类别
sliding_window_shapes=(3, 15, 15), # 遮挡滑块尺寸
baselines=0) # 被遮挡滑块覆盖的像素值
attributions_occ.shape
# 转为 224 x 224 x 3的数据维度
attributions_occ_norm = np.transpose(attributions_occ.detach().cpu().squeeze().numpy(), (1,2,0))
attributions_occ_norm.shape
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()
# 更改遮挡滑块的尺寸
attributions_occ = occlusion.attribute(input_tensor,
strides = (3, 50, 50), # 遮挡滑动移动步长
target=pred_id, # 目标类别
sliding_window_shapes=(3, 60, 60), # 遮挡滑块尺寸
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()
# 更改遮挡滑块的尺寸
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()
Integrated Gradients可解释性分析
使用Integrated Gradients,在输入图像上,对指定类别进行可解释性分析
import os
import json
import numpy as np
import pandas as pd
from PIL import Image
import torch
import torch.nn.functional as F
import torchvision
from torchvision import models
from torchvision import transforms
from captum.attr import IntegratedGradients
from captum.attr import GradientShap
# from captum.attr import Occlusion
from captum.attr import NoiseTunnel
from captum.attr import visualization as viz
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
%matplotlib inline
# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)
Integrated Gradients 原理:
输入图像像素由空白变为输入图像像素的过程中,模型预测为某一特定类别的概率相对于输入图像像素`的梯度积分。
# 初始化可解释性分析方法
integrated_gradients = IntegratedGradients(model)
# 获得输入图像每个像素的 IG 值
attributions_ig = integrated_gradients.attribute(input_tensor, target=pred_id, n_steps=200)
attributions_ig.shape
# 转为 224 x 224 x 3的数据维度
attributions_ig_norm = np.transpose(attributions_ig.detach().cpu().squeeze().numpy(), (1,2,0))
attributions_ig_norm.shape
attributions_ig_norm[:, :, 0].shape
plt.imshow(attributions_ig_norm[:, :, 0] * 100)
# plt.imshow(attributions_ig_norm[:, :, 1] * 100)
# plt.imshow(attributions_ig_norm[:, :, 2] * 100)
plt.show()
# 设置配色方案
default_cmap = LinearSegmentedColormap.from_list('custom blue',
[(0, '#ffffff'),
(0.25, '#000000'),
(1, '#000000')], N=256)
# 可视化 IG 值
viz.visualize_image_attr(attributions_ig_norm, # 224,224,3
rc_img_norm, # 224,224,3
method='heat_map',
cmap=default_cmap,
show_colorbar=True,
sign='positive',
outlier_perc=1)
plt.show()
在输入图像中加入高斯噪声,构造nt_samples
个噪声样本,分别计算IG值,再使smoothgrad_sq(先平均再平方)
平滑
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()
Feature Ablation特征消融可解释性分析
根据实例分割标注图,分别除去图像中的不同语义分组区域,观察对模型预测结果的影响
import torch
import torch.nn.functional as F
import torchvision
from torchvision import transforms
from torchvision import models
from captum.attr import visualization as viz
from captum.attr import FeatureAblation
from PIL import Image
import numpy as np
import os
import json
import matplotlib.pyplot as plt
%matplotlib inline
img_path = 'test_img/2007_002953.jpg'
mask_path = 'test_img/2007_002953_mask.png'
img = Image.open(img_path)
img
mask_img = Image.open(mask_path)
mask_img
plt.show()
np.unique(mask_img)
from torchvision import transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
input_tensor = transform(img).unsqueeze(0)
pred_logits = model(input_tensor)
pred_softmax = F.softmax(pred_logits, dim=1)
plt.figure(figsize=(8,4))
x = range(1000)
y = pred_softmax.cpu().detach().numpy()[0]
ax = plt.bar(x, y, alpha=0.5, width=0.3, color='yellow', edgecolor='red', lw=3)
# plt.ylim([0, 1.0]) # y轴取值范围
# plt.bar_label(ax, fmt='%.2f', fontsize=15) # 置信度数值
plt.xlabel('Class', fontsize=20)
plt.ylabel('Confidence', fontsize=20)
plt.tick_params(labelsize=16) # 坐标文字大小
plt.title(img_path, fontsize=25)
plt.show()
n = 10
top_n = torch.topk(pred_softmax, n)
pred_ids = top_n[1].cpu().detach().numpy().squeeze() # 解析出类别
confs = top_n[0].cpu().detach().numpy().squeeze() # 解析出置信度
for i in range(n):
class_name = idx_to_labels[pred_ids[i]] # 获取类别名称
confidence = confs[i] * 100 # 获取置信度
text = '{:<15} {:>.4f}'.format(class_name, confidence)
print(text)
pred_conf, pred_id = torch.topk(pred_softmax, 1)
pred_conf = pred_conf.detach().cpu().numpy().squeeze().item()
pred_id = pred_id.detach().cpu().numpy().squeeze().item()
pred_label = idx_to_labels[pred_id]
print('最高置信度预测类别', pred_label)
# 将实例分割标注图像转为 count, channels, height, width 维度
feature_mask = np.array(mask_img.getdata()).reshape(1, 1, mask_img.size[1], mask_img.size[0])
feature_mask.shape
np.unique(feature_mask)
# 将实例分割标注图转为从 1 开始的标注值(而不是 0-255 的256个标注值),便于后续处理。
feature_mask[feature_mask == 5] = 1 # bottle
feature_mask[feature_mask == 20] = 2 # tvmonitor
feature_mask[feature_mask == 255] = 3 # void
np.unique(feature_mask)
ablator = FeatureAblation(model)
# 计算每个 feature group 对模型预测为 pred_id对应类别 概率的影响
attribution_map = ablator.attribute(input_tensor, target=pred_id, feature_mask=torch.tensor(feature_mask))
attribution_map = attribution_map.detach().cpu().numpy().squeeze()
attribution_map = np.transpose(attribution_map, (1,2,0))
viz.visualize_image_attr(attribution_map,
method="heat_map",
sign="all",
show_colorbar=True)
plt.show()
attribution_map = ablator.attribute(input_tensor, target=664, feature_mask=torch.tensor(feature_mask))
attribution_map = attribution_map.detach().cpu().numpy().squeeze()
attribution_map = np.transpose(attribution_map, (1,2,0))
viz.visualize_image_attr(attribution_map,
method="heat_map",
sign="all",
show_colorbar=True)
plt.show()