李理:詳解卷積神經網絡

2021-02-13 CSDN

本系列文章面向深度學習研發者,希望通過Image Caption Generation,一個有意思的具體任務,深入淺出地介紹深度學習的知識。本系列文章涉及到很多深度學習流行的模型,如CNN,RNN/LSTM,Attention等。本文為第6篇。文中所有標藍部分均可閱讀原文獲取詳情連結。

接下來介紹一種非常重要的神經網絡——卷積神經網絡。這種神經網絡在計算機視覺領域取得了重大的成功,而且在自然語言處理等其它領域也有很好的應用。深度學習受到大家的關注很大一個原因就是Alex等人實現的AlexNet(一種深度卷積神經網絡)在LSVRC-2010 ImageNet這個比賽中取得了非常好的成績。此後,卷積神經網絡及其變種被廣泛應用於各種圖像相關任務。

這裡主要參考了Neural Networks and Deep Learning和cs231n的課程來介紹CNN,兩部分都會有理論和代碼。前者會用theano來實現,而後者會使用我們前一部分介紹的自動梯度來實現。下面首先介紹Michael Nielsen的部分(其實主要是翻譯,然後加一些我自己的理解)。

如果讀者自己嘗試了上一部分的代碼,調過3層和5層全連接的神經網絡的參數,我們會發現神經網絡的層數越多,參數(超參數)就越難調。但是如果參數調得好,深的網絡的效果確實比較淺的好(這也是為什麼我們要搞深度學習的原因)。所以深度學習有這樣的說法:「三個 bound 不如一個 heuristic,三個 heuristic 不如一個trick」。以前搞機器學習就是feature engineering加調參,現在就剩下調參了。網絡的結構,參數的初始化,learning_rate,迭代次數等等都會影響最終的結果。有興趣的同學可以看看Michael Nielsen這個電子書的相應章節,cs231n的Github資源也有介紹,另外《Neural Networks: Tricks of the Trade》這本書,看名字就知道講啥的了吧。

不過我們還是回到正題「卷積神經網絡」吧。

在之前的章節我們使用了神經網絡來解決手寫數字識別(MNIST)的問題。我們使用了全連接的神經網絡,也就是前一層的每一個神經元都會連接到後一層的每一個神經元,如果前一層有m個節點,後一層有n個,那麼總共有m*n條邊(連接)。連接方式如下圖所示:

具體來講,對於輸入圖片的每一個像素,我們把它的灰度值作為對應神經元的輸入。對於28×28的圖像來說,我們的網絡有784個輸入神經元。然後我們訓練這個網絡的weights和biases來使得它可以正確的預測對應的數字。

我們之前設計的神經網絡工作的很好:在MNIST手寫識別數據集上我們得到了超過98%的準確率。但是仔細想一想的話,使用全連接的網絡來識別圖像有一些奇怪。因為這樣的網絡結構沒有考慮圖像的空間結構。比如,它對於空間上很近或者很遠的像素一樣的對待。這些空間的概念【比如7字會出現某些像素在某個水平方向同時灰度值差不多,也就是上面的那一橫】必須靠網絡從訓練數據中推測出來【但是如果訓練數據不夠而且圖像沒有做居中等歸一化的話,如果訓練數據的7的一橫都出現在圖像靠左的地方,而測試數據把7寫到右下角,那麼網絡很可能學不到這樣的特徵】。那為什麼我們不能設計一直網絡結構考慮這些空間結構呢?這樣的想法就是下面我們要討論的CNN的思想。

這種神經網絡利用了空間結構,因此非常適合用來做圖片分類。這種結構訓練也非常的快,因此也可以訓練更「深」的網絡。目前,圖像識別大都使用深層的卷積神經網絡及其變種。

卷積神經網絡有3個基本的idea:局部感知域(Local Recpetive Field),權值共享和池化(Pooling)。下面我們來一個一個的介紹它們。

在前面圖示的全連接的層裡,輸入是被描述成一列神經元。而在卷積網絡裡,我們把輸入看成28×28方格的二維神經元,它的每一個神經元對應於圖片在這個像素點的強度(灰度值),如下圖所示:

和往常一樣,我們把輸入像素連接到隱藏層的神經元。但是我們這裡不再把輸入的每一個像素都連接到隱藏層的每一個神經元。與之不同,我們把很小的相臨近的區域內的輸入連接在一起。

