第三節,如何直觀理解卷積神經網絡的工做原理

本博文主要是較爲直觀的理解卷積神經網絡的工做原理,明白卷積神經網絡是怎麼工做的(轉載)html

近日,Dishashree Gupta 在 Analyticsvidhya 上發表了一篇題爲《Architecture of Convolutional Neural Networks (CNNs) demystified》的文章,對用於圖像識別和分類的卷積神經網絡架構做了深度揭祕;做者在文中還做了通盤演示,指望對 CNN 的工做機制有一個深刻的剖析。機器之心對本文進行了編譯, 原文連接在此,但願對你有幫助。    機器視角:長文揭祕圖像處理和卷積神經網絡架構

引言

先坦白地說,有一段時間我沒法真正理解深度學習。我查看相關研究論文和文章,感受深度學習異常複雜。我嘗試去理解神經網絡及其變體,但依然感到困難。python

接着有一天,我決定一步一步,從基礎開始。我把技術操做的步驟分解開來,並手動執行這些步驟(和計算),直到我理解它們如何工做。這至關費時,且使人緊張,可是結果非凡。算法

如今,我不只對深度學習有了全面的理解,還在此基礎上有了好想法,由於個人基礎很紮實。隨意地應用神經網絡是一回事,理解它是什麼以及背後的發生機制是另一回事。數組

今天,我將與你共享個人心得,展現我如何上手卷積神經網絡並最終弄明白了它。我將作一個通盤的展現,從而使你對 CNN 的工做機制有一個深刻的瞭解。網絡

在本文中,我將會討論 CNN 背後的架構,其設計初衷在於解決圖像識別和分類問題。同時我也會假設你對神經網絡已經有了初步瞭解。架構

1. 機器如何看圖?

人類大腦是一很是強大的機器,每秒內能看(捕捉)多張圖,並在乎識不到的狀況下就完成了對這些圖的處理。但機器並不是如此。機器處理圖像的第一步是理解,理解如何表達一張圖像,進而讀取圖片。app

簡單來講,每一個圖像都是一系列特定排序的圖點(像素)。若是你改變像素的順序或顏色,圖像也隨之改變。舉個例子,存儲並讀取一張上面寫着數字 4 的圖像。機器學習

基本上,機器會把圖像打碎成像素矩陣,存儲每一個表示位置像素的顏色碼。在下圖的表示中,數值 1 是白色,256 是最深的綠色(爲了簡化,咱們示例限制到了一種顏色)。ide

<img src="https://pic3.zhimg.com/50/v2-ce7584843d74f0c90b60b5439250635a_hd.jpg" data-rawwidth="387" data-rawheight="380" class="content_image" width="387">

一旦你以這種格式存儲完圖像信息,下一步就是讓神經網絡理解這種排序與模式。函數

2. 如何幫助神經網絡識別圖像?

表徵像素的數值是以特定的方式排序的。

<img src="https://pic4.zhimg.com/50/v2-d6e748862f4f995047b53a87009b3fb5_hd.jpg" data-rawwidth="848" data-rawheight="808" class="origin_image zh-lightbox-thumb" width="848" data-original="https://pic4.zhimg.com/v2-d6e748862f4f995047b53a87009b3fb5_r.jpg">

假設咱們嘗試使用全鏈接網絡識別圖像,該如何作?

全鏈接網絡能夠經過平化它,把圖像看成一個數組,並把像素值看成預測圖像中數值的特徵。明確地說,讓網絡理解理解下面圖中發生了什麼,很是的艱難。

<img src="https://pic2.zhimg.com/50/v2-25be163b4687fa9f6bf174bc95613a21_hd.jpg" data-rawwidth="1340" data-rawheight="140" class="origin_image zh-lightbox-thumb" width="1340" data-original="https://pic2.zhimg.com/v2-25be163b4687fa9f6bf174bc95613a21_r.jpg">

即便人類也很難理解上圖中表達的含義是數字 4。咱們徹底丟失了像素的空間排列。

