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

  • 车船税规定怎么缴纳
  • 增值税普通
  • 临时用工的会计分录
  • 企业出售房产要交哪些税
  • 快递费属于哪个商品编码
  • 保本理财收益计入什么科目
  • 红字信息表状态是B900071
  • 企业开出的普票和专票对税额有影响吗
  • 购买的二手车可以抵扣进项税额吗
  • 外购商品赠送客户怎么做账
  • 受托代为投资账务处理
  • 长期待摊费用摊销表
  • 股权转让时其他股东不配合怎么转让
  • 银行抵债资产怎么管理
  • 公司管理层的作用
  • 核定征收公司有什么用
  • 工业企业增值税缓交政策
  • 金税三期如何报个税
  • 个人出租房屋合同协议书
  • 固定资产出售时增值税怎么处理
  • 苹果助手hi
  • 去年的成本发票怎么做账
  • win10下载的软件打不开
  • 苹果15手机价格和图片颜色
  • PHP:mb_strimwidth()的用法_mbstring函数
  • 子公司开票给母公司,冲减利润,怎么避免税务风险
  • 逾期利息算法
  • 无形资产摊销会影响利润吗
  • 什么情况下个人需缴纳房产税
  • php多级分类
  • 企业借给个人钱合法吗
  • php点击跳转
  • 现在前端的现状
  • php initialize
  • php强制下载文件在哪
  • 社会团体收取的会费是否缴纳企业所得税
  • 对附属单位补助的资金性质是财政预算资金
  • 资产负债表和利润表的勾稽关系
  • 会计成本确认
  • 哪几类账户期末余额最大
  • 应收帐款出售给银行会计分录
  • 合伙企业利润分配
  • 建筑施工企业收入怎么确认
  • python中Plotly Express是什么?
  • 哪些行业不适用税前加计扣除政策
  • 发票种类8种分别是
  • 企业给灾区捐款可以抵税吗
  • 土地出让合同的法律效力
  • 单位工程可以是一栋楼吗
  • 企业贷款业务
  • 高速过路费抵扣增值税
  • 没计提的税费怎么处理
  • 现金存入银行是什么凭证
  • 出库未开发票
  • 收到银行季度结转怎么做
  • 收入的利息如何计算
  • 过节费可以发现金吗
  • 账薄的使用规则
  • 数据库访问机制
  • win7怎样安装摄像头驱动
  • WIN8系统安装教程
  • 涌泉的准确位置图 图解
  • fedora最新版
  • win7升级win10黑屏
  • mac m1读取ntfs
  • win7系统如何设置息屏
  • 检查linux是否安装了,可用哪些命令
  • opengl mesa
  • css网站布局实录
  • javascript闭包详解
  • 什么是质数
  • android事件响应和处理机制
  • python 编程技巧
  • 非涉密网络严禁什么
  • 海关快递怎么上门寄件
  • 上海浦东税务局电话 工作时间
  • 虚假纳税申报的法律责任
  • 乐陵到德州多远距离
  • 三证一码是什么
  • 地税是什么税种
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设