更加具體的來講,隱藏層的每一個神經元都會與輸入層一個很小的區域(比如一個5×5的區域,也就是25個像素點)相連接。隱藏對於隱藏層的某一個神經元,連接如下圖所示:

輸入圖像的這個區域叫做那個隱藏層神經元的局部感知域。這是輸入像素的一個小窗口。每個連接都有一個可以學習的權重,此外還有一個bias。你可以把那個神經元想像成用來分析這個局部感知域的。

我們然後在整個輸入圖像上滑動這個局部感知域。對於每一個局部感知域,都有一個隱藏層的神經元與之對應。為了具體一點的展示,我們首先從最左上角的局部感知域開始:

然後我們向右滑動這個局部感知域:

以此類推,我們可以構建出第一個隱藏層。注意,如果我們的輸入是28×28,並且使用5×5的局部關注域,那麼隱藏層是24×24。因為我們只能向右和向下移動23個像素,再往下移動就會移出圖像的邊界了。【說明,後面我們會介紹padding和striding,從而讓圖像在經過這樣一次卷積處理後尺寸可以不變小】

這裡我們展示了一次向右/下移動一個像素。事實上,我們也可以使用一次移動不止一個像素【這個移動的值叫stride】。比如,我們可以一次向右/下移動兩個像素。在這篇文章裡,我們只使用stride為1來實驗,但是請讀者知道其他人可能會用不同的stride值。

之前提到過每一個隱藏層的神經元有一個5×5的權值。這24×24個隱藏層對應的權值是相同的。也就是說,對於隱藏層的第j,k個神經元,輸出如下:


這裡,σ是激活函數,可以是我們之前提到的sigmoid函數。b是共享的bias,Wl,m 是5×5的共享權值。ax,y 是輸入在x,y的激活。

【從這個公式可以看出,權值是5×5的矩陣,不同的局部感知域使用這一個參數矩陣和bias】

這意味著這一個隱藏層的所有神經元都是檢測同一個特徵,只不過它們位於圖片的不同位置而已。比如這組weights和bias是某個局部感知域學到的用來識別一個垂直的邊。那麼預測的時候不管這條邊在哪個位置,它都會被某個對於的局部感知域檢測到。更抽象一點,卷積網絡能很好的適應圖片的位置變化:把圖片中的貓稍微移動一下位置,它仍然知道這是一隻貓。

因為這個原因,我們有時把輸入層到隱藏層的映射叫做特徵映射(feature map)。我們把定義特徵映射的權重叫做共享的權重(shared weights),bias叫做共享的bias(shared bais)。這組weights和bias定義了一個kernel或者filter。

上面描述的網絡結構只能檢測一種局部的特徵。為了識別圖片,我們需要更多的特徵映射。隱藏一個完整的卷積神經網絡會有很多不同的特徵映射:

在上面的例子裡,我們有3個特徵映射。每個映射由一個5×5的weights和一個biase確定。因此這個網絡能檢測3種特徵,不管這3個特徵出現在圖像的那個局部感知域裡。

為了簡化,上面之展示了3個特徵映射。在實際使用的卷積神經網絡中我們會使用非常多的特徵映射。早期的一個卷積神經網絡——LeNet-5,使用了6個特徵映射,每一個都是5×5的局部感知域,來識別MNIST數字。因此上面的例子和LeNet-5很接近。後面我們開發的卷積層將使用20和40個特徵映射。下面我們先看看模型學習到的一些特徵:

這20個圖片對應了20個不同的特徵映射。每個映射是一個5×5的圖像,對應於局部感知域的5×5個權重。顏色越白(淺)說明權值越小(一般都是負的),因此對應像素對於識別這個特徵越不重要。顏色越深(黑)說明權值越大,對應的像素越重要。

那麼我們可以從這些特徵映射裡得出什麼結論呢?很顯然這裡包含了非隨機的空間結構。這說明我們的網絡學到了一些空間結構。但是,也很難說它具體學到了哪些特徵。我們學到的不是一個 Gabor濾波器 的。事實上有很多研究工作嘗試理解機器到底學到了什麼樣的特徵。如果你感興趣,可以參考Matthew Zeiler 和 Rob Fergus在2013年的論文 Visualizing and Understanding Convolutional Networks。

