pytorch中網絡特徵圖(feture map)、卷積核權重、卷積核最匹配樣本、類別激活圖(Class Activation Map/CAM)、網絡結構的可視化方法

0,可視化的重要性:

深度學習不少方向所謂改進模型、改進網絡都是在按照人的主觀思想在改進,經常在說模型的本質是提取特徵,但並不知道它提取了什麼特徵、哪些區域對於識別真正起做用、也不知道網絡是根據什麼得出了分類結果。爲了加強結果的可解釋性,須要給出模型的一些可視化圖來證實模型或新methods對於任務的做用,這一點不只能增長新模型或新methods可信度;還能夠根據可視化某個網絡的結果分析其不足之處,從而提出新的改進方法。(寫論文還能夠用來湊字數、湊工做量:)python

無特殊說明,本文中所用的網絡是:網絡

torchvision.models.resnet50(pretrained=True)

所用的圖片爲
案例16C1app

1,特徵圖(feture map)

  • 概念
    特徵圖是一個在深度學習的研究過程當中常常會遇到的概念,簡單來講就是對輸入進行一次計算處理後的輸出,經過對特徵圖的可視化能夠看出輸入樣本在網絡中的變化狀況。
  • 可視化方法:
    • 直接可視化:直接讓輸入數據通過各層網絡,獲取各層網絡處理後的輸出,而後繪製想要展現的特徵圖便可。
    • 反捲積網絡(deconvnet)From 'Visualizing and Understanding Convolutional Networks':對一個訓練好的神經網絡中任意一層feature map通過反捲積網絡後重構出像素空間,主要操做是
      • Unpooling/反池化:將最大值放到原位置,而其餘位置直接置零。
      • Rectification:一樣使用Relu做爲激活函數。
      • Filtering/反捲積:使用原網絡的卷積核的轉置做爲卷積核,對Rectification後的輸出進行卷積。
    • 導向反向傳播(Guided-backpropagation)From 'Striving for simplicity: The all convolutional net':其與反捲積網絡的區別在於對ReLU的處理方式,在反捲積網絡中使用ReLU處理梯度,只回傳梯度大於0的位置;而在普通反向傳播中只回傳feature map中大於0的位置;在導向反向傳播中結合這二者,只回傳輸入和梯度都大於0的位置。
  • 例子
'''方法1,直接可視化'''
import torch
import torchvision
import cv2
from PIL import Image
import torchvision.models as models
import torch.nn as nn
from matplotlib import pyplot as plt
import math

'''1,加載訓練模型'''
resnet50 = models.resnet50(pretrained=True)
print(resnet50)

'''2,提取CNN層,非必須'''
conv_layers = []
model_weights = []
model_children = list(models.resnet50().children())
counter = 0

for i in range(len(model_children)):
    if type(model_children[i]) == nn.Conv2d:
        counter += 1
        model_weights.append(model_children[i].weight)
        conv_layers.append(model_children[i])
    elif type(model_children[i]) == nn.Sequential:
        for j in range(len(model_children[i])):
            for child in model_children[i][j].children():
                if type(child) == nn.Conv2d:
                    counter += 1
                    model_weights.append(child.weight)
                    conv_layers.append(child)

'''3,讀取數據'''
img = cv2.cvtColor(cv2.imread('data.jpg'), cv2.COLOR_BGR2RGB)
img = torchvision.transforms.Compose([
    torchvision.transforms.ToPILImage(),
    torchvision.transforms.Resize((1050, 1680)),
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])(img).unsqueeze(0)

'''4,特徵映射,這裏繪製第一層卷積的feature map'''
featuremaps = [conv_layers[0](img)]
plt.figure(1)
for i in range(64):
    plt.subplot(8, 8, i + 1)
    plt.axis('off')
    plt.imshow(featuremaps[0][0, i, :, :].detach(), cmap='gray')
plt.show()
案例16C1

2,卷積核權重

  • 概念
    卷積核的計算能夠看做是在計算類似度,二維卷積核自己能夠看做是一副縮略圖,一維卷積核自己也能夠看做是一段一維信號,卷積核權重的可視化就是將這些信息可視化。(雖然這些信息幾乎都是雜亂無章的,沒什麼用:)
  • 可視化方法
    取出某個想要可視化的卷積核權重,而後繪圖便可。
  • 例子
    for i in range(64):
        plt.subplot(8, 8, i+1)
        plt.axis('off')
        plt.imshow(model_weights[0][i][0, :, :].detach(), cmap='gray')
    plt.show()
    案例16C1

