《Scikit-Learn與TensorFlow機(jī)器學(xué)習(xí)實(shí)用指南》 第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(下)


(第一部分 機(jī)器學(xué)習(xí)基礎(chǔ))
第01章 機(jī)器學(xué)習(xí)概覽
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(上)
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(下)
第03章 分類
第04章 訓(xùn)練模型
第05章 支持向量機(jī)
第06章 決策樹
第07章 集成學(xué)習(xí)和隨機(jī)森林
第08章 降維
(第二部分 神經(jīng)網(wǎng)絡(luò)和深度學(xué)習(xí))
第9章 啟動(dòng)和運(yùn)行TensorFlow
第10章 人工神經(jīng)網(wǎng)絡(luò)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(上)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(下)
第12章 設(shè)備和服務(wù)器上的分布式 TensorFlow
第13章 卷積神經(jīng)網(wǎng)絡(luò)
第14章 循環(huán)神經(jīng)網(wǎng)絡(luò)
第15章 自編碼器
第16章 強(qiáng)化學(xué)習(xí)(上)
第16章 強(qiáng)化學(xué)習(xí)(下)


更快的優(yōu)化器

訓(xùn)練一個(gè)非常大的深度神經(jīng)網(wǎng)絡(luò)可能會(huì)非常緩慢。 到目前為止,我們已經(jīng)看到了四種加速訓(xùn)練的方法(并且達(dá)到更好的解決方案):對(duì)連接權(quán)重應(yīng)用良好的初始化策略,使用良好的激活函數(shù),使用批量規(guī)范化以及重用預(yù)訓(xùn)練網(wǎng)絡(luò)的部分。 另一個(gè)巨大的速度提升來自使用比普通漸變下降優(yōu)化器更快的優(yōu)化器。 在本節(jié)中,我們將介紹最流行的:動(dòng)量優(yōu)化,Nesterov 加速梯度,AdaGrad,RMSProp,最后是 Adam 優(yōu)化。

劇透:本節(jié)的結(jié)論是,您幾乎總是應(yīng)該使用Adam_optimization,所以如果您不關(guān)心它是如何工作的,只需使用AdamOptimizer替換您的GradientDescentOptimizer,然后跳到下一節(jié)! 只需要這么小的改動(dòng),訓(xùn)練通常會(huì)快幾倍。 但是,Adam 優(yōu)化確實(shí)有三個(gè)可以調(diào)整的超參數(shù)(加上學(xué)習(xí)率)。 默認(rèn)值通常工作的不錯(cuò),但如果您需要調(diào)整它們,知道他們怎么實(shí)現(xiàn)的可能會(huì)有幫助。 Adam 優(yōu)化結(jié)合了來自其他優(yōu)化算法的幾個(gè)想法,所以先看看這些算法是有用的。

動(dòng)量優(yōu)化

想象一下,一個(gè)保齡球在一個(gè)光滑的表面上平緩的斜坡上滾動(dòng):它會(huì)緩慢地開始,但是它會(huì)很快地達(dá)到最終的速度(如果有一些摩擦或空氣阻力的話)。 這是 Boris Polyak 在 1964 年提出的動(dòng)量優(yōu)化背后的一個(gè)非常簡單的想法。相比之下,普通的梯度下降只需要沿著斜坡進(jìn)行小的有規(guī)律的下降步驟,所以需要更多的時(shí)間才能到達(dá)底部。

回想一下,梯度下降只是通過直接減去損失函數(shù)J(θ)相對(duì)于權(quán)重θ的梯度,乘以學(xué)習(xí)率η來更新權(quán)重θ。 方程是:θ ← θ – η?θJ(θ)。它不關(guān)心早期的梯度是什么。 如果局部梯度很小,則會(huì)非常緩慢。

動(dòng)量優(yōu)化很關(guān)心以前的梯度:在每次迭代時(shí),它將動(dòng)量矢量m(乘以學(xué)習(xí)率η)與局部梯度相加,并且通過簡單地減去該動(dòng)量矢量來更新權(quán)重(參見公式 11-4)。 換句話說,梯度用作加速度,不用作速度。 為了模擬某種摩擦機(jī)制,避免動(dòng)量過大,該算法引入了一個(gè)新的超參數(shù)β,簡稱為動(dòng)量,它必須設(shè)置在 0(高摩擦)和 1(無摩擦)之間。 典型的動(dòng)量值是 0.9。

公式11-4 動(dòng)量算法

您可以很容易地驗(yàn)證,如果梯度保持不變,則最終速度(即,權(quán)重更新的最大大?。┑扔谠撎荻瘸艘詫W(xué)習(xí)率η乘以1/(1-β)。 例如,如果β = 0.9,則最終速度等于學(xué)習(xí)率的梯度乘以 10 倍,因此動(dòng)量優(yōu)化比梯度下降快 10 倍! 這使動(dòng)量優(yōu)化比梯度下降快得多。 特別是,我們在第四章中看到,當(dāng)輸入量具有非常不同的尺度時(shí),損失函數(shù)看起來像一個(gè)細(xì)長的碗(見圖 4-7)。 梯度下降速度很快,但要花很長的時(shí)間才能到達(dá)底部。 相反,動(dòng)量優(yōu)化會(huì)越來越快地滾下山谷底部,直到達(dá)到底部(最佳)。

在不使用批標(biāo)準(zhǔn)化的深層神經(jīng)網(wǎng)絡(luò)中,較高層往往會(huì)得到具有不同的尺度的輸入,所以使用動(dòng)量優(yōu)化會(huì)有很大的幫助。 它也可以幫助滾過局部最優(yōu)值。

由于動(dòng)量的原因,優(yōu)化器可能會(huì)超調(diào)一些,然后再回來,再次超調(diào),并在穩(wěn)定在最小值之前多次振蕩。 這就是為什么在系統(tǒng)中有一點(diǎn)摩擦的原因之一:它消除了這些振蕩,從而加速了收斂。

在 TensorFlow 中實(shí)現(xiàn)動(dòng)量優(yōu)化是一件簡單的事情:只需用MomentumOptimizer替換GradientDescentOptimizer,然后躺下來賺錢!

optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,
                                       momentum=0.9)

動(dòng)量優(yōu)化的一個(gè)缺點(diǎn)是它增加了另一個(gè)超參數(shù)來調(diào)整。 然而,0.9 的動(dòng)量值通常在實(shí)踐中運(yùn)行良好,幾乎總是比梯度下降快。

Nesterov 加速梯度

