在深度學(xué)習(xí)中,為了有效地訓(xùn)練深度神經(jīng)網(wǎng)絡(luò),有一些值得我們強(qiáng)烈推薦的做法。在本文中,我將介紹一些最常用的方法,從高質(zhì)量訓(xùn)練數(shù)據(jù)的重要性,到超參數(shù)的選擇,再到更快的做出 DNN 原型的通用技巧。這些方法的大多數(shù)由學(xué)術(shù)界和工業(yè)界的研究驗(yàn)證過,并且在諸如 Yann LeCun 等人寫的《Efficient BackProp》和 Yoshua Bengio 的《Practical Recommendations for Deep Architectures》中也給出了數(shù)學(xué)和實(shí)驗(yàn)證明。
你會(huì)注意到,我沒有在這篇文章中提到任何數(shù)學(xué)證明。在這里建議的所有要點(diǎn),更多地應(yīng)該被看作是 DNN 的最佳訓(xùn)練方法中獲得的總結(jié)。為了更深入地了解,我強(qiáng)烈建議您最后看一遍上面提到的研究論文和后面提到的參考文獻(xiàn)。
訓(xùn)練數(shù)據(jù)
許多機(jī)器學(xué)習(xí)從業(yè)者習(xí)慣于在任何深度神經(jīng)網(wǎng)絡(luò)(DNN)中使用原始(raw)訓(xùn)練數(shù)據(jù)。為什么不這樣做呢,任何 DNN(大概)仍會(huì)給出好的結(jié)果,對(duì)吧?但是,說出「給出正確的數(shù)據(jù)類型,一個(gè)十分簡單的模型會(huì)比一個(gè)復(fù)雜的 DNN 更快地給出更好的結(jié)果」(雖然,這可能會(huì)有例外)并不是很守舊派的。因此,無論你是在做計(jì)算機(jī)視覺,還是在做自然語言處理或統(tǒng)計(jì)建模等,你都應(yīng)該嘗試預(yù)處理您的原始數(shù)據(jù)。你可以采取以下幾個(gè)方法來獲得更好的訓(xùn)練數(shù)據(jù):
盡可能大的數(shù)據(jù)集(DNN 對(duì)數(shù)據(jù)的需求是相當(dāng)大的:越多越好)
刪除所有具有損壞數(shù)據(jù)的訓(xùn)練樣本(短文本、高失真圖像、虛假輸出標(biāo)簽、具有大量空值的特征等)
數(shù)據(jù)增強(qiáng)——創(chuàng)建新樣本(如果是圖像,可以重新縮放、添加噪聲等)
選擇適當(dāng)?shù)募せ詈瘮?shù)
激活函數(shù)在任何神經(jīng)網(wǎng)絡(luò)中都是重要組成部分之一。激活將大家非常期望的非線性效果引入到了模型中。多年來,sigmoid 激活函數(shù)一直是最好的選擇。但是 sigmoid 函數(shù)本質(zhì)上有兩個(gè)缺陷:
1.sigmoid 尾部的飽和(會(huì)進(jìn)一步導(dǎo)致梯度消失問題)
2.sigmoid 不是以 0 為中心的。
一個(gè)更好的選擇是 tanh 函數(shù)——在數(shù)學(xué)上,tanh 只是一個(gè)重新縮放和移位的 sigmoid,tanh(x) = 2*sigmoid(x) - 1。雖然 tanh 仍然可能遭受梯度消失問題,但好消息是 tanh 是以零為中心的。因此,使用 tanh 為激活函數(shù)可以更快地收斂。我使用中也發(fā)現(xiàn)使用 tanh 作為激活函數(shù)通常比使用 sigmoid 函數(shù)好。
你可以根據(jù)具體任務(wù)進(jìn)一步探索其它選擇,如已經(jīng)表現(xiàn)出可以改善一些問題的 ReLU,SoftSign 等函數(shù)。
隱含單元和隱含層的數(shù)量
使用比最佳隱含單元數(shù)更多的數(shù)量通常是安全的。因?yàn)椋魏握齽t化方法在一定程度上都可以處理多余的單元。而另一方面,使用比最佳隱含單元數(shù)更少的數(shù)量時(shí),發(fā)生欠擬合的概率更高一些。
此外,當(dāng)采用無監(jiān)督學(xué)習(xí)預(yù)訓(xùn)練的表示(pre-trained representations,在后面部分中描述)時(shí),隱含單元數(shù)的最佳數(shù)量通常要更大一些。因?yàn)椋诟鞣N表示中(對(duì)于特定的監(jiān)督任務(wù)),預(yù)訓(xùn)練表示可能會(huì)包含大量的無關(guān)信息。通過增加隱含單元的數(shù)量,模型將具有足夠支持從預(yù)訓(xùn)練表示中過濾出最合適的信息的靈活性。
選擇最佳隱含層數(shù)是相對(duì)簡單的。正如 Yoshua Bengio 在 Quora 上提到的:「你只需要繼續(xù)添加層,直到測(cè)試錯(cuò)誤不再改善為止」。
權(quán)重初始化
始終使用小隨機(jī)數(shù)(random numbers)初始化權(quán)重,以打破不同單元之間的對(duì)稱性。但是權(quán)重應(yīng)該多小呢?推薦的上限是多少?使用什么概率分布來生成隨機(jī)數(shù)?此外,當(dāng)使用 sigmoid 激活函數(shù)時(shí),如果權(quán)重被初始化為非常大的數(shù),則 sigmoid 函數(shù)將會(huì)飽和(尾部區(qū)域),導(dǎo)致死亡神經(jīng)元(dead neurons)。如果權(quán)重非常小,則梯度也會(huì)很小。因此,最好在中間范圍選擇權(quán)重,并且使它們圍繞平均值均勻分布。
幸運(yùn)的是,目前已經(jīng)有很多關(guān)于初始權(quán)重的適當(dāng)值的研究,這對(duì)于有效的收斂是非常重要的。為了初始化得到均勻分布的權(quán)重,uniform distribution 可能是最好的選擇之一。此外,如論文(Glorot and Bengio, 2010)所示,具有更多傳入連接(fan_in)的單元應(yīng)具有相對(duì)較小的權(quán)重。
由于所有這些深入的實(shí)驗(yàn),現(xiàn)在我們有一個(gè)測(cè)量公式,可以直接用于權(quán)重初始化;例如從~ Uniform(-r, r) 范圍獲得權(quán)重,對(duì)于 tanh 作為激活函數(shù)的時(shí)候,r=sqrt(6/(fan_in+fan_out));而對(duì)于 sigmoid 作為激活函數(shù)的時(shí)候,r=4*(sqrt(6/fan_in+fan_out)),其中 fan_in 是上一層的大小,fan_out 是下一層的大小。
訓(xùn)練速率
這可能是最重要的超參數(shù)之一,決定了整個(gè)學(xué)習(xí)過程。如果設(shè)置的學(xué)習(xí)速率太小,你的模型可能需要幾年才能收斂;如果學(xué)習(xí)速率太大,在開始訓(xùn)練幾個(gè)樣本之后,你的損失值(loss)可能會(huì)迅速增加。一般來說,0.01 的學(xué)習(xí)速率是安全的,但這不應(yīng)被視為一個(gè)嚴(yán)格的規(guī)則;因?yàn)樽罴褜W(xué)習(xí)速率應(yīng)該根據(jù)具體任務(wù)來調(diào)整。
相比之下,在每個(gè) epoch 之后選擇固定的學(xué)習(xí)率(learning rate)或者逐漸降低學(xué)習(xí)率(learning rate)是另一個(gè)選擇。雖然這可能有助于訓(xùn)練得更快,但需要人工確定新的學(xué)習(xí)率。一般來說,學(xué)習(xí)率可以在每個(gè) epoch 后減半——這幾類策略在幾年前相當(dāng)普遍。
幸運(yùn)的是,現(xiàn)在我們有更好的基于動(dòng)量(momentum based methods)方法來改變學(xué)習(xí)率,就是基于誤差函數(shù)的曲率。這種方法也可以幫助我們?yōu)槟P椭械母鱾€(gè)參數(shù)設(shè)置不同的學(xué)習(xí)率;使得一些參數(shù)可能以相對(duì)較慢或較快的速率學(xué)習(xí)。
近期大量針對(duì)優(yōu)化方法的研究也產(chǎn)生了自適應(yīng)學(xué)習(xí)率(adaptive learning rate)方法。現(xiàn)在,我們有很多可選擇的方法,從老牌的動(dòng)量方法(Momentum Method)到 Adagrad、Adam(我最喜歡的方法)、RMSProp 等。像 Adagrad 或 Adam 這樣的方法有效地避免了手動(dòng)選擇初始學(xué)習(xí)速率,并且模型能在一定的時(shí)間內(nèi)順利地收斂(當(dāng)然,如果選擇好的初始速率會(huì)進(jìn)一步幫助模型收斂)。
超參數(shù)微調(diào):旋轉(zhuǎn)的網(wǎng)格搜索——擁抱隨機(jī)搜索
網(wǎng)格搜索(Grid Search)是經(jīng)典的機(jī)器學(xué)習(xí)方法。但是,在尋找 DNN 的最佳超參數(shù)時(shí),網(wǎng)格搜索并不高效。主要是因?yàn)閲L試不同的 DNN 超參數(shù)組合所花費(fèi)的時(shí)間太長。隨著超參數(shù)的數(shù)量不斷增加,網(wǎng)格搜索所需的計(jì)算也呈指數(shù)增長。
這里有兩種方法:
1. 根據(jù)經(jīng)驗(yàn)手動(dòng)調(diào)整一些常用的超參數(shù),如學(xué)習(xí)率、層數(shù)(number of layer)等。
2. 使用隨機(jī)搜索/隨機(jī)抽樣(Random Search/Random Sampling)來選擇最優(yōu)超參數(shù)。超參數(shù)的組合通常從可行域的均勻分布中選擇。還可以加入先驗(yàn)知識(shí)以進(jìn)一步減少搜索空間(例如學(xué)習(xí)速率不應(yīng)該太大或太小)。經(jīng)證明,隨機(jī)搜索比網(wǎng)格搜索更有效率。
學(xué)習(xí)方法
老牌的隨機(jī)梯度下降(Stochastic Gradient Descent)可能不像 DNN 一樣有效(這不是一個(gè)嚴(yán)格的規(guī)則),因此最近很多人正研究開發(fā)更靈活的優(yōu)化算法。例如:Adagrad、Adam、AdaDelta、RMSProp 等。這些復(fù)雜的方法除了提供自適應(yīng)學(xué)習(xí)率之外,還對(duì)不同的模型參數(shù)應(yīng)用不同的速率,使得收斂曲線更加平滑。將學(xué)習(xí)率、層數(shù)等作為超參數(shù)是很好的,建議在訓(xùn)練數(shù)據(jù)的子集上進(jìn)行嘗試。
使權(quán)重的維度以 2 的指數(shù)冪級(jí)增加
即使用最新的硬件資源處理最新的深度學(xué)習(xí)模型時(shí),內(nèi)存管理仍然是以字節(jié)衡量的;所以,盡量將參數(shù)的大小設(shè)置為 2 的冪指數(shù),如 64、128、512、1024。這可能有助于分割矩陣、權(quán)重等,從而略微提高學(xué)習(xí)效率。這個(gè)現(xiàn)象在使用 GPU 時(shí)變得更加顯著。
無監(jiān)督預(yù)訓(xùn)練
無論你是否用 NLP、機(jī)器視覺(Computer Vision)、語音識(shí)別等技術(shù),無監(jiān)督預(yù)訓(xùn)練總能幫助有監(jiān)督模型和無監(jiān)督模型的訓(xùn)練。詞向量(Word Vector)在 NLP 中無處不在;對(duì)于有監(jiān)督二分類問題,你可以使用 ImageNet 的數(shù)據(jù)集以無監(jiān)督的方式預(yù)訓(xùn)練模型;或者對(duì)于說話者消歧模型(speaker disambiguation model),可以進(jìn)一步利用語音樣本的信息訓(xùn)練模型。
Mini-Batch vs. 隨機(jī)學(xué)習(xí)
訓(xùn)練模型的主要目的是學(xué)習(xí)得到適當(dāng)?shù)膮?shù),從而產(chǎn)生從輸入到輸出的最佳映射(mapping)。不管是否使用批處理(batch)、Mini-Batch 或隨機(jī)學(xué)習(xí)(stochastic learning),這些參數(shù)都會(huì)因訓(xùn)練樣本的不同而有所調(diào)整。在使用隨機(jī)學(xué)習(xí)方法時(shí),在每個(gè)訓(xùn)練樣本之后調(diào)整權(quán)重梯度,因此將噪聲引入梯度。這具有非常理想的效果;即在訓(xùn)練期間引入噪聲,模型變得不太容易過度擬合。
然而,現(xiàn)在計(jì)算機(jī)的計(jì)算能力大大提高,使得隨機(jī)學(xué)習(xí)方法的效率可能相對(duì)較低。隨機(jī)學(xué)習(xí)可能浪費(fèi)了很大一部分資源。如果我們能夠計(jì)算矩陣與矩陣的乘法(Matrix-Matrix multiplication),那么為什么要局限于使用迭代相加的向量與向量的乘法?因此,為了實(shí)現(xiàn)更大的吞吐量(throughput)/更快的學(xué)習(xí),建議使用 Mini-Batch 而不是隨機(jī)學(xué)習(xí)。
但是,選擇適當(dāng)?shù)呐幚泶笮∫簿哂邢嗤闹匾?因此我們也可以保留一些噪音(通過減少批的數(shù)據(jù)量實(shí)現(xiàn)),并有效利用計(jì)算機(jī)的計(jì)算能力。同樣的,一個(gè)批的樣本量控制在 16 到 128 之間也是一個(gè)不錯(cuò)的選擇(2 的指數(shù)冪)。通常,一旦你找到更重要的超參數(shù)(或手動(dòng)或隨機(jī)搜索),批處理的大小是選定的。然而,有些情況下(如在線學(xué)習(xí)/online learning),模型的訓(xùn)練數(shù)據(jù)是數(shù)據(jù)流(stream),這時(shí)求助于隨機(jī)學(xué)習(xí)是一個(gè)很好的選擇。
重排訓(xùn)練樣本
信息論中有這樣的話——「學(xué)習(xí)發(fā)生的極小概率事件比學(xué)習(xí)發(fā)生的大概率事件更有信息」。類似地,隨機(jī)排列訓(xùn)練樣本的順序(不同的迭代或批處理中)將導(dǎo)致更快的收斂。當(dāng)訓(xùn)練樣本的順序不同時(shí),模型的結(jié)果會(huì)有輕微的提升。
作為正則化的 dropout
考慮到要學(xué)習(xí)數(shù)百萬的參數(shù),正則化成為防止 DNN 過度擬合的必然要求。你也可以繼續(xù)使用 L1/L2 正則化,但是 dropout 是更好的檢查 DNN 過擬合的方法。dropout 在實(shí)現(xiàn)方法上比較瑣碎,它通常能更快地學(xué)習(xí)模型。默認(rèn)值 0.5 是一個(gè)好的選擇,但是這也取決于具體的任務(wù)。如果模型較不復(fù)雜,則 0.2 的 dropout 也可能就足夠了。
在測(cè)試階段,應(yīng)該相應(yīng)地標(biāo)準(zhǔn)化權(quán)重,同時(shí)暫緩使用舍棄方法,如論文《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》中所做的那樣。賦予模型舍棄正則化方法,經(jīng)過一定的訓(xùn)練時(shí)間錯(cuò)誤肯定會(huì)下降。
訓(xùn)練的迭代數(shù)
「用多個(gè)迭代(epoch)來訓(xùn)練深度學(xué)習(xí)模型將產(chǎn)生更好的結(jié)果」——但是我們?nèi)绾瘟炕付嗌佟?原來,有一個(gè)簡單的策略 - 只要對(duì)訓(xùn)練模型設(shè)置固定數(shù)量的訓(xùn)練樣本或迭代,比如 20,000 個(gè)樣本或 1 個(gè)迭代。在每組訓(xùn)練樣本訓(xùn)練完成后,將測(cè)試誤差與訓(xùn)練誤差進(jìn)行比較,如果它們之間的差距正在減小,則繼續(xù)訓(xùn)練。除此之外,在每個(gè)這樣的集合之后,保存這組數(shù)據(jù)訓(xùn)練模型的參數(shù)(以便從多個(gè)模型中選擇)。
可視化
有一千種可能使得訓(xùn)練的深度學(xué)習(xí)模型出錯(cuò)。我們都有過這樣的經(jīng)歷,當(dāng)模型訓(xùn)練了幾個(gè)小時(shí)或幾天后,在訓(xùn)練完成后我們才意識(shí)到模型存在一些問題。為了避免這種問題——總是可視化訓(xùn)練過程。最明顯的步驟是打印/保存損失(loss)函數(shù)值的日志、訓(xùn)練誤差或測(cè)試誤差等。
除此之外,另一個(gè)好的做法是在訓(xùn)練一定樣本后或在 epoch 之間使用可視化庫繪制權(quán)重的直方圖。這可能有助于跟蹤深度學(xué)習(xí)模型中的一些常見問題,例如梯度消失(Vanishing Gradient)、梯度爆炸(Exploding Gradient)等。
多核計(jì)算機(jī),GPU 集群
隨著 GPU、提供向量化操作的庫(library)、具有更強(qiáng)計(jì)算能力的計(jì)算機(jī)的出現(xiàn),這些可能是深度學(xué)習(xí)成功的最重要的因素。如果你足夠耐心,你可以嘗試在你的筆記本電腦上運(yùn)行 DNN(這時(shí)你甚至不能打開 10 個(gè) Chrome 瀏覽器標(biāo)簽),并需要等待很長時(shí)間才能得到結(jié)果。要么你有非常好的硬件(很昂貴)與至少多個(gè) CPU 核和幾百個(gè) GPU 核。GPU 已經(jīng)徹底改變了深度學(xué)習(xí)研究(難怪 Nvidia 的股票井噴式漲價(jià)),主要是因?yàn)?GPU 能夠更大規(guī)模地執(zhí)行矩陣操作。
因此,以前在正常的機(jī)器上花幾個(gè)星期的訓(xùn)練,由于并行(parallelization)技術(shù),將訓(xùn)練的時(shí)間減少到幾天甚至幾個(gè)小時(shí)。
使用具有 GPU 計(jì)算和自動(dòng)微分支持的庫
幸運(yùn)的是,對(duì)于快速訓(xùn)練,我們有一些很好的庫,如 Theano、Tensorflow、Keras 等。幾乎所有這些深度學(xué)習(xí)庫提供對(duì) GPU 計(jì)算和自動(dòng)微分(Automatic Differentiation)的支持。所以,你不必深入了解 GPU 的核心編程(除非你想,絕對(duì)有趣);你也不必編寫自己的微分代碼,這在真正復(fù)雜的模型中可能會(huì)有點(diǎn)費(fèi)力(雖然你應(yīng)該能夠做到這一點(diǎn))。TensorFlow 能進(jìn)一步支持在分布式架構(gòu)上訓(xùn)練模型。
以上并不是訓(xùn)練 DNN 的詳盡列表。它只包括了最常見的做法,上面已經(jīng)去掉了如數(shù)據(jù)標(biāo)準(zhǔn)化、批規(guī)范化/層規(guī)范化、梯度檢查(Gradient Check)等概念。