咱們能作什麼呢?能夠嘗試從原圖像中提取特徵,從而保留空間排列。

案例 1

這裏咱們使用一個權重乘以初始像素值。

<img src="https://pic1.zhimg.com/50/v2-97541d570892f2b8580758655b2b737c_hd.jpg" data-rawwidth="1326" data-rawheight="604" class="origin_image zh-lightbox-thumb" width="1326" data-original="https://pic1.zhimg.com/v2-97541d570892f2b8580758655b2b737c_r.jpg">
如今裸眼識別出這是「4」就變得更簡單了。但把它交給全鏈接網絡以前,還須要平整化(flatten) 它,要讓咱們可以保留圖像的空間排列。

案例 2

如今咱們能夠看到,把圖像平整化徹底破壞了它的排列。咱們須要想出一種方式在沒有平整化的狀況下把圖片饋送給網絡,而且還要保留空間排列特徵,也就是須要饋送像素值的 2D/3D 排列。

咱們能夠嘗試一次採用圖像的兩個像素值,而非一個。這能給網絡很好的洞見,觀察鄰近像素的特徵。既然一次採用兩個像素,那也就須要一次採用兩個權重值了

<img src="https://pic3.zhimg.com/50/v2-5a8235754cd0ae57f475683ed6cba2b5_hd.gif" data-rawwidth="994" data-rawheight="383" data-thumbnail="https://pic3.zhimg.com/50/v2-5a8235754cd0ae57f475683ed6cba2b5_hd.jpg" class="origin_image zh-lightbox-thumb" width="994" data-original="https://pic3.zhimg.com/v2-5a8235754cd0ae57f475683ed6cba2b5_r.gif">

 但願你能注意到圖像從以前的 4 列數值變成了 3 列。由於咱們如今一次移用兩個像素(在每次移動中像素被共享),圖像變的更小了。雖然圖像變小了,咱們仍能在很大程度上理解這是「4」。並且,要意識到的一個重點是,咱們採用的是兩個連貫的水平像素,所以只會考慮水平的排列。

 這是咱們從圖像中提取特徵的一種方式。咱們能夠看到左邊和中間部分,但右邊部分看起來不那麼清楚。主要是由於兩個問題:

 1. 圖片角落左邊和右邊是權重相乘一次獲得的。

2. 左邊仍舊保留,由於權重值高;右邊由於略低的權重,有些丟失。

 如今咱們有兩個問題,須要兩個解決方案。 

案例 3

遇到的問題是圖像左右兩角只被權重經過一次。咱們須要作的是讓網絡像考慮其餘像素同樣考慮角落。咱們有一個簡單的方法解決這一問題:把零放在權重運動的兩邊。

<img src="https://pic2.zhimg.com/50/v2-0014d5f30b87ae12947cffa0f429d5f1_hd.gif" data-rawwidth="1225" data-rawheight="389" data-thumbnail="https://pic2.zhimg.com/50/v2-0014d5f30b87ae12947cffa0f429d5f1_hd.jpg" class="origin_image zh-lightbox-thumb" width="1225" data-original="https://pic2.zhimg.com/v2-0014d5f30b87ae12947cffa0f429d5f1_r.gif">

 你能夠看到經過添加零,來自角落的信息被再訓練。圖像也變得更大。這可被用於咱們不想要縮小圖像的狀況下。 

案例 4

這裏咱們試圖解決的問題是右側角落更小的權重值正在下降像素值,所以使其難以被咱們識別。咱們所能作的是採起多個權重值並將其結合起來。

 (1,0.3) 的權重值給了咱們一個輸出表格

<img src="https://pic3.zhimg.com/50/v2-ca6e6b4afb696bccdcb67687a04c44da_hd.jpg" data-rawwidth="333" data-rawheight="376" class="content_image" width="333">

同時表格 (0.1,5) 的權重值也將給咱們一個輸出表格。