共享權重和bias的一大好處是它極大的減少了網絡的參數數量。對於每一個特徵映射,我們只需要 25=5×5 個權重,再加一個bias。因此一個特徵映射只有26個參數。如果我們有20個特徵映射,那麼只有20×26=520個參數。如果我們使用全連接的神經網絡結構,假設隱藏層有30個神經元(這並不算很多),那麼就有784*30個權重參數,再加上30個bias,總共有23,550個參數。換句話說,全連接的網絡比卷積網絡的參數多了40倍。

當然,我們不能直接比較兩種網絡的參數,因為這兩種模型有本質的區別。但是,憑直覺,由於卷積網絡有平移不變的特性,為了達到相同的效果,它也可能使用更少的參數。由於參數變少,卷積網絡的訓練速度也更快,從而相同的計算資源我們可以訓練更深的網絡。

「卷積」神經網絡是因為公式(1)裡的運算叫做「卷積運算」。更加具體一點,我們可以把公式(1)裡的求和寫成卷積:$a^1 = \sigma(b + w * a^0)$。*在這裡不是乘法,而是卷積運算。這裡不會討論卷積的細節,所以讀者如果不懂也不要擔心,這裡只不過是為了解釋卷積神經網絡這個名字的由來。【建議感興趣的讀者參考colah的博客文章 《Understanding Convolutions》】

除了上面的卷積層,卷積神經網絡也包括池化層(pooling layers)。池化層一般都直接放在卷積層後面池化層的目的是簡化從卷積層輸出的信息。

更具體一點,一個池化層把卷積層的輸出作為其輸入並且輸出一個更緊湊(condensed)的特徵映射。比如,池化層的每一個神經元都提取了之前那個卷積層的一個2×2區域的信息。更為具體的一個例子,一種非常常見的池化操作叫做Max-pooling。在Max-Pooling中,這個神經元選擇2×2區域裡激活值最大的值,如下圖所示:

注意卷積層的輸出是24×24的,而池化後是12×12的。

就像上面提到的,卷積層通常會有多個特徵映射。我們會對每一個特徵映射進行max-pooling操作。因此,如果一個卷積層有3個特徵映射,那麼卷積加max-pooling後就如下圖所示:

我們可以把max-pooling看成神經網絡關心某個特徵在這個區域裡是否出現。它忽略了這個特徵出現的具體位置。直覺上看,如果某個特徵出現了,那麼這個特徵相對於其它特徵的精確位置是不重要的【精確位置不重要,但是大致的位置是重要的,比如識別一個貓,兩隻眼睛和鼻子有一個大致的相對位置關係,但是在一個2×2的小區域裡稍微移動一下眼睛,應該不太影響我們識別一隻貓,而且它還能解決圖像拍攝角度變化,扭曲等問題】。而且一個很大的好處是池化可以減少特徵的個數【2×2的max-pooling讓特徵的大小變為原來的1/4】,因此減少了之後層的參數個數。

Max-pooling不是唯一的池化方法。另外一種常見的是L2 Pooling。這種方法不是取2×2區域的最大值,而是2×2區域的每個值平方然後求和然後取平方根。雖然細節有所不同,但思路和max-pooling是類似的:L2 Pooling也是從卷積層壓縮信息的一種方法。在實踐中,兩種方法都被廣泛使用。有時人們也使用其它的池化方法。如果你真的想嘗試不同的方法來提供性能,那麼你可以使用validation數據來嘗試不同池化方法然後選擇最合適的方法。但是這裡我們不在討論這些細節。【Max-Pooling是用的最多的,甚至也有人認為Pooling並沒有什麼卵用。深度學習一個問題就是很多經驗的tricks由於沒有太多理論依據,只是因為最早的人用了,而且看起來效果不錯(但可能換一個數據集就不一定了),所以後面的人也跟著用。但是過了沒多久又被認為這個trick其實沒啥用】

現在我們可以把這3個idea放到一起來構建一個完整的卷積神經網絡了。它和之前我們看到的結構類似,不過增加了一個有10個神經元的輸出層,這個層的每個神經元對應於0-9直接的一個數字:

這個網絡的輸入的大小是28×28,每一個輸入對於MNIST圖像的一個像素。然後使用了3個特徵映射,局部感知域的大小是5×5。這樣得到3×24×24的輸出。然後使用對每一個特徵映射的輸出應用2×2的max-pooling,得到3×12×12的輸出。

