構建神經(jīng)網(wǎng)絡來識別手寫數(shù)字

曾梓龍 材料班 2014301020006

前言

最近的研究課題是神經(jīng)網(wǎng)絡,偶然在知乎上看到了一篇關于入門深度學習的文章,介紹了一下從數(shù)學知識到機器學習斩熊,再到神經(jīng)網(wǎng)絡和深度學習的資源里逆,因此開始通過看Andrew Ng的機器學習課程入門機器學習进胯,并在網(wǎng)上在線看Neural Networks and Deep Learning這份線上教程學習神經(jīng)網(wǎng)絡和深度學習。

在花了幾天時間看線上教程原押,我開始著手操作識別手寫數(shù)字圖像的任務胁镐,為了簡化代碼,我使用python構建神經(jīng)網(wǎng)絡诸衔。

背景

首先觀察下面的一副圖片


我們可以很容易地辨識這些數(shù)字是504192盯漂。通過對大腦的研究,我們知道笨农,人類大腦有大約1000億個神經(jīng)元就缆,在這些神經(jīng)元之間有一萬億個鏈接。

而在大腦的每個半球磁餐,人類有一個初級視覺皮層V1违崇,大概有1.4億個神經(jīng)元,以及數(shù)百億的相互連接诊霹,但是人類視覺不僅涉及V1羞延,而且還有V2,V3脾还,V4伴箩,V5這一系列的視覺皮層。

實際上鄙漏,人類大腦就是一臺超級計算機嗤谚,幾百萬年來的調(diào)整和適應使得人類能夠辨識外部世界,因此用計算機模擬書寫數(shù)字是很艱難的怔蚌。

這里有一個問題值得思考巩步。為什么我們能夠辨識手寫數(shù)字9?是因為我們天生就能夠辨識數(shù)字9還是因為我們學習過如何辨識數(shù)字9桦踊?

答案顯然是我們學習過如何辨識并書寫數(shù)字9.

從這個問題出發(fā)來看椅野,我們也可以用計算機模擬人類學習的過程來獲得辨識手寫數(shù)字圖像的能力。

近年來籍胯,神經(jīng)網(wǎng)絡在這類問題的解決上取得了比較大的進展竟闪。而神經(jīng)網(wǎng)絡模擬的就是人類大腦的工作方式——使用大量的訓練數(shù)據(jù)訓練神經(jīng)網(wǎng)絡,使神經(jīng)網(wǎng)絡的從這些數(shù)據(jù)中獲取“經(jīng)驗”杖狼,提高辨識手寫數(shù)字圖像的準確率炼蛤。

何為神經(jīng)網(wǎng)絡以及神經(jīng)網(wǎng)絡的工作原理

Perceptrons(感知器)

在介紹神經(jīng)網(wǎng)絡之前,我們首先需要理解Perceptrons(感知器)的概念蝶涩。Perceptrons實際上就是一個人工模擬的神經(jīng)元理朋,每個感知器有若干個二進制輸入以及一個二進制輸出絮识。


上面的這個Perceptrons有三個輸入:x1、x2和x3嗽上,每條輸入都有相應的權重:w1笋除、w2和w3.

Perceptrons的輸入是由輸入的加權之和決定的,神經(jīng)網(wǎng)絡的研究人員通常用下面的這個公式來確定Perceptrons的輸出:

![](http://latex.codecogs.com/png.latex?output={{1,if \sum{j}w_jx_j>threhold}^{0,if \sum_{j}w_jx_j \leq threhold})

也就是說炸裆,當Perceptrons的輸入加權和大于某一閾值時,輸出1鲜屏;小于某一閾值時輸出0.

這和大腦神經(jīng)元的工作原理比較類似烹看,當神經(jīng)元接收到來自其他神經(jīng)元的信號時,經(jīng)過處理之后再發(fā)送給其他的神經(jīng)元洛史。

在實際應用中惯殊,為了計算方便,我們將上面的式子改為:

![](http://latex.codecogs.com/png.latex?output={{1,if \sum{j}w_jx_j+b>0}^{0,if \sum_{j}w_jx_j+b \leq 0})

其中:
![](http://latex.codecogs.com/png.latex?b\equiv -threhold)

b為bias.

同時令X為輸入數(shù)據(jù)的矩陣也殖,W為相應的權重矩陣土思,即:

因此輸出表達式為:

![](http://latex.codecogs.com/png.latex?output={_{1,if\ \vec{w}\cdot\vec{x}+b>0}^{0,if\ \vec{w}\cdot\vec{x}+b \leq 0})

我們在之前也說過,人類大腦識別圖像是通過多層的視覺皮層來解析的信息忆嗜,因此我們這里開始構建一個三層的神經(jīng)網(wǎng)絡:

第一層稱為輸入層己儒,最后一層稱為輸出層,第一層和最后一層之間的層被稱為隱藏層捆毫。

我們首先考慮一下這樣一個Perceptrons闪湾,有兩個輸入端,每個輸入端的權重為-2绩卤,結點bias為3:

如果我們輸入00途样,則有:
(-2)0+(-2)0+3 = 3>0,類似的,輸入為01或者10時輸出都為1濒憋,但是當輸入為11時何暇,輸出為0,因此這樣的一個Perceptrons可以看作一個與非門凛驮。

在數(shù)字邏輯中裆站,我們知道,邏輯門的組合可以組成一定的邏輯開關辐烂,如下圖所示:

將上圖中的與非門換成之前討論的Perceptrons遏插,則有:

減少一些線條,并將輸入端轉換為Perceptrons纠修,則有:



從上面的例子可以看到胳嘲,不同的Perceptrons的組合網(wǎng)絡可以具有一定的邏輯功能,利用神經(jīng)網(wǎng)絡識別手寫數(shù)字也是一樣的原理扣草。

Sigmod 神經(jīng)元

在實際應用中了牛,并不會使用之前介紹的Perceptrons組成的神經(jīng)網(wǎng)絡颜屠,那樣構建的神經(jīng)網(wǎng)絡不夠穩(wěn)定,在使用訓練集訓練網(wǎng)絡的時候網(wǎng)絡權重鹰祸、bias甚至網(wǎng)絡的輸出的變化非常大甫窟。因此,往往使用Sigmod神經(jīng)元蛙婴。

Sigmod神經(jīng)元里的Perceptrons和之前的Perceptrons并無兩樣粗井,但是Perceptrons的輸出output的值并非0或1,相反街图,output 為:

![](http://latex.codecogs.com/png.latex?\sigma(w\cdot x+b))
其中σ稱為sigmoid函數(shù)浇衬,sigmoid函數(shù)定義如下:
![](http://latex.codecogs.com/png.latex?\sigma(z)\equiv \frac{1}{1+e^{-z}}.)

因此對于輸入為x1,x2,...,輸入權重為w1,w2,...,且bias為b時,sigmoid神經(jīng)元的輸出為:


sigmoid函數(shù)σ(z)的圖像如下圖所示:


當z大于0時餐济,sigmoid函數(shù)小于1大于0.5耘擂,當z小于0時,sigmoid函數(shù)大于0小于0.5.


這是之前剛開始介紹的Perceptrons的輸出函數(shù)(Step function)的圖像

sigmoid function與step function相比更為平滑絮姆,當權重w醉冤、bias b有微小變化時,sigmoid函數(shù)的輸出的變化也很微小篙悯。因此一般采用sigmoid函數(shù)作為輸出函數(shù)蚁阳。

從sigmoid函數(shù)圖像可以看到,sigmoid函數(shù)的輸出是連續(xù)變化的辕近,可能是0.173或者0.689韵吨,那么如何界定sigmoid的輸出值以確定output值?
我們一般界定移宅,當sigmoid函數(shù)大于0.5時output為1归粉,當sigmoid函數(shù)小于0.5時,output為0

識別手寫數(shù)字的神經(jīng)網(wǎng)絡搭建

首先漏峰,我們識別一個手寫數(shù)字:

為了識別單個數(shù)字糠悼,我們使用一個三層的神經(jīng)網(wǎng)絡:

由于我們的手寫數(shù)字是一個28*28的像素的圖像,因此手寫數(shù)字圖像中一共有784個像素浅乔,這些像素塊經(jīng)過編碼后輸入到神經(jīng)網(wǎng)絡中倔喂,因此這個三層神經(jīng)網(wǎng)絡的輸入端有784個神經(jīng)元。

輸入端值為1時代表像素塊為1靖苇,值為0時代表像素塊為白色席噩,當輸入端值介于0到1之間時,代表深度不同的灰色贤壁。

第二層也就是隱藏層悼枢,在圖中僅有15個神經(jīng)元,不過在實際情況中有可能不只15個神經(jīng)元脾拆。

第三層也就是輸出層馒索,有十個神經(jīng)元莹妒,如果哪個神經(jīng)元輸出為1,則代表神經(jīng)網(wǎng)絡認定輸入的圖像為相應的數(shù)字绰上。例如旨怠,如果第一個神經(jīng)元輸出為1,則神經(jīng)網(wǎng)絡認定輸入的手寫數(shù)字為0.

梯度下降算法

之前我們說過蜈块,神經(jīng)網(wǎng)絡可以通過訓練數(shù)據(jù)集來獲得辨識圖像的能力鉴腻,其模擬的就是人類的學習能力,在這里我們提出一個梯度下降學習算法模擬學習的過程百揭。

我們使用向量x表示輸入的值拘哨,則x是一個784維度的向量,輸入端用y表示信峻,則有

![](http://latex.codecogs.com/png.latex? y = y(x))

其中,y是一個10維的向量瓮床。
舉個例子來說盹舞,如果一個訓練圖像被神經(jīng)網(wǎng)絡認定為6,則

![](http://latex.codecogs.com/png.latex? y = (0,0,0,0,0,0,1,0,0,0)^T)
就是神經(jīng)網(wǎng)絡的輸出向量隘庄。

為了不斷調(diào)整神經(jīng)網(wǎng)絡內(nèi)的權重和bias踢步,我們提出梯度下降算法,首先定義一個代價函數(shù):
![](http://latex.codecogs.com/png.latex? C(w,b) \equiv \frac{1}{2n}\sum_x ||y(x)-a||^2)

其中w為神經(jīng)網(wǎng)絡中所有的權重值丑掺,b為所有的bias获印,n為輸入的訓練集的數(shù)量,a是當輸入為x時神經(jīng)網(wǎng)絡的輸出向量街州。由此可見兼丰,w、b和n為a的自變量唆缴。


如上圖為代價函數(shù)C(w,b)的等勢圖鳍征,中心處為代價函數(shù)圖像的最低點,越往外面徽,點所處的位置越高艳丛。因此為了求得代價函數(shù)C的極小值,我們需要找到相應代價函數(shù)的圖像的最低點趟紊。

更直觀一點就像上面這幅圖所示氮双,小球處于某一位置,總有向最低點運動的趨勢霎匈。為了在數(shù)學語言上描述這一過程戴差,我們提出梯度下降算法:
![](http://latex.codecogs.com/png.latex?\Delta C\approx \frac{\partial C}{\partial v_1}\Delta v_1+\frac{\partial C}{\partial v_2}\Delta v_2)
令:
![](http://latex.codecogs.com/png.latex?\Delta \vec{v} = (\Delta v_1,\Delta v_2)^T)

![](http://latex.codecogs.com/png.latex?\bigtriangledown C=(\frac{\partial C}{\partial v_1},\frac{\partial C}{\partial v_2}))
有:
![](http://latex.codecogs.com/png.latex?\Delta C\approx \bigtriangledown C\cdot \Delta v)

因為代價函數(shù)需要沿梯度下降,所以應當有
![](http://latex.codecogs.com/png.latex?\Delta C <0)
令:
![](http://latex.codecogs.com/png.latex?\Delta v=-\eta \bigtriangledown C)
其中唧躲,η是一個很小的正參數(shù):

![](http://latex.codecogs.com/png.latex?\Delta C \approx -\eta \bigtriangledown C\cdot \bigtriangledown C=-\eta||\bigtriangledown C||^2)

更新參數(shù)v造挽,有:
![](http://latex.codecogs.com/png.latex?v\to v^{'}=v-\eta \bigtriangledown C)

因此碱璃,分別更新權重w和偏重b:
![](http://latex.codecogs.com/png.latex?w_k \to w_k^{'} = w_k-\eta \frac{\partial C}{\partial w_k})
![](http://latex.codecogs.com/png.latex?b_l \to b_l^{'}=b_l-\eta \frac{\partial C}{\partial b_l})

但是在實際情況中,使用梯度下降學習算法訓練神經(jīng)網(wǎng)絡效率比較低饭入,因此我們采用隨機梯度下降學習算法嵌器。

隨機梯度下降學習算法的基本思想是在所有訓練集中隨機挑選m個訓練集輸入到神經(jīng)網(wǎng)絡中進行訓練,m足夠大以至于可以認為這m個訓練集的訓練效果相當于所有訓練集訓練神經(jīng)網(wǎng)絡的效果谐丢,用數(shù)學公式表達就是:
![](http://latex.codecogs.com/png.latex?\frac{\sum^m_{j=1}\bigtriangledown C_{X_j}}{m}\approx \frac{\sum_x \bigtriangledown C_x}{n} = \bigtriangledown C)
也就是說爽航,w與b的更新函數(shù)可以改為:
![](http://latex.codecogs.com/png.latex?w_k \to w_k^{'}=w_k-\frac{\eta}{m}\sum_j \frac{\partial C_X_j}{\partial w_k})
![](http://latex.codecogs.com/png.latex?b_l\to b_l^{'}=b_l-\frac{\eta}{m}\sum_j \frac{\partial C_X_j}{\partial b_l})

編程實現(xiàn)

先建立一個Network類:

class Network(self):
    def __init__(self,sizes):

        self.num_layers = len(sizes)
        self.sizes = sizes
        # if we want to create a Network object with 2 neurons 
        # in the first layer,3 neurons in the second layer,and
        # 1 neurons in the final layer,we can do this in this 
        # code:
        # net = Network([2,3,1])
        self.biases = [np.random.randn(y,1) for y in sizes[1:]]
        self.weights = [np.random.randn(y,x) 
                        for x,y in zip(sizes[:-1],sizes[1:])]

我們建立的是一個三層的神經(jīng)網(wǎng)絡,傳入一個三維的向量,例如:
如果我們想在輸入層放入2個神經(jīng)元乾忱,隱藏層放入3個神經(jīng)元讥珍,輸出層放入1個神經(jīng)元,則將[2,3,1]傳入神經(jīng)網(wǎng)絡中窄瘟。

net = Network([2,3,1])

biases和weight則先用random函數(shù)初始化衷佃,隨機設置。

定義sigmoid函數(shù):

def sigmod(z):
    return 1.0/(1.0+np.exp(-z))

每一層神經(jīng)元的輸出由上一層神經(jīng)元的輸入決定蹄葱,由數(shù)學公式表達就是:



其中氏义,a是上一層神經(jīng)元的輸出,即這一神經(jīng)元的輸入图云,w是相應兩層神經(jīng)元之間的權重向量惯悠。

同時在Network類中定義feedforward方法:

def feedforward(self,a):
        for b,w in zip(self.biases,self.weights):
            a = sigmod(np.dot(w,a)+b)
        return a

同時在Network類中定義SGD方法,SGD方法使神經(jīng)網(wǎng)絡能夠使用隨機梯度下降算法進行學習:

    def SGD(self,training_data,epochs,mini_batch_size,eta,test_data=None):
        # epochs:the number of epochs to train for
        # mini_batch_size:the size of the mini-batches to use
        # when sampling.eta is the learning rate /eta.
        if test_data:
            n_test = len(test_data)
        n = len(training_data)
        for j in xrange(epochs):
            random.shuffle(training_data)
            mini_batches = [
                training_data[k:k+mini_batch_size]
                for k in xrange(0,n,mini_batch_size)]
            # catch k in every mini_batch_size
            for mini_batch in mini_batches:
                self.update_mini_batch(mini_batch,eta)
            if test_data:
                print("Epoch {0}:{1}/{2}".format(j,self.evaluate(test_data),n_test))
            else:
                print("Epoch {0} complete".format(j))   

在之前討論隨機梯度下降學習算法的時候竣况,我們談到w與b要隨神經(jīng)網(wǎng)絡的學習不斷更新克婶,在這里定義一個update_mini_batch方法:

def update_mini_batch(self,mini_batch,eta):
        # update the network's weights and biases by applying gradient
        # descent using backpropagation to a single mini batch
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x,y in mini_batch:
            delta_nabla_b,delta_nabla_w = self.backprop(x,y)
            nabla_b = [nb+dnb for nb,dnb in zip(nabla_b,delta_nabla_b)]
            nabla_w = [nw+dnw for nw,dnw in zip(nabla_w,delta_nabla_w)]
        self.weights = [w-((eta)/len(mini_batch))*nw
                        for w,nw in zip(self.weights,nabla_w)]
        self.biases = [b-(eta/len(mini_batch))*nb
                        for b,nb in zip(self.biases,nabla_b)]

完整代碼已經(jīng)托管到github上了。
在使用神經(jīng)網(wǎng)絡的時候丹泉,還需要一個python文件將訓練集輸入到神經(jīng)網(wǎng)絡中情萤,我使用了國外某大佬的mnist_loader.py文件,也托管在github上了摹恨。

運行操作

打開python shell紫岩,切換到程序文件目錄,輸入下列代碼:

在進入python交互環(huán)境之后睬塌,第一行和第二行代碼導入了訓練集泉蝌,第三行和第四行代碼創(chuàng)建了一個第一層為784個神經(jīng)元 第二層為30個神經(jīng)元 第三層為10個神經(jīng)元的神經(jīng)網(wǎng)絡。

再輸入下列這行代碼啟動神經(jīng)網(wǎng)絡:

net.SGD(training_data,30,10,3.0,test_data=test_data)

這行代碼的意思是訓練30次揩晴,同時挑選10組訓練集作為隨機梯度下降算法的訓練集勋陪,學習速率η為3.0,運行一段時間后硫兰,得到:


經(jīng)過30次訓練之后诅愚,神經(jīng)網(wǎng)絡辨識圖像的準確度可以達到94.89%,實際上在第28次訓練的時候準確度已經(jīng)達到了94.99%.

如果我們將這個神經(jīng)網(wǎng)絡的第二層改為100個神經(jīng)元,神經(jīng)網(wǎng)絡的辨識度可以達到多少劫映?
輸入下列代碼:

net = network.Network([784,100,10])

啟動神經(jīng)網(wǎng)絡违孝,我們可以將神經(jīng)網(wǎng)絡的辨識率提高到96%以上刹前。

重新回到第二層為30個神經(jīng)元的情況上,如果將學習速率η提高到20的話雌桑,將程序運行一遍得到:


可以發(fā)現(xiàn)喇喉,神經(jīng)網(wǎng)絡辨識圖像的準確率有所下降。

總結

訓練神經(jīng)網(wǎng)絡所需的計算量十分巨大校坑,用普通的筆記本運行整個程序需要花費比較長的一段時間拣技,因此,在實際應用中一般采用GPU來訓練神經(jīng)網(wǎng)絡耍目。在實際模擬的過程中膏斤,我們可以發(fā)現(xiàn),神經(jīng)網(wǎng)絡的辨識率雖然可以提高到95%以上邪驮,但是在這之后其辨識率很難得到提高莫辨,這迫使我們不斷發(fā)展神經(jīng)網(wǎng)絡模型。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毅访,一起剝皮案震驚了整個濱河市衔掸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俺抽,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件较曼,死亡現(xiàn)場離奇詭異磷斧,居然都是意外死亡,警方通過查閱死者的電腦和手機捷犹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門弛饭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萍歉,你說我怎么就攤上這事侣颂。” “怎么了枪孩?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵憔晒,是天一觀的道長。 經(jīng)常有香客問我蔑舞,道長拒担,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任攻询,我火速辦了婚禮从撼,結果婚禮上,老公的妹妹穿的比我還像新娘钧栖。我一直安慰自己低零,他們只是感情好婆翔,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掏婶,像睡著了一般啃奴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上气堕,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天纺腊,我揣著相機與錄音,去河邊找鬼茎芭。 笑死揖膜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的梅桩。 我是一名探鬼主播壹粟,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宿百!你這毒婦竟也來了趁仙?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤垦页,失蹤者是張志新(化名)和其女友劉穎雀费,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痊焊,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡盏袄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了薄啥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辕羽。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖垄惧,靈堂內(nèi)的尸體忽然破棺而出刁愿,到底是詐尸還是另有隱情,我是刑警寧澤到逊,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布铣口,位于F島的核電站,受9級特大地震影響觉壶,放射性物質(zhì)發(fā)生泄漏枷踏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一掰曾、第九天 我趴在偏房一處隱蔽的房頂上張望旭蠕。 院中可真熱鬧,春花似錦、人聲如沸掏熬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旗芬。三九已至舌胶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疮丛,已是汗流浹背幔嫂。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留誊薄,地道東北人履恩。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像呢蔫,于是被迫代替她去往敵國和親切心。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容