Yurii Nesterov 在 1983 年提出的動(dòng)量優(yōu)化的一個(gè)小變體幾乎總是比普通的動(dòng)量優(yōu)化更快。 Nesterov 動(dòng)量優(yōu)化或 Nesterov 加速梯度(Nesterov Accelerated Gradient,NAG)的思想是測量損失函數(shù)的梯度不是在局部位置,而是在動(dòng)量方向稍微靠前(見公式 11-5)。 與普通的動(dòng)量優(yōu)化的唯一區(qū)別在于梯度是在θ+βm而不是在θ處測量的。

公式11-5 Nesterov加速梯度算法

這個(gè)小小的調(diào)整是可行的,因?yàn)橐话銇碚f,動(dòng)量矢量將指向正確的方向(即朝向最優(yōu)方向),所以使用在該方向上測得的梯度稍微更精確,而不是使用 原始位置的梯度,如圖11-6所示(其中?1代表在起點(diǎn)θ處測量的損失函數(shù)的梯度,?2代表位于θ+βm的點(diǎn)處的梯度)。

圖11-6 常規(guī)vsNesterov動(dòng)量優(yōu)化

正如你所看到的,Nesterov 更新稍微靠近最佳值。 過了一段時(shí)間,這些小的改進(jìn)加起來,NAG 最終比常規(guī)的動(dòng)量優(yōu)化快得多。 此外,請(qǐng)注意,當(dāng)動(dòng)量推動(dòng)權(quán)重橫跨山谷時(shí),▽1繼續(xù)推進(jìn)越過山谷,而▽2推回山谷的底部。 這有助于減少振蕩,從而更快地收斂。

與常規(guī)的動(dòng)量優(yōu)化相比,NAG 幾乎總能加速訓(xùn)練。 要使用它,只需在創(chuàng)建MomentumOptimizer時(shí)設(shè)置use_nesterov = True

optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,
                                       momentum=0.9, use_nesterov=True)

AdaGrad

再次考慮細(xì)長碗的問題:梯度下降從最陡峭的斜坡快速下降,然后緩慢地下到谷底。 如果算法能夠早期檢測到這個(gè)問題并且糾正它的方向來指向全局最優(yōu)點(diǎn),那將是非常好的。

AdaGrad 算法通過沿著最陡的維度縮小梯度向量來實(shí)現(xiàn)這一點(diǎn)(見公式 11-6):

公式11-6 AdaGrad算法

第一步將梯度的平方累加到矢量s中(?符號(hào)表示單元乘法)。 這個(gè)向量化形式相當(dāng)于向量s的每個(gè)元素si計(jì)算si ← si + (? / ? θi J(θ))2。換一種說法,每個(gè) si 累加損失函數(shù)對(duì)參數(shù)θi的偏導(dǎo)數(shù)的平方。 如果損失函數(shù)沿著第i維陡峭,則在每次迭代時(shí), si 將變得越來越大。

第二步幾乎與梯度下降相同,但有一個(gè)很大的不同:梯度矢量按比例縮小 (s+ε)^0.5(?符號(hào)表示元素分割,ε是避免被零除的平滑項(xiàng),通常設(shè)置為10-10。 這個(gè)矢量化的形式相當(dāng)于所有θi同時(shí)計(jì)算

簡而言之,這種算法會(huì)降低學(xué)習(xí)速度,但對(duì)于陡峭的尺寸,其速度要快于具有溫和的斜率的尺寸。 這被稱為自適應(yīng)學(xué)習(xí)率。 它有助于將更新的結(jié)果更直接地指向全局最優(yōu)(見圖 11-7)。 另一個(gè)好處是它不需要那么多的去調(diào)整學(xué)習(xí)率超參數(shù)η。

圖11-7 AdaGard vs 梯度下降

對(duì)于簡單的二次問題,AdaGrad 經(jīng)常表現(xiàn)良好,但不幸的是,在訓(xùn)練神經(jīng)網(wǎng)絡(luò)時(shí),它經(jīng)常停止得太早。 學(xué)習(xí)率被縮減得太多,以至于在達(dá)到全局最優(yōu)之前,算法完全停止。 所以,即使 TensorFlow 有一個(gè)AdagradOptimizer,你也不應(yīng)該用它來訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(雖然對(duì)線性回歸這樣簡單的任務(wù)可能是有效的)。

RMSProp

盡管 AdaGrad 的速度變慢了一點(diǎn),并且從未收斂到全局最優(yōu),但是 RMSProp 算法通過僅累積最近迭代(而不是從訓(xùn)練開始以來的所有梯度)的梯度來修正這個(gè)問題。 它通過在第一步中使用指數(shù)衰減來實(shí)現(xiàn)(見公式 11-7)。

公式11-7 RMSProp算法

它的衰變率β通常設(shè)定為 0.9。 是的,它又是一個(gè)新的超參數(shù),但是這個(gè)默認(rèn)值通常運(yùn)行良好,所以你可能根本不需要調(diào)整它。

正如您所料,TensorFlow 擁有一個(gè)RMSPropOptimizer類:

optimizer = tf.train.RMSPropOptimizer(learning_rate=learning_rate,
                                      momentum=0.9, decay=0.9, epsilon=1e-10)

除了非常簡單的問題,這個(gè)優(yōu)化器幾乎總是比 AdaGrad 執(zhí)行得更好。 它通常也比動(dòng)量優(yōu)化和 Nesterov 加速梯度表現(xiàn)更好。 事實(shí)上,這是許多研究人員首選的優(yōu)化算法,直到 Adam 優(yōu)化出現(xiàn)。

Adam 優(yōu)化

Adam,代表自適應(yīng)矩估計(jì),結(jié)合了動(dòng)量優(yōu)化和 RMSProp 的思想:就像動(dòng)量優(yōu)化一樣,它追蹤過去梯度的指數(shù)衰減平均值,就像 RMSProp 一樣,它跟蹤過去平方梯度的指數(shù)衰減平均值 (見方程式 11-8)。

公式11-8 Adam算法

T 代表迭代次數(shù)(從 1 開始)。

如果你只看步驟 1, 2 和 5,你會(huì)注意到 Adam 與動(dòng)量優(yōu)化和 RMSProp 的相似性。 唯一的區(qū)別是第 1 步計(jì)算指數(shù)衰減的平均值,而不是指數(shù)衰減的和,但除了一個(gè)常數(shù)因子(衰減平均值只是衰減和的1 - β1倍)之外,它們實(shí)際上是等效的。 步驟 3 和步驟 4 是一個(gè)技術(shù)細(xì)節(jié):由于ms初始化為 0,所以在訓(xùn)練開始時(shí)它們會(huì)偏向0,所以這兩步將在訓(xùn)練開始時(shí)幫助提高ms

動(dòng)量衰減超參數(shù)β1通常初始化為 0.9,而縮放衰減超參數(shù)β2通常初始化為 0.999。 如前所述,平滑項(xiàng)ε通常被初始化為一個(gè)很小的數(shù),例如10-8。這些是 TensorFlow 的AdamOptimizer類的默認(rèn)值,所以你可以簡單地使用:

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)