<img src="https://pic1.zhimg.com/50/v2-cf8ff64d38ce55d2c1d1513b08e079e2_hd.jpg" data-rawwidth="331" data-rawheight="396" class="content_image" width="331">

兩張圖像的結合版本將會給咱們一個清晰的圖片。所以,咱們所作的是簡單地使用多個權重而不是一個,從而再訓練圖像的更多信息。最終結果將是上述兩張圖像的一個結合版本。

案例 5

咱們到如今經過使用權重,試圖把水平像素(horizontal pixel)結合起來。可是大多數狀況下咱們須要在水平和垂直方向上保持空間佈局。咱們採起 2D 矩陣權重,把像素在水平和垂直方向上結合起來。一樣,記住已經有了水平和垂直方向的權重運動,輸出會在水平和垂直方向上低一個像素。

<img src="https://pic1.zhimg.com/50/v2-f1206f480077d6064e2a445ebef4ea66_hd.gif" data-rawwidth="1002" data-rawheight="363" data-thumbnail="https://pic1.zhimg.com/50/v2-f1206f480077d6064e2a445ebef4ea66_hd.jpg" class="origin_image zh-lightbox-thumb" width="1002" data-original="https://pic1.zhimg.com/v2-f1206f480077d6064e2a445ebef4ea66_r.gif">

特別感謝 Jeremy Howard 啓發我創做了這些圖像。

所以咱們作了什麼?

上面咱們所作的事是試圖經過使用圖像的空間的安排從圖像中提取特徵。爲了理解圖像,理解像素如何安排對於一個網絡極其重要。上面咱們所作的也偏偏是一個卷積網絡所作的。咱們能夠採用輸入圖像,定義權重矩陣,而且輸入被卷積以從圖像中提取特殊特徵而無需損失其有關空間安排的信息。

這個方法的另外一個重大好處是它能夠減小圖像的參數數量。正如所見,卷積圖像相比於原始圖像有更少的像素。

3.定義一個卷積神經網絡

咱們須要三個基本的元素來定義一個基本的卷積網絡

1. 卷積層

2. 池化層(可選)

3. 輸出層

卷積層

在這一層中,實際所發生的就像咱們在上述案例 5 中見到的同樣。假設咱們有一個 6*6 的圖像。咱們定義一個權值矩陣,用來從圖像中提取必定的特徵。

<img src="https://pic1.zhimg.com/50/v2-1e95f13c1f0d650e826e813c0e1f493a_hd.jpg" data-rawwidth="425" data-rawheight="159" class="origin_image zh-lightbox-thumb" width="425" data-original="https://pic1.zhimg.com/v2-1e95f13c1f0d650e826e813c0e1f493a_r.jpg">

咱們把權值初始化成一個 3*3 的矩陣。這個權值如今應該與圖像結合,全部的像素都被覆蓋至少一次,從而來產生一個卷積化的輸出。上述的 429,是經過計算權值矩陣和輸入圖像的 3*3 高亮部分以元素方式進行的乘積的值而獲得的。

 如今 6*6 的圖像轉換成了 4*4 的圖像。想象一下權值矩陣就像用來刷牆的刷子。首先在水平方向上用這個刷子進行刷牆,而後再向下移,對下一行進行水平粉刷。當權值矩陣沿着圖像移動的時候,像素值再一次被使用。實際上,這樣可使參數在卷積神經網絡中被共享。

下面咱們以一個真實圖像爲例。

<img src="https://pic2.zhimg.com/50/v2-56acff7742146f37317e4c36aba47234_hd.jpg" data-rawwidth="640" data-rawheight="249" class="origin_image zh-lightbox-thumb" width="640" data-original="https://pic2.zhimg.com/v2-56acff7742146f37317e4c36aba47234_r.jpg">

權值矩陣在圖像裏表現的像一個從原始圖像矩陣中提取特定信息的過濾器。一個權值組合可能用來提取邊緣(edge)信息,另外一個多是用來提取一個特定顏色,下一個就可能就是對不須要的噪點進行模糊化。

