環(huán)顧我們的四周,原本被認(rèn)為只有人類才能做到的事情,現(xiàn)在人工智能都能毫無差錯地完成,甚至試圖超越人類。在這個發(fā)展速度驚人的世界背后,深度學(xué)習(xí)技術(shù)發(fā)揮著重要作用,世界各地研究人員不吝褒獎之詞,稱其為革新性技術(shù)。
入門深度學(xué)習(xí),應(yīng)該盡量不依靠任何內(nèi)容不明的黑盒,即各種庫、工具等,盡量從最基礎(chǔ)的知識出發(fā),只用NumPy和Matplotlib等基礎(chǔ)庫,一步一步實現(xiàn)各種神經(jīng)網(wǎng)絡(luò)。聞之不若見之,見之不若知之,知之不若行之。我們開搞!
編程語言我們使用Python。除了簡單、易讀、易記、開源、免費(fèi)、高性能等優(yōu)點(diǎn),它憑借著NumPy、SciPy等優(yōu)秀的數(shù)值計算、統(tǒng)計分析庫,在數(shù)據(jù)科學(xué)領(lǐng)域占有不可動搖的地位。很多深度學(xué)習(xí)的框架都提供了Python接口,比如Caffe、TensorFlow、Chainer、Theano等。所以Python是目前最適合入門機(jī)器學(xué)習(xí)和深度學(xué)習(xí)的編程語言。最好直接安裝Anaconda,里面除了Python很多庫都包了。 Python的入門可以去康康我的博客哦《Python簡介,無代碼》。
幾乎必加的兩行:
import numpy as np
import matplotlib.pyplot as plt
感知機(jī)
感知機(jī)是具有輸入和輸出的算法。給定一個輸入,將輸出一個既定的值。它將權(quán)重w和偏置b設(shè)定為參數(shù)。**單層感知機(jī)只能表示線性空間,而多層感知機(jī)可以表示非線性空間。**使用感知機(jī)可以表示與門、或門、與非門等邏輯電路。異或門無法通過單層感知機(jī)來表示,但使用2層感知機(jī)就可以!所以理論上,多層感知機(jī)可以表示計算機(jī)?。ㄅc非門能構(gòu)成任何邏輯電路,而計算機(jī)的本質(zhì)就是一堆邏輯電路)
多層感知機(jī)很簡單但也很重要,之后的神經(jīng)網(wǎng)絡(luò)跟它長得很像很像。
神經(jīng)網(wǎng)絡(luò)
多層感知機(jī)能表示復(fù)雜的函數(shù),但是對于合適的、能符合預(yù)期的輸入與輸出的權(quán)重的確定,現(xiàn)在還是由人工進(jìn)行,在面臨大量的權(quán)重和偏置參數(shù)時,人力是無法勝任的。
神經(jīng)網(wǎng)絡(luò)的出現(xiàn)就是為了解決這個壞消息。具體地講,神經(jīng)網(wǎng)絡(luò)的一個重要性質(zhì)是它可以自動地從數(shù)據(jù)中學(xué)習(xí)到合適的權(quán)重參數(shù)。
上面是一個經(jīng)典的神經(jīng)網(wǎng)絡(luò),由3層神經(jīng)元構(gòu)成,因為實際上只有2層神經(jīng)元有權(quán)重,因此稱其為“2層網(wǎng)絡(luò)”。神經(jīng)網(wǎng)絡(luò)的形狀感知機(jī)。實際上,就神經(jīng)元的連接方式而言,與感知機(jī)并沒有任何差異。
激活函數(shù)
h(x)函數(shù)會將輸入信號的總和轉(zhuǎn)換為輸出信號,這種函數(shù)一般稱為激活函數(shù)(activation function)。如“激活”一詞所示,激活函數(shù)的作用在于決定如何來激活輸入信號的總和。激活函數(shù)是連接感知機(jī)和神經(jīng)網(wǎng)絡(luò)的橋梁。(一般而言,“樸素感知機(jī)”是指單層網(wǎng)絡(luò),指的是激活函數(shù)使用了階躍函數(shù)的模型?!岸鄬痈兄獧C(jī)”是指神經(jīng)網(wǎng)絡(luò),即使用 sigmoid函數(shù)等平滑的激活函數(shù)的多層網(wǎng)絡(luò)。)
激活函數(shù)以閾值為界,一旦輸入超過閾值,就切換輸出。這樣的函數(shù)稱為“階躍函數(shù)”。因此,可以說感知機(jī)中使用了階躍函數(shù)作為激活函數(shù)。也就是說,在激活函數(shù)的眾多候選函數(shù)中,感知機(jī)使用了階躍函數(shù)。實際上,如果將激活函數(shù)從階躍函數(shù)換成其他函數(shù),就可以進(jìn)入神經(jīng)網(wǎng)絡(luò)的世界了。
Sigmoid函數(shù)
下面我們就來介紹一下神經(jīng)網(wǎng)絡(luò)使用的激活函數(shù)。神經(jīng)網(wǎng)絡(luò)中經(jīng)常使用的一個激活函數(shù)就是sigmoid函數(shù),函數(shù)就是給定某個輸入后,會返回某個輸出的轉(zhuǎn)換器。比如,向sigmoid函數(shù)輸入1.0或2.0后,就會有某個值被輸出,類似h(1.0) = 0.731 …、h(2.0) = 0.880 …
sigmoid函數(shù)是一條平滑的曲線,輸出隨著輸入發(fā)生連續(xù)性的變化。而階躍函數(shù)以0為界,輸出發(fā)生急劇性的變化。sigmoid函數(shù)的平滑性對神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)具有重要意義。感知機(jī)中神經(jīng)元之間流動的是0或1的二元信號,而神經(jīng)網(wǎng)絡(luò)中流動的是連續(xù)的實數(shù)值信號。
ReLU函數(shù)
還有一個激活函數(shù)明星就是ReLU函數(shù),后面會大量用到!
如果從宏觀視角看圖,可以發(fā)現(xiàn)激活函數(shù)具有相似的形狀。實際上,兩者的結(jié)構(gòu)均是“輸入小時,輸出接近0(為0);隨著輸入增大,輸出向1靠近(變成1)”。也就是說,當(dāng)輸入信號為重要信息時,階躍函數(shù)和sigmoid函數(shù)都會輸出較大的值;當(dāng)輸入信號為不重要的信息時,兩者都輸出較小的值。還有一個共同點(diǎn)是,不管輸入信號有多小,或者有多大,輸出信號的值都在0到1之間。
小總結(jié)
神經(jīng)網(wǎng)絡(luò)的激活函數(shù)必須使用非線性函數(shù)!換句話說,激活函數(shù)不能使用線性函數(shù)。因為使用線性函數(shù)時,無法發(fā)揮多層網(wǎng)絡(luò)帶來的優(yōu)勢,不管如何加深層數(shù),總是存在與之等效的“無隱藏層的神經(jīng)網(wǎng)絡(luò)”。
神經(jīng)網(wǎng)絡(luò)中的激活函數(shù)大多使用平滑變化的sigmoid函數(shù)或ReLU函數(shù)。
輸出層的設(shè)計
神經(jīng)網(wǎng)絡(luò)可以用在分類問題和回歸問題上,不過需要根據(jù)情況改變輸出層的激活函數(shù)。一般而言,回歸問題用恒等函數(shù),分類問題用softmax函數(shù)!
恒等函數(shù)會將輸入按原樣輸出,對于輸入的信息,不加以任何改動地直接輸出。
softmax函數(shù)
softmax函數(shù)可以用下面的式表示:
softmax函數(shù)的輸出是0*.0到1.*0之間的實數(shù)。并且,softmax函數(shù)的輸出值的總和是1。輸出總和為1是softmax函數(shù)的一個重要性質(zhì)。正因為有了這個性質(zhì),我們才可以把softmax函數(shù)的輸出解釋為“概率”!通過使用softmax函數(shù),我們可以用概率的(統(tǒng)計的)方法處理問題。
小總結(jié)
一般而言,神經(jīng)網(wǎng)絡(luò)只把輸出值最大的神經(jīng)元所對應(yīng)的類別作為識別結(jié)果。并且,即便使用softmax函數(shù),輸出值最大的神經(jīng)元的位置也不會變。因此,神經(jīng)網(wǎng)絡(luò)在進(jìn)行分類時,輸出層的softmax函數(shù)可以省略。在實際的問題中,由于指數(shù)函數(shù)的運(yùn)算需要一定的計算機(jī)運(yùn)算量,因此輸出層的softmax函數(shù)一般會被省略。但是,softmax函數(shù)對于神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)過程(反向傳播)具有重大作用!
通過巧妙地使用NumPy多維數(shù)組,可以高效地實現(xiàn)神經(jīng)網(wǎng)絡(luò)。
分類問題中,輸出層的神經(jīng)元的數(shù)量設(shè)置為要分類的類別數(shù)。
輸入數(shù)據(jù)的集合稱為批(batch)。通過以批為單位進(jìn)行推理處理,能夠?qū)崿F(xiàn)高速的運(yùn)算。
神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)!
終于到了重頭戲!前面我們知道了整個網(wǎng)絡(luò)的前向傳播過程,其實就是很直觀地不斷用數(shù)據(jù)乘權(quán)重再加偏置再經(jīng)激活函數(shù),一直流動到最后,如果輸出層的激活函數(shù)選softmax,則能很直觀地看到當(dāng)前數(shù)據(jù)經(jīng)過網(wǎng)絡(luò)后屬于不同分類的概率。在這個過程,所有權(quán)重、偏置都是固定的,這些參數(shù)才是這個深度神經(jīng)網(wǎng)絡(luò)的靈魂,合適的參數(shù)才能帶來正常工作的神經(jīng)網(wǎng)絡(luò)。所以,網(wǎng)絡(luò)需要學(xué)習(xí)!
數(shù)據(jù)是機(jī)器學(xué)習(xí)的命根子,機(jī)器學(xué)習(xí)從數(shù)據(jù)中尋找答案、從數(shù)據(jù)中發(fā)現(xiàn)模式、根據(jù)數(shù)據(jù)講故事。神經(jīng)網(wǎng)絡(luò)的特征就是可以從數(shù)據(jù)中學(xué)習(xí)。所謂“從數(shù)據(jù)中學(xué)習(xí)”,是指可以由數(shù)據(jù)自動決定權(quán)重參數(shù)的值。在實際的神經(jīng)網(wǎng)絡(luò)中,參數(shù)的數(shù)量成千上萬,在層數(shù)更深的深度學(xué)習(xí)中,參數(shù)的數(shù)量甚至可以上億,想要人工決定這些參數(shù)的值是不可能的。所以神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)的本質(zhì),就是它自動調(diào)參(權(quán)重、偏置),讓損失函數(shù)不斷降低至滿意。
機(jī)器學(xué)習(xí)的方法中,由機(jī)器從收集到的數(shù)據(jù)中找出規(guī)律性。與從零開始想出算法相比,這種方法可以更高效地解決問題,也能減輕人的負(fù)擔(dān)。但是需要注意的是,將圖像轉(zhuǎn)換為向量時使用的特征量仍是由人設(shè)計的。對于不同的問題,必須使用合適的特征量(必須設(shè)計專門的特征量),才能得到好的結(jié)果。比如,為了區(qū)分狗的臉部,人們需要考慮與用于識別5的特征量不同的其他特征量。也就是說,即使使用特征量和機(jī)器學(xué)習(xí)的方法,也需要針對不同的問題人工考慮合適的特征量。而在神經(jīng)網(wǎng)絡(luò)中,連圖像中包含的重要特征量也都是由機(jī)器來學(xué)習(xí)的。
深度學(xué)習(xí)有時也稱為端到端機(jī)器學(xué)習(xí)(end-to-end machine learning)。這里所說的端到端是指從一端到另一端的意思,也就是從原始數(shù)據(jù)(輸入)中獲得目標(biāo)結(jié)果(輸出)的意思。神經(jīng)網(wǎng)絡(luò)的優(yōu)點(diǎn)是對所有的問題都可以用同樣的流程來解決。比如,不管要求解的問題是識別5,還是識別狗,抑或是識別人臉,神經(jīng)網(wǎng)絡(luò)都是通過不斷地學(xué)習(xí)所提供的數(shù)據(jù),嘗試發(fā)現(xiàn)待求解的問題的模式。也就是說,與待處理的問題無關(guān),神經(jīng)網(wǎng)絡(luò)可以將數(shù)據(jù)直接作為原始數(shù)據(jù),進(jìn)行“端對端”的學(xué)習(xí)。
損失函數(shù)
神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)通過某個指標(biāo)表示現(xiàn)在的狀態(tài)。然后,以這個指標(biāo)為基準(zhǔn),尋找最優(yōu)權(quán)重參數(shù)。神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中所用的指標(biāo)稱為損失函數(shù)(loss function)。這個損失函數(shù)可以使用任意函數(shù),但一般用均方誤差和交叉熵誤差等。
在進(jìn)行神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)時,不能將識別精度作為指標(biāo)。因為如果以識別精度為指標(biāo),則參數(shù)的導(dǎo)數(shù)在絕大多數(shù)地方都會變?yōu)?。此時識別精度為32%。如果以識別精度為指標(biāo),即使稍微改變權(quán)重參數(shù)的值,識別精度也仍將保持在32%,不會出現(xiàn)變化。也就是說,僅僅微調(diào)參數(shù),是無法改善識別精度的。即便識別精度有所改善,它的值也不會像32.0123 …%這樣連續(xù)變化,而是變?yōu)?3%、34%這樣的不連續(xù)的、離散的值。而如果把損失函數(shù)作為指標(biāo),則當(dāng)前損失函數(shù)的值可以表示為0.92543 …這樣的值。并且,如果稍微改變一下參數(shù)的值,對應(yīng)的損失函數(shù)也會像0.93432…這樣發(fā)生連續(xù)性的變化。
均方誤差
就是所有數(shù)據(jù)前向傳播的輸出與對應(yīng)標(biāo)簽值相減再平方再加起來,二分之一是為了讓后面的求導(dǎo)省去系數(shù)。
交叉熵誤差
log表示以e為底數(shù)的自然對數(shù)(log e)。yk是神經(jīng)網(wǎng)絡(luò)的輸出,tk是正確解標(biāo)簽。并且,tk中只有正確解標(biāo)簽的索引為1,其他均為0(one-hot表示)。因此,式子實際上只計算對應(yīng)正確解標(biāo)簽的輸出的自然對數(shù)。比如,假設(shè)正確解標(biāo)簽的索引是“2”,與之對應(yīng)的神經(jīng)網(wǎng)絡(luò)的輸出是0.6,則交叉熵誤差是?log 0.6 = 0.51;若“2”對應(yīng)的輸出是0.1,則交叉熵誤差為?log 0.1 = 2.30。也就是說,交叉熵誤差的值是由正確解標(biāo)簽所對應(yīng)的輸出結(jié)果決定的。從圖中可以知道,若輸出(一般經(jīng)過softmax層,表示分類的概率)越接近1,表示正確類別上的概率越大,則交叉熵誤差就越接近0,誤差就越小,很不錯。
mini-batch
機(jī)器學(xué)習(xí)使用訓(xùn)練數(shù)據(jù)進(jìn)行學(xué)習(xí)。使用訓(xùn)練數(shù)據(jù)進(jìn)行學(xué)習(xí),嚴(yán)格來說,就是針對訓(xùn)練數(shù)據(jù)計算損失函數(shù)的值,找出使該值盡可能小的參數(shù)。MNIST數(shù)據(jù)集的訓(xùn)練數(shù)據(jù)有60000個,如果以全部數(shù)據(jù)為對象求損失函數(shù)的和,則計算過程需要花費(fèi)較長的時間。再者,如果遇到大數(shù)據(jù),數(shù)據(jù)量會有幾百萬、幾千萬之多,這種情況下以全部數(shù)據(jù)為對象計算損失函數(shù)是不現(xiàn)實的。因此,我們從全部數(shù)據(jù)中選出一部分,作為全部數(shù)據(jù)的“近似”。神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)也是從訓(xùn)練數(shù)據(jù)中選出一批數(shù)據(jù)(稱為mini-batch,小批量),然后對每個mini-batch進(jìn)行學(xué)習(xí)。比如,從60000個訓(xùn)練數(shù)據(jù)中隨機(jī)選擇100筆,再用這100筆數(shù)據(jù)進(jìn)行學(xué)習(xí)。這種學(xué)習(xí)方式稱為mini-batch學(xué)習(xí)。
梯度法
機(jī)器學(xué)習(xí)的主要任務(wù)是在學(xué)習(xí)時尋找最優(yōu)參數(shù)。同樣地,神經(jīng)網(wǎng)絡(luò)也必須在學(xué)習(xí)時找到最優(yōu)參數(shù)(權(quán)重和偏置)。這里所說的最優(yōu)參數(shù)是指損失函數(shù)。取最小值時的參數(shù)。但是,一般而言,損失函數(shù)很復(fù)雜,參數(shù)空間龐大,我們不知道它在何處能取得最小值。而通過巧妙地使用梯度來尋找函數(shù)最小值(或者盡可能小的值)的方法就是梯度法。這里需要注意的是,**梯度表示的是各點(diǎn)處的函數(shù)值減小最多的方向。因此,無法保證梯度所指的方向就是函數(shù)的最小值或者真正應(yīng)該前進(jìn)的方向。**實際上,在復(fù)雜的函數(shù)中,梯度指示的方向基本上都不是函數(shù)值最小處。
雖然梯度的方向并不一定指向最小值,但沿著它的方向能夠最大限度地減小函數(shù)的值。因此,在尋找函數(shù)的最小值(或者盡可能小的值)的位置的任務(wù)中,要以梯度的信息為線索,決定前進(jìn)的方向。 在梯度法中,函數(shù)的取值從當(dāng)前位置沿著梯度方向前進(jìn)一定距離,然后在新的地方重新求梯度,再沿著新梯度方向前進(jìn),如此反復(fù),不斷地沿梯度方向前進(jìn)。 像這樣,通過不斷地沿梯度方向前進(jìn),逐漸減小函數(shù)值的過程就是梯度法(gradient method)。梯度法是解決機(jī)器學(xué)習(xí)中最優(yōu)化問題的常用方法,特別是在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中經(jīng)常被使用。
η 表示更新量,在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中,稱為學(xué)習(xí)率(learning rate)。學(xué)習(xí)率決定在一次學(xué)習(xí)中,應(yīng)該學(xué)習(xí)多少,以及在多大程度上更新參數(shù)。實驗結(jié)果表明,**學(xué)習(xí)率過大的話,會發(fā)散成一個很大的值;反過來,學(xué)習(xí)率過小的話,基本上沒怎么更新就結(jié)束了。**也就是說,設(shè)定合適的學(xué)習(xí)率是一個很重要的問題。
上面式子表示更新一次的式子,這個步驟會反復(fù)執(zhí)行。也就是說,每一步都按式子更新變量的值,通過反復(fù)執(zhí)行此步驟,逐漸減小函數(shù)值。雖然這里只展示了有兩個變量時的更新過程,但是即便增加變量的數(shù)量,也可以通過類似的式子(各個變量的偏導(dǎo)數(shù))進(jìn)行更新。
像學(xué)習(xí)率、權(quán)重初始值等的參數(shù)稱為超參數(shù)。這是一種和神經(jīng)網(wǎng)絡(luò)的參數(shù)(權(quán)重和偏置)性質(zhì)不同的參數(shù)。相對于神經(jīng)網(wǎng)絡(luò)的權(quán)重參數(shù)是通過訓(xùn)練數(shù)據(jù)和學(xué)習(xí)算法自動獲得的,學(xué)習(xí)率這樣的超參數(shù)則是人工設(shè)定的。一般來說,超參數(shù)需要嘗試多個值,以便找到一種可以使學(xué)習(xí)順利進(jìn)行的設(shè)定,這篇博客最后兩章會講到超參數(shù)的優(yōu)化問題。
在上圖的gradient_descent()函數(shù)中,參數(shù)f是要進(jìn)行最優(yōu)化的函數(shù),init_x是初始值,lr是學(xué)習(xí)率learning rate,step_num是梯度法的重復(fù)次數(shù)。numerical_gradient(f,x)會求函數(shù)的梯度,用該梯度乘以學(xué)習(xí)率得到的值進(jìn)行更新操作,由step_num指定重復(fù)的次數(shù)。使用這個函數(shù)可以求函數(shù)的極小值,順利的話,還可以求函數(shù)的最小值。
在右圖的numerical_gradient()函數(shù)里使用了數(shù)值微分的方法算梯度,比較簡單,但計算會相對繁瑣,等下介紹的反向傳播能極快地完成梯度的運(yùn)算。
更多梯度法的細(xì)節(jié),這里不好展開,可以康康我的博客哦《無廢話的機(jī)器學(xué)習(xí)筆記(三)(梯度下降)》
神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)全貌!
前提
神經(jīng)網(wǎng)絡(luò)存在合適的權(quán)重和偏置,調(diào)整權(quán)重和偏置以便擬合訓(xùn)練數(shù)據(jù)的過程稱為“學(xué)習(xí)”。神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)分成下面4個步驟。
步驟1(mini-batch)
從訓(xùn)練數(shù)據(jù)中隨機(jī)選出一部分?jǐn)?shù)據(jù),這部分?jǐn)?shù)據(jù)稱為mini-batch。我們的目標(biāo)是減小mini-batch的損失函數(shù)的值。
步驟2(計算梯度)
為了減小mini-batch的損失函數(shù)的值,需要求出各個權(quán)重參數(shù)的梯度。梯度表示損失函數(shù)的值減小最多的方向。
步驟3(更新參數(shù))
將權(quán)重參數(shù)沿梯度方向進(jìn)行微小更新。
步驟4(算誤差、精度)
每次循環(huán)都算一下誤差,若到一次epoch,算一下精度。
步驟5(重復(fù))
重復(fù)步驟1、步驟2、步驟3、步驟4。
epoch是一個單位。一個 epoch表示學(xué)習(xí)中所有訓(xùn)練數(shù)據(jù)均被使用過一次時的更新次數(shù)。比如,對于 10000筆訓(xùn)練數(shù)據(jù),用大小為 100筆數(shù)據(jù)的mini-batch進(jìn)行學(xué)習(xí)時,重復(fù)隨機(jī)梯度下降法 100次,所有的訓(xùn)練數(shù)據(jù)就都被“看過”了A。此時,100次就是一個 epoch。
代碼的實現(xiàn)可以看這篇!《純手?jǐn)]一個神經(jīng)網(wǎng)絡(luò)》
小總結(jié)
誤差反向傳播法
上面,我們介紹了神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí),并通過數(shù)值微分計算了神經(jīng)網(wǎng)絡(luò)的權(quán)重參數(shù)的梯度(嚴(yán)格來說,是損失函數(shù)關(guān)于權(quán)重參數(shù)的梯度)。數(shù)值微分雖然簡單,也容易實現(xiàn),但缺點(diǎn)是計算上比較費(fèi)時間。本章我們將學(xué)習(xí)一個能夠高效計算權(quán)重參數(shù)的梯度的方法——誤差反向傳播法。
首先介紹計算圖,計算圖將計算過程用圖形表示出來。這里說的圖形是數(shù)據(jù)結(jié)構(gòu)圖,通過多個節(jié)點(diǎn)和邊表示(連接節(jié)點(diǎn)的直線稱為“邊”)。
問題1:太郎在超市買了2個100日元一個的蘋果,消費(fèi)稅是10%,請計算支付金額。
計算圖將復(fù)雜的計算分割成簡單的局部計算,和流水線作業(yè)一樣,將局部計算的結(jié)果傳遞給下一個節(jié)點(diǎn)。在將復(fù)雜的計算分解成簡單的計算這一點(diǎn)上與汽車的組裝有相似之處。
計算圖的優(yōu)點(diǎn):
- 能局部計算
- 能保存中間計算結(jié)果(方便反向傳播)
- 可直觀表現(xiàn)反向傳播法求導(dǎo)數(shù)
鏈?zhǔn)椒▌t(chain rule)!
介紹鏈?zhǔn)椒▌t時,我們需要先從復(fù)合函數(shù)說起。復(fù)合函數(shù)是由多個函數(shù)構(gòu)成的函數(shù)。比如,z = (x + y)2是由下式所示的兩個式子構(gòu)成的。
如果某個函數(shù)由復(fù)合函數(shù)表示,則該復(fù)合函數(shù)的導(dǎo)數(shù)可以用構(gòu)成復(fù)合函數(shù)的各個函數(shù)的導(dǎo)數(shù)的乘積表示。
反向傳播的計算順序是,先將節(jié)點(diǎn)的輸入信號乘以節(jié)點(diǎn)的局部導(dǎo)數(shù)(偏導(dǎo)數(shù)),然后再傳遞給下一個節(jié)點(diǎn)。反向傳播是基于鏈?zhǔn)椒▌t的。
反向傳播
上面介紹了計算圖的反向傳播是基于鏈?zhǔn)椒▌t成立的。這里將以“加法+”和“乘法×”等運(yùn)算為例,介紹反向傳播的結(jié)構(gòu)。
加法
首先來考慮加法節(jié)點(diǎn)的反向傳播。這里以z = x + y為對象,觀察它的反向傳播。z = x + y的導(dǎo)數(shù)可由下式計算出來。
反向傳播將從上游傳過來的導(dǎo)數(shù)乘以1,然后傳向下游。也就是說,因為加法節(jié)點(diǎn)的反向傳播只乘以1,所以輸入的值會原封不動地流向下一個節(jié)點(diǎn)。
所以看見加法節(jié)點(diǎn),無腦地將后面?zhèn)鱽淼闹低聜骶托?/strong>!
用Python實現(xiàn)加法層:
乘法
接下來,我們看一下乘法節(jié)點(diǎn)的反向傳播。這里我們考慮z = xy。這個式子的導(dǎo)數(shù)用式表示:
乘法的反向傳播會將上游的值乘以正向傳播時的輸入信號的“翻轉(zhuǎn)值”后傳遞給下游,如下圖。
買蘋果的例子也能輕松完成反向傳播:
用Python實現(xiàn)乘法層:
MulLayer要初始化self.x和self.y是因為backward要用到這倆變量,所以需要在forward后保存下來(保存中間計算結(jié)果)。而AddLayer的backward直接無腦傳就行,不用初始化什么變量。
用Python從零搭建買蘋果和橘子的計算圖:
這個實現(xiàn)稍微有一點(diǎn)長,但是每一條命令都很簡單。首先,生成必要的層,以合適的順序調(diào)用正向傳播的forward()方法。然后,用與正向傳播相反的順序調(diào)用反向傳播的backward()方法,就可以求出想要的導(dǎo)數(shù)。
激活函數(shù)層的實現(xiàn)
現(xiàn)在,我們將計算圖的思路應(yīng)用到神經(jīng)網(wǎng)絡(luò)中。這里,我們把構(gòu)成神經(jīng)網(wǎng)絡(luò)的層實現(xiàn)為一個類。
ReLU層的實現(xiàn)
如果正向傳播時的輸入x大于0,則反向傳播會將上游的值原封不動地傳給下游。反過來,如果正向傳播時的x小于等于0,則反向傳播中傳給下游的信號將停在此處。
ReLU層的作用就像電路中的開關(guān)一樣。正向傳播時,有電流通過的話,就將開關(guān)設(shè)為 ON;沒有電流通過的話,就將開關(guān)設(shè)為 OFF。反向傳播時,開關(guān)為ON的話,電流會直接通過;開關(guān)為OFF的話,則不會有電流通過。
Sigmoid層的實現(xiàn)
Affine層的實現(xiàn)
Softmax-with-Loss層的實現(xiàn)
softmax函數(shù)會將輸入值正規(guī)化之后再輸出。
考慮到這里也包含作為損失函數(shù)的交叉熵誤差(cross entropy error),所以稱為“Softmax-with-Loss層”。
圖中要注意的是反向傳播的結(jié)果。Softmax層的反向傳播得到了(y1 ? t1, y2 ? t2, y3 ? t3)這樣“漂亮”的結(jié)果。由于(y1, y2, y3)是Softmax層的輸出,(t1, t2, t3)是監(jiān)督數(shù)據(jù),所以(y1 ? t1, y2 ? t2, y3 ? t3)是Softmax層的輸出和標(biāo)簽的差分。神經(jīng)網(wǎng)絡(luò)的反向傳播會把這個差分表示的誤差傳遞給前面的層,這是神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)中的重要性質(zhì)。神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)的目的就是通過調(diào)整權(quán)重參數(shù),使神經(jīng)網(wǎng)絡(luò)的輸出(Softmax的輸出)接近標(biāo)簽。因此,必須將神經(jīng)網(wǎng)絡(luò)的輸出與標(biāo)簽的誤差高效地傳遞給前面的層。剛剛的(y1 ? t1, y2 ? t2, y3 ? t3)正是Softmax層的輸出與標(biāo)簽的差,直截了當(dāng)?shù)乇硎玖水?dāng)前神經(jīng)網(wǎng)絡(luò)的輸出與標(biāo)簽的誤差。
這里考慮一個具體的例子,比如思考標(biāo)簽是(0,1,0),Softmax層的輸出是 (0.3,0.2,0.5) 的情形。因為正確解標(biāo)簽處的概率是0.2(20%),這個時候的神經(jīng)網(wǎng)絡(luò)未能進(jìn)行正確的識別。此時,Softmax層的反向傳播傳遞的是 (0.3, ?0.8,0.5) 這樣一個大的誤差。因為這個大的誤差會向前面的層傳播,所以Softmax層前面的層會從這個大的誤差中學(xué)習(xí)到“大”的內(nèi)容。
使用交叉熵誤差作為 softmax函數(shù)的損失函數(shù)后,反向傳播得到(y1 ? t1, y2 ? t2, y3 ? t3)這樣“漂亮”的結(jié)果。實際上,這樣 “漂亮”的結(jié)果并不是偶然的,而是為了得到這樣的結(jié)果,特意設(shè)計了交叉熵誤差函數(shù)!回歸問題中輸出層使用“恒等函數(shù)”,損失函數(shù)使用“平方和誤差”,也是出于同樣的理由。也就是說,使用“平方和誤差”作為“恒等函數(shù)”的損失函數(shù),反向傳播才能得到(y1 ?t1, y2 ? t2, y3 ? t3)這樣“漂亮”的結(jié)果。
小總結(jié)
通過像組裝樂高積木一樣組裝上面實現(xiàn)的層,可以構(gòu)建神經(jīng)網(wǎng)絡(luò)。
學(xué)習(xí)中的技巧
參數(shù)的更新(optimizer)
神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)的目的是找到使損失函數(shù)的值盡可能小的參數(shù)。這是尋找最優(yōu)參數(shù)的問題,解決這個問題的過程稱為最優(yōu)化(optimization)。在深度學(xué)習(xí)中,優(yōu)化器(optimizer)有很多種選擇,如SGD、Momentum、AdaGrad、Adam等,下面一一介紹。
SGD
為了找到最優(yōu)參數(shù),我們將參數(shù)的梯度(導(dǎo)數(shù))作為了線索。使用參數(shù)的梯度,沿梯度方向更新參數(shù),并重復(fù)這個步驟多次,從而逐漸靠近最優(yōu)參數(shù),這個過程稱為隨機(jī)梯度下降法(stochastic gradient descent),簡稱SGD。隨機(jī)是因為我們加入了mini-batch,即每次我們只隨機(jī)選擇一定數(shù)量的數(shù)據(jù)參與學(xué)習(xí)。SGD相比正常的GD是一個簡單、高效的方法,比起胡亂地搜索參數(shù)空間,不失為一種“聰明”的方法。
SGD呈“之”字形移動。這是一個相當(dāng)?shù)托У穆窂?。也就是說,SGD的缺點(diǎn)是,如果函數(shù)的形狀非均向(anisotropic),比如呈延伸狀,搜索的路徑就會非常低效。因此,我們需要比單純朝梯度方向前進(jìn)的SGD更聰明的方法。SGD低效的根本原因是,梯度的方向并沒有指向最小值的方向。
Momentum
Momentum是“動量”的意思,和物理有關(guān)。因為SGD只關(guān)注了梯度,但很多時候梯度下降的方向并不是最小值,很有可能是一片平地或一個小坑,當(dāng)梯度為0,參數(shù)就會立刻停止更新,小球會立刻停止?jié)L動。為了改善這種問題,給小球加上速度這一概念,當(dāng)往下滑動時,即使遇到了平地或小坑,也能由積累的速度助其越過障礙,這種方法就是Momentum!
我們可以看到,即使梯度為0,因為之前積累的v不為0,所以w會繼續(xù)更新!
AdaGrad
在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中,學(xué)習(xí)率(數(shù)學(xué)式中記為η)的值很重要。學(xué)習(xí)率過小,會導(dǎo)致學(xué)習(xí)花費(fèi)過多時間;反過來,學(xué)習(xí)率過大,則會導(dǎo)致學(xué)習(xí)發(fā)散而不能正確進(jìn)行。在關(guān)于學(xué)習(xí)率的有效技巧中,有一種被稱為學(xué)習(xí)率衰減(learning ratedecay)的方法,即隨著學(xué)習(xí)的進(jìn)行,使學(xué)習(xí)率逐漸減小。實際上,一開始“多”學(xué),然后逐漸“少”學(xué)的方法,在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中經(jīng)常被使用。
AdaGrad會為參數(shù)的每個元素適當(dāng)?shù)卣{(diào)整學(xué)習(xí)率,與此同時進(jìn)行學(xué)習(xí)。
Adam
Momentum參照小球在碗中滾動的物理規(guī)則進(jìn)行移動,AdaGrad為參數(shù)的每個元素適當(dāng)?shù)卣{(diào)整更新步伐。合體!變成Adam!Adam是2015年提出的新方法。它的理論有些復(fù)雜,直觀地講,就是融合了Momentum和AdaGrad的方法。通過組合前面兩個方法的優(yōu)點(diǎn),有望實現(xiàn)參數(shù)空間的高效搜索。此外,進(jìn)行**超參數(shù)的“偏置校正”**也是Adam的特征。
Adam會設(shè)置 3個超參數(shù)。一個是學(xué)習(xí)率(論文中以α出現(xiàn)),另外兩個是一次momentum系數(shù)β1和二次momentum系數(shù)β2。根據(jù)論文,標(biāo)準(zhǔn)的設(shè)定值是β1為 0.9,β2 為 0.999。設(shè)置了這些值后,大多數(shù)情況下都能順利運(yùn)行。
小總結(jié)
上面我們介紹了SGD、Momentum、AdaGrad、Adam這4種方法,那么用哪種方法好呢?非常遺憾,(目前)并不存在能在所有問題中都表現(xiàn)良好的方法。這4種方法各有各的特點(diǎn),都有各自擅長解決的問題和不擅長解決的問題。很多研究中至今仍在使用SGD。Momentum和AdaGrad也是值得一試的方法。一般而言,與SGD相比,其他3種方法可以學(xué)習(xí)得更快,有時最終的識別精度也更高。最近,很多研究人員和技術(shù)人員都喜歡用Adam。
權(quán)重的初始值
在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中,權(quán)重的初始值特別重要。實際上,設(shè)定什么樣的權(quán)重初始值,經(jīng)常關(guān)系到神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)能否成功。
為什么不能將權(quán)重初始值設(shè)為0呢?嚴(yán)格地說,為什么不能將權(quán)重初始值設(shè)成一樣的值呢? 這是因為在誤差反向傳播法中,所有的權(quán)重值都會進(jìn)行相同的更新。比如,在2層神經(jīng)網(wǎng)絡(luò)中,假設(shè)第1層和第2層的權(quán)重為0。這樣一來,正向傳播時,因為輸入層的權(quán)重為0,所以第2層的神經(jīng)元全部會被傳遞相同的值。第2層的神經(jīng)元中全部輸入相同的值,這意味著反向傳播時第2層的權(quán)重全部都會進(jìn)行相同的更新。因此,權(quán)重被更新為相同的值,并擁有了對稱的值(重復(fù)的值)。這使得神經(jīng)網(wǎng)絡(luò)擁有許多不同的權(quán)重的意義喪失了。為了防止“權(quán)重均一化”(嚴(yán)格地講,是為了瓦解權(quán)重的對稱結(jié)構(gòu)),必須隨機(jī)生成初始值。
因為激活函數(shù)sigmoid函數(shù)是S型函數(shù),隨著輸出不斷地靠近0(或者靠近1),它的導(dǎo)數(shù)的值逐漸接近0。因此,偏向0和1的數(shù)據(jù)分布會造成反向傳播中梯度的值不斷變小,最后消失。這個問題稱為梯度消失(gradient vanishing)。層次加深的深度學(xué)習(xí)中,梯度消失的問題可能會更加嚴(yán)重。
Xavier初始值
為了使各層的激活值呈現(xiàn)出具有相同廣度的分布,推薦使用Xavier初始值。
使用Xavier初始值后,前一層的節(jié)點(diǎn)數(shù)越多,要設(shè)定為目標(biāo)節(jié)點(diǎn)的初始值的權(quán)重尺度就越小。
He初始值
Xavier初始值是以激活函數(shù)是線性函數(shù)為前提而推導(dǎo)出來的。因為sigmoid函數(shù)和tanh函數(shù)左右對稱,且中央附近可以視作線性函數(shù),所以適合使用Xavier初始值。但當(dāng)激活函數(shù)使用ReLU時,一般推薦使用ReLU專用的初始值,也就是Kaiming He等人推薦的初始值,也稱為“He初始值”。
小總結(jié)
當(dāng)激活函數(shù)使用ReLU時,權(quán)重初始值使用He初始值,當(dāng)激活函數(shù)為sigmoid或tanh等S型曲線函數(shù)時,初始值使用Xavier初始值。這是目前的最佳實踐。
在神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中,權(quán)重初始值非常重要。很多時候權(quán)重初始值的設(shè)定關(guān)系到神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)能否成功。權(quán)重初始值的重要性容易被忽視,而任何事情的開始(初始值)總是關(guān)鍵的。
Batch Normalization
為了使各層擁有適當(dāng)?shù)膹V度,“強(qiáng)制性”地調(diào)整激活值的分布,Batch Normalization方法就是基于這個想法而產(chǎn)生的。簡單來說,就是在ReLU前正規(guī)化一下數(shù)據(jù)。
Batch Norm,顧名思義,以進(jìn)行學(xué)習(xí)時的mini-batch為單位,按mini-batch進(jìn)行正規(guī)化。具體而言,就是進(jìn)行使數(shù)據(jù)分布的均值為0、方差為1的正規(guī)化。
幾乎所有的情況下都是使用Batch Norm時學(xué)習(xí)進(jìn)行得更快。實際上,在不使用Batch Norm的情況下,如果不賦予一個尺度好的初始值,學(xué)習(xí)將完全無法進(jìn)行。通過使用Batch Norm,可以推動學(xué)習(xí)的進(jìn)行。并且,對權(quán)重初始值變得健壯(“對初始值健壯”表示不那么依賴初始值)。Batch Norm具備了如此優(yōu)良的性質(zhì),一定能應(yīng)用在更多場合中。
正規(guī)化
機(jī)器學(xué)習(xí)的問題中,過擬合是一個很常見的問題。過擬合指的是只能擬合訓(xùn)練數(shù)據(jù),但不能很好地擬合不包含在訓(xùn)練數(shù)據(jù)中的其他數(shù)據(jù)的狀態(tài)。機(jī)器學(xué)習(xí)的目標(biāo)是提高泛化能力,即便是沒有包含在訓(xùn)練數(shù)據(jù)里的未觀測數(shù)據(jù),也希望模型可以進(jìn)行正確的識別。
發(fā)生過擬合的原因:
- 模型擁有大量參數(shù)、表現(xiàn)力強(qiáng)。
- 訓(xùn)練數(shù)據(jù)少。
權(quán)重衰減(L1、L2正則化)
權(quán)值衰減是一直以來經(jīng)常被使用的一種抑制過擬合的方法。該方法通過在學(xué)習(xí)的過程中對大的權(quán)重進(jìn)行懲罰,來抑制過擬合。很多過擬合原本就是因為權(quán)重參數(shù)取值過大才發(fā)生的。正則化就是在損失函數(shù)后面加上權(quán)重,抑制權(quán)重過大的情況。
歡迎去康康我的博客《無廢話的機(jī)器學(xué)習(xí)筆記(番外)(數(shù)據(jù)集,方差-偏差,過擬合,正則化,降維)》哦。
Dropout
如果網(wǎng)絡(luò)的模型變得很復(fù)雜,只用權(quán)值衰減就難以應(yīng)對了。在這種情況下,我們經(jīng)常會使用Dropout 方法。
Dropout是一種在學(xué)習(xí)的過程中隨機(jī)刪除神經(jīng)元的方法。 訓(xùn)練時,隨機(jī)選出隱藏層的神經(jīng)元,然后將其刪除。被刪除的神經(jīng)元不再進(jìn)行信號的傳遞,如圖所示。訓(xùn)練時,每傳遞一次數(shù)據(jù),就會隨機(jī)選擇要刪除的神經(jīng)元。然后,測試時,雖然會傳遞所有的神經(jīng)元信號,但是對于各個神經(jīng)元的輸出,要乘上訓(xùn)練時的刪除比例后再輸出。
機(jī)器學(xué)習(xí)中經(jīng)常使用集成學(xué)習(xí)。所謂集成學(xué)習(xí),就是讓多個模型單獨(dú)進(jìn)行學(xué)習(xí),推理時再取多個模型的輸出的平均值。用神經(jīng)網(wǎng)絡(luò)的語境來說,比如,準(zhǔn)備 5個結(jié)構(gòu)相同(或者類似)的網(wǎng)絡(luò),分別進(jìn)行學(xué)習(xí),測試時,以這 5個網(wǎng)絡(luò)的輸出的平均值作為答案。實驗告訴我們,通過進(jìn)行集成學(xué)習(xí),神經(jīng)網(wǎng)絡(luò)的識別精度可以提高好幾個百分點(diǎn)。這個集成學(xué)習(xí)與 Dropout有密切的關(guān)系。這是因為可以將 Dropout理解為,通過在學(xué)習(xí)過程中隨機(jī)刪除神經(jīng)元,從而每一次都讓不同的模型進(jìn)行學(xué)習(xí)。并且,推理時,通過對神經(jīng)元的輸出乘以刪除比例(比如,0.5等),可以取得模型的平均值。也就是說,可以理解成,Dropout將集成學(xué)習(xí)的效果(模擬地)通過一個網(wǎng)絡(luò)實現(xiàn)了。
超參數(shù)的驗證
神經(jīng)網(wǎng)絡(luò)中,除了權(quán)重和偏置等參數(shù),超參數(shù)(hyper-parameter)也經(jīng)常出現(xiàn)。這里所說的超參數(shù)是指,比如各層的神經(jīng)元數(shù)量、batch大小、參數(shù)更新時的學(xué)習(xí)率或權(quán)值衰減等。如果這些超參數(shù)沒有設(shè)置合適的值,模型的性能就會很差。雖然超參數(shù)的取值非常重要,但是在決定超參數(shù)的過程中一般會伴隨很多的試錯。
調(diào)整超參數(shù)時,必須使用超參數(shù)專用的確認(rèn)數(shù)據(jù)。用于調(diào)整超參數(shù)的數(shù)據(jù),一般稱為驗證數(shù)據(jù)(validation data)。我們使用這個驗證數(shù)據(jù)來評估超參數(shù)的好壞。所以數(shù)據(jù)集一般事先分成訓(xùn)練數(shù)據(jù)、驗證數(shù)據(jù)、測試數(shù)據(jù)三部分(6:2:2)。
進(jìn)行超參數(shù)的最優(yōu)化時,逐漸縮小超參數(shù)的“好值”的存在范圍非常重要。所謂逐漸縮小范圍,是指一開始先大致設(shè)定一個范圍,從這個范圍中隨機(jī)選出一個超參數(shù)(采樣),用這個采樣到的值進(jìn)行識別精度的評估;然后,多次重復(fù)該操作,觀察識別精度的結(jié)果,根據(jù)這個結(jié)果縮小超參數(shù)的“好值”的范圍。通過重復(fù)這一操作,就可以逐漸確定超參數(shù)的合適范圍。在進(jìn)行神經(jīng)網(wǎng)絡(luò)的超參數(shù)的最優(yōu)化時,與網(wǎng)格搜索等有規(guī)律的搜索相比,隨機(jī)采樣的搜索方式效果更好。這是因為在多個超參數(shù)中,各個超參數(shù)對最終的識別精度的影響程度不同。
在超參數(shù)的最優(yōu)化中,要注意的是深度學(xué)習(xí)需要很長時間(比如,幾天或幾周)。因此,在超參數(shù)的搜索中,需要盡早放棄那些不符合邏輯的超參數(shù)。于是,在超參數(shù)的最優(yōu)化中,減少學(xué)習(xí)的epoch,縮短一次評估所需的時間是一個不錯的辦法。
小總結(jié)
卷積神經(jīng)網(wǎng)絡(luò) CNN
CNN被用于圖像識別、語音識別等各種場合,在圖像識別的比賽中,基于深度學(xué)習(xí)的方法幾乎都以CNN為基礎(chǔ)。
CNN和之前介紹的神經(jīng)網(wǎng)絡(luò)一樣,可以像樂高積木一樣通過組裝層來構(gòu)建。不過,CNN中新出現(xiàn)了卷積層(Convolution層)和池化層(Pooling層)。之前介紹的神經(jīng)網(wǎng)絡(luò)中,相鄰層的所有神經(jīng)元之間都有連接,這稱為全連接(fully-connected)。另外,我們用Affine層實現(xiàn)了全連接層。如果使用這個Affine層,一個5層的全連接的神經(jīng)網(wǎng)絡(luò)就可以通過圖所示的網(wǎng)絡(luò)結(jié)構(gòu)來實現(xiàn)。
那么,CNN會是什么樣的結(jié)構(gòu)呢?下圖是CNN的一個例子。
卷積層
在全連接層中,相鄰層的神經(jīng)元全部連接在一起,輸出的數(shù)量可以任意決定。全連接層存在什么問題呢?那就是數(shù)據(jù)的形狀被“忽視”了。 比如,輸入數(shù)據(jù)是圖像時,圖像通常是高、長、通道方向上的3維形狀。但是,向全連接層輸入時,需要將3維數(shù)據(jù)拉平為1維數(shù)據(jù)。實際上,前面提到的使用了MNIST數(shù)據(jù)集的例子中,輸入圖像就是1通道、高28像素、長28像素的(1, 28,28)形狀,但卻被排成1列,以784個數(shù)據(jù)的形式輸入到最開始的Affine層。
圖像是3維形狀,這個形狀中應(yīng)該含有重要的空間信息。比如,空間上鄰近的像素為相似的值、RBG的各個通道之間分別有密切的關(guān)聯(lián)性、相距較遠(yuǎn)的像素之間沒有什么關(guān)聯(lián)等,3維形狀中可能隱藏有值得提取的本質(zhì)模式。但是,因為全連接層會忽視形狀,將全部的輸入數(shù)據(jù)作為相同的神經(jīng)元(同一維度的神經(jīng)元)處理,所以無法利用與形狀相關(guān)的信息。
而卷積層可以保持形狀不變。當(dāng)輸入數(shù)據(jù)是圖像時,卷積層會以3維數(shù)據(jù)的形式接收輸入數(shù)據(jù),并同樣以3維數(shù)據(jù)的形式輸出至下一層。因此,在CNN中,可以(有可能)正確理解圖像等具有形狀的數(shù)據(jù)。
卷積運(yùn)算
卷積運(yùn)算對輸入數(shù)據(jù)應(yīng)用濾波器。在下面例子中,輸入數(shù)據(jù)是有高長方向的形狀的數(shù)據(jù),濾波器也一樣,有高長方向上的維度。假設(shè)用(height, width)表示數(shù)據(jù)和濾波器的形狀,則在本例中,輸入大小是(4, 4),濾波器大小是(3, 3),輸出大小是(2, 2)。另外,有的文獻(xiàn)中也會用“核”這個詞來表示這里所說的“濾波器”。卷積運(yùn)算以一定間隔滑動濾波器的窗口并應(yīng)用。這里所說的窗口是指圖中灰色的3 × 3的部分。如圖所示,將各個位置上濾波器的元素和輸入的對應(yīng)元素相乘,然后再求和(有時將這個計算稱為乘積累加運(yùn)算)。然后,將這個結(jié)果保存到輸出的對應(yīng)位置。將這個過程在所有位置都進(jìn)行一遍,就可以得到卷積運(yùn)算的輸出。
填充(padding)
在進(jìn)行卷積層的處理之前,有時要向輸入數(shù)據(jù)的周圍填入固定的數(shù)據(jù)(比如0等),這稱為填充(padding),是卷積運(yùn)算中經(jīng)常會用到的處理。比如,在圖中,對大小為(4, 4)的輸入數(shù)據(jù)應(yīng)用了幅度為1的填充?!胺葹?的填充”是指用幅度為1像素的0填充周圍。
通過填充,大小為(4, 4)的輸入數(shù)據(jù)變成了(6, 6)的形狀。然后,應(yīng)用大小為(3, 3)的濾波器,生成了大小為(4, 4)的輸出數(shù)據(jù)。這個例子中將填充設(shè)成了1,不過填充的值也可以設(shè)置成2、3等任意的整數(shù)。使用填充主要是為了調(diào)整輸出的大小。如果每次進(jìn)行卷積運(yùn)算都會縮小空間,那么在某個時刻輸出大小就有可能變?yōu)?1,導(dǎo)致無法再應(yīng)用卷積運(yùn)算。為了避免出現(xiàn)這樣的情況,就要使用填充。
步幅(stride)
應(yīng)用濾波器的位置間隔稱為步幅(stride)。之前的例子中步幅都是1,如果將步幅設(shè)為2,則如圖所示,應(yīng)用濾波器的窗口的間隔變?yōu)?個元素。
綜上,增大步幅后,輸出大小會變小。而增大填充后,輸出大小會變大。
3維數(shù)據(jù)卷積運(yùn)算
需要注意的是,在3維數(shù)據(jù)的卷積運(yùn)算中,輸入數(shù)據(jù)和濾波器的通道數(shù)要設(shè)為相同的值。在這個例子中,輸入數(shù)據(jù)和濾波器的通道數(shù)一致,均為3。濾波器大小可以設(shè)定為任意值(不過,每個通道的濾波器大小要全部相同)。這個例子中濾波器大小為(3, 3),但也可以設(shè)定為(2, 2)、(1, 1)、(5, 5)等任意值。再強(qiáng)調(diào)一下,通道數(shù)只能設(shè)定為和輸入數(shù)據(jù)的通道數(shù)相同的值。
池化層
池化是縮小高、長方向上的空間的運(yùn)算。比如,如圖7-14所示,進(jìn)行將2 × 2的區(qū)域集約成1個元素的處理,縮小空間大小。
就是求最大值,非常簡單,叫做Max池化。也有Average池化。Max池化是從目標(biāo)區(qū)域中取出最大值,Average池化則是計算目標(biāo)區(qū)域的平均值。
池化層的特征:
- 沒有要學(xué)習(xí)的參數(shù)
- 通道數(shù)不發(fā)生變化
- 對微小的位置變化具有魯棒性(健壯)
im2col
如果老老實實地實現(xiàn)卷積運(yùn)算,估計要重復(fù)好幾層的for語句。這樣的實現(xiàn)有點(diǎn)麻煩,而且,NumPy中存在使用for語句后處理變慢的缺點(diǎn)(NumPy中,訪問元素時最好不要用for語句)。這里,我們不使用for語句,而是使用im2col這個便利的函數(shù)進(jìn)行簡單的實現(xiàn)。
**im2col是一個函數(shù),將輸入數(shù)據(jù)展開以適合濾波器(權(quán)重)。**如圖所示,對3維的輸入數(shù)據(jù)應(yīng)用im2col后,數(shù)據(jù)轉(zhuǎn)換為2維矩陣(正確地講,是把包含批數(shù)量的4維數(shù)據(jù)轉(zhuǎn)換成了2維數(shù)據(jù))。im2col會把輸入數(shù)據(jù)展開以適合濾波器(權(quán)重)。具體地說,如圖所示,對于輸入數(shù)據(jù),將應(yīng)用濾波器的區(qū)域(3維方塊)橫向展開為1列。im2col會在所有應(yīng)用濾波器的地方進(jìn)行這個展開處理。
使用im2col展開輸入數(shù)據(jù)后,之后就只需將卷積層的濾波器(權(quán)重)縱向展開為1列,并計算2個矩陣的乘積即可(參照圖7-19)。這和全連接層的Affine層進(jìn)行的處理基本相同。
在濾波器的應(yīng)用區(qū)域重疊的情況下,使用im2col展開后,展開后的元素個數(shù)會多于原方塊的元素個數(shù)。因此,使用im2col的實現(xiàn)存在比普通的實現(xiàn)消耗更多內(nèi)存的缺點(diǎn)。但是,匯總成一個大的矩陣進(jìn)行計算,對計算機(jī)的計算頗有益處。比如,在矩陣計算的庫(線性代數(shù)庫)等中,矩陣計算的實現(xiàn)已被高度最優(yōu)化,可以高速地進(jìn)行大矩陣的乘法運(yùn)算。因此,通過歸結(jié)到矩陣計算上,可以有效地利用線性代數(shù)庫。
卷積層的初始化方法將濾波器(權(quán)重)、偏置、步幅、填充作為參數(shù)接收。濾波器是 (FN, C, FH, FW)的 4 維形狀。另外,F(xiàn)N、C、FH、FW分別是 FilterNumber(濾波器數(shù)量)、Channel、Filter Height、Filter Width的縮寫。這里用粗體字表示Convolution層的實現(xiàn)中的重要部分。在這些粗體字部分,用im2col展開輸入數(shù)據(jù),并用reshape將濾波器展開為2維數(shù)組。然后,計算展開后的矩陣的乘積。通過在reshape時指定為-1,reshape函數(shù)會自動計算-1維度上的元素個數(shù),以使多維數(shù)組的元素個數(shù)前后一致。(10, 3, 5, 5)形狀的數(shù)組的元素個數(shù)共有750個,指定reshape(10,-1)后,就會轉(zhuǎn)換成(10, 75)形狀的數(shù)組。transpose會更改多維數(shù)組的軸的順序。
池化層也easy:
CNN的實現(xiàn)
搭積木!
如果堆疊了多層卷積層,則隨著層次加深,提取的信息也愈加復(fù)雜、抽象,這是深度學(xué)習(xí)中很有意思的一個地方。最開始的層對簡單的邊緣有響應(yīng),接下來的層對紋理有響應(yīng),再后面的層對更加復(fù)雜的物體部件有響應(yīng)。也就是說,隨著層次加深,神經(jīng)元從簡單的形狀向“高級”信息變化。換句話說,就像我們理解東西的“含義”一樣,響應(yīng)的對象在逐漸變化。
是不是想趕緊動起手來,看看這篇!《最簡單用TensorFlow實現(xiàn)CNN》
具有代表性的CNN
LeNet
LeNet在1998年被提出,是進(jìn)行手寫數(shù)字識別的網(wǎng)絡(luò)。如圖所示,它有連續(xù)的卷積層和池化層(正確地講,是只“抽選元素”的子采樣層),最后經(jīng)全連接層輸出結(jié)果。
和“現(xiàn)在的CNN”相比,LeNet有幾個不同點(diǎn)。第一個不同點(diǎn)在于激活函數(shù)。LeNet中使用sigmoid函數(shù),而現(xiàn)在的CNN中主要使用ReLU函數(shù)。此外,原始的LeNet中使用子采樣(subsampling)縮小中間數(shù)據(jù)的大小,而現(xiàn)在的CNN中Max池化是主流。
AlexNet
在LeNet問世20多年后,AlexNet被發(fā)布出來。AlexNet是引發(fā)深度學(xué)習(xí)熱潮的導(dǎo)火線,不過它的網(wǎng)絡(luò)結(jié)構(gòu)和LeNet基本上沒有什么不同。
AlexNet疊有多個卷積層和池化層,最后經(jīng)由全連接層輸出結(jié)果。雖然結(jié)構(gòu)上AlexNet和LeNet沒有大的不同,但有以下幾點(diǎn)差異:
1、激活函數(shù)使用ReLU
2、使用進(jìn)行局部正規(guī)化的LRN(Local Response Normalization)層
3、使用Dropout
VGG
VGG是由卷積層和池化層構(gòu)成的基礎(chǔ)的CNN。不過,如圖所示,它的特點(diǎn)在于將有權(quán)重的層(卷積層或者全連接層)疊加至16層(或者19層),具備了深度(根據(jù)層的深度,有時也稱為“VGG16”或“VGG19”)。VGG中需要注意的地方是,基于3×3的小型濾波器的卷積層的運(yùn)算是連續(xù)進(jìn)行的。如圖所示,重復(fù)進(jìn)行“卷積層重疊2次到4次,再通過池化層將大小減半”的處理,最后經(jīng)由全連接層輸出結(jié)果。
GoogLeNet
圖中的矩形表示卷積層、池化層等。只看圖的話,這似乎是一個看上去非常復(fù)雜的網(wǎng)絡(luò)結(jié)構(gòu),但實際上它基本上和之前介紹的CNN結(jié)構(gòu)相同。不過,GoogLeNet的特征是,網(wǎng)絡(luò)不僅在縱向上有深度,在橫向上也有寬度,這稱為“Inception結(jié)構(gòu)”。Inception結(jié)構(gòu)使用了多個大小不同的濾波器(和池化),最后再合并它們的結(jié)果。GoogLeNet的特征就是將這個Inception結(jié)構(gòu)用作一個構(gòu)件(構(gòu)成元素)。此外,在GoogLeNet中,很多地方都使用了大小為1 × 1的濾波器的卷積層。這個1 × 1的卷積運(yùn)算通過在通道方向上減小大小,有助于減少參數(shù)和實現(xiàn)高速化處理。
ResNet
ResNet是微軟團(tuán)隊開發(fā)的網(wǎng)絡(luò)。它的特征在于具有比以前的網(wǎng)絡(luò)更深的結(jié)構(gòu)。我們已經(jīng)知道加深層對于提升性能很重要。但是,在深度學(xué)習(xí)中,過度加深層的話,很多情況下學(xué)習(xí)將不能順利進(jìn)行,導(dǎo)致最終性能不佳。ResNet中,為了解決這類問題,導(dǎo)入了“快捷結(jié)構(gòu)”(也稱為“捷徑”或“小路”)。導(dǎo)入這個快捷結(jié)構(gòu)后,就可以隨著層的加深而不斷提高性能了(當(dāng)然,層的加深也是有限度的)。
小總結(jié)
關(guān)于網(wǎng)絡(luò)結(jié)構(gòu),其實上面的結(jié)構(gòu)沒有太大的不同。但是,圍繞它們的環(huán)境和計算機(jī)技術(shù)有了很大的進(jìn)步。具體地說,現(xiàn)在任何人都可以獲得大量的數(shù)據(jù)。而且,擅長大規(guī)模并行計算的GPU得到普及,高速進(jìn)行大量的運(yùn)算已經(jīng)成為可能。大數(shù)據(jù)和GPU已成為深度學(xué)習(xí)發(fā)展的巨大的原動力。大多數(shù)情況下,深度學(xué)習(xí)(加深了層次的網(wǎng)絡(luò))存在大量的參數(shù)。因此,學(xué)習(xí)需要大量的計算,并且需要使那些參數(shù)“滿意”的大量數(shù)據(jù)??梢哉f是 GPU和大數(shù)據(jù)給這些課題帶來了希望。
深度學(xué)習(xí)
更深
關(guān)于加深層的重要性,現(xiàn)狀是理論研究還不夠透徹。盡管目前相關(guān)理論還比較貧乏,但是有幾點(diǎn)可以從過往的研究和實驗中得以解釋。從很多比賽的結(jié)果顯示,最近前幾名的方法多是基于深度學(xué)習(xí)的,并且有逐漸加深網(wǎng)絡(luò)的層的趨勢。也就是說,可以看到層越深,識別性能也越高。
加深層可以減少網(wǎng)絡(luò)的參數(shù)數(shù)量。說得詳細(xì)一點(diǎn),就是與沒有加深層的網(wǎng)絡(luò)相比,加深了層的網(wǎng)絡(luò)可以用更少的參數(shù)達(dá)到同等水平(或者更強(qiáng))的表現(xiàn)力。這一點(diǎn)結(jié)合卷積運(yùn)算中的濾波器大小來思考就好理解了。
一次5 × 5的卷積運(yùn)算的區(qū)域可以由兩次3 × 3的卷積運(yùn)算抵充。并且,相對于前者的參數(shù)數(shù)量25(5 × 5),后者一共是18(2 × 3 × 3),通過疊加卷積層,參數(shù)數(shù)量減少了。而且,這個參數(shù)數(shù)量之差會隨著層的加深而變大。
加深層可以使學(xué)習(xí)更加高效。與沒有加深層的網(wǎng)絡(luò)相比,通過加深層,可以減少學(xué)習(xí)數(shù)據(jù),從而高效地進(jìn)行學(xué)習(xí)。通過加深網(wǎng)絡(luò),就可以分層次地分解需要學(xué)習(xí)的問題。因此,各層需要學(xué)習(xí)的問題就變成了更簡單的問題。比如,最開始的層只要專注于學(xué)習(xí)邊緣就好,這樣一來,只需用較少的學(xué)習(xí)數(shù)據(jù)就可以高效地進(jìn)行學(xué)習(xí)。這是為什么呢?因為和印有“狗”的照片相比,包含邊緣的圖像數(shù)量眾多,并且邊緣的模式比“狗”的模式結(jié)構(gòu)更簡單。通過加深層,可以分層次地傳遞信息,這一點(diǎn)也很重要。比如,因為提取了邊緣的層的下一層能夠使用邊緣的信息,所以應(yīng)該能夠高效地學(xué)習(xí)更加高級的模式。也就是說,通過加深層,可以將各層要學(xué)習(xí)的問題分解成容易解決的簡單問題,從而可以進(jìn)行高效的學(xué)習(xí)。
更快
在一般的CNN中,大多數(shù)時間都被耗費(fèi)在卷積層上。實際上,卷積層的處理時間加起來占GPU整體的95%,占CPU整體的89%!因此,如何高速、高效地進(jìn)行卷積層中的運(yùn)算是深度學(xué)習(xí)的一大課題。卷積層中進(jìn)行的運(yùn)算可以追溯至乘積累加運(yùn)算。因此,深度學(xué)習(xí)的高速化的主要課題就變成了如何高速、高效地進(jìn)行大量的乘積累加運(yùn)算。
GPU計算
雖然到目前為止,我們都是使用CPU進(jìn)行計算的,但現(xiàn)實是只用CPU來應(yīng)對深度學(xué)習(xí)無法令人放心。實際上,環(huán)視一下周圍,大多數(shù)深度學(xué)習(xí)的框架都支持GPU(Graphics Processing Unit),可以高速地處理大量的運(yùn)算。另外,最近的框架也開始支持多個GPU或多臺機(jī)器上的分布式學(xué)習(xí)。
GPU原本是作為圖像專用的顯卡使用的,但最近不僅用于圖像處理,也用于通用的數(shù)值計算。由于GPU可以高速地進(jìn)行并行數(shù)值計算,因此GPU計算的目標(biāo)就是將這種壓倒性的計算能力用于各種用途。
深度學(xué)習(xí)中需要進(jìn)行大量的乘積累加運(yùn)算(或者大型矩陣的乘積運(yùn)算)。這種大量的并行運(yùn)算正是GPU所擅長的(反過來說,CPU比較擅長連續(xù)的、復(fù)雜的計算)。因此,與使用單個CPU相比,使用GPU進(jìn)行深度學(xué)習(xí)的運(yùn)算可以達(dá)到驚人的高速化。如果對于一個網(wǎng)絡(luò),使用CPU要花40天以上的時間,而使用GPU則可以將時間縮短至6天。
GPU主要由NVIDIA和AMD兩家公司提供。雖然兩家的GPU都可以用于通用的數(shù)值計算,但與深度學(xué)習(xí)比較“親近”的是NVIDIA的GPU。實際上,大多數(shù)深度學(xué)習(xí)框架只受益于NVIDIA的GPU。這是因為深度學(xué)習(xí)的框架中使用了NVIDIA提供的CUDA這個面向GPU計算的綜合開發(fā)環(huán)境。
分布式學(xué)習(xí)
雖然通過GPU可以實現(xiàn)深度學(xué)習(xí)運(yùn)算的高速化,但即便如此,當(dāng)網(wǎng)絡(luò)較深時,學(xué)習(xí)還是需要幾天到幾周的時間。并且,前面也說過,深度學(xué)習(xí)伴隨著很多試錯。為了創(chuàng)建良好的網(wǎng)絡(luò),需要反復(fù)進(jìn)行各種嘗試,這樣一來就必然會產(chǎn)生盡可能地縮短一次學(xué)習(xí)所需的時間的要求。于是,將深度學(xué)習(xí)的學(xué)習(xí)過程擴(kuò)展開來的想法(也就是分布式學(xué)習(xí))就變得重要起來。
現(xiàn)在的深度學(xué)習(xí)框架中,出現(xiàn)了好幾個支持多GPU或者多機(jī)器的分布式學(xué)習(xí)的框架。其中,Google的TensorFlow、微軟的CNTK(Computational Network Toolki)在開發(fā)過程中高度重視分布式學(xué)習(xí)。以大型數(shù)據(jù)中心的低延遲·高吞吐網(wǎng)絡(luò)作為支撐,基于這些框架的分布式學(xué)習(xí)呈現(xiàn)出驚人的效果。
隨著GPU個數(shù)的增加,學(xué)習(xí)速度也在提高。實際上,與使用1個GPU時相比,使用100個GPU(設(shè)置在多臺機(jī)器上,共100個)似乎可以實現(xiàn)56倍的高速化!這意味著之前花費(fèi)7天的學(xué)習(xí)只要3個小時就能完成,充分說明了分布式學(xué)習(xí)驚人的效果。
運(yùn)算精度的位數(shù)縮減
在深度學(xué)習(xí)的高速化中,除了計算量之外,內(nèi)存容量、總線帶寬等也有可能成為瓶頸。關(guān)于內(nèi)存容量,需要考慮將大量的權(quán)重參數(shù)或中間數(shù)據(jù)放在內(nèi)存中。關(guān)于總線帶寬,當(dāng)流經(jīng)GPU(或者CPU)總線的數(shù)據(jù)超過某個限制時,就會成為瓶頸??紤]到這些情況,我們希望盡可能減少流經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)的位數(shù)。
計算機(jī)中表示小數(shù)時,有32位的單精度浮點(diǎn)數(shù)和64位的雙精度浮點(diǎn)數(shù)等格式。根據(jù)以往的實驗結(jié)果,在深度學(xué)習(xí)中,即便是16位的半精度浮點(diǎn)數(shù)(half float),也可以順利地進(jìn)行學(xué)習(xí)。實際上,NVIDIA的下一代GPU框架Pascal也支持半精度浮點(diǎn)數(shù)的運(yùn)算,由此可以認(rèn)為今后半精度浮點(diǎn)數(shù)將被作為標(biāo)準(zhǔn)使用。
為了實現(xiàn)深度學(xué)習(xí)的高速化,位數(shù)縮減是今后必須關(guān)注的一個課題,特別是在面向嵌入式應(yīng)用程序中使用深度學(xué)習(xí)時,位數(shù)縮減非常重要。
小總結(jié)
結(jié)尾
上面的全部圖片和內(nèi)容來自于《深度學(xué)習(xí)入門-基于Python的理論和實現(xiàn)》這本好書,它真正地從零開始一步一步教導(dǎo)著我如何用Python搭建神經(jīng)網(wǎng)絡(luò),所以這篇博客相當(dāng)于讀書筆記,我將其中比較關(guān)鍵的核心知識簡練地提取出來,圖也適當(dāng)進(jìn)行了整合,希望能讓朋友們最快速地入門或回顧深度學(xué)習(xí)基礎(chǔ)知識。如果覺得有幫助可以給個贊哦蟹蟹。