1. 程式人生 > >【Python】搭建你的第一個簡單的神經網路_準備篇_NN&DL學習筆記(二)

【Python】搭建你的第一個簡單的神經網路_準備篇_NN&DL學習筆記(二)

前言

本文為《Neural Network and Deep Learning》學習筆記(二),可以轉載但請標明原文地址。

本人剛剛入門、筆記簡陋不足、多有謬誤,而原書精妙易懂、不長篇幅常有柳暗花明之處,故推薦閱讀原書。

《Neural Network and Deep Learning》下載地址(中文版):Neural Network and Deep Learning(中文版)

注:這本書網上有很多免費版,隨便搜一下就有了,不用非得花積分下載我上傳的資料。


第二部分:準備篇(搭建神經網路、選擇學習演算法)

理論篇中,我們講述了神經元與神經網路的基本知識,那麼下面我們就來搭建一個神經網路吧!

本篇中依然不涉及程式碼部分,只講解了一些前期準備過程,想要直接食用原始碼,可以跳到實踐篇

我們將以【手寫數字識別】專案為例進行講解。

手寫數字識別是指:輸入一張圖片,圖片中包含一個數字,輸出該數字是什麼。

一、搭建一個三層神經網路

在理論篇中,我們講到:神經網路的架構可以分為三部分:輸入層、輸出層、隱藏層。下面我們就來分別設計:

1.1 設計輸入層(input layer)

輸入圖片,就相當於輸入圖片的每一個畫素。因此我們設計的網路的輸入層,必須包含給輸入畫素的值進行編碼的神經元。

例如,在手寫數字識別專案中,我們的輸入影象來源於MNIST資料集,這些影象是28×28大小的灰度影象。這意味著我們的輸入層需要包含784=28×28個輸入神經元,值為0.0表示白色,值為1.0表示黑色,中間數值表示逐漸暗淡的灰色。

這樣就完成了輸入層的設計。

1.2 設計輸出層(output layer)

我們需要的輸出是:“判斷輸入圖片是什麼數字”,一共有0-9共十個數字,因此我們的輸出神經元有10個,將它們按0-9編號。

接下來我們需要人為定義輸出值的意義:如果編號為0的輸出神經元被啟用,那麼表明輸出是“數字0”;如果編號為1的輸出神經元被啟用,那麼表明輸出是“數字1”;以此類推。

這裡出現了一個詞:“啟用”。還記得在理論篇中,我們曾經講過啟用函式嗎?啟用函式的作用在於:將神經元的輸入(x)對映到輸出(output),在這裡我們將output稱作啟用值

我們共有10個輸出神經元,每個神經元都有一個output,也就是說都有一個啟用值。我們可以這樣定義:哪個輸出神經元輸出的啟用值最高,就證明網路判斷哪個數字最有可能。

比如:10個輸出神經元中,編號為0的神經元啟用值最高,那麼表明輸出是“數字0”.

這樣就完成了輸出層的設計及輸出值的解釋定義。

1.3 設計隱藏層(hidden layer)

作者曰:設計隱藏層是一門藝(xuan)術(xue)。

簡單起見,我們只設計一層隱藏層,假設該層中有n個隱藏神經元,我們將給n實驗不同的數值,觀察準確率的變化。

以上,三層神經網路設計完畢,網路拓撲圖如下所示:

二、設計學習演算法:梯度下降演算法

神經網路、機器學習,到底是怎麼學習的?!?!學習的到底是什麼?!?!

2.1 學習是什麼

讓我們再來看看我們的啟用函式(=S型函式)(=output的計算公式):

output=\sigma \left ( wx+b \right )=\frac{1}{1+e^{-wx-b}}=\frac{1}{1+exp\left ( -wx-b\right )}

其中:x是輸入,output是輸出,權重w和偏置b是我們引入的引數。

問題來了:對於隨機輸入一張圖片x,如何使輸出output的準確率更高呢?

答案是:不斷調整引數w和b,使輸出值向著準確率更高的方向發展。

這就是學習過程,學習內容就是引數w和b,學習目的就是讓output準確率更高。

這就好比:我在藍翔開挖掘機,需要鏟一個雞蛋起來,我一鏟子下去往左偏了,那麼下次我就往右調整一點,我一鏟子下去力氣大了,那麼下次我就力氣小一點。我就這麼一次次調整引數,總有一天我能夠一鏟子下去就剷起雞蛋,這時候我會說:我學習到了最佳下鏟方向和最佳力氣,現在我鏟雞蛋的準確率非常高。

當然,在神經網路中,我們不可能一次次執行神經網路然後根據結果手動調整引數,於是我們需要設計學習演算法。

學習演算法的作用:讓網路自動調整權重w和偏置b,使模型向著準確率越來越高的方向發展。

在這裡我們介紹一種學習演算法:梯度下降演算法。

2.2 MNIST資料集