實(shí)際上,由于 Adam 是一種自適應(yīng)學(xué)習(xí)率算法(如 AdaGrad 和 RMSProp),所以對(duì)學(xué)習(xí)率超參數(shù)η的調(diào)整較少。 您經(jīng)??梢允褂媚J(rèn)值η= 0.001,使 Adam 更容易使用相對(duì)于梯度下降。

筆記:
迄今為止所討論的所有優(yōu)化技術(shù)都只依賴于一階偏導(dǎo)數(shù)(雅可比矩陣)。 優(yōu)化文獻(xiàn)包含基于二階偏導(dǎo)數(shù)(海森矩陣)的驚人算法。 不幸的是,這些算法很難應(yīng)用于深度神經(jīng)網(wǎng)絡(luò),因?yàn)槊總€(gè)輸出有n ^ 2個(gè)海森值(其中n是參數(shù)的數(shù)量),而不是每個(gè)輸出只有n個(gè)雅克比值。 由于 DNN 通常具有數(shù)以萬計(jì)的參數(shù),二階優(yōu)化算法通常甚至不適合內(nèi)存,甚至在他們這樣做時(shí),計(jì)算海森矩陣也是太慢了。

訓(xùn)練稀疏模型

所有剛剛提出的優(yōu)化算法都會(huì)產(chǎn)生密集的模型,這意味著大多數(shù)參數(shù)都是非零的。 如果你在運(yùn)行時(shí)需要一個(gè)非常快速的模型,或者如果你需要它占用較少的內(nèi)存,你可能更喜歡用一個(gè)稀疏模型來代替。

實(shí)現(xiàn)這一點(diǎn)的一個(gè)微不足道的方法是像平常一樣訓(xùn)練模型,然后擺脫微小的權(quán)重(將它們設(shè)置為 0)。

另一個(gè)選擇是在訓(xùn)練過程中應(yīng)用強(qiáng) l1 正則化,因?yàn)樗鼤?huì)推動(dòng)優(yōu)化器盡可能多地消除權(quán)重(如第 4 章關(guān)于 Lasso 回歸的討論)。

但是,在某些情況下,這些技術(shù)可能仍然不足。 最后一個(gè)選擇是應(yīng)用雙重平均,通常稱為遵循正則化領(lǐng)導(dǎo)者(FTRL),一種由尤里·涅斯捷羅夫(Yurii Nesterov)提出的技術(shù)。 當(dāng)與 l1 正則化一起使用時(shí),這種技術(shù)通常導(dǎo)致非常稀疏的模型。 TensorFlow 在FTRLOptimizer類中實(shí)現(xiàn)稱為 FTRL-Proximal 的 FTRL 變體。

學(xué)習(xí)率調(diào)整

找到一個(gè)好的學(xué)習(xí)速度可能會(huì)非常棘手。 如果設(shè)置太高,訓(xùn)練實(shí)際上可能偏離(如我們在第 4 章)。 如果設(shè)置得太低,訓(xùn)練最終會(huì)收斂到最佳狀態(tài),但這需要很長時(shí)間。 如果將其設(shè)置得太高,開始的進(jìn)度會(huì)非???,但最終會(huì)在最優(yōu)解周圍跳動(dòng),永遠(yuǎn)不會(huì)安頓下來(除非您使用自適應(yīng)學(xué)習(xí)率優(yōu)化算法,如 AdaGrad,RMSProp 或 Adam,但是 即使這樣可能需要時(shí)間來解決)。 如果您的計(jì)算預(yù)算有限,那么您可能必須在正確收斂之前中斷訓(xùn)練,產(chǎn)生次優(yōu)解決方案(參見圖 11-8)。

圖11-8 不同學(xué)習(xí)速率的學(xué)習(xí)曲線

通過使用各種學(xué)習(xí)率和比較學(xué)習(xí)曲線,在幾個(gè)迭代內(nèi)對(duì)您的網(wǎng)絡(luò)進(jìn)行多次訓(xùn)練,您也許能夠找到相當(dāng)好的學(xué)習(xí)率。 理想的學(xué)習(xí)率將會(huì)快速學(xué)習(xí)并收斂到良好的解決方案。

然而,你可以做得比不斷的學(xué)習(xí)率更好:如果你從一個(gè)高的學(xué)習(xí)率開始,然后一旦它停止快速的進(jìn)步就減少它,你可以比最佳的恒定學(xué)習(xí)率更快地達(dá)到一個(gè)好的解決方案。 有許多不同的策略,以減少訓(xùn)練期間的學(xué)習(xí)率。 這些策略被稱為學(xué)習(xí)率調(diào)整(我們在第 4 章中簡要介紹了這個(gè)概念),其中最常見的是:

預(yù)定的分段恒定學(xué)習(xí)率:

例如,首先將學(xué)習(xí)率設(shè)置為 η0 = 0.1,然后在 50 個(gè)迭代之后將學(xué)習(xí)率設(shè)置為 η0 = 0.001。雖然這個(gè)解決方案可以很好地工作,但是通常需要弄清楚正確的學(xué)習(xí)速度以及何時(shí)使用它們。

性能調(diào)度:

每 N 步測量驗(yàn)證誤差(就像提前停止一樣),當(dāng)誤差下降時(shí),將學(xué)習(xí)率降低一個(gè)因子λ

指數(shù)調(diào)度:

將學(xué)習(xí)率設(shè)置為迭代次數(shù)t的函數(shù):η(t) = η0 10–t/r。 這很好,但它需要調(diào)整η0r。 學(xué)習(xí)率將由每r步下降 10 倍。

冪調(diào)度:

設(shè)學(xué)習(xí)率為 η(t) = η0 (1 + t/r)–c。 超參數(shù)c通常被設(shè)置為 1。這與指數(shù)調(diào)度類似,但是學(xué)習(xí)率下降要慢得多。

Andrew Senior 等人在2013年的論文比較了使用動(dòng)量優(yōu)化訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)進(jìn)行語音識(shí)別時(shí)一些最流行的學(xué)習(xí)率調(diào)整的性能。 作者得出結(jié)論:在這種情況下,性能調(diào)度和指數(shù)調(diào)度都表現(xiàn)良好,但他們更喜歡指數(shù)調(diào)度,因?yàn)樗鼘?shí)現(xiàn)起來比較簡單,容易調(diào)整,收斂速度略快于最佳解決方案。