先對權值進行學習,而後損失函數能夠被最小化,相似於多層感知機(MLP)。所以須要經過對參數進行學習來從原始圖像中提取信息,從而來幫助網絡進行正確的預測。當咱們有多個卷積層的時候,初始層每每提取較多的通常特徵,隨着網絡結構變得更深,權值矩陣提取的特徵愈來愈複雜,而且愈來愈適用於眼前的問題。

步長(stride)和邊界(padding)的概念

像咱們在上面看到的同樣,過濾器或者說權值矩陣,在整個圖像範圍內一次移動一個像素。咱們能夠把它定義成一個超參數(hyperparameter),從而來表示咱們想讓權值矩陣在圖像內如何移動。若是權值矩陣一次移動一個像素,咱們稱其步長爲 1。下面咱們看一下步長爲 2 時的狀況。

你能夠看見當咱們增長步長值的時候,圖像的規格持續變小。在輸入圖像四周填充 0 邊界能夠解決這個問題。咱們也能夠在高步長值的狀況下在圖像四周填加不僅一層的 0 邊界。

<img src="https://pic1.zhimg.com/50/v2-67c54ac5b709f0d13ce78e9e17c8991f_hd.jpg" data-rawwidth="246" data-rawheight="188" class="content_image" width="246">
咱們能夠看見在咱們給圖像填加一層 0 邊界後,圖像的原始形狀是如何被保持的。因爲輸出圖像和輸入圖像是大小相同的,因此這被稱爲 same padding。

 

這就是 same padding(意味着咱們僅考慮輸入圖像的有效像素)。中間的 4*4 像素是相同的。這裏咱們已經利用邊界保留了更多信息,而且也已經保留了圖像的原大小。

多過濾與激活圖

須要記住的是權值的縱深維度(depth dimension)和輸入圖像的縱深維度是相同的。權值會延伸到輸入圖像的整個深度。所以,和一個單一權值矩陣進行卷積會產生一個單一縱深維度的卷積化輸出。大多數狀況下都不使用單一過濾器(權值矩陣),而是應用維度相同的多個過濾器。

每個過濾器的輸出被堆疊在一塊兒,造成卷積圖像的縱深維度。假設咱們有一個 32*32*3 的輸入。咱們使用 5*5*3,帶有 valid padding 的 10 個過濾器。輸出的維度將會是 28*28*10。

以下圖所示:

<img src="https://pic3.zhimg.com/50/v2-defbc123f52a27bddd902761243121f8_hd.jpg" data-rawwidth="640" data-rawheight="220" class="origin_image zh-lightbox-thumb" width="640" data-original="https://pic3.zhimg.com/v2-defbc123f52a27bddd902761243121f8_r.jpg">

 

激活圖是卷積層的輸出。

池化層

有時圖像太大,咱們須要減小訓練參數的數量,它被要求在隨後的卷積層之間週期性地引進池化層。池化的惟一目的是減小圖像的空間大小。池化在每個縱深維度上獨自完成,所以圖像的縱深保持不變。池化層的最多見形式是最大池化。

<img src="https://pic1.zhimg.com/50/v2-cae74f34159e48d581156d80e8e12ec6_hd.jpg" data-rawwidth="259" data-rawheight="110" class="content_image" width="259">
在這裏,咱們把步幅定爲 2,池化尺寸也爲 2。最大化執行也應用在每一個卷機輸出的深度尺寸中。正如你所看到的,最大池化操做後,4*4 卷積的輸出變成了 2*2。

讓咱們看看最大池化在真實圖片中的效果如何。

正如你看到的,咱們卷積了圖像,並最大池化了它。最大池化圖像仍然保留了汽車在街上的信息。若是你仔細觀察的話,你會發現圖像的尺寸已經減半。這能夠很大程度上減小參數。

一樣,其餘形式的池化也能夠在系統中應用,如平均池化和 L2 規範池化。

