位置: IT常识 - 正文

Grad-CAM简介(grad_cam)

编辑:rootadmin
Grad-CAM简介

推荐整理分享Grad-CAM简介(grad_cam),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:grad cam pytorch,grad c,gradcas,guided grad cam,guided grad cam,grad cam pytorch,grad cam pytorch,grad_cam,内容如对您有帮助,希望把文章链接给更多的朋友!

论文名称:Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization 论文下载地址:https://arxiv.org/abs/1610.02391 推荐代码(Pytorch):https://github.com/jacobgil/pytorch-grad-cam bilibili视频讲解:https://b23.tv/1kccjmb

文章目录0 前言1 Grad-CAM介绍以及实验1.1 理论介绍1.2 梯度计算示例1.3 Pytorch梯度计算实验2 使用Pytorch绘制热力图0 前言

对于常用的深度学习网络(例如CNN),普遍认为是个黑盒可解释性并不强(至少现在是这么认为的),它为什么会这么预测,它关注的点在哪里,我们并不知道。很多科研人员想方设法地去探究其内在的联系,也有很多相关的论文。今天本文简单聊一聊Grad-CAM,这并不是一篇新的文章,但很有参考意义。通过Grad-CAM我们能够绘制出如下的热力图(对应给定类别,网络到底关注哪些区域)。Grad-CAM(Gradient-weighted Class Activation Mapping)是CAM(Class Activation Mapping)的升级版(论文3.1节中给出了详细的证明),Grad-CAM相比与CAM更具一般性。CAM比较致命的问题是需要修改网络结构并且重新训练,而Grad-CAM完美避开了这些问题。本文不对CAM进行讲解,有兴趣的小伙伴自行了解。

刚刚提到Grad-CAM能够帮我们分析网络对于某个类别的关注区域,那么我们通过网络关注的区域能够反过来分析网络是否学习到正确的特征或者信息。在论文6.3章节中举了个非常有意思的例子,作者训练了一个二分类网络,Nurse和Doctor。如下图所示,第一列是预测时输入的原图,第二列是Biased model(具有偏见的模型)通过Grad-CAM绘制的热力图。第三列是Unbiased model(不具偏见的模型)通过Grad-CAM绘制的热力图。通过对比发现,Biased model对于Nurse(护士)这个类别关注的是人的性别,可能模型认为Nurse都是女性,很明显这是带有偏见的。比如第二行第二列这个图,明明是个女Doctor(医生),但Biased model却认为她是Nurse(因为模型关注到这是个女性)。而Unbiased model关注的是Nurse和Doctor使用的工作器具以及服装,明显这更合理。

1 Grad-CAM介绍以及实验1.1 理论介绍

作者的想法还是比较简单的,参见下图。这里我们简单看下Image Classification任务,首先网络进行正向传播,得到特征层AAA(一般指的是最后一个卷积层的输出)和网络预测值yyy(注意,这里指的是softmax激活之前的数值)。假设我们想看下网络针对Tiger Cat这个类别的感兴趣区域,假设网络针对Tiger Cat类别的预测值为ycy^cyc。接着对ycy^cyc进行反向传播,能够得到反传回特征层AAA的梯度信息Aˊ\acute{A}Aˊ。通过计算得到针对特征层AAA每个通道的重要程度,然后进行加权求和通过ReLUReLUReLU就行了,最终得到的结果即是Grad-CAM。

至于为什么要这么做,我这里讲下我个人的观点(若有不对请指出)。首先得到的特征层AAA是网络对原图进行特征提取得到的结果,越往后的特征层抽象程度越高,语义信息越丰富,而且利用CNN抽取得到的特征图是能够保留空间信息的(Transformer同样)。所以Grad-CAM在CNN中一般AAA都指的是最后一个卷积层的输出(参考下图实验,越往后的特征层效果越好)。当然特征层AAA包含了所有我们感兴趣目标的语义信息,但具体哪些语义信息对应哪个类别我们并不清楚。接着通过对类别ccc的预测值ycy^cyc进行反向传播,得到反传回特征层AAA的梯度信息Aˊ\acute{A}Aˊ,那么Aˊ\acute{A}Aˊ就是ycy^cyc对AAA求得的偏导,换句话说,Aˊ\acute{A}Aˊ代表AAA中每个元素对ycy^cyc的贡献,贡献越大网络就认为越重要。然后对Aˊ\acute{A}Aˊ在w,hw, hw,h上求均值就能得到针对AAA每个通道的重要程度(这里是对于类别ccc而言的)。最后进行简单的加权求和在通过ReLUReLUReLU就能得到文中所说的Grad-CAM。

关于Grad-CAM总结下来就是下面这个公式: LGrad−CAMc=ReLU(∑kαkcAk)(1)L_{\rm Grad-CAM}^c=ReLU(\sum_{k}\alpha _k^cA^k) \quad \quad (1)LGrad−CAMc​=ReLU(k∑​αkc​Ak)(1) 其中:

AAA代表某个特征层,在论文中一般指的是最后一个卷积层输出的特征层kkk代表特征层AAA中第k个通道(channel)ccc代表类别cccAkA^kAk代表特征层A中通道k的数据αkc\alpha_k^cαkc​代表针对AkA^kAk的权重

关于αkc\alpha_k^cαkc​的计算公式如下: αkc=1Z∑i∑j∂yc∂Aijk(2)\alpha_k^c = \frac{1}{Z}\sum_{i}\sum_{j} \frac{\partial y^c}{\partial A_{ij}^k} \quad \quad (2)αkc​=Z1​i∑​j∑​∂Aijk​∂yc​(2) 其中:

ycy^cyc代表网络针对类别ccc预测的分数(score),注意这里没有通过softmax激活AijkA_{ij}^kAijk​代表特征层AAA在通道kkk中,坐标为ijijij位置处的数据ZZZ等于特征层的宽度×\times×高度

通过计算公式(2)可知αkc\alpha_k^cαkc​就是通过预测类别ccc的预测分数ycy^cyc进行反向传播,然后利用反传到特征层AAA上的梯度信息计算特征层AAA每个通道kkk的重要程度。接着通过α\alphaα对特征层AAA每个通道的数据进行加权求和,最后通过ReLUReLUReLU激活函数得到Grad-CAM(论文中说使用ReLU是为了过滤掉Negative pixles,而Negative pixles很可能是归属于其他类别的pixles)。当然一般还要通过一些后处理,插值等方法与原图叠加得到最终的可视化结果。

光说公式没意思,这里举个例子,下图中CNN Extractor代表CNN特征提取器,GAP代表Global Average Pooling,FC代表全连接层:

假设网络正向传播得到的特征层AAA如图所示(这里为了方便只画了两个channel,数据都是随便写的不必深究),针对类别Cat的预测值进行反向传播得到针对特征层AAA的梯度信息Aˊ\acute{A}Aˊ(关于梯度是如何计算的,可以参考本文1.2和1.3的内容),接着利用上述提到的公式(2)计算针对特征层AAA每个通道的权重,就是求Aˊ\acute{A}Aˊ每个通道的均值。 αkc=1Z∑i∑j∂yc∂Aijk(2)\alpha_k^c = \frac{1}{Z}\sum_{i}\sum_{j} \frac{\partial y^c}{\partial A_{ij}^k} \quad \quad (2)αkc​=Z1​i∑​j∑​∂Aijk​∂yc​(2)

Grad-CAM简介(grad_cam)

那么有: αCat=(α1Catα2Cat)=(13−23)\alpha^{\rm Cat} = \begin{pmatrix} \alpha^{\rm Cat}_{1} \\ \\ \alpha^{\rm Cat}_{2} \end{pmatrix} = \begin{pmatrix} \frac{1}{3} \\ \\ -\frac{2}{3} \end{pmatrix}αCat=⎝⎛​α1Cat​α2Cat​​⎠⎞​=⎝⎛​31​−32​​⎠⎞​

然后我们再带入公式(1): LGrad−CAMc=ReLU(∑kαkcAk)(1)L_{\rm Grad-CAM}^c=ReLU(\sum_{k}\alpha _k^cA^k) \quad \quad (1)LGrad−CAMc​=ReLU(k∑​αkc​Ak)(1) 得到对应类别Cat的Grad-CAM: LGrad−CAMCat=ReLU(13⋅(1235111)+(−23)⋅(13111))=ReLU((13−2323−11−1313−13))=(1323113)L_{\rm Grad-CAM}^{\rm Cat} = ReLU(\frac{1}{3} \cdot \begin{pmatrix} 1& 0& 2 \\ 3& 5& 0 \\ 1& 1& 1 \end{pmatrix} + (-\frac{2}{3}) \cdot \begin{pmatrix} 0& 1& 0 \\ 3& 1& 0 \\ 1& 0& 1 \end{pmatrix}) = ReLU(\begin{pmatrix} \frac{1}{3}& -\frac{2}{3}& \frac{2}{3} \\ -1& 1& 0 \\ -\frac{1}{3}& \frac{1}{3}& -\frac{1}{3} \end{pmatrix}) = \begin{pmatrix} \frac{1}{3}& 0& \frac{2}{3} \\ 0& 1& 0 \\ 0& \frac{1}{3}& 0 \end{pmatrix}LGrad−CAMCat​=ReLU(31​⋅⎝⎛​131​051​201​⎠⎞​+(−32​)⋅⎝⎛​031​110​001​⎠⎞​)=ReLU(⎝⎛​31​−1−31​​−32​131​​32​0−31​​⎠⎞​)=⎝⎛​31​00​0131​​32​00​⎠⎞​