3,卷積核最匹配樣本

  • 概念
    正如2中提到的,卷積核的計算能夠看做是在計算類似度,那麼在一批樣本中通過卷積覈計算之後的類似度值確定有高有底,那麼類似度越高也就越知足這個卷積核的「口味」,這也意味着該樣本與該卷積核越匹配。那麼,有沒有方法可以超越有限的訓練樣本,生成與特定卷積核最匹配的樣本呢?答案是確定的。一個可行的思路是:隨機初始化生成一個樣本(指的是對樣本的各個數據點隨機取值,而不是在數據集中隨機選一個樣本),而後通過前向傳播到該卷積核;咱們但願這個隨機生成的樣本在通過這一層卷積核時響應值能儘量的大(響應值比較大的樣本是這個卷積核比較承認的,是與識別任務更相關的);咱們要作的就是不斷調整樣本各個數據點的值,直到響應值足夠大,咱們就能夠認爲此時的樣本就是這個卷積核所承認的,從而達到生成卷積核最匹配樣本的目的。
  • 可視化方法
    設計一個損失函數(例如:通過變換後的響應值),使用梯度上升,更新數據點的值,使響應值最大。
  • 例子
    下圖展現的是pytorch中的resnet50中的layer1的最後一次卷積操做中的256個卷積核中的0,10,...,150這16個卷積核的最匹配樣本。優化過程當中用了兩種方法,結果如圖:
    方法1
    案例16C1
    方法2,能夠明顯看出方法2的結果區別性更大
    案例16C1

4,類別激活圖(Class Activation Map/CAM)

  • 概念
    特徵圖可視化、卷積核權重可視化、卷積核最匹配樣本這些方法更可能是用於分析模型在某一層學習到的東西;可是對於不一樣的類,咱們又如何知道模型是根據哪些信息進行識別的?是否能將這些信息在原始數據上表示出來(好比說熱力圖)?答案依舊是確定的。這個方法主要是CAM系列,目前有CAM, Grad-CAM, Grad-CAM++。其中CAM須要特定的結構-GAP,但大部分現有的模型沒有這個結構,想要使用該方法便須要修改原模型結構,並從新訓練,所以適用範圍有限。針對CAM的缺陷,有了以後Grad-CAM的提出。
  • 可視化方法
    Grad-CAM的最大特色就是再也不須要修改現有的模型結構,也不須要從新訓練,能夠直接在原模型上可視化。Grad-CAM對於想要可視化的類別C,使最後輸出的類別C的機率值經過反向傳播到最後一層feature maps,獲得類別C對該feature maps的每一個像素的梯度值;對每一個像素的梯度值取全局平均池化,便可獲得對feature maps的加權係數alpha;接下來對特徵圖加權求和,使用ReLU進行修正,再進行上採樣。使用ReLU的緣由是對於那些負值,可認爲與識別類別C無關,這些負值多是與其餘類別有關,而正值纔是對識別C有正面影響的。
案例16C1

計算梯度及全局平均池化:ide

\[\alpha_k^c=\overbrace{\frac{1}{Z}\displaystyle{\sum_i\sum_j}}^{global~average~pooling}\underbrace{\frac{\partial{y^c}}{\partial{A^k_{ij}}}}_{grdients~via~backprop} \]

加權:函數

\[L^c_{Grad-CAM}=ReLU\underbrace{\left(\displaystyle\sum_k\alpha^c_kA^k\right)}_{linear~combination} \]

  • 例子
    1,由藍到紅,越紅表明關注度越高,對於類別分配的結果影響越大,這個例子將圖片識別爲n02909870 水桶,從關注的區域也能夠看出預測是失敗的,沒有關注到有效的信息
    案例16C1
    這個預訓練模型眼裏好像不是水桶就是鉤子。。。

2,從網上找了一個模型,效果就挺好的
案例16C1案例16C1案例16C1案例16C1學習

案例16C1案例16C1案例16C1案例16C1

案例16C1案例16C1案例16C1案例16C1

識別青蛙的結果頗有意思,這意味着網絡關注的重點不在於青蛙,反而在於青蛙周圍的環境,並且網絡的預測結果仍是正確的!
案例16C1案例16C1案例16C1案例16C1優化

5,網絡結構的可視化

  • 可視化方法
    用tensorboard直接繪製就行,注意兩點
    • 路徑不要有中文
    • pytorch版本在1.3.0及以上(低版本不顯示)
  • 例子
from torch.utils.tensorboard import SummaryWriter
import torch
import torch.nn as nn
import torch.nn.functional as F

'''
1,初始化writer
'''
writer = SummaryWriter('runs/resnet50')    # 指定寫入文件的位置

'''
2,加載模型
'''
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(6 * 12 * 12, 120)
        self.fc2 = nn.Linear(120, 84)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = x.view(-1, 6 * 12 * 12)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return x
net = Net()

'''
3,添加網絡結構
'''
writer.add_graph(net, torch.randn(1, 1, 28, 28))
writer.close()
案例16C1
相關文章
相關標籤/搜索