最後一層是全連接的網絡,3×12×12個神經元會連接到輸出10個神經元中的每一個。這和之前介紹的全連接神經網絡是一樣的。

卷積結構和之前的全連接結構有很大的差別。但是整體的圖景是類似的:一個神經網絡有很多神經元,它們的行為有weights和biase確定。並且整體的目標也是類似的:使用訓練數據來訓練網絡的weights和biases使得網絡能夠儘量好的識別圖片。

和之前介紹的一樣,這裡我們仍然使用隨機梯度下降來訓練。不過反向傳播算法有所不同。原因是之前bp算法的推導是基於全連接的神經網絡。不過幸運的是求卷積和max-pooling的導數是非常簡單的。如果你想了解細節,請自己推導。【這篇文章不會介紹CNN的梯度求解,後面實現使用的是theano,後面介紹CS231N的CNN是會介紹怎麼自己來基於自動求導來求這個梯度,而且還會介紹高效的算法,感興趣的讀者請持續關注】

前面我們介紹了CNN的基本理論,但是沒有講怎麼求梯度。這裡的代碼是用theano來自動求梯度的。我們可以暫時把cnn看出一個黑盒,試試用它來識別MNIST的數字。後面的文章會介紹theano以及怎麼用theano實現CNN。

代碼

首先得到代碼: git clone

安裝theano

參考這裡 ;如果是ubuntu的系統,可以參考這裡 ;如果您的機器有gpu,請安裝好cuda以及讓theano支持gpu。

默認的network3.py的第52行是 GPU = True,如果您的機器沒有gpu,請把這一行改成GPU = False

baseline

首先我們實現一個baseline的系統,我們構建一個只有一個隱藏層的3層全連接網絡,隱藏層100個神經元。我們訓練時60個epoch,使用learning rate $\eta = 0.1$,batch大小是10,沒有正則化:

$cd src

$ipython

>>> import network3

>>> from network3 import Network

>>> from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer

>>> training_data, validation_data, test_data = network3.load_data_shared()

>>> mini_batch_size = 10

>>> net = Network([

        FullyConnectedLayer(n_in=784, n_out=100),

        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 

            validation_data, test_data)

得到的分類準確率是97.8%。這是在test_data上的準確率,這個模型使用訓練數據訓練,並根據validation_data來選擇當前最好的模型。使用validation數據來可以避免過擬合。讀者運行時可能結果會有一些差異,因為模型的參數是隨機初始化的。

改進版本1

我們首先在輸入的後面增加一個卷積層。我們使用5 5的局部感知域,stride等於1,20個特徵映射。然後接一個2 2的max-pooling層。之後接一個全連接的層,最後是softmax(仿射變換加softmax):

在這種網絡結構中,我們可以認為卷積和池化層可以學會輸入圖片的局部的空間特徵,而全連接的層整合全局的信息,學習出更抽象的特徵。這是卷積神經網絡的常見結構。

下面是代碼:

>>> net = Network([

        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 

                      filter_shape=(20, 1, 5, 5), 

                      poolsize=(2, 2)),

        FullyConnectedLayer(n_in=20*12*12, n_out=100),

        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 

            validation_data, test_data)   

【注意圖片的大小,開始是(mini_batch_size, 1, 28 ,28),經過一個20個5 5的卷積池層後變成了(mini_batch_size, 20, 24,24),然後在經過2 2的max-pooling後變成了(mini_batch_size, 20, 12, 12),然後接全連接層的時候可以理解成把所以的特徵映射展開,也就是20 12 12,所以FullyConnectedLayer的n_in是20 12 12】

這個模型得到98.78%的準確率,這相對之前的97.8%是一個很大的提高。事實上我們的錯誤率減少了1/3,這是一個很大的提高。【準確率很高的時候就看錯誤率的減少,這樣比較有成就感,哈哈】

如果要用gpu,可以把上面的命令保存到一個文件test.py,然後:

$THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python test.py 

在這個網絡結構中,我們吧卷積和池化層看出一個整體。這只是一種習慣。network3.py會把它們當成一個整體,每個卷積層後面都會跟一個池化層。但實際的一些卷積神經網絡並不都要接池化層。

改進版本2

我們再加入第二個卷積-池化層。這個卷積層插入在第一個卷積層和全連接層中間。我們使用同樣的5×5的局部感知域和2×2的max-pooling。代碼如下:

>>> net = Network([

        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 

                      filter_shape=(20, 1, 5, 5), 

                      poolsize=(2, 2)),

        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 

                      filter_shape=(40, 20, 5, 5), 

                      poolsize=(2, 2)),

        FullyConnectedLayer(n_in=40*4*4, n_out=100),

        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 

            validation_data, test_data) 

