文章:
1,Grad-CAM: Why did you say that?
2,Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization
目标:通过全图的热力图显示出图中重点部位,表示该部位是将图形判断为某类的依据。
代码:pytorch:
1,https://github.com/utkuozbulak/pytorch-cnn-visualizations;
2, https://github.com/jacobgil/pytorch-grad-cam
另有keras和tensorflow以及文章提供的源码。
阅读记录参考:
1,https://www.jianshu.com/p/1d7b5c4ecb93
2,http://spytensor.com/index.php/archives/20/
另对于理解上的记录:
由于CAM通过GAP(Global Average Pooling)需要最后一层卷积层)强制生成了和目标类别数量一致的特征图,这样就能通过GAP得到与后一层的fc层之间的权重,也就是gap前一层的每个特征图都有权重,这样就可以利用类似下图
这种方式得到某个类别的解释。但需要更改模型结构,因此,如何在不更改结构的情况下求得权重就是Grad-CAM做的事情。
Grad-CAM用梯度的全局平均来计算权重。针对某类C,需要获得判断为c的解释,则首先要获得最后一层输入softmax层之前的类别c的分数值,假设最后一层卷积层(语义信息最丰富)特征图为A,大小为20维,则通过类别的数值反向传播到对应的卷积层,得到对应的梯度,梯度尺寸与特征图A一致,再将任意单个维度的梯度求平均(也就是结果就等于通道维数的向量),即可得到对应通道的权重。最后类似CAM那样通过加权和的方式就可以得到热力图的原始特征图,再经过处理得到热力图。
注意点:
1,由于用的pytorch,故而模型创建或者Grad-CAM模型书写的时候,将模型分成 model.features和 model.classifier为最佳,方便使用,不这样操作也可以,就是算法模型部分针对不同的算法改的多。
2,在特征提取模块,利用model.features提取对应层特征的时候,需要像下面这样保存梯度:
class FeatureExtractor(): """ Class for extracting activations and registering gradients from targetted intermediate layers """ def __init__(self, model, target_layers): self.model = model self.target_layers = target_layers self.gradients = [] def save_gradient(self, grad): self.gradients.append(grad) def __call__(self, x): outputs = [] self.gradients = [] for name, module in self.model._modules.items(): x = module(x) if name in self.target_layers: # 保留固定层特征 x.register_hook(self.save_gradient) # 保存梯度 outputs += [x] return outputs, x
3,softmax前一层的类别分数是在model.features链接 model.classifier后的结果,也就是模型从头到脚的输出,不是单纯特征图的输出。
4,对于不同模型,需要更改模型内部的模块,有时间可以改成自适应的。