神經網絡:卷積神經網絡


一、前言

這篇卷積神經網絡是前面介紹的多層神經網絡的進一步深入,它將深度學習的思想引入到了神經網絡當中,通過卷積運算來由淺入深的提取圖像的不同層次的特征,而利用神經網絡的訓練過程讓整個網絡自動調節卷積核的參數,從而無監督的產生了最適合的分類特征。這個概括可能有點抽象,我盡量在下面描述細致一些,但如果要更深入了解整個過程的原理,需要去了解DeepLearning。

這篇文章會涉及到卷積的原理與圖像特征提取的一般概念,並詳細描述卷積神經網絡的實現。但是由於精力有限,沒有對人類視覺的分層以及機器學習等原理有進一步介紹,后面會在深度學習相關文章中展開描述。

二、卷積

卷積是分析數學中一種很重要的運算,其實是一個很簡單的概念,但是很多做圖像處理的人對這個概念都解釋不清,為了簡單起見,這里面我們只介紹離散形式的卷積,那么在圖像上,對圖像用一個卷積核進行卷積運算,實際上是一個濾波的過程。我們先看一下卷積的基本數學表示:

$$f(x,y)\circ w(x,y)=\sum_{s=-a}^a \sum_{t=-b}^b w(s,t)f(x-s,y-t)$$

其中$I=f(x,y)$是一個圖像,$f(x,y)$是圖像I上面$x$行$y$列上點的灰度值。而$w(x,y)$有太多名字叫了,濾波器、卷積核、響應函數等等,而$a$和$b$定義了卷積核即$w(x,y)$的大小。

從上面的式子中,可以很明顯的看到,卷積實際上是提供了一個權重模板,這個模板在圖像上滑動,並將中心依次與圖像中每一個像素對齊,然后對這個模板覆蓋的所有像素進行加權,並將結果作為這個卷積核在圖像上該點的響應。所以從整個卷積運算我們可以看到以下幾點:

1)卷積是一種線性運算

2)卷積核的大小,定義了圖像中任何一點參與運算的鄰域的大小。

3)卷積核上的權值大小說明了對應的鄰域點對最后結果的貢獻能力,權重越大,貢獻能力越大。

4)卷積核沿着圖像所有像素移動並計算響應,會得到一個和原圖像等大圖像。

5)在處理邊緣上點時,卷積核會覆蓋到圖像外層沒有定義的點,這時候有幾種方法設定這些沒有定義的點,可以用內層像素鏡像復制,也可以全設置為0。

image

三、卷積特征層

其實圖像特征提取在博客里的一些其他文章都有提過,這里我想說一下圖像特征提取與卷積的關系。其實大部分的圖像特征提取都依賴於卷積運算,比如顯著的邊緣特征就是用各種梯度卷積算子對圖像進行濾波的結果。一個圖像里目標特征主要體現在像素與周圍像素之間形成的關系,這些鄰域像素關系形成了線條、角點、輪廓等。而卷積運算正是這種用鄰域點按一定權重去重新定義該點值的運算。

水平梯度的卷積算子:

image

豎直梯度的卷積算子:

image

根據深度學習關於人的視覺分層的理論,人的視覺對目標的辨識是分層的,低層會提取一些邊緣特征,然后高一些層次進行形狀或目標的認知,更高層的會分析一些運動和行為。也就是說高層的特征是低層特征的組合,從低層到高層的特征表示越來越抽象,越來越能表現語義或者意圖。而抽象層面越高,存在的可能猜測就越少,就越利於分類。

而深度學習就是通過這種分層的自動特征提取來達到目標分類,先構建一些基本的特征層,然后用這些基礎特征去構建更高層的抽象,更精准的分類特征。

那整個卷積神經網絡的結構也就很容易理解了,它在普通的多層神經網絡的前面加了2層特征層,這兩層特征層是通過權重可調整的卷積運算實現的。

四、卷積神經網絡

在原來的多層神經網絡結構中,每一層的所有結點會按照連續線的權重向前計算,成為下一層結點的輸出。而每一條權重連結線都彼此不同,互不共享。每一個下一層結點的值與前一層所有結點都相關。

與普通多層神經網絡不同的是,卷積神經網絡里,有特征抽取層與降維層,這些層的結點連結是部分連接且,一幅特征圖由一個卷積核生成,這一幅特征圖上的所有結點共享這一組卷積核的參數。

這里我們設計一個5層的卷積神經網絡,一個輸入層,一個輸出層,2個特征提取層,一個全連接的隱藏層。下面詳細說明每一層的設計思路。

image

在一般的介紹卷積神經網絡的文章中你可能會看到在特征層之間還有2層降維層,在這里我們將卷積與降維同步進行,只用在卷積運算時,遍歷圖像中像素時按步長間隔遍歷即可。

輸入層:普通的多層神經網絡,第一層就是特征向量。一般圖像經過人為的特征挑選,通過特征函數計算得到特征向量,並作為神經網絡的輸入。而卷積神經網絡的輸出層則為整個圖像,如上圖示例29*29,那么我們可以將圖像按列展開,形成841個結點。而第一層的結點向前沒有任何的連結線。

第1層:第1層是特征層,它是由6個卷積模板與第一層的圖像做卷積形成的6幅13*13的圖像。也就是說這一層中我們算上一個偏置權重,一共有只有(5*5+1)*6=156權重參數,但是,第二層的結點是將6張圖像按列展開,即有6*13*13=1014個結點。

而第2層構建的真正難點在於,連結線的設定,當前層的結點並不是與前一層的所有結點相連,而是只與25鄰域點連接,所以第二層一定有(25+1)*13*13*6=26364條連線,但是這些連結線共享了156個權值。按前面多層網絡C++的設計中,每個連線對象有2個成員,一個是權重的索引,一個是上一層結點的索引,所以這里面要正確的設置好每個連線的索引值,這也是卷積神經網絡與一般全連結層的區別。