【注意圖片的大小,開始是(mini_batch_size, 1, 28 ,28),經過一個20個5 5的卷積池層後變成了(mini_batch_size, 20, 24,24),然後在經過2 2的max-pooling後變成了(mini_batch_size, 20, 12, 12)。然後是40個5*5的卷積層,變成了(mini_batch_size, 40, 8, 8),然後是max-pooling得到(mini_batch_size, 40, 4, 4)。然後是全連接的層】 

這個模型得到99.6%的準確率!

這裡有兩個很自然的問題。第一個是:加第二個卷積-池化層有什麼意義呢?事實上,你可以認為第二個卷積層的輸入是12*12的」圖片「,它的」像素「代表某個局部特徵。【比如你可以認為第一個卷積層識別眼睛鼻子,而第二個卷積層識別臉,不同生物的臉上面鼻子和眼睛的相對位置是有意義的】

這是個看起來不錯的解釋,那麼第二個問題來了:第一個卷積層的輸出是不同的20個不同的局部特徵,因此第二個卷積層的輸入是20 12 12。這就像我們輸入了20個不同的」圖片「,而不是一個」圖片「。那第二個卷積層的神經元學到的是什麼呢?【如果第一層的卷積網絡能識別」眼睛「,」鼻子「,」耳朵「。那麼第二層的」臉「就是2個眼睛,2個耳朵,1個鼻子,並且它們滿足一定的空間約束。所以第二層的每一個神經元需要連接第一層的每一個輸出,如果第二層只連接」眼睛「這個特徵映射,那麼只能學習出2個眼睛,3個眼睛這樣的特徵,那就沒有什麼用處了】

改進版本3

使用ReLU激活函數。ReLU的定義是:


>>> from network3 import ReLU