1.2 梯度计算示例

上面在介绍计算Grad-CAM时,其实主要是计算正向传播得到的特征层AAA,和反向传播得到的Aˊ\acute{A}Aˊ,得到特征层AAA很简单,大家也经常会提取某个特征层进行分析或者特征融合等等。但获取Aˊ\acute{A}Aˊ会相对麻烦点,计算倒不是难点因为常用的深度学习框架都会自动帮我们计算,只是很少有人会使用到反传的梯度信息。那Aˊ\acute{A}Aˊ究竟要怎么去计算,如果大家有兴趣的话可以看下下面的例子,不感兴趣的话可以直接跳过。

下面构建了一个非常简单的神经网络,主要结构就是一个卷积层 + 一个全连接层,通过这个简单的例子来演示如何计算反向传播过程中某个特征层的梯度。

根据上图,可得output第一个元素的计算公式如下: y1=ffc(fconv2d(X,W1),W21)y_1 = f_{fc}(f_{conv2d}(X, W_1), W_2^1)y1​=ffc​(fconv2d​(X,W1​),W21​) 其中,XXX代表输入(input),fconv2df_{conv2d}fconv2d​表示卷积层的计算,ffcf_{fc}ffc​表示全连接层的计算,W1W_1W1​代表卷积层对应的权重(为了方便,这里都不考虑偏执bias),W21W_2^1W21​代表全连接层中第一个节点对应的权重。

这里先令fconv2d(X,W1)f_{conv2d}(X, W_1)fconv2d​(X,W1​)即卷积层输出的结果为O=(O11,O12,O21,O22)TO=(O_{11}, O_{12}, O_{21}, O_{22})^TO=(O11​,O12​,O21​,O22​)T(为了方便后续计算,这里直接展平写成向量形式)分别对应图中的(4,7,5,6)T(4, 7, 5, 6)^T(4,7,5,6)T,注意这里的OOO并不是标量,是向量,那么y1y_1y1​的计算公式为: y1=ffc(O,W21)=O11⋅W211+O12⋅W212+O21⋅W213+O22⋅W214y_1 = f_{fc}(O, W_2^1) = O_{11} \cdot W_2^{11} + O_{12} \cdot W_2^{12} + O_{21} \cdot W_2^{13} + O_{22} \cdot W_2^{14}y1​=ffc​(O,W21​)=O11​⋅W211​+O12​⋅W212​+O21​⋅W213​+O22​⋅W214​

接着对OOO求偏导: ∂y1∂O=∂y1∂(O11,O12,O21,O22)T=(W211,W212,W213,W214)T=(,1,,1)T\frac{\partial y_1}{\partial O}=\frac{\partial y_1}{\partial (O_{11}, O_{12}, O_{21}, O_{22})^T}=(W_2^{11}, W_2^{12}, W_2^{13}, W_2^{14})^T=(0, 1, 0, 1)^T∂O∂y1​​=∂(O11​,O12​,O21​,O22​)T∂y1​​=(W211​,W212​,W213​,W214​)T=(0,1,0,1)T 将∂y1∂O\frac{\partial y_1}{\partial O}∂O∂y1​​得到的结果进行reshape一下得到(后面有使用Pytorch进行的实验,结果是一致的): (11)\begin{pmatrix} 0 & 1\\ 0 & 1 \end{pmatrix}(00​11​)