第2層:第2層也是特征層,它是由50個特征圖像組成,每個特征圖像是5*5的大小,這個特征圖像上的每一點都是由前一層6張特征圖像中每個圖像取25個鄰域點最后在一起加權而成,所以每個點也就是一個結點有(5*5+1)*6=156個連結線,那么當前層一共有5*5*50=1250個結點,所以一共有195000個權重連結線,但是只有(5*5+1)*6*50=7800個權重參數,每個權重連結線的值都可以在7800個權重參數數組中找到索引。

所以第3層的關鍵也是,如何建立好每根連結線的權重索引與與前一層連結的結點的索引。

第3層:和普通多神經網絡沒有區別了,是一個隱藏層,有100個結點構成,每個結點與上一層中1250個結點相聯,共有125100個權重與125100個連結線。

第4層:輸出層,它個結點個數與分類數目有關,假設這里我們設置為10類,則輸出層為10個結點,相應的期望值的設置在多層神經網絡里已經介紹過了,每個輸出結點與上面隱藏層的100個結點相連,共有(100+1)*10=1010條連結線,1010個權重。

從上面可以看出,卷積神經網絡的核心在於卷積層的創建,所以在設計CNNLayer類的時候,需要兩種創建網絡層的成員函數,一個用於創建普通的全連接層,一個用於創建卷積層。

 1 class CNNlayer
 2 {
 3 private:
 4     CNNlayer* preLayer;
 5     vector<CNNneural> m_neurals;
 6     vector<double> m_weights;
 7 public:
 8     CNNlayer(){ preLayer = nullptr; }
 9     // 創建卷積層
10     void createConvLayer(unsigned curNumberOfNeurals, unsigned preNumberOfNeurals, unsigned preNumberOfFeatMaps, unsigned curNumberOfFeatMaps);
11     // 創建 普通層
12     void createLayer(unsigned curNumberOfNeurals,unsigned preNumberOfNeurals);
13     void backPropagate(vector<double>& dErrWrtDxn, vector<double>& dErrWrtDxnm, double eta);
14 };

 

創建普通層,在前面介紹的多層神經網絡中已經給出過代碼,它接收兩個參數,一個是前面一層結點數,一個是當前層結點數。

而卷積層的創建則復雜的多,所有連結線的索引值的確定需要對整個網絡有較清楚的了解。這里設計的createConvLayer函數,它接收4個參數,分別對應,當前層結點數,前一層結點數,前一層特征圖的個數和當前層特征圖像的個數。

下面是C++代碼,要理解這一部分可以會稍有點難度,因為特征圖實際中都被按列展開了,所以鄰域這個概念會比較抽象,我們考慮把特征圖像還原,從圖像的角度去考慮。

 1 void CNNlayer::createConvLayer
 2 (unsigned curNumberOfNeurals, unsigned preNumberOfNeurals, unsigned preNumberOfFeatMaps, unsigned curNumberOfFeatMaps)
 3 {
 4     // 前一層和當前層特征圖的結點數
 5     unsigned preImgSize = preNumberOfNeurals / preNumberOfFeatMaps;
 6     unsigned curImgSize = curNumberOfNeurals / curNumberOfFeatMaps;
 7 
 8     // 初始化權重
 9     unsigned numberOfWeights = preNumberOfFeatMaps*curNumberOfFeatMaps*(5 * 5 + 1);
10     for (unsigned i = 0; i != numberOfWeights; i++)
11     {
12         m_weights.push_back(0.05*rand() / RAND_MAX);
13     }
14     // 建立所有連結線
15 
16     for (unsigned i = 0; i != curNumberOfFeatMaps; i++)
17     {
18         unsigned imgRow = sqrt(preImgSize);  //上一層特征圖像的大小 ,imgRow=imgCol
19         //  間隙2進行取樣,鄰域周圍25個點
20         for (int c = 2; c < imgRow-2; c= c+ 2)
21         {
22             for (int r = 2; r < imgRow-2; r =r + 2)
23             {
24                 CNNneural neural;
25                 for (unsigned k = 0; k != preNumberOfNeurals; k++)
26                 {
27                     for (int kk = 0; kk < (5*5+1); kk ++)
28                     {
29                         CNNconnection connection;
30                         // 權重的索引
31                         connection.weightIdx = i*(curNumberOfFeatMaps*(5 * 5 + 1)) + k*(5 * 5 + 1) + kk;
32                         // 結點的索引
33                         connection.neuralIdx = k*preImgSize + c*imgRow + r;
34                         neural.m_connections.push_back(connection);
35                     }
36                     m_neurals.push_back(neural);
37                 }
38             }
39         }
40     }
41 }

 

五、訓練與識別

整個網絡結構搭建好以后,訓練只用按照多層神經網絡那樣訓練即可,其中的權值更新策略都是一致的。所以總體來說,卷積神經網絡與普通的多層神經網絡,就是結構上不同。卷積神經網絡多了特征提取層與降維層,他們之間結點的連結方式是部分連結,多個連結線共享權重。而多層神經網絡前后兩層之間結點是全連結。除了這以外,權值更新、訓練、識別都是一致的。

訓練得到一組權值,也就是說在這組權值下網絡可以更好的提取圖像特征用於分類識別。

關於源碼的問題:個人非常不推薦直接用別人的源碼,所以我的博客里所有文章不會給出整個工程的源碼,但是會給出一些核心函數的代碼,如果你仔細閱讀文章,一定能夠很好的理解算法的核心思想。嘗試着去自己實現,會對你的理解更有幫助。有什么疑問可以直接在下面留言。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM