PyTorch可視化理解卷積神經網絡

摘要: 神經網絡工具像一個黑匣子,沒法知道它的中間是如何處理的。本文使用圖片加代碼的形式講解CNN網絡,並對每層的輸出進行可視化,便於初學者理解,能夠動手實踐下哦!

現在,機器已經可以在理解、識別圖像中的特徵和對象等領域實現99%級別的準確率。生活中,咱們天天都會運用到這一點,好比,智能手機拍照的時候可以識別臉部、在相似於谷歌搜圖中搜索特定照片、從條形碼掃描文本或掃描書籍等。造就機器可以得到在這些視覺方面取得優異性能多是源於一種特定類型的神經網絡——卷積神經網絡(CNN)。若是你是一個深度學習愛好者,你可能早已據說過這種神經網絡,而且可能已經使用一些深度學習框架好比caffe、TensorFlow、pytorch實現了一些圖像分類器。然而,這仍然存在一個問題:數據是如何在人工神經網絡傳送以及計算機是如何從中學習的。爲了從頭開始得到清晰的視角,本文將經過對每一層進行可視化以深刻理解卷積神經網絡。算法

卷積神經網絡

在學習卷積神經網絡以前,首先要了解神經網絡的工做原理。神經網絡是模仿人類大腦來解決複雜問題並在給定數據中找到模式的一種方法。在過去幾年中,這些神經網絡算法已經超越了許多傳統的機器學習和計算機視覺算法。「神經網絡」是由幾層或多層組成,不一樣層中具備多個神經元。每一個神經網絡都有一個輸入和輸出層,根據問題的複雜性增長隱藏層的個數。一旦將數據送入網絡中,神經元就會學習並進行模式識別。一旦神經網絡模型被訓練好後,模型就可以預測測試數據。網絡

另外一方面,CNN是一種特殊類型的神經網絡,它在圖像領域中表現得很是好。該網絡是由YanLeCunn在1998年提出的,被應用於數字手寫體識別任務中。其它應用領域包括語音識別、圖像分割和文本處理等。在CNN被髮明以前,多層感知機(MLP)被用於構建圖像分類器。圖像分類任務是指從多波段(彩色、黑白)光柵圖像中提取信息類的任務。MLP須要更多的時間和空間來查找圖片中的信息,由於每一個輸入元素都與下一層中的每一個神經元鏈接。而CNN經過使用稱爲局部鏈接的概念避免這些,將每一個神經元鏈接到輸入矩陣的局部區域。這經過容許網絡的不一樣部分專門處理諸如紋理或重複模式的高級特徵來最小化參數的數量。下面經過比較說明上述這一點。app

比較MLP和CNN

由於輸入圖像的大小爲28x28=784(MNIST數據集),MLP的輸入層神經元總數將爲784。網絡預測給定輸入圖像中的數字,輸出數字範圍是0-9。在輸出層,通常返回的是類別分數,好比說給定輸入是數字「3」的圖像,那麼在輸出層中,相應的神經元「3」與其它神經元相比具備更高的類別分數。這裏又會出現一個問題,模型須要包含多少個隱藏層,每層應該包含多少神經元?這些都是須要人爲設置的,下面是一個構建MLP模型的例子:框架

Num_classes = 10
Model = Sequntial()
Model.add(Dense(512, activation=’relu’, input_shape=(784, )))
Model.add(Dropout(0.2))
Model.add(Dense(512, activation=’relu’))
Model.add(Dropout(0.2))
Model.add(Dense(num_classes, activation=’softmax’))

上面的代碼片斷是使用Keras框架實現(暫時忽略語法錯誤),該代碼代表第一個隱藏層中有512個神經元,鏈接到維度爲784的輸入層。隱藏層後面加一個dropout層,丟棄比例設置爲0.2,該操做在必定程度上克服過擬合的問題。以後再次添加第二個隱藏層,也具備512谷歌神經元,而後再添加一個dropout層。最後,使用包含10個類的輸出層完成模型構建。其輸出的向量中具備最大值的該類將是模型的預測結果。機器學習

這種多層感知器的一個缺點是層與層之間徹底鏈接,這致使模型須要花費更多的訓練時間和參數空間。而且,MLP只接受向量做爲輸入。ide

卷積使用稀疏鏈接的層,而且其輸入能夠是矩陣,優於MLP。輸入特徵鏈接到局部編碼節點。在MLP中,每一個節點都有能力影響整個網絡。而CNN將圖像分解爲區域(像素的小局部區域),每一個隱藏節點與輸出層相關,輸出層將接收的數據進行組合以查找相應的模式。工具

計算機如何查看輸入的圖像?

看着圖片並解釋其含義,這對於人類來講很簡單的一件事情。咱們生活在世界上,咱們使用本身的主要感受器官(即眼睛)拍攝環境快照,而後將其傳遞到視網膜。這一切看起來都頗有趣。如今讓咱們想象一臺計算機也在作一樣的事情。post