使用 TensorFlow 實(shí)現(xiàn)學(xué)習(xí)率調(diào)整非常簡單:

    initial_learning_rate = 0.1
    decay_steps = 10000
    decay_rate = 1/10
    global_step = tf.Variable(0, trainable=False, name="global_step")
    learning_rate = tf.train.exponential_decay(initial_learning_rate, global_step,
                                               decay_steps, decay_rate)
    optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9)
    training_op = optimizer.minimize(loss, global_step=global_step)

設(shè)置超參數(shù)值后,我們創(chuàng)建一個(gè)不可訓(xùn)練的變量global_step(初始化為 0)以跟蹤當(dāng)前的訓(xùn)練迭代次數(shù)。 然后我們使用 TensorFlow 的exponential_decay()函數(shù)來定義指數(shù)衰減的學(xué)習(xí)率(η0= 0.1r = 10,000)。 接下來,我們使用這個(gè)衰減的學(xué)習(xí)率創(chuàng)建一個(gè)優(yōu)化器(在這個(gè)例子中是一個(gè)MomentumOptimizer)。 最后,我們通過調(diào)用優(yōu)化器的minimize()方法來創(chuàng)建訓(xùn)練操作;因?yàn)槲覀儗?code>global_step變量傳遞給它,所以請(qǐng)注意增加它。 就是這樣!

由于 AdaGrad,RMSProp 和 Adam 優(yōu)化自動(dòng)降低了訓(xùn)練期間的學(xué)習(xí)率,因此不需要添加額外的學(xué)習(xí)率調(diào)整。 對(duì)于其他優(yōu)化算法,使用指數(shù)衰減或性能調(diào)度可顯著加速收斂。

完整代碼:

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_outputs = 10

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
    logits = tf.layers.dense(hidden2, n_outputs, name="outputs")

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
with tf.name_scope("train"):       # not shown in the book
    initial_learning_rate = 0.1
    decay_steps = 10000
    decay_rate = 1/10
    global_step = tf.Variable(0, trainable=False, name="global_step")
    learning_rate = tf.train.exponential_decay(initial_learning_rate, global_step,
                                               decay_steps, decay_rate)
    optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9)
    training_op = optimizer.minimize(loss, global_step=global_step)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 5
batch_size = 50

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_model_final.ckpt")

通過正則化避免過擬合

有四個(gè)參數(shù),我可以擬合一個(gè)大象,五個(gè)我可以讓他擺動(dòng)他的象鼻。

—— John von Neumann,cited by Enrico Fermi in Nature 427

深度神經(jīng)網(wǎng)絡(luò)通常具有數(shù)以萬計(jì)的參數(shù),有時(shí)甚至是數(shù)百萬。 有了這么多的參數(shù),網(wǎng)絡(luò)擁有難以置信的自由度,可以適應(yīng)各種復(fù)雜的數(shù)據(jù)集。 但是這個(gè)很大的靈活性也意味著它很容易過擬合訓(xùn)練集。

有了數(shù)以百萬計(jì)的參數(shù),你可以適應(yīng)整個(gè)動(dòng)物園。 在本節(jié)中,我們將介紹一些最流行的神經(jīng)網(wǎng)絡(luò)正則化技術(shù),以及如何用 TensorFlow 實(shí)現(xiàn)它們:早期停止,l1 和 l2 正則化,drop out,最大范數(shù)正則化和數(shù)據(jù)增強(qiáng)。

早期停止

為避免過度擬合訓(xùn)練集,一個(gè)很好的解決方案就是盡早停止訓(xùn)練(在第 4 章中介紹):只要在訓(xùn)練集的性能開始下降時(shí)中斷訓(xùn)練。

與 TensorFlow 實(shí)現(xiàn)方法之一是評(píng)估其對(duì)設(shè)置定期(例如,每 50 步)驗(yàn)證模型,并保存一個(gè)“winner”的快照,如果它優(yōu)于以前“winner”的快照。 計(jì)算自上次“winner”快照保存以來的步數(shù),并在達(dá)到某個(gè)限制時(shí)(例如 2000 步)中斷訓(xùn)練。 然后恢復(fù)最后的“winner”快照。

雖然早期停止在實(shí)踐中運(yùn)行良好,但是通過將其與其他正則化技術(shù)相結(jié)合,您通??梢栽诰W(wǎng)絡(luò)中獲得更高的性能。

L1 和 L2 正則化

就像你在第 4 章中對(duì)簡單線性模型所做的那樣,你可以使用 l1 和 l2 正則化約束一個(gè)神經(jīng)網(wǎng)絡(luò)的連接權(quán)重(但通常不是它的偏置)。

使用 TensorFlow 做到這一點(diǎn)的一種方法是簡單地將適當(dāng)?shù)恼齽t化項(xiàng)添加到您的損失函數(shù)中。 例如,假設(shè)您只有一個(gè)權(quán)重為weights1的隱藏層和一個(gè)權(quán)重為weight2的輸出層,那么您可以像這樣應(yīng)用 l1 正則化:

我們可以將正則化函數(shù)傳遞給tf.layers.dense()函數(shù),該函數(shù)將使用它來創(chuàng)建計(jì)算正則化損失的操作,并將這些操作添加到正則化損失集合中。 開始和上面一樣:

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_outputs = 10

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

接下來,我們將使用 Python partial()函數(shù)來避免一遍又一遍地重復(fù)相同的參數(shù)。 請(qǐng)注意,我們設(shè)置了內(nèi)核正則化參數(shù)(正則化函數(shù)有l1_regularizer(),l2_regularizer(),l1_l2_regularizer()):

scale = 0.001
my_dense_layer = partial(
    tf.layers.dense, activation=tf.nn.relu,
    kernel_regularizer=tf.contrib.layers.l1_regularizer(scale))

with tf.name_scope("dnn"):
    hidden1 = my_dense_layer(X, n_hidden1, name="hidden1")
    hidden2 = my_dense_layer(hidden1, n_hidden2, name="hidden2")
    logits = my_dense_layer(hidden2, n_outputs, activation=None,
                            name="outputs")

該代碼創(chuàng)建了一個(gè)具有兩個(gè)隱藏層和一個(gè)輸出層的神經(jīng)網(wǎng)絡(luò),并且還在圖中創(chuàng)建節(jié)點(diǎn)以計(jì)算與每個(gè)層的權(quán)重相對(duì)應(yīng)的 l1 正則化損失。TensorFlow 會(huì)自動(dòng)將這些節(jié)點(diǎn)添加到包含所有正則化損失的特殊集合中。 您只需要將這些正則化損失添加到您的整體損失中,如下所示:

接下來,我們必須將正則化損失加到基本損失上:

with tf.name_scope("loss"):                                     # not shown in the book
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(  # not shown
        labels=y, logits=logits)                                # not shown
    base_loss = tf.reduce_mean(xentropy, name="avg_xentropy")   # not shown
    reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
    loss = tf.add_n([base_loss] + reg_losses, name="loss")

其余的和往常一樣:

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

learning_rate = 0.01

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 20
batch_size = 200

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_model_final.ckpt")

不要忘記把正則化的損失加在你的整體損失上,否則就會(huì)被忽略。

Dropout

深度神經(jīng)網(wǎng)絡(luò)最流行的正則化技術(shù)可以說是 dropout。 它由 GE Hinton 于 2012 年提出,并在 Nitish Srivastava 等人的論文中進(jìn)一步詳細(xì)描述,并且已被證明是非常成功的:即使是最先進(jìn)的神經(jīng)網(wǎng)絡(luò),僅僅通過增加丟失就可以提高1-2%的準(zhǔn)確度。 這聽起來可能不是很多,但是當(dāng)一個(gè)模型已經(jīng)具有 95% 的準(zhǔn)確率時(shí),獲得 2% 的準(zhǔn)確度提升意味著將誤差率降低近 40%(從 5% 誤差降至大約 3%)。

這是一個(gè)相當(dāng)簡單的算法:在每個(gè)訓(xùn)練步驟中,每個(gè)神經(jīng)元(包括輸入神經(jīng)元,但不包括輸出神經(jīng)元)都有一個(gè)暫時(shí)“丟棄”的概率p,這意味著在這個(gè)訓(xùn)練步驟中它將被完全忽略, 在下一步可能會(huì)激活(見圖 11-9)。 超參數(shù)p稱為丟失率,通常設(shè)為 50%。 訓(xùn)練后,神經(jīng)元不會(huì)再下降。 這就是全部(除了我們將要討論的技術(shù)細(xì)節(jié))。

圖11-9 丟失正則化

一開始這個(gè)技術(shù)是相當(dāng)粗魯,這是相當(dāng)令人驚訝的。如果一個(gè)公司的員工每天早上被告知要擲硬幣來決定是否上班,公司的表現(xiàn)會(huì)不會(huì)更好呢?那么,誰知道;也許會(huì)!公司顯然將被迫適應(yīng)這樣的組織構(gòu)架;它不能依靠任何一個(gè)人填寫咖啡機(jī)或執(zhí)行任何其他關(guān)鍵任務(wù),所以這個(gè)專業(yè)知識(shí)將不得不分散在幾個(gè)人身上。員工必須學(xué)會(huì)與其他的許多同事合作,而不僅僅是其中的一小部分。該公司將變得更有彈性。如果一個(gè)人離開了,并沒有什么區(qū)別。目前還不清楚這個(gè)想法是否真的可以在公司實(shí)行,但它確實(shí)對(duì)于神經(jīng)網(wǎng)絡(luò)是可以的。神經(jīng)元被dropout訓(xùn)練不能與其相鄰的神經(jīng)元共適應(yīng);他們必須盡可能讓自己變得有用。他們也不能過分依賴一些輸入神經(jīng)元;他們必須注意他們的每個(gè)輸入神經(jīng)元。他們最終對(duì)輸入的微小變化會(huì)不太敏感。最后,你會(huì)得到一個(gè)更強(qiáng)大的網(wǎng)絡(luò),更好地推廣。

了解 dropout 的另一種方法是認(rèn)識(shí)到每個(gè)訓(xùn)練步驟都會(huì)產(chǎn)生一個(gè)獨(dú)特的神經(jīng)網(wǎng)絡(luò)。 由于每個(gè)神經(jīng)元可以存在或不存在,總共有2 ^ N個(gè)可能的網(wǎng)絡(luò)(其中 N 是可丟棄神經(jīng)元的總數(shù))。 這是一個(gè)巨大的數(shù)字,實(shí)際上不可能對(duì)同一個(gè)神經(jīng)網(wǎng)絡(luò)進(jìn)行兩次采樣。 一旦你運(yùn)行了 10,000 個(gè)訓(xùn)練步驟,你基本上已經(jīng)訓(xùn)練了 10,000 個(gè)不同的神經(jīng)網(wǎng)絡(luò)(每個(gè)神經(jīng)網(wǎng)絡(luò)只有一個(gè)訓(xùn)練實(shí)例)。 這些神經(jīng)網(wǎng)絡(luò)顯然不是獨(dú)立的,因?yàn)樗鼈児蚕碓S多權(quán)重,但是它們都是不同的。 由此產(chǎn)生的神經(jīng)網(wǎng)絡(luò)可以看作是所有這些較小的神經(jīng)網(wǎng)絡(luò)的平均集成。

有一個(gè)小而重要的技術(shù)細(xì)節(jié)。 假設(shè)p = 50%,在這種情況下,在測試期間,在訓(xùn)練期間神經(jīng)元將被連接到兩倍于(平均)的輸入神經(jīng)元。 為了彌補(bǔ)這個(gè)事實(shí),我們需要在訓(xùn)練之后將每個(gè)神經(jīng)元的輸入連接權(quán)重乘以 0.5。 如果我們不這樣做,每個(gè)神經(jīng)元的總輸入信號(hào)大概是網(wǎng)絡(luò)訓(xùn)練的兩倍,這不太可能表現(xiàn)良好。 更一般地說,我們需要將每個(gè)輸入連接權(quán)重乘以訓(xùn)練后的保持概率(1-p)。 或者,我們可以在訓(xùn)練過程中將每個(gè)神經(jīng)元的輸出除以保持概率(這些替代方案并不完全等價(jià),但它們工作得同樣好)。

要使用 TensorFlow 實(shí)現(xiàn)dropout,可以簡單地將dropout()函數(shù)應(yīng)用于輸入層和每個(gè)隱藏層的輸出。 在訓(xùn)練過程中,這個(gè)功能隨機(jī)丟棄一些節(jié)點(diǎn)(將它們設(shè)置為 0),并用保留概率來劃分剩余項(xiàng)目。 訓(xùn)練結(jié)束后,這個(gè)函數(shù)什么都不做。下面的代碼將dropout正則化應(yīng)用于我們的三層神經(jīng)網(wǎng)絡(luò):

注意:本書使用tf.contrib.layers.dropout()而不是tf.layers.dropout()(本章寫作時(shí)不存在)。 現(xiàn)在最好使用tf.layers.dropout(),因?yàn)?code>contrib模塊中的任何內(nèi)容都可能會(huì)改變或被刪除,恕不另行通知。tf.layers.dropout()函數(shù)幾乎與tf.contrib.layers.dropout()函數(shù)相同,只是有一些細(xì)微差別。 最重要的是:

  • 您必須指定丟失率(率)而不是保持概率(keep_prob),其中rate簡單地等于1 - keep_prob
  • is_training參數(shù)被重命名為training
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")
training = tf.placeholder_with_default(False, shape=(), name='training')

dropout_rate = 0.5  # == 1 - keep_prob
X_drop = tf.layers.dropout(X, dropout_rate, training=training)

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X_drop, n_hidden1, activation=tf.nn.relu,
                              name="hidden1")
    hidden1_drop = tf.layers.dropout(hidden1, dropout_rate, training=training)
    hidden2 = tf.layers.dense(hidden1_drop, n_hidden2, activation=tf.nn.relu,
                              name="hidden2")
    hidden2_drop = tf.layers.dropout(hidden2, dropout_rate, training=training)
    logits = tf.layers.dense(hidden2_drop, n_outputs, name="outputs")
with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("train"):
    optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9)
    training_op = optimizer.minimize(loss)    

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 20
batch_size = 50

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={training: True, X: X_batch, y: y_batch})
        acc_test = accuracy.eval(feed_dict={X: mnist.test.images, y: mnist.test.labels})
        print(epoch, "Test accuracy:", acc_test)

    save_path = saver.save(sess, "./my_model_final.ckpt")

你想在tensorflow.contrib.layers中使用dropout()函數(shù),而不是tensorflow.nn中的那個(gè)。 第一個(gè)在不訓(xùn)練的時(shí)候關(guān)掉(沒有操作),這是你想要的,而第二個(gè)不是。

如果觀察到模型過擬合,則可以增加 dropout 率(即,減少keep_prob超參數(shù))。 相反,如果模型欠擬合訓(xùn)練集,則應(yīng)嘗試降低 dropout 率(即增加keep_prob)。 它也可以幫助增加大層的 dropout 率,并減少小層的 dropout 率。

dropout 似乎減緩了收斂速度,但通常會(huì)在調(diào)整得當(dāng)時(shí)使模型更好。 所以,這通常值得花費(fèi)額外的時(shí)間和精力。

Dropconnect是dropout的變體,其中單個(gè)連接隨機(jī)丟棄而不是整個(gè)神經(jīng)元。 一般而言,dropout表現(xiàn)會(huì)更好。

最大范數(shù)正則化

另一種在神經(jīng)網(wǎng)絡(luò)中非常流行的正則化技術(shù)被稱為最大范數(shù)正則化:對(duì)于每個(gè)神經(jīng)元,它約束輸入連接的權(quán)重w,使得 w ∥2 ≤ r,其中r是最大范數(shù)超參數(shù),∥ · ∥2 是 l2 范數(shù)。

我們通常通過在每個(gè)訓(xùn)練步驟之后計(jì)算∥w∥2 來實(shí)現(xiàn)這個(gè)約束,并且如果需要的話可以剪切W

減少r增加了正則化的數(shù)量,并有助于減少過擬合。 最大范數(shù)正則化還可以幫助減輕梯度消失/爆炸問題(如果您不使用批量標(biāo)準(zhǔn)化)。

讓我們回到 MNIST 的簡單而簡單的神經(jīng)網(wǎng)絡(luò),只有兩個(gè)隱藏層:

n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 50
n_outputs = 10

learning_rate = 0.01
momentum = 0.9

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
    logits = tf.layers.dense(hidden2, n_outputs, name="outputs")

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("train"):
    optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)
    training_op = optimizer.minimize(loss)    

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

接下來,讓我們來處理第一個(gè)隱藏層的權(quán)重,并創(chuàng)建一個(gè)操作,使用clip_by_norm()函數(shù)計(jì)算剪切后的權(quán)重。 然后我們創(chuàng)建一個(gè)賦值操作來將權(quán)值賦給權(quán)值變量:

threshold = 1.0
weights = tf.get_default_graph().get_tensor_by_name("hidden1/kernel:0")
clipped_weights = tf.clip_by_norm(weights, clip_norm=threshold, axes=1)
clip_weights = tf.assign(weights, clipped_weights)

我們也可以為第二個(gè)隱藏層做到這一點(diǎn):

weights2 = tf.get_default_graph().get_tensor_by_name("hidden2/kernel:0")
clipped_weights2 = tf.clip_by_norm(weights2, clip_norm=threshold, axes=1)
clip_weights2 = tf.assign(weights2, clipped_weights2)

讓我們添加一個(gè)初始化器和一個(gè)保存器:

init = tf.global_variables_initializer()
saver = tf.train.Saver()

現(xiàn)在我們可以訓(xùn)練模型。 與往常一樣,除了在運(yùn)行training_op之后,我們運(yùn)行clip_weightsclip_weights2操作:

n_epochs = 20
batch_size = 50
with tf.Session() as sess:                                              # not shown in the book
    init.run()                                                          # not shown
    for epoch in range(n_epochs):                                       # not shown
        for iteration in range(mnist.train.num_examples // batch_size):  # not shown
            X_batch, y_batch = mnist.train.next_batch(batch_size)       # not shown
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
            clip_weights.eval()
            clip_weights2.eval()                                        # not shown
        acc_test = accuracy.eval(feed_dict={X: mnist.test.images,       # not shown
                                            y: mnist.test.labels})      # not shown
        print(epoch, "Test accuracy:", acc_test)                        # not shown

    save_path = saver.save(sess, "./my_model_final.ckpt")               # not shown

上面的實(shí)現(xiàn)很簡單,工作正常,但有點(diǎn)麻煩。 更好的方法是定義一個(gè)max_norm_regularizer()函數(shù):

def max_norm_regularizer(threshold, axes=1, name="max_norm",
                         collection="max_norm"):
    def max_norm(weights):
        clipped = tf.clip_by_norm(weights, clip_norm=threshold, axes=axes)
        clip_weights = tf.assign(weights, clipped, name=name)
        tf.add_to_collection(collection, clip_weights)
        return None # there is no regularization loss term
    return max_norm

然后你可以調(diào)用這個(gè)函數(shù)來得到一個(gè)最大范數(shù)調(diào)節(jié)器(與你想要的閾值)。 當(dāng)你創(chuàng)建一個(gè)隱藏層時(shí),你可以將這個(gè)正則化器傳遞給kernel_regularizer參數(shù):

n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 50
n_outputs = 10

learning_rate = 0.01
momentum = 0.9

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")
max_norm_reg = max_norm_regularizer(threshold=1.0)
with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                              kernel_regularizer=max_norm_reg, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,
                              kernel_regularizer=max_norm_reg, name="hidden2")
    logits = tf.layers.dense(hidden2, n_outputs, name="outputs")
