位置: 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在线预览备注)

  • 三星s21无线投屏功能在哪(三星s21 无线投屏)

    三星s21无线投屏功能在哪(三星s21 无线投屏)

  • 华为nova5的广角怎么开(华为nova5广角模式怎么开)

    华为nova5的广角怎么开(华为nova5广角模式怎么开)

  • 苹果耳机一直闪白灯(苹果耳机一直闪红灯)

    苹果耳机一直闪白灯(苹果耳机一直闪红灯)

  • 惠普打印机硒鼓怎么拆(惠普打印机硒鼓怎么取出来)

    惠普打印机硒鼓怎么拆(惠普打印机硒鼓怎么取出来)

  • 打印机老是用户干预(打印机老是用户名错误)

    打印机老是用户干预(打印机老是用户名错误)

  • 抖音企业号怎么取消认证(抖音企业号怎么挂小黄车商品)

    抖音企业号怎么取消认证(抖音企业号怎么挂小黄车商品)

  • 芒果tv国际版怎么下载(芒果tv国际版怎么投屏)

    芒果tv国际版怎么下载(芒果tv国际版怎么投屏)

  • 微信更新在哪里找(企业微信更新在哪里)

    微信更新在哪里找(企业微信更新在哪里)

  • 充电慢是手机问题还是充电器问题(充电慢是手机问题还是数据线)

    充电慢是手机问题还是充电器问题(充电慢是手机问题还是数据线)

  • 计算机的性能主要取决于什么(计算机的性能主要包括软件性能和硬件性能)

    计算机的性能主要取决于什么(计算机的性能主要包括软件性能和硬件性能)

  • 手机响多久会自动挂断(一般手机响多久没人接会自己挂断)

    手机响多久会自动挂断(一般手机响多久没人接会自己挂断)

  • 互联网ucg是什么意思(互联网ug是什么意思)

    互联网ucg是什么意思(互联网ug是什么意思)

  • 苹果手机怎么一键加速(苹果手机怎么一键关闭所有程序)

    苹果手机怎么一键加速(苹果手机怎么一键关闭所有程序)

  • vue字体怎么变颜色(vue怎么改字体颜色)

    vue字体怎么变颜色(vue怎么改字体颜色)

  • 三星s6来电转移怎么设置(三星s6来电转移怎么取消)

    三星s6来电转移怎么设置(三星s6来电转移怎么取消)

  • boss直聘怎么申请面试(boss直聘怎么申请退款)

    boss直聘怎么申请面试(boss直聘怎么申请退款)

  • 屏幕坏了怎么关机(屏幕坏了怎么关闭手机闹钟)

    屏幕坏了怎么关机(屏幕坏了怎么关闭手机闹钟)

  • 喊siri没反应怎么回事(喊siri不出来)

    喊siri没反应怎么回事(喊siri不出来)

  • 微信面对面建群怎么弄(微信面对面建群无法进入聊天)

    微信面对面建群怎么弄(微信面对面建群无法进入聊天)

  • 阀控式铅酸蓄电池用途(阀控式铅酸蓄电池能加修复液吗)

    阀控式铅酸蓄电池用途(阀控式铅酸蓄电池能加修复液吗)

  • 酷我如何上传歌曲(酷我音乐怎么上传自己的音乐作品?)

    酷我如何上传歌曲(酷我音乐怎么上传自己的音乐作品?)

  • 怎么看黑名单电话(怎么看黑名单电话有没有打进来)

    怎么看黑名单电话(怎么看黑名单电话有没有打进来)

  • neoCapture.exe - neoCapture是什么进程 有什么用

    neoCapture.exe - neoCapture是什么进程 有什么用

  • 织梦dede模板增加文章自定义属性的教程(织梦添加文章如何修改高级参数)

    织梦dede模板增加文章自定义属性的教程(织梦添加文章如何修改高级参数)

  • 取得投资款属于什么过程
  • 土地使用权转让是什么意思
  • 股份有限公司个人所得税缴纳比例
  • 餐饮业可以开增值税专用发票吗?
  • 人力资源的差额征收怎么报企业所得税
  • 政府扶持资金要还吗如村委借给企业对吗
  • 低值易耗品如何报废
  • 取得的接受捐赠怎么做账
  • 天然气安装工程施工劳务协议
  • 上季度开的发票这季度冲红怎么做会计分录?
  • 电商平台收取会员费吗
  • 研发废料收入抵研发
  • 将自制的一批新产品用于对外投资,生产成本为300000
  • 非独立核算分公司个税怎么申报
  • 税收缴款书税务收现专用的用途
  • 经营租赁固定资产折旧计入什么科目
  • 活动策划费可以开专票吗
  • 分公司税款
  • 酒店住宿收入确认原则的条件
  • 兼职劳务费个税怎么算
  • 收到工伤保险怎么做分录
  • 诊所免税政策
  • 退货需要进行的操作
  • 房地产企业使用什么准则
  • 结转进项税销项税额的分录怎么写
  • 生产企业出口退税全部流程
  • mcdlc.exe - mcdlc是什么进程文件 有何作用
  • 公司刚成立多久可以注销
  • 摊余成本计入哪个科目
  • 企业房产税如何计算方法
  • 无偿赠送是否需要交个人所得税分哪些情况
  • 二手车 交易税费
  • 工程资金占用费的法律依据
  • html怎么引入图片
  • docker-p
  • 制造费用期末有余额怎么处理
  • 快速搞懂Pinia及数据持久化存储(详细教程)
  • uniapp下拉
  • vue和react差别
  • 补提企业所得税财务报表应该如何调整
  • 开红字信息表后的账务处理
  • 出售无形资产净收益计入什么科目
  • python监控模块
  • db2入门视频教程
  • 研发费用的范围包括哪些
  • 公司借个人款利息产生个人所得税公司可以承担吗
  • 库存现金的账务处理流程
  • 长期待摊费用的最新账务处理
  • 销售旧固定资产开票编码
  • 贴现凭证如何做账务处理
  • 股本权益性投资和股权投资有区别吗
  • 权益净利率如何分析
  • 收到党支部经费如何做账
  • 一达通退税需要具备的条件?
  • 被收购企业账务处理流程
  • 所得税汇算清缴需要调增的项目
  • 企业的经济补偿包括哪些
  • 营改增之后还有营业税金及附加吗
  • 股东增资有哪几种方法
  • 备查账一般采用什么账簿
  • mysql5.6免安装版配置
  • windows xp简单操作教程
  • freebsd ports安装
  • mac电脑自带软件介绍
  • win7自动更新在哪
  • 利用python进行
  • shell date 设置日期
  • 着色器模型
  • opengl纹理贴图图片位置
  • python伪装请求头
  • unity如何得到输入框的输入
  • python数据导出
  • python相似模块用例
  • javascript缓存
  • python字符串的用法
  • js如何判断输入输出
  • jQuery EasyUI Pagination实现分页的常用方法
  • 吉林省税务局发票查询真伪
  • 纳税人未按照规定纳税
  • 海关进口增值税如何入账
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设