輸出維度

理解每一個卷積層輸入和輸出的尺寸可能會有點難度。如下三點或許可讓你瞭解輸出尺寸的問題。有三個超參數能夠控制輸出卷的大小。

1. 過濾器數量-輸出卷的深度與過濾器的數量成正比。請記住該如何堆疊每一個過濾器的輸出以造成激活映射。激活圖的深度等於過濾器的數量。

2. 步幅(Stride)-若是步幅是 1,那麼咱們處理圖片的精細度就進入單像素級別了。更高的步幅意味着同時處理更多的像素,從而產生較小的輸出量。

3. 零填充(zero padding)-這有助於咱們保留輸入圖像的尺寸。若是添加了單零填充,則單步幅過濾器的運動會保持在原圖尺寸。

咱們能夠應用一個簡單的公式來計算輸出尺寸。輸出圖像的空間尺寸能夠計算爲([W-F + 2P] / S)+1。在這裏,W 是輸入尺寸,F 是過濾器的尺寸,P 是填充數量,S 是步幅數字。假如咱們有一張 32*32*3 的輸入圖像,咱們使用 10 個尺寸爲 3*3*3 的過濾器,單步幅和零填充。

那麼 W=32,F=3,P=0,S=1。輸出深度等於應用的濾波器的數量,即 10,輸出尺寸大小爲 ([32-3+0]/1)+1 = 30。所以輸出尺寸是 30*30*10。

輸出層

在多層卷積和填充後,咱們須要以類的形式輸出。卷積和池化層只會提取特徵,並減小原始圖像帶來的參數。然而,爲了生成最終的輸出,咱們須要應用全鏈接層來生成一個等於咱們須要的類的數量的輸出。僅僅依靠卷積層是難以達到這個要求的。卷積層能夠生成 3D 激活圖,而咱們只須要圖像是否屬於一個特定的類這樣的內容。輸出層具備相似分類交叉熵的損失函數,用於計算預測偏差。一旦前向傳播完成,反向傳播就會開始更新權重與誤差,以減小偏差和損失。

4. 小結

正如你所看到的,CNN 由不一樣的卷積層和池化層組成。讓咱們看看整個網絡是什麼樣子:

<img src="https://pic3.zhimg.com/50/v2-a182b75112c9c6d60892a95f47359d24_hd.jpg" data-rawwidth="640" data-rawheight="188" class="origin_image zh-lightbox-thumb" width="640" data-original="https://pic3.zhimg.com/v2-a182b75112c9c6d60892a95f47359d24_r.jpg">
  • 咱們將輸入圖像傳遞到第一個卷積層中,卷積後以激活圖形式輸出。圖片在卷積層中過濾後的特徵會被輸出,並傳遞下去。
  • 每一個過濾器都會給出不一樣的特徵,以幫助進行正確的類預測。由於咱們須要保證圖像大小的一致,因此咱們使用一樣的填充(零填充),不然填充會被使用,由於它能夠幫助減小特徵的數量。
  • 隨後加入池化層進一步減小參數的數量。
  • 在預測最終提出前,數據會通過多個卷積和池化層的處理。卷積層會幫助提取特徵,越深的卷積神經網絡會提取越具體的特徵,越淺的網絡提取越淺顯的特徵。
  • 如前所述,CNN 中的輸出層是全鏈接層,其中來自其餘層的輸入在這裏被平化和發送,以便將輸出轉換爲網絡所需的參數。
  • 隨後輸出層會產生輸出,這些信息會互相比較排除錯誤。損失函數是全鏈接輸出層計算的均方根損失。隨後咱們會計算梯度錯誤。
  • 錯誤會進行反向傳播,以不斷改進過濾器(權重)和誤差值。
  • 一個訓練週期由單次正向和反向傳遞完成。

5. 在 KERAS 中使用 CNN 對圖像進行分類