with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("train"):
    optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)
    training_op = optimizer.minimize(loss)    

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

init = tf.global_variables_initializer()
saver = tf.train.Saver()

訓(xùn)練與往常一樣,除了每次訓(xùn)練后必須運(yùn)行重量裁剪操作:

請(qǐng)注意,最大范數(shù)正則化不需要在整體損失函數(shù)中添加正則化損失項(xiàng),所以max_norm()函數(shù)返回None。 但是,在每個(gè)訓(xùn)練步驟之后,仍需要運(yùn)行clip_weights操作,因此您需要能夠掌握它。 這就是為什么max_norm()函數(shù)將clip_weights節(jié)點(diǎn)添加到最大范數(shù)剪裁操作的集合中的原因。您需要獲取這些裁剪操作并在每個(gè)訓(xùn)練步驟后運(yùn)行它們:

n_epochs = 20
batch_size = 50
clip_all_weights = tf.get_collection("max_norm")

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
            sess.run(clip_all_weights)
        acc_test = accuracy.eval(feed_dict={X: mnist.test.images,     # not shown in the book
                                            y: mnist.test.labels})    # not shown
        print(epoch, "Test accuracy:", acc_test)                      # not shown

    save_path = saver.save(sess, "./my_model_final.ckpt")             # not shown

數(shù)據(jù)增強(qiáng)

最后一個(gè)正則化技術(shù),數(shù)據(jù)增強(qiáng),包括從現(xiàn)有的訓(xùn)練實(shí)例中產(chǎn)生新的訓(xùn)練實(shí)例,人為地增加了訓(xùn)練集的大小。 這將減少過擬合,使之成為正則化技術(shù)。 訣竅是生成逼真的訓(xùn)練實(shí)例; 理想情況下,一個(gè)人不應(yīng)該能夠分辨出哪些是生成的,哪些不是生成的。 而且,簡單地加白噪聲也無濟(jì)于事。 你應(yīng)用的修改應(yīng)該是可以學(xué)習(xí)的(白噪聲不是)。

例如,如果您的模型是為了分類蘑菇圖片,您可以稍微移動(dòng),旋轉(zhuǎn)和調(diào)整訓(xùn)練集中的每個(gè)圖片的大小,并將結(jié)果圖片添加到訓(xùn)練集(見圖 11-10)。 這迫使模型更能容忍圖片中蘑菇的位置,方向和大小。 如果您希望模型對(duì)光照條件更加寬容,則可以類似地生成具有各種對(duì)比度的許多圖像。 假設(shè)蘑菇是對(duì)稱的,你也可以水平翻轉(zhuǎn)圖片。 通過結(jié)合這些轉(zhuǎn)換,可以大大增加訓(xùn)練集的大小。

圖11-10 利用現(xiàn)有實(shí)例生成新的訓(xùn)練實(shí)例

通常最好在訓(xùn)練期間生成訓(xùn)練實(shí)例,而不是浪費(fèi)存儲(chǔ)空間和網(wǎng)絡(luò)帶寬。TensorFlow 提供了多種圖像處理操作,例如轉(zhuǎn)置(移位),旋轉(zhuǎn),調(diào)整大小,翻轉(zhuǎn)和裁剪,以及調(diào)整亮度,對(duì)比度,飽和度和色調(diào)(請(qǐng)參閱 API 文檔以獲取更多詳細(xì)信息)。 這可以很容易地為圖像數(shù)據(jù)集實(shí)現(xiàn)數(shù)據(jù)增強(qiáng)。

訓(xùn)練非常深的神經(jīng)網(wǎng)絡(luò)的另一個(gè)強(qiáng)大的技術(shù)是添加跳過連接(跳過連接是將層的輸入添加到更高層的輸出時(shí))。 我們將在第 13 章中談?wù)撋疃葰埐罹W(wǎng)絡(luò)時(shí)探討這個(gè)想法。

實(shí)踐指南

在本章中,我們講解了很多技術(shù),你可能想知道應(yīng)該使用哪些技術(shù)。 表 11-2 中的配置在大多數(shù)情況下都能正常工作。

表11-2 默認(rèn)的DNN配置

當(dāng)然,如果你能找到解決類似問題的方法,你應(yīng)該嘗試重用預(yù)訓(xùn)練的神經(jīng)網(wǎng)絡(luò)的一部分。

這個(gè)默認(rèn)配置可能需要調(diào)整:

  • 如果你找不到一個(gè)好的學(xué)習(xí)率(收斂速度太慢,所以你增加了訓(xùn)練速度,現(xiàn)在收斂速度很快,但是網(wǎng)絡(luò)的準(zhǔn)確性不是最理想的),那么你可以嘗試添加一個(gè)學(xué)習(xí)率調(diào)整,如指數(shù)衰減。
  • 如果你的訓(xùn)練集太小,你可以實(shí)現(xiàn)數(shù)據(jù)增強(qiáng)。
  • 如果你需要一個(gè)稀疏的模型,你可以添加 l1 正則化混合(并可以選擇在訓(xùn)練后將微小的權(quán)重歸零)。 如果您需要更稀疏的模型,您可以嘗試使用 FTRL 而不是 Adam 優(yōu)化以及 l1 正則化。
  • 如果在運(yùn)行時(shí)需要快速模型,則可能需要?jiǎng)h除批量標(biāo)準(zhǔn)化,并可能用 leakyReLU 替換 ELU 激活函數(shù)。 有一個(gè)稀疏的模型也將有所幫助。

有了這些指導(dǎo)方針,你現(xiàn)在已經(jīng)準(zhǔn)備好訓(xùn)練非常深的網(wǎng)絡(luò) - 好吧,如果你非常有耐心的話,那就是! 如果使用單臺(tái)機(jī)器,則可能需要等待幾天甚至幾個(gè)月才能完成訓(xùn)練。 在下一章中,我們將討論如何使用分布式 TensorFlow 在許多服務(wù)器和 GPU 上訓(xùn)練和運(yùn)行模型。

