學習率(learning rate)是調整深度神經網絡最重要的超參數之一,本文作者Pavel Surmenok描述了一個簡單而有效的辦法來幫助你找尋合理的學習率。
我正在舊金山大學的 fast.ai 深度學習課程中學習相關知識。目前這門課程還沒有對公眾開放,但是現在網絡上有去年的版本。
學習率如何影響訓練?
深度學習模型通常由隨機梯度下降算法進行訓練。隨機梯度下降算法有許多變形:例如 Adam、RMSProp、Adagrad 等等。這些算法都需要你設置學習率。學習率決定了在一個小批量(mini-batch)中權重在梯度方向要移動多遠。
如果學習率很低,訓練會變得更加可靠,但是優化會耗費較長的時間,因為朝向損失函數最小值的每個步長很小。
如果學習率很高,訓練可能根本不會收斂,甚至會發散。權重的改變量可能非常大,使得優化越過最小值,使得損失函數變得更糟。
訓練應當從相對較大的學習率開始。這是因為在開始時,初始的隨機權重遠離最優值。在訓練過程中,學習率應當下降,以允許細粒度的權重更新。
有很多方式可以為學習率設置初始值。一個簡單的方案就是嘗試一些不同的值,看看哪個值能夠讓損失函數最優,且不損失訓練速度。我們可以從 0.1 這樣的值開始,然后再指數下降學習率,比如 0.01,0.001 等等。當我們以一個很大的學習率開始訓練時,在起初的幾次迭代訓練過程中損失函數可能不會改善,甚至會增大。當我們以一個較小的學習率進行訓練時,損失函數的值會在最初的幾次迭代中從某一時刻開始下降。這個學習率就是我們能用的最大值,任何更大的值都不能讓訓練收斂。不過,這個初始學習率也過大了:它不足以訓練多個 epoch,因為隨著時間的推移網絡將需要更加細粒度的權重更新。因此,開始訓練的合理學習率可能需要降低 1-2 個數量級。
一定有更好的方法
Leslie N. Smith?在 2015 年的論文「Cyclical Learning Rates for Training Neural Networks」的第 3.3 節,描述了一種為神經網絡選擇一系列學習率的強大方法。
訣竅就是從一個低學習率開始訓練網絡,并在每個批次中指數提高學習率。
在每個小批量處理后提升學習率
為每批樣本記錄學習率和訓練損失。然后,根據損失和學習率畫圖。典型情況如下:
一開始,損失下降,然后訓練過程開始發散
首先,學習率較低,損失函數值緩慢改善,然后訓練加速,直到學習速度變得過高導致損失函數值增加:訓練過程發散。
我們需要在圖中找到一個損失函數值降低得最快的點。在這個例子中,當學習率在 0.001 和 0.01 之間,損失函數快速下降。
另一個方式是觀察計算損失函數變化率(也就是損失函數關于迭代次數的導數),然后以學習率為 x 軸,以變化率為 y 軸畫圖。
損失函數的變化率
上圖看起來噪聲太大,讓我們使用簡單移動平均線(SMA)來做平緩化處理。
使用 SMA 平緩化處理后的損失函數變化率
這樣看起來就好多了。在這個圖中,我們需要找到最小值位置??雌饋恚咏趯W習率為 0.01 這個位置。
實現代碼教程
Jeremy Howard 和他在 USF 數據研究所的團隊開發了 fast.ai。這是一個基于 PyTorch 的高級抽象的深度學習庫。fast.ai 是一個簡單而強大的工具集,可以用于訓練最先進的深度學習模型。Jeremy 在他最新的深度學習課程()中使用了這個庫。
fast.ai 提供了學習率搜索器的一個實現。你只需要寫幾行代碼就能繪制模型的損失函數-學習率的圖像(來自 GitHub:plot_loss.py):
# learn is an instance of Learnerclass or one of derived classes like ConvLearner
learn.lr_find()
learn.sched.plot_lr()
庫中并沒有提供代碼繪制損失函數變化率的圖像,但計算起來非常簡單(plot_change_loss.py):
def plot_loss_change(sched, sma=1, n_skip=20, y_lim=(-0.01,0.01)):
"""
Plots rate of change of the loss function.
sched - learning rate scheduler, an instance of LR_Finder class.
sma - number of batches for simple moving average to smooth out the curve.
n_skip - number of batches to skip on the left.
y_lim - limits for the y axis.
"""
derivatives = [0] * (sma + 1)
for i in range(1 + sma, len(learn.sched.lrs)):
derivative = (learn.sched.losses[i] - learn.sched.losses[i - sma]) / sma
derivatives.append(derivative)
plt.ylabel("d/loss")
plt.xlabel("learning rate (log scale)")
plt.plot(learn.sched.lrs[n_skip:], derivatives[n_skip:])
plt.xscale('log')
plt.ylim(y_lim)
plot_loss_change(learn.sched, sma=20)
請注意:只在訓練之前選擇一次學習率是不夠的。訓練過程中,最優學習率會隨著時間推移而下降。你可以定期重新運行相同的學習率搜索程序,以便在訓練的稍后時間查找學習率。
使用其他庫實現本方案
我還沒有準備好將這種學習率搜索方法應用到諸如 Keras 等其他庫中,但這應該不是什么難事。只需要做到:
多次運行訓練,每次只訓練一個小批量;
在每次分批訓練之后通過乘以一個小的常數的方式增加學習率;
當損失函數值高于先前觀察到的最佳值時,停止程序。(例如,可以將終止條件設置為「當前損失 > *4 最佳損失」)
學習計劃
選擇學習率的初始值只是問題的一部分。另一個需要優化的是學習計劃(learning schedule):如何在訓練過程中改變學習率。傳統的觀點是,隨著時間推移學習率要越來越低,而且有許多方法進行設置:例如損失函數停止改善時逐步進行學習率退火、指數學習率衰退、余弦退火等。
我上面引用的論文描述了一種循環改變學習率的新方法,它能提升卷積神經網絡在各種圖像分類任務上的性能表現。?
評論