>>> net = Network([

        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 

                      filter_shape=(20, 1, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 

                      filter_shape=(40, 20, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),

        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

>>> net.SGD(training_data, 60, mini_batch_size, 0.03, 

            validation_data, test_data, lmbda=0.1)

使用ReLU後準確率從99.06%提高到99.23%。從作者的經驗來看,ReLU總是要比sigmoid激活函數要好。

但為什麼ReLU就比sigmoid或者tanh要好呢?目前並沒有很好的理論介紹。ReLU只是在最近幾年開始流行起來的。為什麼流行的原因是經驗:有一些人嘗試了ReLU,然後在他們的任務裡取得了比sigmoid好的結果,然後其他人也就跟風。理論上沒有人證明ReLU是更好的激活函數。【所以說深度學習有很多tricks,可能某幾年就流行起來了,但過幾年又有人認為這些tricks沒有意義。比如最早的pretraining,現在幾乎沒人用了。】

改進版本4

擴展數據。

深度學習非常依賴於數據。我們可以根據任務的特點」構造「新的數據。一種簡單的方法是把訓練數據裡的數字進行一下平移,旋轉等變換。雖然理論上卷積神經網絡能學到與位置無關的特徵,但如果訓練數據裡數字總是出現在固定的位置,實際的模型也不一定能學到。所以我們構造一些這樣的數據效果會更好。

$ python expand_mnist.py

expand_mnist.py這個腳本就會擴展數據。它只是簡單的把圖片向上下左右各移動了一個像素。擴展後訓練數據從50000個變成了250000個。

接下來我們用擴展後的數據來訓練模型:

>>> expanded_training_data, _, _ = network3.load_data_shared(

        "../data/mnist_expanded.pkl.gz")

>>> net = Network([

        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 

                      filter_shape=(20, 1, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 

                      filter_shape=(40, 20, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),

        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03, 

            validation_data, test_data, lmbda=0.1)

這個模型的準確率是99.37%。擴展數據看起來非常trival,但是卻極大的提高了識別準確率。

改進版本5

接下來還有改進的辦法嗎?我們的全連接層只有100個神經元,增加神經元有幫助嗎? 作者嘗試了300和1000個神經元的全連接層,得到了99.46%和99.43%的準確率。相對於99.37%並沒有本質的提高。

那再加一個全連接的層有幫助嗎?我們了嘗試一下:

>>> net = Network([

        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 

                      filter_shape=(20, 1, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 

                      filter_shape=(40, 20, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        FullyConnectedLayer(n_in=40*4*4, n_out=100, activation_fn=ReLU),

        FullyConnectedLayer(n_in=100, n_out=100, activation_fn=ReLU),

        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)

>>> net.SGD(expanded_training_data, 60, mini_batch_size, 0.03, 

            validation_data, test_data, lmbda=0.1)

在第一個全連接的層之後有加了一個100個神經元的全連接層。得到的準確率是99.43%,把這一層的神經元個數從100增加到300個和1000個得到的準確率是99.48 %和99.47%。有一些提高但是也不明顯。

為什麼增加更多層提高不多呢,按說它的表達能力變強了,可能的原因是過擬合。那怎麼解決過擬合呢?一種方法就是dropout。drop的詳細解釋請參考這裡。簡單來說,dropout就是在訓練的時候隨機的讓一些神經元的激活「丟失」,這樣網絡就能學到更加魯棒的特徵,因為它要求某些神經元」失效「的情況下網絡仍然能工作,因此就不會那麼依賴某一些神經元,而是每個神經元都有貢獻。

下面是在兩個全連接層都加入50%的dropout:

>>> net = Network([

        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 

                      filter_shape=(20, 1, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 

                      filter_shape=(40, 20, 5, 5), 

                      poolsize=(2, 2), 

                      activation_fn=ReLU),

        FullyConnectedLayer(

            n_in=40*4*4, n_out=1000, activation_fn=ReLU, p_dropout=0.5),

        FullyConnectedLayer(

            n_in=1000, n_out=1000, activation_fn=ReLU, p_dropout=0.5),

        SoftmaxLayer(n_in=1000, n_out=10, p_dropout=0.5)], 

        mini_batch_size)

>>> net.SGD(expanded_training_data, 40, mini_batch_size, 0.03, 

            validation_data, test_data)

使用dropout後,我們得到了99.60%的一個模型。

這裡有兩點值得注意:

改進版本6

ensemble多個神經網絡。作者分別訓練了5個神經網絡,每一個都達到了99.6%的準確率,然後用它們來投票,得到了99.67%準確率的模型。

這是一個非常不錯的模型了,10000個測試數據只有33個是錯誤的,我們把錯誤的圖片都列舉了出來:

圖片的右上角是正確的分類,右下角是模型的分類。可以發現有些錯誤可能人也會犯,因為有些數字人也很難分清楚。

【為什麼只對全連接的層使用dropout?】

如果讀者仔細的閱讀代碼,你會發現我們只對全連接層進行了dropout,而卷積層沒有。當然我們也可以對卷積層進行dropout。但是沒有必要。因為卷積層本身就有防止過擬合的能力。原因是權值共享強制網絡學到的特徵是能夠應用到任何位置的特徵。這讓它不太容易學習到特別局部的特徵。因此也就沒有必要對它進行的dropout了。

更進一步

感興趣的讀者可以參考這裡,列舉了MNIST數據集的最好結果以及對應的論文。目前最好的結果是99.79%

接下來的文章會介紹theano,一個非常流行的深度學習框架,然後會講解network3.py,也就是怎麼用theano實現CNN。敬請關注。

作者:李理,目前就職於環信,即時通訊雲平臺和全媒體智能客服平臺,在環信從事智能客服和智慧機器人相關工作,致力於用深度學習來提高智慧機器人的性能。

相關文章: 

李理:從Image Caption Generation理解深度學習(part I) 

李理:從Image Caption Generation理解深度學習(part II) 

李理:從Image Caption Generation理解深度學習(part III) 

李理:自動梯度求解 反向傳播算法的另外一種視角 

李理:自動梯度求解——cs231n的notes 

李理:自動梯度求解——使用自動求導實現多層神經網絡

相關焦點

  • 詳解卷積神經網絡
    接下來介紹一種非常重要的神經網絡——卷積神經網絡。這種神經網絡在計算機視覺領域取得了重大的成功,而且在自然語言處理等其它領域也有很好的應用。深度學習受到大家的關注很大一個原因就是Alex等人實現的AlexNet(一種深度卷積神經網絡)在LSVRC-2010 ImageNet這個比賽中取得了非常好的成績。此後,卷積神經網絡及其變種被廣泛應用於各種圖像相關任務。
  • 深度學習元老Yann Lecun詳解卷積神經網絡
    註:卷積神經網絡(Convolutional Neural Network)是一種前饋神經網絡,它的人工神經元可以響應一部分覆蓋範圍內的周圍單元,對於大型圖像處理有出色表現。早在20世紀80年代末,Yann LeCun就作為貝爾實驗室的研究員提出了卷積網絡技術,並展示如何使用它來大幅度提高手寫識別能力。上世紀末本世紀初,當神經網絡失寵時Yann LeCun是少數幾名一直堅持的科學家之一。他於2003年成為紐約大學教授,並從此引領了深度學習的發展,目前任職於Facebook FAIR實驗室。
  • 李理:卷積神經網絡之Dropout
    上圖a是標準的一個全連接的神經網絡,b是對a應用了dropout的結果,它會以一定的概率(dropout probability)隨機的丟棄掉一些神經元。4.2 Dropout的實現實現Dropout最直觀的思路就是按照dropout的定義來計算,比如上面的3層(2個隱藏層)的全連接網絡,我們可以這樣實現:""" 最原始的dropout實現,不推薦使用 """p = 0.5 def train_step
  • AI入門:卷積神經網絡
    講到AI不得不講深度學習,而講到深度學習,又不能不講卷積神經網絡。如果把深度學習比作中國的網際網路界,那捲積神經網絡和循環神經網絡就是騰訊和阿里級別的地位。今天我們主要討論的卷積神經網絡,到底卷積神經網絡能解決什麼問題,它的結構是怎樣的?是怎麼學習的?應用在哪些具體的產品上?本文將為大家一一解答。
  • 人工智慧之卷積神經網絡(CNN)
    ^_^本文引用地址:http://www.eepw.com.cn/article/201806/381807.htm  20世紀60年代,Hubel和Wiesel在研究貓腦皮層中用於局部敏感和方向選擇的神經元時發現其獨特的網絡結構可以有效地降低反饋神經網絡的複雜性,繼而提出了卷積神經網絡CNN(Convolutional Neural Networks)。
  • 最全的DNN概述論文:詳解前饋、卷積和循環神經網絡技術
    本論文技術性地介紹了三種最常見的神經網絡:前饋神經網絡、卷積神經網絡和循環神經網絡。
  • 圖解:卷積神經網絡數學原理解析
    介紹過去我們已經知道被稱為緊密連接的神經網絡。這些網絡的神經元被分成若干組,形成連續的層。每一個這樣的神經元都與相鄰層的每一個神經元相連。下圖顯示了這種體系結構的一個示例。圖1. 密集連接的神經網絡結構當我們根據一組有限的人工設計的特徵來解決分類問題時,這種方法很有效。
  • 圖解:卷積神經網絡的數學原理分析
    卷積神經網絡可能是取得巨大成功的最關鍵的基礎。這次,我們將加深對神經網絡如何在CNN上工作的理解。出於建議,本文將包含非常複雜的數學方程式。如果您不習慣線性代數和微分,請不要氣our。我的目標不是讓您記住這些公式,而是讓您直觀地了解下面發生的事情。 在過去,我們知道了稱為緊密連接的神經網絡。這些網絡的神經元被分為幾組以形成連續的層。每個這樣的神經元連接到相鄰層中的每個神經元。
  • 了解卷積神經網絡的原理與基本結構
    受Hubel和Wiesel對貓視覺皮層電生理研究啟發,有人提出卷積神經網絡(CNN),Yann Lecun 最早將CNN用於手寫數字識別並一直保持了其在該問題的霸主地位。近年來卷積神經網絡在多個方向持續發力,在語音識別、人臉識別、通用物體識別、運動分析、自然語言處理甚至腦電波分析方面均有突破。
  • 一文讓你了解卷積神經網絡
    卷積神經網絡(Convolutional Neural Network,CNN)是一種前饋神經網絡,它的人工神經元可以響應一部分覆蓋範圍內的周圍單元,對於大型圖像處理有出色表現。 它包括卷積層(convolutional layer)和池化層(pooling layer)。對比:卷積神經網絡、全連接神經網絡
  • 深度學習之卷積神經網絡經典模型
    雖然層數只有7層,這在如今龐大的神經網絡中可是說是非常少的了,但是包含了卷積層,池化層,全連接層,可謂麻雀雖小五臟俱全了。為了方便,我們把卷積層稱為C層,下採樣層叫做下採樣層。首先,輸入層輸入原始圖像,原始圖像被處理成32×32個像素點的值。然後,後面的隱層計在卷積和子抽樣之間交替進行。C1層是卷積層,包含了六個特徵圖。每個映射也就是28x28個神經元。
  • 乾貨| Excel圖解卷積神經網絡結構
    隨意地應用神經網絡是一回事,理解它是什麼以及背後的發生機制是另外一回事。今天,我將與你共享我的心得,展示我如何上手卷積神經網絡並最終弄明白了它。我將做一個通盤的展示,從而使你對 CNN 的工作機制有一個深入的了解。
  • 對卷積神經網絡工作原理做一個直觀的解釋
    第二,神經網絡,是指利用一層層神經元構建成一個類似大腦結構的模型,從而自動實現端到端的學習。為什麼要用神經網絡?因為傳統計算機視覺,對圖像的理解非常重要,圖像的特徵有顏色特徵、幾何特徵、邊緣特徵、局部特徵、梯度特徵等等,光學這些就需要大量的時間,然後才能學習計算機視覺的一些方法和模型。
  • Clarifai:卷積神經網絡是什麼,如何工作
    然而,在2012年,第一個深度卷積神經網絡(CNN)被引入競爭,擊敗了它的競爭對手,錯誤率下降到16%。從那以後,每一個獲獎的算法都是CNN,包括2013年,Clarifai獲得了所有前五名獎項,那麼CNN到底是什麼?CNNs是一種特殊類型的神經網絡,通常是深度神經網絡,用於計算機視覺任務。CNN的目的是將原始像素轉換成人類(或算法)能夠理解的有意義的概念。
  • 深度學習基礎:一步一步講解卷積神經網絡
    我們通過計算,從6×6×3的輸入推導出一個4×4×2矩陣,它是卷積神經網絡的一層,把它映射到標準神經網絡中四個卷積層中的某一層或者一個非卷積神經網絡中。而這節課我想講的重點是,卷積神經網絡的某一卷積層的工作原理,以及如何計算某一卷積層的激活函數,並映射到下一層的激活值。了解了卷積神經網絡中某一卷積層的工作原理,我們就可以把它們堆疊起來形成一個深度卷積神經網絡,我們下節課再講。
  • 如何入手卷積神經網絡
    選自medium作者:Tirmidzi Faizal Aflahi參與:韓放、王淑婷卷積神經網絡可以算是深度神經網絡中很流行的網絡了本文從基礎入手,介紹了卷積網絡的基本原理以及相關的其它技術,並利用卷積網絡做了一個簡單項目作為示例參考。想入手 CNN 的朋友不可錯過~首先,我們先看看下面這張照片:
  • ImageNet冠軍帶你入門計算機視覺:卷積神經網絡
    重點介紹經典的卷積神經網絡,全卷積網絡的基本概念和基本單元,以及卷積神經網絡與神經網絡的異同。最後通過實現一個在實際中有廣泛應用的人臉關鍵點檢測算法,介紹如何用 TensorFlow 構建卷積神經網絡。
  • 萬字長文|如何直觀解釋卷積神經網絡的工作原理?
    作者:YJango 日本會津大學|人機互動實驗室博士生 量子位 已獲授權編輯發布 該文是對卷積神經網絡的介紹,並假設你理解前饋神經網絡。 如果不是,強烈建議讀完《如何簡單形象又有趣地講解神經網絡是什麼?》
  • 深入了解VGG卷積神經網絡濾波器
    基本思路是選擇一個特定的 ImageNet 類別並理解 VGG16 網絡如何來學習表示它。第一步是選擇 ImageNet 上的一個特定類別來訓練 VGG16 網絡。 網絡可以進一步抽象,如下圖所示: 神經網絡內部就是這樣表示一隻鳥的。這是一種虛幻的形象,但這正是在沒有人為幹預的情況下該神經網絡自然學到的東西!
  • 典型的深度學習算法(一):卷積神經網絡(CNN)
    在深度學習領域中,已經驗證的典型成熟算法有卷積神經網絡(CNN)、循環神經網絡(RNN)、生成對抗網絡(GAN)、深度強化學習(DRL)等,下面企通查將帶領大家對典型的深度學習算法之一——卷積神經網絡(CNN)一探究竟。