練習(xí)

  1. 使用 He 初始化隨機(jī)選擇權(quán)重,是否可以將所有權(quán)重初始化為相同的值?
  2. 可以將偏置初始化為 0 嗎?
  3. 說出 ELU 激活功能與 ReLU 相比的三個(gè)優(yōu)點(diǎn)。
  4. 在哪些情況下,您想要使用以下每個(gè)激活函數(shù):ELU,leaky ReLU(及其變體),ReLU,tanh,logistic 以及 softmax?
  5. 使用MomentumOptimizer時(shí),如果將momentum超參數(shù)設(shè)置得太接近 1(例如,0.99999),會(huì)發(fā)生什么情況?
  6. 請(qǐng)列舉您可以生成稀疏模型的三種方法。
  7. dropout 是否會(huì)減慢訓(xùn)練? 它是否會(huì)減慢推斷(即預(yù)測新的實(shí)例)?
  8. 深度學(xué)習(xí)。
    1. 建立一個(gè) DNN,有五個(gè)隱藏層,每層 100 個(gè)神經(jīng)元,使用 He 初始化和 ELU 激活函數(shù)。
    2. 使用 Adam 優(yōu)化和提前停止,請(qǐng)嘗試在 MNIST 上進(jìn)行訓(xùn)練,但只能使用數(shù)字 0 到 4,因?yàn)槲覀儗⒃谙乱粋€(gè)練習(xí)中在數(shù)字 5 到 9 上進(jìn)行遷移學(xué)習(xí)。 您需要一個(gè)包含五個(gè)神經(jīng)元的 softmax 輸出層,并且一如既往地確保定期保存檢查點(diǎn),并保存最終模型,以便稍后再使用它。
    3. 使用交叉驗(yàn)證調(diào)整超參數(shù),并查看你能達(dá)到什么準(zhǔn)確度。
    4. 現(xiàn)在嘗試添加批量標(biāo)準(zhǔn)化并比較學(xué)習(xí)曲線:它是否比以前收斂得更快? 它是否會(huì)產(chǎn)生更好的模型?
    5. 模型是否過擬合訓(xùn)練集? 嘗試將 dropout 添加到每一層,然后重試。 它有幫助嗎?
  9. 遷移學(xué)習(xí)。
    1. 創(chuàng)建一個(gè)新的 DNN,它復(fù)制先前模型的所有預(yù)訓(xùn)練的隱藏層,凍結(jié)它們,并用新的一層替換 softmax 輸出層。
    2. 在數(shù)字 5 到 9 訓(xùn)練這個(gè)新的 DNN ,每個(gè)數(shù)字只使用 100 個(gè)圖像,需要多長時(shí)間? 盡管樣本這么少,你能達(dá)到高準(zhǔn)確度嗎?
    3. 嘗試緩存凍結(jié)的層,并再次訓(xùn)練模型:現(xiàn)在速度有多快?
    4. 再次嘗試復(fù)用四個(gè)隱藏層而不是五個(gè)。 你能達(dá)到更高的準(zhǔn)確度嗎?
    5. 現(xiàn)在,解凍前兩個(gè)隱藏層并繼續(xù)訓(xùn)練:您可以讓模型表現(xiàn)得更好嗎?
  10. 輔助任務(wù)的預(yù)訓(xùn)練。
    1. 在本練習(xí)中,你將構(gòu)建一個(gè) DNN,用于比較兩個(gè) MNIST 數(shù)字圖像,并預(yù)測它們是否代表相同的數(shù)字。 然后,你將復(fù)用該網(wǎng)絡(luò)的較低層,來使用非常少的訓(xùn)練數(shù)據(jù)來訓(xùn)練 MNIST 分類器。 首先構(gòu)建兩個(gè) DNN(我們稱之為 DNN A 和 B),它們與之前構(gòu)建的 DNN 類似,但沒有輸出層:每個(gè) DNN 應(yīng)該有五個(gè)隱藏層,每個(gè)層包含 100 個(gè)神經(jīng)元,使用 He 初始化和 ELU 激活函數(shù)。 接下來,在兩個(gè) DNN 上添加一個(gè)輸出層。 你應(yīng)該使用 TensorFlow 的concat()函數(shù)和axis = 1`,將兩個(gè) DNN 的輸出沿著橫軸連接,然后將結(jié)果輸入到輸出層。 輸出層應(yīng)該包含單個(gè)神經(jīng)元,使用 logistic 激活函數(shù)。
    2. 將 MNIST 訓(xùn)練集分為兩組:第一部分應(yīng)包含 55,000個(gè) 圖像,第二部分應(yīng)包含 5000 個(gè)圖像。 創(chuàng)建一個(gè)生成訓(xùn)練批次的函數(shù),其中每個(gè)實(shí)例都是從第一部分中挑選的一對(duì) MNIST 圖像。 一半的訓(xùn)練實(shí)例應(yīng)該是屬于同一類的圖像對(duì),而另一半應(yīng)該是來自不同類別的圖像。 對(duì)于每一對(duì),如果圖像來自同一類,訓(xùn)練標(biāo)簽應(yīng)該為 0;如果來自不同類,則標(biāo)簽應(yīng)為 1。
    3. 在這個(gè)訓(xùn)練集上訓(xùn)練 DNN。 對(duì)于每個(gè)圖像對(duì),你可以同時(shí)將第一張圖像送入 DNN A,將第二張圖像送入 DNN B。整個(gè)網(wǎng)絡(luò)將逐漸學(xué)會(huì)判斷兩張圖像是否屬于同一類別。
    4. 現(xiàn)在通過復(fù)用和凍結(jié) DNN A 的隱藏層,并添加 1 0個(gè)神經(jīng)元的 softmax 輸出層來創(chuàng)建一個(gè)新的 DNN。 在第二部分上訓(xùn)練這個(gè)網(wǎng)絡(luò),看看你是否可以實(shí)現(xiàn)較好的表現(xiàn),盡管每類只有 500 個(gè)圖像。

這些問題的答案在附錄 A 中。


(第一部分 機(jī)器學(xué)習(xí)基礎(chǔ))
第01章 機(jī)器學(xué)習(xí)概覽
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(上)
第02章 一個(gè)完整的機(jī)器學(xué)習(xí)項(xiàng)目(下)
第03章 分類
第04章 訓(xùn)練模型
第05章 支持向量機(jī)
第06章 決策樹
第07章 集成學(xué)習(xí)和隨機(jī)森林
第08章 降維
(第二部分 神經(jīng)網(wǎng)絡(luò)和深度學(xué)習(xí))
第9章 啟動(dòng)和運(yùn)行TensorFlow
第10章 人工神經(jīng)網(wǎng)絡(luò)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(上)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)(下)
第12章 設(shè)備和服務(wù)器上的分布式 TensorFlow
第13章 卷積神經(jīng)網(wǎng)絡(luò)
第14章 循環(huán)神經(jīng)網(wǎng)絡(luò)
第15章 自編碼器
第16章 強(qiáng)化學(xué)習(xí)(上)
第16章 強(qiáng)化學(xué)習(xí)(下)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容