我們先來介紹一下MNIST資料集,MNIST 資料分為兩個部分:

第⼀部分包含 60,000 幅⽤於訓練資料的影象。這些影象掃描⾃250人的⼿寫樣本,這些影象是28 × 28 ⼤⼩的灰度影象。

第⼆部分包含 10,000 幅⽤於測試資料的影象,同樣是 28 × 28 的灰度影象,測試資料取⾃和訓練資料不同的另外⼀組 250人。

我們可以把第一部分的 60,000 副影象再劃分成兩個部分:

① ⼀部分 50,000 個影象作為訓練集,我們將⽤來訓練我們的神經⽹絡,使其學習引數w和b。

② 剩下的 10,000 個影象作為驗證集,在本文中我們沒有用到驗證集,但在之後的學習中我們會發現,它對於設定某些超引數很有用 —— 例如學習速率等。

第二部分的 10,000 副影象作為測試集,我們將⽤這些測試資料來評估我們的神經⽹絡準確性。

2.3 梯度下降演算法

1、什麼是代價函式/損失函式/loss

按照慣例,我們用x表示訓練輸入(訓練集影象的輸入),x是一個28×28=784維的向量,每個分量代表影象中單個畫素的灰度值。

我們用y=y\left ( x \right )表示【輸入x】對應的【期望輸出y】,y是一個10維向量。

什麼叫【期望輸出】呢?說白了就是正確答案,比如:輸入一張圖片0,期望輸出就是y\left ( x \right )=\left ( 1,0,0,0,0,0,0,0,0,0 \right )^{T},代表“只有編號為0的輸出神經元被激活了”。

我們希望有一個學習w和b的演算法,能夠讓我們的output與【期望輸出y】無限接近……等等,如何判斷output和【期望輸出y】的接近程度?

我們定義一個代價函式(損失函式/loss)來描述:“對於不同的w和b,output和【期望輸出y】的接近程度”

其中:w表示所有的網路中權重的集合,b是所有的偏置;

           x是輸入,y(x)是對應x的期望輸出(也就是正確答案),a是對應x的實際輸出output;

           n是訓練輸入資料的個數。

整個函式的意思是:對每個輸入x,計算一下期望值y和實際值a的差,平方後再求和,再除以2倍的總個數。

可以看出,C(w,b)≥0,而C(w,b)越接近0,證明期望值和實際值越接近,證明我們的神經網路準確率越高!

我們把C稱為二次代價函式,有時也稱為均方誤差MSE

到現在,我們的學習演算法目的已經非常明確了:找到合適的w和b,讓C越小越好!

2、梯度下降演算法

終於到正題了,再重複一遍,我們訓練神經網路的目的是,找到權重w和偏置b,最小化二次代價函式 C(w,b) 。

2.1 第一個目的:找到w和b,最小化C(w,b)

我們需要使用梯度下降演算法來達到這個目的,這裡需要一些導數的知識。

顯然,C是一個具有兩個變數的函式,我們給這兩個變數換個名字,v1和v2,那麼函式影象如下所示:

我們的目的現在變成:找到變數v1和v2,使函式C取最小值(儘量最小)。

2.2 第二個目的:找到變數v1和v2,最小化C(v1,v2)

梯度下降演算法的思想是:把我們的函式C想象成一個坑,假設把一個小球隨機放在某個位置,那麼小球總是會向低處滾,最終到達坑底。

這個過程的關鍵在於:“向低處滾”如何實現?

小球的位置可以用(v1,v2)描述,我們先來看看,當小球隨便移動時,函式C的值會有什麼變化:

(公式1)

這個公式的意思是:

假設小球原位置在(v1,v2),此時函式值是C;我們把小球移動到(v1*,v2*),此時函式值是C*;

令ΔC=C*-C;Δv1=v1*-v1;Δv2=v2*-v2;那麼我們可以通過Δv1和Δv2計算出ΔC,其中Δv1和Δv2的引數是偏導數,可以看作常量。

我們當然希望:小球每次移動,C越來越小,也就是C*<C,也就是ΔC<0。

我們的目的現在變成:選擇Δv1和Δv2,使ΔC<0

2.3 第三個目的:選擇Δv1和Δv2,使ΔC<0

2.3.1 簡化公式

我們需要將上面那個(公式1)簡化一下(就像簡化啟用函式的公式那樣):

① 定義Δv為v變化的變數:

② 定義\triangledown C,給它起個名字叫做梯度向量:

那麼上面那個(公式1)就簡化成下面這樣:

(公式2)

2.3.2 分析公式2

在(公式2)中,我們可以得到下面兩個資訊:

① 梯度向量\triangledown C的作用在於:對於v的變化,計算得到C的變化,就好像一個梯子把它們關聯起來,這就是被稱為“梯度向量”的原因。

② 我們的目的可以實現了!我們可以這麼選取Δv,使ΔC<0:

(規則1)

看上去是不是很簡單呢?代入上面那個公式2,就變成:

\triangle C\approx -\eta \cdot \left \| \triangledown C \right \|^{2}(公式3)

其中, \eta 是個很小的正數,我們把它叫做學習速率

又因為 \left \| \triangledown C \right \|^{2} 肯定大於0,所以這樣計算得到的\triangle C肯定小於0.

目標達成!

2.3.3 具體怎麼做呢?

挺過了公式推導,實現就簡單許多啦,我們只需要每次這樣做:

① 計算一下梯度向量\triangledown C

② 根據(規則1)計算一下\triangle v

③ 表明:根據\triangle v就可以得到v1和v2的變化。

所以,只要一直迭代上面三步,\triangle C一直小於0,C就會越來越小、越來越小……

直到C已經不再減小,我們就得到了最好的v1和v2(就算不是最好,也是相當好的)。

總結一下,梯度下降演算法工作的方式就是重複計算梯度 ∇C,然後讓小球沿著相反的⽅向移動,沿著坑底“滾落”。我們可以想象它像這樣:

當然,求最小值的方法不僅僅有梯度下降法這一種,但有觀點認為這是最佳策略。理由在於:梯度下降法是在C下降最快的方向上,每次做微小移動。

2.3.4 學習速率\eta

在開心之餘不要忘了還有一個問題:超引數【學習速率\eta】該如何設定呢?

一方面,\eta越小,我們越能夠逐步逼近全域性最低值,(公式2)的近似度越好;

另一方面,\eta太小的話,梯度下降演算法又非常緩慢。

在真正的實踐中,\eta通常是變化的,使近似度和速度得到折中。

我們將在實踐篇瞭解,設定不同的\eta,對於準確率的影響有多麼巨大。

2.3.4 道理我都懂,但我們最初的目標:w和b怎麼沒出現呢?

我們需要把上面的梯度下降演算法應用到我們的神經網路中:

那麼,C(v1,v2)對應神經網路中的C(w,b);

(規則1)對應神經網路中的:

\triangle w=-\eta \cdot \frac{\partial C}{\partial w}

\triangle b=-\eta \cdot \frac{\partial C}{\partial b}(規則2)

假設w和b是原引數,w*和b*是新引數,那麼新引數計算公式如下:

w*=w-\eta \cdot \frac{\partial C}{\partial w}

b*=b-\eta \cdot \frac{\partial C}{\partial b}  (公式4)

運用(公式4)來不斷更新我們的w和b,就可以讓神經網路學習了。

2.4 隨機梯度下降演算法

梯度下降演算法的缺點在於:需要計算\triangledown C,變數增多,計算量成平方倍的增大,導致學習時間增大,令人難以忍受。

因而出現了許多替代演算法,隨機梯度下降就是一種加速學習的演算法。

其思想在於:通過隨機選取小量訓練輸入樣本來計算\triangledown C_{x},進而估算\triangledown C,減少計算量。

更準確地說,我們隨機選取小量的m個訓練輸入,標記為X1,X2,……,Xm,把它們稱為小批量資料。

假設m足夠大,我們可以用\triangledown C_{m}的平均值來估算\triangledown C

而迭代公式也變成了下面這樣:

w*=w-\frac{\eta}{m} \cdot \frac{\partial C}{\partial w}

b*=b-\frac{\eta}{m} \cdot \frac{\partial C}{\partial b}

直到我們用完了所有的訓練輸入,被稱為完成了一個訓練迭代期。然後我們就會開始新一個迭代期。

需要注意的是,這相當於學習速率從 \eta 變成了 \frac{\eta}{m}

實踐證明,隨機梯度下降演算法能夠千倍加速學習過程。

 

神經網路架構已經設計好,學習演算法也已經選好,下一步,就該寫程式碼了。實踐篇,敬請期待。


【2018/11/19後記】

1、我真的瘋了,今天下午看到訪問量1w+,新建一篇部落格就開始碼字,全然不顧我還有一門考試、兩個報告、一個大作業、兩篇論文、一個專案要做……對CSDN真的是嘔心瀝血、真情實感了。

2、這篇從下午到晚上碼了將近三個小時,飯沒吃,晚課差點遲到,本來想一鼓作氣把實踐篇寫完,結果越寫越多、越寫越多,只能臨時加了個準備篇,實踐篇爭取今晚寫完,然後就真的要專心準備報告和考試了!!!立個flag,下週二之前不上部落格了!!!

3、第一次寫學習筆記,很粗糙,圖都是截自原書,有的公式懶得寫也直接截圖了,畢竟這種超級入門級的東西大概也沒多少人看,全理論也很難鑽研,我的講述又肯定比原書差一萬倍orz,只能聊以自慰吧,畢竟整理出來是真的很開心,感覺也收穫了很多。