2022-11-21 10:34:05 +08:00
|
|
|
|
# 类别激活图(CAM)可视化
|
2021-10-20 10:28:21 +08:00
|
|
|
|
|
2022-11-21 10:34:05 +08:00
|
|
|
|
## 类别激活图可视化工具介绍
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
2023-03-09 11:36:54 +08:00
|
|
|
|
MMPretrain 提供 `tools/visualization/vis_cam.py` 工具来可视化类别激活图。请使用 `pip install "grad-cam>=1.3.6"` 安装依赖的 [pytorch-grad-cam](https://github.com/jacobgil/pytorch-grad-cam)。
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
目前支持的方法有:
|
|
|
|
|
|
2022-06-02 15:22:01 +08:00
|
|
|
|
| Method | What it does |
|
|
|
|
|
| :----------: | :-----------------------------------------------------------------------------------------------: |
|
|
|
|
|
| GradCAM | 使用平均梯度对 2D 激活进行加权 |
|
|
|
|
|
| GradCAM++ | 类似 GradCAM,但使用了二阶梯度 |
|
|
|
|
|
| XGradCAM | 类似 GradCAM,但通过归一化的激活对梯度进行了加权 |
|
|
|
|
|
| EigenCAM | 使用 2D 激活的第一主成分(无法区分类别,但效果似乎不错) |
|
|
|
|
|
| EigenGradCAM | 类似 EigenCAM,但支持类别区分,使用了激活 * 梯度的第一主成分,看起来和 GradCAM 差不多,但是更干净 |
|
|
|
|
|
| LayerCAM | 使用正梯度对激活进行空间加权,对于浅层有更好的效果 |
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
2023-03-27 14:32:26 +08:00
|
|
|
|
也可以使用新版本 `pytorch-grad-cam` 支持的更多 CAM 方法,但我们尚未验证可用性。
|
|
|
|
|
|
2021-12-23 18:53:40 +08:00
|
|
|
|
**命令行**:
|
|
|
|
|
|
|
|
|
|
```bash
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
${IMG} \
|
|
|
|
|
${CONFIG_FILE} \
|
|
|
|
|
${CHECKPOINT} \
|
|
|
|
|
[--target-layers ${TARGET-LAYERS}] \
|
|
|
|
|
[--preview-model] \
|
|
|
|
|
[--method ${METHOD}] \
|
|
|
|
|
[--target-category ${TARGET-CATEGORY}] \
|
|
|
|
|
[--save-path ${SAVE_PATH}] \
|
|
|
|
|
[--vit-like] \
|
|
|
|
|
[--num-extra-tokens ${NUM-EXTRA-TOKENS}]
|
|
|
|
|
[--aug_smooth] \
|
|
|
|
|
[--eigen_smooth] \
|
|
|
|
|
[--device ${DEVICE}] \
|
|
|
|
|
[--cfg-options ${CFG-OPTIONS}]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**所有参数的说明**:
|
|
|
|
|
|
|
|
|
|
- `img`:目标图片路径。
|
|
|
|
|
- `config`:模型配置文件的路径。
|
|
|
|
|
- `checkpoint`:权重路径。
|
2023-03-27 14:32:26 +08:00
|
|
|
|
- `--target-layers`:所查看的网络层名称,可输入一个或者多个网络层,如果不设置,将使用最后一个`block`中的`norm`层。
|
2021-12-23 18:53:40 +08:00
|
|
|
|
- `--preview-model`:是否查看模型所有网络层。
|
|
|
|
|
- `--method`:类别激活图图可视化的方法,目前支持 `GradCAM`, `GradCAM++`, `XGradCAM`, `EigenCAM`, `EigenGradCAM`, `LayerCAM`,不区分大小写。如果不设置,默认为 `GradCAM`。
|
|
|
|
|
- `--target-category`:查看的目标类别,如果不设置,使用模型检测出来的类别做为目标类别。
|
|
|
|
|
- `--save-path`:保存的可视化图片的路径,默认不保存。
|
|
|
|
|
- `--eigen-smooth`:是否使用主成分降低噪音,默认不开启。
|
|
|
|
|
- `--vit-like`: 是否为 `ViT` 类似的 Transformer-based 网络
|
|
|
|
|
- `--num-extra-tokens`: `ViT` 类网络的额外的 tokens 通道数,默认使用主干网络的 `num_extra_tokens`。
|
|
|
|
|
- `--aug-smooth`:是否使用测试时增强
|
|
|
|
|
- `--device`:使用的计算设备,如果不设置,默认为'cpu'。
|
2022-11-21 10:34:05 +08:00
|
|
|
|
- `--cfg-options`:对配置文件的修改,参考[学习配置文件](../user_guides/config.md)。
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
```{note}
|
|
|
|
|
在指定 `--target-layers` 时,如果不知道模型有哪些网络层,可使用命令行添加 `--preview-model` 查看所有网络层名称;
|
|
|
|
|
```
|
|
|
|
|
|
2022-11-21 10:34:05 +08:00
|
|
|
|
## 如何可视化 CNN 网络的类别激活图(如 ResNet-50)
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
2023-03-27 14:32:26 +08:00
|
|
|
|
`--target-layers` 在 `Resnet-50` 中的一些示例如下:
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
- `'backbone.layer4'`,表示第四个 `ResLayer` 层的输出。
|
|
|
|
|
- `'backbone.layer4.2'` 表示第四个 `ResLayer` 层中第三个 `BottleNeck` 块的输出。
|
|
|
|
|
- `'backbone.layer4.2.conv1'` 表示上述 `BottleNeck` 块中 `conv1` 层的输出。
|
|
|
|
|
|
|
|
|
|
1. 使用不同方法可视化 `ResNet50`,默认 `target-category` 为模型检测的结果,使用默认推导的 `target-layers`。
|
|
|
|
|
|
|
|
|
|
```shell
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
demo/bird.JPEG \
|
|
|
|
|
configs/resnet/resnet50_8xb32_in1k.py \
|
|
|
|
|
https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth \
|
|
|
|
|
--method GradCAM
|
|
|
|
|
# GradCAM++, XGradCAM, EigenCAM, EigenGradCAM, LayerCAM
|
|
|
|
|
```
|
|
|
|
|
|
2022-06-02 15:22:01 +08:00
|
|
|
|
| Image | GradCAM | GradCAM++ | EigenGradCAM | LayerCAM |
|
|
|
|
|
| ------------------------------------ | --------------------------------------- | ----------------------------------------- | -------------------------------------------- | ---------------------------------------- |
|
|
|
|
|
| <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429496-628d3fb3-1f6e-41ff-aa5c-1b08c60c32a9.JPEG' height="auto" width="160" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065002-f1c86516-38b2-47ba-90c1-e00b49556c70.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065119-82581fa1-3414-4d6c-a849-804e1503c74b.jpg' height="auto" width="150"></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065096-75a6a2c1-6c57-4789-ad64-ebe5e38765f4.jpg' height="auto" width="150"></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/147065129-814d20fb-98be-4106-8c5e-420adcc85295.jpg' height="auto" width="150"></div> |
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
2023-03-27 14:32:26 +08:00
|
|
|
|
2. 同一张图不同类别的激活图效果图,在 `ImageNet` 数据集中,类别 238 为 'Greater Swiss Mountain dog',类别 281 为 'tabby, tabby cat'。
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
```shell
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
demo/cat-dog.png configs/resnet/resnet50_8xb32_in1k.py \
|
|
|
|
|
https://download.openmmlab.com/mmclassification/v0/resnet/resnet50_batch256_imagenet_20200708-cfb998bf.pth \
|
|
|
|
|
--target-layers 'backbone.layer4.2' \
|
|
|
|
|
--method GradCAM \
|
|
|
|
|
--target-category 238
|
|
|
|
|
# --target-category 281
|
|
|
|
|
```
|
|
|
|
|
|
2022-06-02 15:22:01 +08:00
|
|
|
|
| Category | Image | GradCAM | XGradCAM | LayerCAM |
|
|
|
|
|
| -------- | ---------------------------------------------- | ------------------------------------------------ | ------------------------------------------------- | ------------------------------------------------- |
|
|
|
|
|
| Dog | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429526-f27f4cce-89b9-4117-bfe6-55c2ca7eaba6.png' height="auto" width="165" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144433562-968a57bc-17d9-413e-810e-f91e334d648a.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144433853-319f3a8f-95f2-446d-b84f-3028daca5378.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144433937-daef5a69-fd70-428f-98a3-5e7747f4bb88.jpg' height="auto" width="150" ></div> |
|
|
|
|
|
| Cat | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429526-f27f4cce-89b9-4117-bfe6-55c2ca7eaba6.png' height="auto" width="165" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144434518-867ae32a-1cb5-4dbd-b1b9-5e375e94ea48.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144434603-0a2fd9ec-c02e-4e6c-a17b-64c234808c56.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144434623-b4432cc2-c663-4b97-aed3-583d9d3743e6.jpg' height="auto" width="150" ></div> |
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
3. 使用 `--eigen-smooth` 以及 `--aug-smooth` 获取更好的可视化效果。
|
|
|
|
|
|
|
|
|
|
```shell
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
demo/dog.jpg \
|
2022-09-20 18:09:05 +08:00
|
|
|
|
configs/mobilenet_v3/mobilenet-v3-large_8xb128_in1k.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
https://download.openmmlab.com/mmclassification/v0/mobilenet_v3/convert/mobilenet_v3_large-3ea3c186.pth \
|
|
|
|
|
--target-layers 'backbone.layer16' \
|
|
|
|
|
--method LayerCAM \
|
|
|
|
|
--eigen-smooth --aug-smooth
|
|
|
|
|
```
|
|
|
|
|
|
2022-06-02 15:22:01 +08:00
|
|
|
|
| Image | LayerCAM | eigen-smooth | aug-smooth | eigen&aug |
|
|
|
|
|
| ------------------------------------ | --------------------------------------- | ------------------------------------------- | ----------------------------------------- | ----------------------------------------- |
|
|
|
|
|
| <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557492-98ac5ce0-61f9-4da9-8ea7-396d0b6a20fa.jpg' height="auto" width="160"></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557541-a4cf7d86-7267-46f9-937c-6f657ea661b4.jpg' height="auto" width="145" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557547-2731b53e-e997-4dd2-a092-64739cc91959.jpg' height="auto" width="145" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557545-8189524a-eb92-4cce-bf6a-760cab4a8065.jpg' height="auto" width="145" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144557548-c1e3f3ec-3c96-43d4-874a-3b33cd3351c5.jpg' height="auto" width="145" ></div> |
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
2022-11-21 10:34:05 +08:00
|
|
|
|
## 如何可视化 Transformer 类型网络的类别激活图
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
2023-03-27 14:32:26 +08:00
|
|
|
|
`--target-layers` 在 Transformer-based 网络中的一些示例如下:
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
- Swin-Transformer 中:`'backbone.norm3'`
|
2023-03-27 14:32:26 +08:00
|
|
|
|
- ViT 中:`'backbone.layers.11.ln1'`
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
对于 Transformer-based 的网络,比如 ViT、T2T-ViT 和 Swin-Transformer,特征是被展平的。为了绘制 CAM 图,我们需要指定 `--vit-like` 选项,从而让被展平的特征恢复方形的特征图。
|
|
|
|
|
|
|
|
|
|
除了特征被展平之外,一些类 ViT 的网络还会添加额外的 tokens。比如 ViT 和 T2T-ViT 中添加了分类 token,DeiT 中还添加了蒸馏 token。在这些网络中,分类计算在最后一个注意力模块之后就已经完成了,分类得分也只和这些额外的 tokens 有关,与特征图无关,也就是说,分类得分对这些特征图的导数为 0。因此,我们不能使用最后一个注意力模块的输出作为 CAM 绘制的目标层。
|
|
|
|
|
|
2023-03-09 11:36:54 +08:00
|
|
|
|
另外,为了去除这些额外的 toekns 以获得特征图,我们需要知道这些额外 tokens 的数量。MMPretrain 中几乎所有 Transformer-based 的网络都拥有 `num_extra_tokens` 属性。而如果你希望将此工具应用于新的,或者第三方的网络,而且该网络没有指定 `num_extra_tokens` 属性,那么可以使用 `--num-extra-tokens` 参数手动指定其数量。
|
2021-12-23 18:53:40 +08:00
|
|
|
|
|
|
|
|
|
1. 对 `Swin Transformer` 使用默认 `target-layers` 进行 CAM 可视化:
|
|
|
|
|
|
|
|
|
|
```shell
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
demo/bird.JPEG \
|
|
|
|
|
configs/swin_transformer/swin-tiny_16xb64_in1k.py \
|
|
|
|
|
https://download.openmmlab.com/mmclassification/v0/swin-transformer/swin_tiny_224_b16x64_300e_imagenet_20210616_090925-66df6be6.pth \
|
|
|
|
|
--vit-like
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
2. 对 `Vision Transformer(ViT)` 进行 CAM 可视化:
|
|
|
|
|
|
|
|
|
|
```shell
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
demo/bird.JPEG \
|
2023-03-27 14:32:26 +08:00
|
|
|
|
configs/vision_transformer/vit-base-p16_64xb64_in1k-384px.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
https://download.openmmlab.com/mmclassification/v0/vit/finetune/vit-base-p16_in21k-pre-3rdparty_ft-64xb64_in1k-384_20210928-98e8652b.pth \
|
|
|
|
|
--vit-like \
|
2023-03-27 14:32:26 +08:00
|
|
|
|
--target-layers 'backbone.layers.11.ln1'
|
2021-12-23 18:53:40 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
3. 对 `T2T-ViT` 进行 CAM 可视化:
|
|
|
|
|
|
|
|
|
|
```shell
|
2023-03-09 11:36:54 +08:00
|
|
|
|
python tools/visualization/vis_cam.py \
|
2021-12-23 18:53:40 +08:00
|
|
|
|
demo/bird.JPEG \
|
|
|
|
|
configs/t2t_vit/t2t-vit-t-14_8xb64_in1k.py \
|
|
|
|
|
https://download.openmmlab.com/mmclassification/v0/t2t-vit/t2t-vit-t-14_3rdparty_8xb64_in1k_20210928-b7c09b62.pth \
|
|
|
|
|
--vit-like \
|
2023-03-27 14:32:26 +08:00
|
|
|
|
--target-layers 'backbone.encoder.12.ln1'
|
2021-12-23 18:53:40 +08:00
|
|
|
|
```
|
|
|
|
|
|
2022-06-02 15:22:01 +08:00
|
|
|
|
| Image | ResNet50 | ViT | Swin | T2T-ViT |
|
|
|
|
|
| --------------------------------------- | ------------------------------------------ | -------------------------------------- | --------------------------------------- | ------------------------------------------ |
|
|
|
|
|
| <div align=center><img src='https://user-images.githubusercontent.com/18586273/144429496-628d3fb3-1f6e-41ff-aa5c-1b08c60c32a9.JPEG' height="auto" width="165" ></div> | <div align=center><img src=https://user-images.githubusercontent.com/18586273/144431491-a2e19fe3-5c12-4404-b2af-a9552f5a95d9.jpg height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144436218-245a11de-6234-4852-9c08-ff5069f6a739.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144436168-01b0e565-442c-4e1e-910c-17c62cff7cd3.jpg' height="auto" width="150" ></div> | <div align=center><img src='https://user-images.githubusercontent.com/18586273/144436198-51dbfbda-c48d-48cc-ae06-1a923d19b6f6.jpg' height="auto" width="150" ></div> |
|