讓咱們嘗試一下,輸入貓和狗的圖片,讓計算機識別它們。這是圖像識別和分類的經典問題,機器在這裏須要作的是看到圖像,並理解貓與狗的不一樣外形特徵。這些特徵能夠是外形輪廓,也能夠是貓的鬍鬚之類,卷積層會攫取這些特徵。讓咱們把數據集拿來試驗一下吧。

如下這些圖片均來自數據集。

<img src="https://pic2.zhimg.com/50/v2-298e3e184c94749666abf4e7102afbd2_hd.jpg" data-rawwidth="640" data-rawheight="254" class="origin_image zh-lightbox-thumb" width="640" data-original="https://pic2.zhimg.com/v2-298e3e184c94749666abf4e7102afbd2_r.jpg">  

咱們首先須要調整這些圖像的大小,讓它們形狀相同。這是處理圖像以前一般須要作的,由於在拍照時,讓照下的圖像都大小相同幾乎不可能。

爲了簡化理解,咱們在這裏只用一個卷積層和一個池化層。注意:在 CNN 的應用階段,這種簡單的狀況是不會發生的。

import various packagesimport os
import numpy as np
import pandas as pd
import scipy
import sklearn
import kerasfrom keras.models
import Sequentialimport cv2from skimage
import io
%matplotlib inline #Defining the File Path cat=os.listdir("/mnt/hdd/datasets/dogs_cats/train/cat") dog=os.listdir("/mnt/hdd/datasets/dogs_cats/train/dog") filepath="/mnt/hdd/datasets/dogs_cats/train/cat/"filepath2="/mnt/hdd/datasets/dogs_cats/train/dog/"#Loading the Images images=[] label = []for i in cat: image = scipy.misc.imread(filepath+i) images.append(image) label.append(0) #for cat imagesfor i in dog: image = scipy.misc.imread(filepath2+i) images.append(image) label.append(1) #for dog images #resizing all the imagesfor i in range(0,23000): images[i]=cv2.resize(images[i],(300,300)) #converting images to arrays images=np.array(images) label=np.array(label) # Defining the hyperparameters filters=10filtersize=(5,5) epochs =5batchsize=128input_shape=(300,300,3) #Converting the target variable to the required sizefrom keras.utils.np_utils import to_categorical label = to_categorical(label) #Defining the model model = Sequential() model.add(keras.layers.InputLayer(input_shape=input_shape)) model.add(keras.layers.convolutional.Conv2D(filters, filtersize, strides=(1, 1), padding='valid', data_format="channels_last", activation='relu')) model.add(keras.layers.MaxPooling2D(pool_size=(2, 2))) model.add(keras.layers.Flatten()) model.add(keras.layers.Dense(units=2, input_dim=50,activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) model.fit(images, label, epochs=epochs, batch_size=batchsize,validation_split=0.3) model.summary()

在這一模型中,我只使用了單一卷積和池化層,可訓練參數是 219,801。很好奇若是我在這種狀況使用了 MLP 會有多少參數。經過增長更多的卷積和池化層,你能夠進一步下降參數的數量。咱們添加的卷積層越多,被提取的特徵就會更具體和複雜。

在該模型中,我只使用了一個卷積層和池化層,可訓練參數量爲 219,801。若是想知道使用 MLP 在這種狀況下會獲得多少,你能夠經過加入更多卷積和池化層來減小參數的數量。越多的卷積層意味着提取出來的特徵更加具體,更加複雜。

結語

但願本文可以讓你認識卷積神經網絡,這篇文章沒有深刻 CNN 的複雜數學原理。若是但願增進了解,你能夠嘗試構建本身的卷積神經網絡,藉此來了解它運行和預測的原理。

參考文章

[1]CS231n課程筆記翻譯:卷積神經網絡筆記

[2]卷積神經網絡反向傳播理論推導

[3]機器學習方法篇(8)------卷積神經網絡公式推導

[4]卷積神經網絡(CNN)反向傳播算法

相關文章
相關標籤/搜索