近日,Dishashree Gupta 在 Analyticsvidhya 上發(fā)表了一篇題為《Architecture of Convolutional Neural Networks (CNNs) demystified》的文章,對(duì)用于圖像識(shí)別和分類的卷積神經(jīng)網(wǎng)絡(luò)架構(gòu)作了深度揭秘;作者在文中還作了通盤演示,期望對(duì) CNN 的工作機(jī)制有一個(gè)深入的剖析。
?
引言
先坦白地說,有一段時(shí)間我無法真正理解深度學(xué)習(xí)。我查看相關(guān)研究論文和文章,感覺深度學(xué)習(xí)異常復(fù)雜。我嘗試去理解神經(jīng)網(wǎng)絡(luò)及其變體,但依然感到困難。
接著有一天,我決定一步一步,從基礎(chǔ)開始。我把技術(shù)操作的步驟分解開來,并手動(dòng)執(zhí)行這些步驟(和計(jì)算),直到我理解它們?nèi)绾喂ぷ?。這相當(dāng)費(fèi)時(shí),且令人緊張,但是結(jié)果非凡。
現(xiàn)在,我不僅對(duì)深度學(xué)習(xí)有了全面的理解,還在此基礎(chǔ)上有了好想法,因?yàn)槲业幕A(chǔ)很扎實(shí)。隨意地應(yīng)用神經(jīng)網(wǎng)絡(luò)是一回事,理解它是什么以及背后的發(fā)生機(jī)制是另外一回事。
今天,我將與你共享我的心得,展示我如何上手卷積神經(jīng)網(wǎng)絡(luò)并最終弄明白了它。我將做一個(gè)通盤的展示,從而使你對(duì) CNN 的工作機(jī)制有一個(gè)深入的了解。
在本文中,我將會(huì)討論 CNN 背后的架構(gòu),其設(shè)計(jì)初衷在于解決圖像識(shí)別和分類問題。同時(shí)我也會(huì)假設(shè)你對(duì)神經(jīng)網(wǎng)絡(luò)已經(jīng)有了初步了解。
1. 機(jī)器如何看圖?
人類大腦是一非常強(qiáng)大的機(jī)器,每秒內(nèi)能看(捕捉)多張圖,并在意識(shí)不到的情況下就完成了對(duì)這些圖的處理。但機(jī)器并非如此。機(jī)器處理圖像的第一步是理解,理解如何表達(dá)一張圖像,進(jìn)而讀取圖片。
簡(jiǎn)單來說,每個(gè)圖像都是一系列特定排序的圖點(diǎn)(像素)。如果你改變像素的順序或顏色,圖像也隨之改變。舉個(gè)例子,存儲(chǔ)并讀取一張上面寫著數(shù)字 4 的圖像。
基本上,機(jī)器會(huì)把圖像打碎成像素矩陣,存儲(chǔ)每個(gè)表示位置像素的顏色碼。在下圖的表示中,數(shù)值 1 是白色,256 是最深的綠色(為了簡(jiǎn)化,我們示例限制到了一種顏色)。
一旦你以這種格式存儲(chǔ)完圖像信息,下一步就是讓神經(jīng)網(wǎng)絡(luò)理解這種排序與模式。
2. 如何幫助神經(jīng)網(wǎng)絡(luò)識(shí)別圖像?
表征像素的數(shù)值是以特定的方式排序的。
假設(shè)我們嘗試使用全連接網(wǎng)絡(luò)識(shí)別圖像,該如何做?
全連接網(wǎng)絡(luò)可以通過平化它,把圖像當(dāng)作一個(gè)數(shù)組,并把像素值當(dāng)作預(yù)測(cè)圖像中數(shù)值的特征。明確地說,讓網(wǎng)絡(luò)理解理解下面圖中發(fā)生了什么,非常的艱難。
即使人類也很難理解上圖中表達(dá)的含義是數(shù)字 4。我們完全丟失了像素的空間排列。
我們能做什么呢?可以嘗試從原圖像中提取特征,從而保留空間排列。
案例 1
這里我們使用一個(gè)權(quán)重乘以初始像素值。
現(xiàn)在裸眼識(shí)別出這是「4」就變得更簡(jiǎn)單了。但把它交給全連接網(wǎng)絡(luò)之前,還需要平整化(flatten) 它,要讓我們能夠保留圖像的空間排列。
案例 2
現(xiàn)在我們可以看到,把圖像平整化完全破壞了它的排列。我們需要想出一種方式在沒有平整化的情況下把圖片饋送給網(wǎng)絡(luò),并且還要保留空間排列特征,也就是需要饋送像素值的 2D/3D 排列。
我們可以嘗試一次采用圖像的兩個(gè)像素值,而非一個(gè)。這能給網(wǎng)絡(luò)很好的洞見,觀察鄰近像素的特征。既然一次采用兩個(gè)像素,那也就需要一次采用兩個(gè)權(quán)重值了。
希望你能注意到圖像從之前的 4 列數(shù)值變成了 3 列。因?yàn)槲覀儸F(xiàn)在一次移用兩個(gè)像素(在每次移動(dòng)中像素被共享),圖像變的更小了。雖然圖像變小了,我們?nèi)阅茉诤艽蟪潭壬侠斫膺@是「4」。而且,要意識(shí)到的一個(gè)重點(diǎn)是,我們采用的是兩個(gè)連貫的水平像素,因此只會(huì)考慮水平的排列。
這是我們從圖像中提取特征的一種方式。我們可以看到左邊和中間部分,但右邊部分看起來不那么清楚。主要是因?yàn)閮蓚€(gè)問題:
1. 圖片角落左邊和右邊是權(quán)重相乘一次得到的。
2. 左邊仍舊保留,因?yàn)闄?quán)重值高;右邊因?yàn)槁缘偷臋?quán)重,有些丟失。
現(xiàn)在我們有兩個(gè)問題,需要兩個(gè)解決方案。
案例 3
遇到的問題是圖像左右兩角只被權(quán)重通過一次。我們需要做的是讓網(wǎng)絡(luò)像考慮其他像素一樣考慮角落。我們有一個(gè)簡(jiǎn)單的方法解決這一問題:把零放在權(quán)重運(yùn)動(dòng)的兩邊。
你可以看到通過添加零,來自角落的信息被再訓(xùn)練。圖像也變得更大。這可被用于我們不想要縮小圖像的情況下。
案例 4
這里我們?cè)噲D解決的問題是右側(cè)角落更小的權(quán)重值正在降低像素值,因此使其難以被我們識(shí)別。我們所能做的是采取多個(gè)權(quán)重值并將其結(jié)合起來。
(1,0.3) 的權(quán)重值給了我們一個(gè)輸出表格
同時(shí)表格 (0.1,5) 的權(quán)重值也將給我們一個(gè)輸出表格。
兩張圖像的結(jié)合版本將會(huì)給我們一個(gè)清晰的圖片。因此,我們所做的是簡(jiǎn)單地使用多個(gè)權(quán)重而不是一個(gè),從而再訓(xùn)練圖像的更多信息。最終結(jié)果將是上述兩張圖像的一個(gè)結(jié)合版本。
案例 5
我們到現(xiàn)在通過使用權(quán)重,試圖把水平像素(horizontal pixel)結(jié)合起來。但是大多數(shù)情況下我們需要在水平和垂直方向上保持空間布局。我們采取 2D 矩陣權(quán)重,把像素在水平和垂直方向上結(jié)合起來。同樣,記住已經(jīng)有了水平和垂直方向的權(quán)重運(yùn)動(dòng),輸出會(huì)在水平和垂直方向上低一個(gè)像素。
特別感謝 Jeremy Howard 啟發(fā)我創(chuàng)作了這些圖像。
因此我們做了什么?
上面我們所做的事是試圖通過使用圖像的空間的安排從圖像中提取特征。為了理解圖像,理解像素如何安排對(duì)于一個(gè)網(wǎng)絡(luò)極其重要。上面我們所做的也恰恰是一個(gè)卷積網(wǎng)絡(luò)所做的。我們可以采用輸入圖像,定義權(quán)重矩陣,并且輸入被卷積以從圖像中提取特殊特征而無需損失其有關(guān)空間安排的信息。
這個(gè)方法的另一個(gè)重大好處是它可以減少圖像的參數(shù)數(shù)量。正如所見,卷積圖像相比于原始圖像有更少的像素。
3.定義一個(gè)卷積神經(jīng)網(wǎng)絡(luò)
我們需要三個(gè)基本的元素來定義一個(gè)基本的卷積網(wǎng)絡(luò)
1. 卷積層
2. 池化層(可選)
3. 輸出層
卷積層
在這一層中,實(shí)際所發(fā)生的就像我們?cè)谏鲜霭咐?5 中見到的一樣。假設(shè)我們有一個(gè) 6*6 的圖像。我們定義一個(gè)權(quán)值矩陣,用來從圖像中提取一定的特征。
我們把權(quán)值初始化成一個(gè) 3*3 的矩陣。這個(gè)權(quán)值現(xiàn)在應(yīng)該與圖像結(jié)合,所有的像素都被覆蓋至少一次,從而來產(chǎn)生一個(gè)卷積化的輸出。上述的 429,是通過計(jì)算權(quán)值矩陣和輸入圖像的 3*3 高亮部分以元素方式進(jìn)行的乘積的值而得到的。
現(xiàn)在 6*6 的圖像轉(zhuǎn)換成了 4*4 的圖像。想象一下權(quán)值矩陣就像用來刷墻的刷子。首先在水平方向上用這個(gè)刷子進(jìn)行刷墻,然后再向下移,對(duì)下一行進(jìn)行水平粉刷。當(dāng)權(quán)值矩陣沿著圖像移動(dòng)的時(shí)候,像素值再一次被使用。實(shí)際上,這樣可以使參數(shù)在卷積神經(jīng)網(wǎng)絡(luò)中被共享。
下面我們以一個(gè)真實(shí)圖像為例。
權(quán)值矩陣在圖像里表現(xiàn)的像一個(gè)從原始圖像矩陣中提取特定信息的過濾器。一個(gè)權(quán)值組合可能用來提取邊緣(edge)信息,另一個(gè)可能是用來提取一個(gè)特定顏色,下一個(gè)就可能就是對(duì)不需要的噪點(diǎn)進(jìn)行模糊化。
先對(duì)權(quán)值進(jìn)行學(xué)習(xí),然后損失函數(shù)可以被最小化,類似于多層感知機(jī)(MLP)。因此需要通過對(duì)參數(shù)進(jìn)行學(xué)習(xí)來從原始圖像中提取信息,從而來幫助網(wǎng)絡(luò)進(jìn)行正確的預(yù)測(cè)。當(dāng)我們有多個(gè)卷積層的時(shí)候,初始層往往提取較多的一般特征,隨著網(wǎng)絡(luò)結(jié)構(gòu)變得更深,權(quán)值矩陣提取的特征越來越復(fù)雜,并且越來越適用于眼前的問題。
步長(zhǎng)(stride)和邊界(padding)的概念
像我們?cè)谏厦婵吹降囊粯樱^濾器或者說權(quán)值矩陣,在整個(gè)圖像范圍內(nèi)一次移動(dòng)一個(gè)像素。我們可以把它定義成一個(gè)超參數(shù)(hyperparameter),從而來表示我們想讓權(quán)值矩陣在圖像內(nèi)如何移動(dòng)。如果權(quán)值矩陣一次移動(dòng)一個(gè)像素,我們稱其步長(zhǎng)為 1。下面我們看一下步長(zhǎng)為 2 時(shí)的情況。
你可以看見當(dāng)我們?cè)黾硬介L(zhǎng)值的時(shí)候,圖像的規(guī)格持續(xù)變小。在輸入圖像四周填充 0 邊界可以解決這個(gè)問題。我們也可以在高步長(zhǎng)值的情況下在圖像四周填加不只一層的 0 邊界。
我們可以看見在我們給圖像填加一層 0 邊界后,圖像的原始形狀是如何被保持的。由于輸出圖像和輸入圖像是大小相同的,所以這被稱為 same padding。
這就是 same padding(意味著我們僅考慮輸入圖像的有效像素)。中間的 4*4 像素是相同的。這里我們已經(jīng)利用邊界保留了更多信息,并且也已經(jīng)保留了圖像的原大小。
多過濾與激活圖
需要記住的是權(quán)值的縱深維度(depth dimension)和輸入圖像的縱深維度是相同的。權(quán)值會(huì)延伸到輸入圖像的整個(gè)深度。因此,和一個(gè)單一權(quán)值矩陣進(jìn)行卷積會(huì)產(chǎn)生一個(gè)單一縱深維度的卷積化輸出。大多數(shù)情況下都不使用單一過濾器(權(quán)值矩陣),而是應(yīng)用維度相同的多個(gè)過濾器。
每一個(gè)過濾器的輸出被堆疊在一起,形成卷積圖像的縱深維度。假設(shè)我們有一個(gè) 32*32*3 的輸入。我們使用 5*5*3,帶有 valid padding 的 10 個(gè)過濾器。輸出的維度將會(huì)是 28*28*10。
如下圖所示:
激活圖是卷積層的輸出。
池化層
有時(shí)圖像太大,我們需要減少訓(xùn)練參數(shù)的數(shù)量,它被要求在隨后的卷積層之間周期性地引進(jìn)池化層。池化的唯一目的是減少圖像的空間大小。池化在每一個(gè)縱深維度上獨(dú)自完成,因此圖像的縱深保持不變。池化層的最常見形式是最大池化。
在這里,我們把步幅定為 2,池化尺寸也為 2。最大化執(zhí)行也應(yīng)用在每個(gè)卷機(jī)輸出的深度尺寸中。正如你所看到的,最大池化操作后,4*4 卷積的輸出變成了 2*2。
讓我們看看最大池化在真實(shí)圖片中的效果如何。
正如你看到的,我們卷積了圖像,并最大池化了它。最大池化圖像仍然保留了汽車在街上的信息。如果你仔細(xì)觀察的話,你會(huì)發(fā)現(xiàn)圖像的尺寸已經(jīng)減半。這可以很大程度上減少參數(shù)。
同樣,其他形式的池化也可以在系統(tǒng)中應(yīng)用,如平均池化和 L2 規(guī)范池化。
輸出維度
理解每個(gè)卷積層輸入和輸出的尺寸可能會(huì)有點(diǎn)難度。以下三點(diǎn)或許可以讓你了解輸出尺寸的問題。有三個(gè)超參數(shù)可以控制輸出卷的大小。
1. 過濾器數(shù)量-輸出卷的深度與過濾器的數(shù)量成正比。請(qǐng)記住該如何堆疊每個(gè)過濾器的輸出以形成激活映射。激活圖的深度等于過濾器的數(shù)量。
2. 步幅(Stride)-如果步幅是 1,那么我們處理圖片的精細(xì)度就進(jìn)入單像素級(jí)別了。更高的步幅意味著同時(shí)處理更多的像素,從而產(chǎn)生較小的輸出量。
3. 零填充(zero padding)-這有助于我們保留輸入圖像的尺寸。如果添加了單零填充,則單步幅過濾器的運(yùn)動(dòng)會(huì)保持在原圖尺寸。
我們可以應(yīng)用一個(gè)簡(jiǎn)單的公式來計(jì)算輸出尺寸。輸出圖像的空間尺寸可以計(jì)算為([W-F + 2P] / S)+1。在這里,W 是輸入尺寸,F(xiàn) 是過濾器的尺寸,P 是填充數(shù)量,S 是步幅數(shù)字。假如我們有一張 32*32*3 的輸入圖像,我們使用 10 個(gè)尺寸為 3*3*3 的過濾器,單步幅和零填充。
那么 W=32,F(xiàn)=3,P=0,S=1。輸出深度等于應(yīng)用的濾波器的數(shù)量,即 10,輸出尺寸大小為 ([32-3+0]/1)+1 = 30。因此輸出尺寸是 30*30*10。
輸出層
在多層卷積和填充后,我們需要以類的形式輸出。卷積和池化層只會(huì)提取特征,并減少原始圖像帶來的參數(shù)。然而,為了生成最終的輸出,我們需要應(yīng)用全連接層來生成一個(gè)等于我們需要的類的數(shù)量的輸出。僅僅依靠卷積層是難以達(dá)到這個(gè)要求的。卷積層可以生成 3D 激活圖,而我們只需要圖像是否屬于一個(gè)特定的類這樣的內(nèi)容。輸出層具有類似分類交叉熵的損失函數(shù),用于計(jì)算預(yù)測(cè)誤差。一旦前向傳播完成,反向傳播就會(huì)開始更新權(quán)重與偏差,以減少誤差和損失。
4. 小結(jié)
正如你所看到的,CNN 由不同的卷積層和池化層組成。讓我們看看整個(gè)網(wǎng)絡(luò)是什么樣子:
我們將輸入圖像傳遞到第一個(gè)卷積層中,卷積后以激活圖形式輸出。圖片在卷積層中過濾后的特征會(huì)被輸出,并傳遞下去。
每個(gè)過濾器都會(huì)給出不同的特征,以幫助進(jìn)行正確的類預(yù)測(cè)。因?yàn)槲覀冃枰WC圖像大小的一致,所以我們使用同樣的填充(零填充),否則填充會(huì)被使用,因?yàn)樗梢詭椭鷾p少特征的數(shù)量。
隨后加入池化層進(jìn)一步減少參數(shù)的數(shù)量。
在預(yù)測(cè)最終提出前,數(shù)據(jù)會(huì)經(jīng)過多個(gè)卷積和池化層的處理。卷積層會(huì)幫助提取特征,越深的卷積神經(jīng)網(wǎng)絡(luò)會(huì)提取越具體的特征,越淺的網(wǎng)絡(luò)提取越淺顯的特征。
如前所述,CNN 中的輸出層是全連接層,其中來自其他層的輸入在這里被平化和發(fā)送,以便將輸出轉(zhuǎn)換為網(wǎng)絡(luò)所需的參數(shù)。
隨后輸出層會(huì)產(chǎn)生輸出,這些信息會(huì)互相比較排除錯(cuò)誤。損失函數(shù)是全連接輸出層計(jì)算的均方根損失。隨后我們會(huì)計(jì)算梯度錯(cuò)誤。
錯(cuò)誤會(huì)進(jìn)行反向傳播,以不斷改進(jìn)過濾器(權(quán)重)和偏差值。
一個(gè)訓(xùn)練周期由單次正向和反向傳遞完成。
5. 在 KERAS 中使用 CNN 對(duì)圖像進(jìn)行分類
讓我們嘗試一下,輸入貓和狗的圖片,讓計(jì)算機(jī)識(shí)別它們。這是圖像識(shí)別和分類的經(jīng)典問題,機(jī)器在這里需要做的是看到圖像,并理解貓與狗的不同外形特征。這些特征可以是外形輪廓,也可以是貓的胡須之類,卷積層會(huì)攫取這些特征。讓我們把數(shù)據(jù)集拿來試驗(yàn)一下吧。
以下這些圖片均來自數(shù)據(jù)集。
我們首先需要調(diào)整這些圖像的大小,讓它們形狀相同。這是處理圖像之前通常需要做的,因?yàn)樵谂恼諘r(shí),讓照下的圖像都大小相同幾乎不可能。
為了簡(jiǎn)化理解,我們?cè)谶@里只用一個(gè)卷積層和一個(gè)池化層。注意:在 CNN 的應(yīng)用階段,這種簡(jiǎn)單的情況是不會(huì)發(fā)生的。
#import various packagesimport osimport numpy as npimport pandas as pdimport scipyimport sklearnimport 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()
在這一模型中,我只使用了單一卷積和池化層,可訓(xùn)練參數(shù)是 219,801。很好奇如果我在這種情況使用了 MLP 會(huì)有多少參數(shù)。通過增加更多的卷積和池化層,你可以進(jìn)一步降低參數(shù)的數(shù)量。我們添加的卷積層越多,被提取的特征就會(huì)更具體和復(fù)雜。
在該模型中,我只使用了一個(gè)卷積層和池化層,可訓(xùn)練參數(shù)量為 219,801。如果想知道使用 MLP 在這種情況下會(huì)得到多少,你可以通過加入更多卷積和池化層來減少參數(shù)的數(shù)量。越多的卷積層意味著提取出來的特征更加具體,更加復(fù)雜。
結(jié)語
希望本文能夠讓你認(rèn)識(shí)卷積神經(jīng)網(wǎng)絡(luò),這篇文章沒有深入 CNN 的復(fù)雜數(shù)學(xué)原理。如果希望增進(jìn)了解,你可以嘗試構(gòu)建自己的卷積神經(jīng)網(wǎng)絡(luò),借此來了解它運(yùn)行和預(yù)測(cè)的原理。
評(píng)論
查看更多