在計算機中,使用一組位於0到255範圍內的像素值來解釋圖像。計算機查看這些像素值並理解它們。乍一看,它並不知道圖像中有什麼物體,也不知道其顏色。它只能識別出像素值,圖像對於計算機來講就至關於一組像素值。以後,經過分析像素值,它會慢慢了解圖像是灰度圖仍是彩色圖。灰度圖只有一個通道,由於每一個像素表明一種顏色的強度。0表示黑色,255表示白色,兩者之間的值代表其它的不一樣等級的灰灰色。彩色圖像有三個通道,紅色、綠色和藍色,它們分別表明3種顏色(三維矩陣)的強度,當三者的值同時變化時,它會產生大量顏色,相似於一個調色板。以後,計算機識別圖像中物體的曲線和輪廓。。

下面使用PyTorch加載數據集並在圖像上應用過濾器:性能

# Load the libraries
import torch
import numpy as np

from torchvision import datasets
import torchvision.transforms as transforms

# Set the parameters
num_workers = 0
batch_size = 20

# Converting the Images to tensors using Transforms
transform = transforms.ToTensor()

train_data = datasets.MNIST(root='data', train=True,
                                   download=True, transform=transform)
test_data = datasets.MNIST(root='data', train=False,
                                  download=True, transform=transform)

# Loading the Data
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size,
    num_workers=num_workers)
test_loader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, 
    num_workers=num_workers)

import matplotlib.pyplot as plt
%matplotlib inline

dataiter = iter(train_loader)
images, labels = dataiter.next()
images = images.numpy()

# Peeking into dataset
fig = plt.figure(figsize=(25, 4))
for image in np.arange(20):
    ax = fig.add_subplot(2, 20/2, image+1, xticks=[], yticks=[])
    ax.imshow(np.squeeze(images[image]), cmap='gray')
    ax.set_title(str(labels[image].item()))

下面看看如何將單個圖像輸入神經網絡中:學習

img = np.squeeze(images[7])

fig = plt.figure(figsize = (12,12)) 
ax = fig.add_subplot(111)
ax.imshow(img, cmap='gray')
width, height = img.shape
thresh = img.max()/2.5
for x in range(width):
    for y in range(height):
        val = round(img[x][y],2) if img[x][y] !=0 else 0
        ax.annotate(str(val), xy=(y,x),
            color='white' if img[x][y]<thresh else 'black')

上述代碼將數字'3'圖像分解爲像素。在一組手寫數字中,隨機選擇「3」。而且將實際像素值(0-255 )標準化,並將它們限制在0到1的範圍內。歸一化的操做可以加快模型訓練收斂速度。

構建過濾器

過濾器,顧名思義,就是過濾信息。在使用CNN處理圖像時,過濾像素信息。爲何須要過濾呢,計算機應該經歷理解圖像的學習過程,這與孩子學習過程很是類似,但學習時間會少的多。簡而言之,它經過從頭學習,而後從輸入層傳到輸出層。所以,網絡必須首先知道圖像中的全部原始部分,即邊緣、輪廓和其它低級特徵。檢測到這些低級特徵以後,傳遞給後面更深的隱藏層,提取更高級、更抽象的特徵。過濾器提供了一種提取用戶須要的信息的方式,而不是盲目地傳遞數據,由於計算機不會理解圖像的結構。在初始狀況下,能夠經過考慮特定過濾器來提取低級特徵,這裏的濾波器也是一組像素值,相似於圖像。能夠理解爲鏈接卷積神經網絡中的權重。這些權重或濾波器與輸入相乘以獲得中間圖像,描繪了計算機對圖像的部分理解。以後,這些中間層輸出將與多個過濾器相乘以擴展其視圖。而後提取到一些抽象的信息,好比人臉等。

就「過濾」而言,咱們有不少類型的過濾器。好比模糊濾鏡、銳化濾鏡、變亮、變暗、邊緣檢測等濾鏡。

下面用一些代碼片斷來理解過濾器的特徵:

Import matplotlib.pyplot as plt
Import matplotib.image as mpimg
Import cv2
Import numpy as np

Image = mpimg.imread(‘dog.jpg’)
Plt.imshow(image)

# 轉換爲灰度圖
gray = cv2.cvtColor(image, cv2.COLOR_RB2GRAY)

# 定義sobel過濾器
sobel = np.array([-1, -2, -1],
[0, 0, 0],
[1, 2, 1]))
# 應用sobel過濾器
Filtered_image = cv2.filter2D(gray, -1, sobel_y)
# 畫圖
Plt.imshow(filtered_image, cmp=’gray’)

以上是應用sobel邊緣檢測濾鏡後圖像的樣子, 能夠看到檢測出輪廓信息。

完整的CNN結構

到目前爲止,已經看到了如何使用濾鏡從圖像中提取特徵。如今要完成整個卷積神經網絡,cnn使用的層是:

  • 1.卷積層(Convolutional layer)
  • 2.池層(Pooling layer)
  • 3.全鏈接層(fully connected layer)

典型的cnn網絡結構是由上述三類層構成:

下面讓咱們看看每一個圖層起到的的做用:
* 卷積層(CONV)——使用過濾器執行卷積操做。由於它掃描輸入圖像的尺寸。它的超參數包括濾波器大小,能夠是2x二、3x三、4x四、5x5(或其它)和步長S。結果輸出O稱爲特徵映射或激活映射,具備使用輸入層計算的全部特徵和過濾器。下面描繪了應用卷積的工做過程:

卷積運算

  • 池化層(POOL)——用於特徵的下采樣,一般在卷積層以後應用。池化處理方式有多種類型,常見的是最大池化(max pooling)和平均池化(ave pooling),分別採用特徵的最大值和平均值。下面描述了池化的工做過程:

  •  全鏈接層(FC)——在展開的特徵上進行操做,其中每一個輸入鏈接到全部的神經元,一般在網絡末端用於將隱藏層鏈接到輸出層,下圖展現全鏈接層的工做過程:

在PyTorch中可視化CNN

在瞭解了CNN網絡的所有構件後,如今讓咱們使用PyTorch框架實現CNN。

步驟1:加載輸入圖像:

import cv2
import matplotlib.pyplot as plt
%matplotlib inline

img_path = 'dog.jpg'

bgr_img = cv2.imread(img_path)
gray_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2GRAY)

# Normalise
gray_img = gray_img.astype("float32")/255

plt.imshow(gray_img, cmap='gray')
plt.show()

步驟2:可視化過濾器

對過濾器進行可視化,以更好地瞭解將使用哪些過濾器:

import numpy as np

filter_vals = np.array([
  [-1, -1, 1, 1],
  [-1, -1, 1, 1],
  [-1, -1, 1, 1],
  [-1, -1, 1, 1]
])

print('Filter shape: ', filter_vals.shape)

# Defining the Filters
filter_1 = filter_vals
filter_2 = -filter_1
filter_3 = filter_1.T
filter_4 = -filter_3
filters = np.array([filter_1, filter_2, filter_3, filter_4])

# Check the Filters
fig = plt.figure(figsize=(10, 5))
for i in range(4):
    ax = fig.add_subplot(1, 4, i+1, xticks=[], yticks=[])
    ax.imshow(filters[i], cmap='gray')
    ax.set_title('Filter %s' % str(i+1))
    width, height = filters[i].shape
    for x in range(width):
        for y in range(height):
            ax.annotate(str(filters[i][x][y]), xy=(y,x),
                        color='white' if filters[i][x][y]<0 else 'black')

步驟3:定義CNN模型

本文構建的CNN模型具備卷積層和最大池層,而且使用上述過濾器初始化權重:

import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self, weight):
        super(Net, self).__init__()
        # initializes the weights of the convolutional layer to be the weights of the 4 defined filters
        k_height, k_width = weight.shape[2:]
        # assumes there are 4 grayscale filters
        self.conv = nn.Conv2d(1, 4, kernel_size=(k_height, k_width), bias=False)
        # initializes the weights of the convolutional layer
        self.conv.weight = torch.nn.Parameter(weight)
        # define a pooling layer
        self.pool = nn.MaxPool2d(2, 2)

    def forward(self, x):
        # calculates the output of a convolutional layer
        # pre- and post-activation
        conv_x = self.conv(x)
        activated_x = F.relu(conv_x)

        # applies pooling layer
        pooled_x = self.pool(activated_x)

        # returns all layers
        return conv_x, activated_x, pooled_x

# instantiate the model and set the weights
weight = torch.from_numpy(filters).unsqueeze(1).type(torch.FloatTensor)
model = Net(weight)

# print out the layer in the network
print(model)
Net(

(conv): Conv2d(1, 4, kernel_size=(4, 4), stride=(1, 1), bias=False)
(pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)

步驟4:可視化過濾器

快速瀏覽一下所使用的過濾器

def viz_layer(layer, n_filters= 4):
    fig = plt.figure(figsize=(20, 20))

    for i in range(n_filters):
        ax = fig.add_subplot(1, n_filters, i+1)
        ax.imshow(np.squeeze(layer[0,i].data.numpy()), cmap='gray')
        ax.set_title('Output %s' % str(i+1))

fig = plt.figure(figsize=(12, 6))
fig.subplots_adjust(left=0, right=1.5, bottom=0.8, top=1, hspace=0.05, wspace=0.05)
for i in range(4):
    ax = fig.add_subplot(1, 4, i+1, xticks=[], yticks=[])
    ax.imshow(filters[i], cmap='gray')
    ax.set_title('Filter %s' % str(i+1))

gray_img_tensor = torch.from_numpy(gray_img).unsqueeze(0).unsqueeze(1)

步驟5:每層過濾器的輸出

在卷積層和池化層輸出的圖像以下所示:

卷積層:

池化層:

能夠看到不一樣層結構獲得的效果會有所差異,正是因爲不一樣層提取到的特徵不一樣,在輸出層集合到的特徵才能很好地抽象出圖像信息。



本文做者:【方向】

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索