看官请留步,后面是选看内容,不想看的可以直接跳到Pytorch实验部分 如果想进一步求y1y_1y1​对输入XXX的偏导,即∂y1∂X\frac{\partial y_1}{\partial X}∂X∂y1​​。X→O→y1X\to O\to y_1X→O→y1​,这里需要稍微注意下,y1y_1y1​是标量,OOO和XXX是向量(为了方便理解,这里将矩阵展平成向量)。根据链式法则以及雅克比矩阵的传递性得(参考的https://github.com/soloice/Matrix_Derivatives内容): ∂Y∂X=∂Y∂O⋅∂O∂X\frac{\partial Y}{\partial X}=\frac{\partial Y}{\partial O} \cdot \frac{\partial O}{\partial X}∂X∂Y​=∂O∂Y​⋅∂X∂O​ 再根据YYY(向量)退化成标量时雅克比矩阵和函数导数的关系有: ∂Y∂X=∂y1∂XT,   ∂Y∂O=∂y1∂OT\frac{\partial Y}{\partial X}=\frac{\partial y_1}{\partial X^T}, \space \space \space \frac{\partial Y}{\partial O}=\frac{\partial y_1}{\partial O^T}∂X∂Y​=∂XT∂y1​​,   ∂O∂Y​=∂OT∂y1​​ 再带入上式得(此公式是把导数视为行向量): ∂y1∂XT=∂y1∂OT⋅∂O∂X\frac{\partial y_1}{\partial X^T}=\frac{\partial y_1}{\partial O^T} \cdot \frac{\partial O}{\partial X}∂XT∂y1​​=∂OT∂y1​​⋅∂X∂O​

前面已经计算出了∂y1∂O\frac{\partial y_1}{\partial O}∂O∂y1​​,那么∂y1∂OT\frac{\partial y_1}{\partial O^T}∂OT∂y1​​转置下即可。接下来就是要求解∂O∂X\frac{\partial O}{\partial X}∂X∂O​: ∂O∂X=∂(O11,O12,O21,O22)T∂(X11,X12,X13,...,X31,X32,X33)T\frac{\partial O}{\partial X} = \frac{\partial (O_{11}, O_{12}, O_{21}, O_{22})^T}{\partial (X_{11}, X_{12}, X_{13}, ..., X_{31}, X_{32}, X_{33})^T}∂X∂O​=∂(X11​,X12​,X13​,...,X31​,X32​,X33​)T∂(O11​,O12​,O21​,O22​)T​ 对应的雅克比矩阵(Jacobian matrix)为一个4×94 \times 94×9大小的矩阵: (∂O11∂X11∂O11∂X12∂O11∂X13...∂O11∂X32∂O11∂X33∂O12∂X11∂O12∂X12∂O12∂X13...∂O12∂X32∂O12∂X33∂O21∂X11∂O21∂X12∂O21∂X13...∂O21∂X32∂O21∂X33∂O22∂X11∂O22∂X12∂O22∂X13...∂O22∂X32∂O22∂X33)\begin{pmatrix} \frac{\partial O_{11}}{\partial X_{11}}& \frac{\partial O_{11}}{\partial X_{12}}& \frac{\partial O_{11}}{\partial X_{13}}& ...& \frac{\partial O_{11}}{\partial X_{32}}& \frac{\partial O_{11}}{\partial X_{33}} \\ \\ \frac{\partial O_{12}}{\partial X_{11}}& \frac{\partial O_{12}}{\partial X_{12}}& \frac{\partial O_{12}}{\partial X_{13}} & ...& \frac{\partial O_{12}}{\partial X_{32}}& \frac{\partial O_{12}}{\partial X_{33}} \\ \\ \frac{\partial O_{21}}{\partial X_{11}}& \frac{\partial O_{21}}{\partial X_{12}}& \frac{\partial O_{21}}{\partial X_{13}}& ...& \frac{\partial O_{21}}{\partial X_{32}}&\frac{\partial O_{21}}{\partial X_{33}} \\ \\ \frac{\partial O_{22}}{\partial X_{11}}& \frac{\partial O_{22}}{\partial X_{12}}& \frac{\partial O_{22}}{\partial X_{13}}& ...& \frac{\partial O_{22}}{\partial X_{32}}& \frac{\partial O_{22}}{\partial X_{33}} \end{pmatrix}⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛​∂X11​∂O11​​∂X11​∂O12​​∂X11​∂O21​​∂X11​∂O22​​​∂X12​∂O11​​∂X12​∂O12​​∂X12​∂O21​​∂X12​∂O22​​​∂X13​∂O11​​∂X13​∂O12​​∂X13​∂O21​​∂X13​∂O22​​​............​∂X32​∂O11​​∂X32​∂O12​​∂X32​∂O21​​∂X32​∂O22​​​∂X33​∂O11​​∂X33​∂O12​​∂X33​∂O21​​∂X33​∂O22​​​⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞​ 比如说对于O11O_{11}O11​,是通过W1W_1W1​与XXX左上角的2×22\times22×2窗口进行加权求和得到的,即: O11=X11⋅W111+X12⋅W112+X21⋅W121+X22⋅W122O_{11} = X_{11} \cdot W_1^{11} + X_{12} \cdot W_1^{12} + X_{21} \cdot W_1^{21} + X_{22} \cdot W_1^{22}O11​=X11​⋅W111​+X12​⋅W112​+X21​⋅W121​+X22​⋅W122​ 通过上面公式可得(O11O_{11}O11​ 只和X11,X12,X21,X22X_{11},X_{12},X_{21},X_{22}X11​,X12​,X21​,X22​有关,故其他的偏导数都为0): ∂O11∂X11,∂O11∂X12,∂O11∂X13,...,∂O11∂X32,∂O11∂X33=W111,W112,,W121,W122,,,,=1,,,1,2,,,,\frac{\partial O_{11}}{\partial X_{11}}, \frac{\partial O_{11}}{\partial X_{12}}, \frac{\partial O_{11}}{\partial X_{13}}, ..., \frac{\partial O_{11}}{\partial X_{32}}, \frac{\partial O_{11}}{\partial X_{33}}=W_1^{11}, W_1^{12}, 0, W_1^{21}, W_1^{22}, 0, 0, 0, 0 = 1, 0, 0, 1, 2, 0, 0, 0, 0∂X11​∂O11​​,∂X12​∂O11​​,∂X13​∂O11​​,...,∂X32​∂O11​​,∂X33​∂O11​​=W111​,W112​,0,W121​,W122​,0,0,0,0=1,0,0,1,2,0,0,0,0 同理可得: (∂O11∂X11∂O11∂X12∂O11∂X13...∂O11∂X32∂O11∂X33∂O12∂X11∂O12∂X12∂O12∂X13...∂O12∂X32∂O12∂X33∂O21∂X11∂O21∂X12∂O21∂X13...∂O21∂X32∂O21∂X33∂O22∂X11∂O22∂X12∂O22∂X13...∂O22∂X32∂O22∂X33)=(112112112112)\begin{pmatrix} \frac{\partial O_{11}}{\partial X_{11}}& \frac{\partial O_{11}}{\partial X_{12}}& \frac{\partial O_{11}}{\partial X_{13}}& ...& \frac{\partial O_{11}}{\partial X_{32}}& \frac{\partial O_{11}}{\partial X_{33}} \\ \\ \frac{\partial O_{12}}{\partial X_{11}}& \frac{\partial O_{12}}{\partial X_{12}}& \frac{\partial O_{12}}{\partial X_{13}} & ...& \frac{\partial O_{12}}{\partial X_{32}}& \frac{\partial O_{12}}{\partial X_{33}} \\ \\ \frac{\partial O_{21}}{\partial X_{11}}& \frac{\partial O_{21}}{\partial X_{12}}& \frac{\partial O_{21}}{\partial X_{13}}& ...& \frac{\partial O_{21}}{\partial X_{32}}&\frac{\partial O_{21}}{\partial X_{33}} \\ \\ \frac{\partial O_{22}}{\partial X_{11}}& \frac{\partial O_{22}}{\partial X_{12}}& \frac{\partial O_{22}}{\partial X_{13}}& ...& \frac{\partial O_{22}}{\partial X_{32}}& \frac{\partial O_{22}}{\partial X_{33}} \end{pmatrix} = \begin{pmatrix} 1& 0& 0& 1& 2& 0& 0& 0& 0\\ 0& 1& 0& 0& 1& 2& 0& 0& 0 \\ 0& 0& 0& 1& 0& 0& 1& 2& 0\\ 0& 0& 0& 0& 1& 0& 0& 1& 2 \end{pmatrix}⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛​∂X11​∂O11​​∂X11​∂O12​​∂X11​∂O21​​∂X11​∂O22​​​∂X12​∂O11​​∂X12​∂O12​​∂X12​∂O21​​∂X12​∂O22​​​∂X13​∂O11​​∂X13​∂O12​​∂X13​∂O21​​∂X13​∂O22​​​............​∂X32​∂O11​​∂X32​∂O12​​∂X32​∂O21​​∂X32​∂O22​​​∂X33​∂O11​​∂X33​∂O12​​∂X33​∂O21​​∂X33​∂O22​​​⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞​=⎝⎜⎜⎛​1000​0100​0000​1010​2101​0200​0010​0021​0002​⎠⎟⎟⎞​

那么: ∂y1∂XT=∂y1∂OT⋅∂O∂X=(11)(112112112112)=(12212)\frac{\partial y_1}{\partial X^T} = \frac{\partial y_1}{\partial O^T} \cdot \frac{\partial O}{\partial X} = \begin{pmatrix} 0& 1& 0& 1 \end{pmatrix} \begin{pmatrix} 1& 0& 0& 1& 2& 0& 0& 0& 0\\ 0& 1& 0& 0& 1& 2& 0& 0& 0 \\ 0& 0& 0& 1& 0& 0& 1& 2& 0\\ 0& 0& 0& 0& 1& 0& 0& 1& 2 \end{pmatrix} = \begin{pmatrix} 0& 1& 0& 0& 2& 2& 0& 1& 2 \end{pmatrix}∂XT∂y1​​=∂OT∂y1​​⋅∂X∂O​=(0​1​0​1​)⎝⎜⎜⎛​1000​0100​0000​1010​2101​0200​0010​0021​0002​⎠⎟⎟⎞​=(0​1​0​0​2​2​0​1​2​) 对得到的结果进行下reshape得到(后面有使用Pytorch进行的实验,结果是一致的): (12212)\begin{pmatrix} 0& 1& 0\\ 0& 2& 2\\ 0& 1& 2 \end{pmatrix}⎝⎛​000​121​022​⎠⎞​

1.3 Pytorch梯度计算实验

这个实验过程中的网络、权重以及输入的数据是严格按照刚刚讲的示例搭建的。 实验代码如下:

import torchdef save_gradient(module, grad_input, grad_output): # print(f"{module.__class__.__name__} input grad:\n{grad_input}\n") print(f"{module.__class__.__name__} output grad:\n{grad_output}\n")def main(): # input tensor x = torch.reshape(torch.as_tensor([[1., 2., 3.], [1., 1., 2.], [2., 1., 2.]], dtype=torch.float32), (1, 1, 3, 3)) x = torch.autograd.Variable(x, requires_grad=True) print(f"input:\n{x}\n") # define model # [kernel_number, kernel_channel, kernel_height, kernel_width] conv_weight = torch.reshape(torch.as_tensor([1, 0, 1, 2], dtype=torch.float32), (1, 1, 2, 2)) conv = torch.nn.Conv2d(1, 1, 2, bias=False) conv.load_state_dict({"weight": conv_weight}) # 注册hook,捕获反向转播过程中流经该模块的梯度信息 handle1 = conv.register_full_backward_hook(save_gradient) # [output_units, input_units] fc_weight = torch.reshape(torch.as_tensor([[0, 1, 0, 1], [1, 0, 1, 1]], dtype=torch.float32), (2, 4)) fc = torch.nn.Linear(4, 2, bias=False) fc.load_state_dict({"weight": fc_weight}) # handle2 = fc.register_full_backward_hook(save_gradient) # forward o1 = conv(x) print(f"feature map o1:\n{o1}\n") flatten = torch.flatten(o1, start_dim=1) o2 = fc(flatten) print(f"feature map o2:\n{o2}\n") # backward y_1 # [batch_size, units] o2[0][0].backward() print(f"input grad: \n{x.grad}\n") # print(f"fc weights grad: \n{fc.weight.grad}\n") # print(f"conv2d weights grad: \n{conv.weight.grad}\n") # release handles handle1.remove() # handle2.remove()if __name__ == '__main__': main()

终端输出结果:

input:tensor([[[[1., 2., 3.], [1., 1., 2.], [2., 1., 2.]]]], requires_grad=True)feature map o1:tensor([[[[4., 7.], [5., 6.]]]], grad_fn=<BackwardHookFunctionBackward>)feature map o2:tensor([[13., 15.]], grad_fn=<MmBackward0>)Conv2d output grad:(tensor([[[[0., 1.], [0., 1.]]]]),)input grad: tensor([[[[0., 1., 0.], [0., 2., 2.], [0., 1., 2.]]]])

利用pytorch计算的有关梯度信息和上面我们自己手动推的结果进行对比,主要是Conv2d output grad和input grad,可以发现结果是一样的。

2 使用Pytorch绘制热力图

首先克隆下我的项目: https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_classification/grad_cam

这个项目是我从https://github.com/jacobgil/pytorch-grad-cam仓库中提取得到的(保留了Grad-CAM相关的代码)。

这里主要简单看下main_cnn.py文件。在该脚本中,直接从TorchVision官方库中调用官方在Imagenet数据上预训练好的模型。这里默认使用的是MobileNet V3 Large模型,使用其他卷积神经网络也是一样的,可参考注释部分的创建模型代码。如果要使用Vision Transformer或者Swin Transformer模型需要使用main_vit.py或者main_swin.py脚本,这里不去讲。创建好模型并且载入预训练权重后,需要指定捕获哪一个特征层AAA,即代码中target_layers,一般默认都是捕获最后一个卷积层的输出。接着还要指定我们感兴趣的类别id,即代码中的target_category,注意这里是直接使用官方在Imagenet数据上预训练好的模型,所以这里的类别id指的是Imagenet数据中1000个类别的id(代码中id默认从0开始),比如说对于tabby, tabby cat这个类别,它对应的target_category = 281,具体可参考我项目中imagenet1k_classes.txt文件,对应类别的行号减1即对应类别id(比如tabby, tabby cat这个类别是在第282行,故id为281)。

import osimport numpy as npimport torchfrom PIL import Imageimport matplotlib.pyplot as pltfrom torchvision import modelsfrom torchvision import transformsfrom utils import GradCAM, show_cam_on_imagedef main(): model = models.mobilenet_v3_large(pretrained=True) target_layers = [model.features[-1]] # model = models.vgg16(pretrained=True) # target_layers = [model.features] # model = models.resnet34(pretrained=True) # target_layers = [model.layer4] # model = models.regnet_y_800mf(pretrained=True) # target_layers = [model.trunk_output] # model = models.efficientnet_b0(pretrained=True) # target_layers = [model.features] data_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) # load image img_path = "both.png" assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path) img = Image.open(img_path).convert('RGB') img = np.array(img, dtype=np.uint8) # [N, C, H, W] img_tensor = data_transform(img) # expand batch dimension input_tensor = torch.unsqueeze(img_tensor, dim=0) cam = GradCAM(model=model, target_layers=target_layers, use_cuda=False) target_category = 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, :] visualization = show_cam_on_image(img.astype(dtype=np.float32) / 255., grayscale_cam, use_rgb=True) plt.imshow(visualization) plt.show()if __name__ == '__main__': main()

运行结果如下:

当然,这里我只是以图像分类任务为例,对于目标检测、语义分割等任务也都适用,详情可参考原项目。

本文链接地址:https://www.jiuchutong.com/zhishi/295294.html 转载请保留说明!

上一篇:uni-app--》uni-app的生命周期讲解

下一篇:pdf在线预览 pdf.js的使用(pdf在线预览备注)

  • win11任务管理器快捷键(win11任务管理器进程不显示gpu)

    win11任务管理器快捷键(win11任务管理器进程不显示gpu)

  • e路商城提现提不出来(e路商城是正规平台吗)

    e路商城提现提不出来(e路商城是正规平台吗)

  • 企业微信打卡定位失败什么原因(企业微信打卡定位修改软件app)

    企业微信打卡定位失败什么原因(企业微信打卡定位修改软件app)

  • 积目弹幕怎么没了(积目只点开对话框)

    积目弹幕怎么没了(积目只点开对话框)

  • ipad属于电脑还是手机(ipad是电脑吗?)

    ipad属于电脑还是手机(ipad是电脑吗?)

  • 抖音提现到支付宝的钱在哪里(抖音提现到支付宝怎么提不了)

    抖音提现到支付宝的钱在哪里(抖音提现到支付宝怎么提不了)

  • 华为手机应该怎么隐藏应用(华为手机应该怎样录屏)

    华为手机应该怎么隐藏应用(华为手机应该怎样录屏)

  • opporeno3pro微信视频美颜怎么设置(opporeno3pro微信视频聊天卡)

    opporeno3pro微信视频美颜怎么设置(opporeno3pro微信视频聊天卡)

  • 苹果手机如何删除永久版app(苹果手机如何删除所有照片)

    苹果手机如何删除永久版app(苹果手机如何删除所有照片)

  • 抖音可以屏蔽某个人吗(抖音可以屏蔽某个人不看我作品吗)

    抖音可以屏蔽某个人吗(抖音可以屏蔽某个人不看我作品吗)

  • 4g打开volte是什么意思(4g volte)

    4g打开volte是什么意思(4g volte)

  • 指纹锁识别不了指纹怎么办(指纹锁识别不了怎么办)

    指纹锁识别不了指纹怎么办(指纹锁识别不了怎么办)

  • 什么是网站标题(网站标题的构成要素)

    什么是网站标题(网站标题的构成要素)

  • 手机内存16g有多大(手机内存16g有多少)

    手机内存16g有多大(手机内存16g有多少)

  • 乘号在键盘上怎么打(乘号在键盘上怎么打快捷键)

    乘号在键盘上怎么打(乘号在键盘上怎么打快捷键)

  • 苹果a1901是什么型号(苹果a1908是什么型号)

    苹果a1901是什么型号(苹果a1908是什么型号)

  • 手机优酷账号怎么看(手机优酷账号怎么登陆到投影上)

    手机优酷账号怎么看(手机优酷账号怎么登陆到投影上)

  • 手机如何缩小照片内存(手机如何缩小照片像素)

    手机如何缩小照片内存(手机如何缩小照片像素)

  • 美易怎么取消自动订阅(美易怎样取消自动续费)

    美易怎么取消自动订阅(美易怎样取消自动续费)

  • 荣耀10电池容量有多大(荣耀10电池容量怎么看损耗)

    荣耀10电池容量有多大(荣耀10电池容量怎么看损耗)

  • 12g内存和8g内存区别(12g内存和8g内存区别电脑)

    12g内存和8g内存区别(12g内存和8g内存区别电脑)

  • qq轻聊版如何退出(qq轻聊版怎么卸载)

    qq轻聊版如何退出(qq轻聊版怎么卸载)

  • 栈和队列都是什么结构(栈和队列都是什么结构,对于栈只能在)

    栈和队列都是什么结构(栈和队列都是什么结构,对于栈只能在)

  • 手机说话别人听不到(手机说话别人听不到,开免提就可以)

    手机说话别人听不到(手机说话别人听不到,开免提就可以)

  • 最小的CMOS传感器(最小的成像传感器)

    最小的CMOS传感器(最小的成像传感器)

  • 个人所得税可以退吗
  • 个人以房产投资企业需要过户吗
  • 专用发票和普通票有区别吗
  • 银行电子承兑到期多久时间之内可以兑现
  • 盘盈固定资产属于企业的会计差错
  • 一个公司帮另一个公司代付款
  • 集团公司向子公司收取管理费的法律规定
  • 外贸出口企业城市排名
  • 小规模纳税人开具增值税专用发票
  • 应对税务检查工作总结
  • 快递费可以抵扣进项税额吗
  • 金蝶现金流量表附表项目如何指定
  • 员工受伤报销
  • 职工福利费要申报吗
  • 一季度所得税费用怎么算
  • 出口退税挂靠业务如何做帐?
  • 注销税务登记后多久注销工商登记
  • 不需要缴纳增值税和免税的区别
  • 鸿蒙工具箱巅峰模式有什么用
  • window10过期
  • 清空收藏夹里面的歌曲
  • 收到厂家返利怎么做账务处理
  • php utf8转gb2312
  • 苹果15手机价格和图片颜色
  • 台式机显示器推荐
  • php string
  • nmstt.exe - nmstt是什么进程 有什么用
  • erl.exe是什么进程
  • 会计科目怎么调账
  • 威尼斯海滩滑板场
  • mac m1 rosetta编译
  • 股东个人消费如何合理报销
  • easyui分页传递表单参数
  • 购买商品的会计分录贷方能写应付账款
  • pyecharts怎么用
  • 鲜花售卖系统
  • 普通发票退税流程图
  • 发票去税务局认证了就可以直接抵扣了吗
  • 租赁房屋开具发票商品名称
  • 进项税和销项税月末怎么结转
  • Yii 连接、修改 MySQL 数据库及phpunit 测试连接
  • 摄影行业开票
  • sql server2014使用
  • 销售应税服务或劳务的纳税义务发生时间的一般规定
  • 建筑业服务包含哪些?
  • 计提工资是计提哪个月的
  • 物业公司维修服务范围
  • 多计提费用 怎么冲
  • 车辆购置税计入什么科目
  • 采用成本法核算的长期股权投资
  • 开业赠送礼品会计属于什么费用
  • 记账凭证是不是转账凭证
  • 专用发票样图
  • 贷内部往来
  • 领购发票的方式有哪些
  • 费用多计提了怎么办
  • sql server的相关技术知识
  • MySQL5.7中 performance和sys schema中的监控参数解释(推荐)
  • SQL里类似SPLIT的分割字符串函数
  • 重装系统注册表会重置吗
  • linux给root权限
  • freebsd使用
  • .exe是什么软件
  • linux wget curl
  • rhel7配置ip地址
  • msxct.exe - msxct是什么进程 有什么用
  • ext.grid.editorgridpanel
  • 比较好的jquery教程
  • windows常用命令操作
  • eclipse4.9.0安装windowbuilder
  • windows8.1 with bing
  • 并结合案例进行深入剖析
  • js中的隐式类型转换
  • shell if -lt
  • jquery设置元素css
  • 文件读写过程中,程序将直接与磁盘文件进行数据交换
  • js写一个类
  • python 判断字符串编码
  • js怎么判断日期大小
  • 国家税务总局l
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设