估算深度神經網路的最優學習率
學習率如何影響訓練?
深度學習模型通常由隨機梯度下降演算法進行訓練。隨機梯度下降演算法有許多變形:例如 Adam、RMSProp、Adagrad 等等。這些演算法都需要你設定學習率。學習率決定了在一個小批量(mini-batch)中權重在梯度方向要移動多遠。
如果學習率很低,訓練會變得更加可靠,但是優化會耗費較長的時間,因為朝向損失函式最小值的每個步長很小。
如果學習率很高,訓練可能根本不會收斂,甚至會發散。權重的改變數可能非常大,使得優化越過最小值,使得損失函式變得更糟。
學習率很小(上圖)和學習率很大(下圖)的梯度下降。來源:Cousera 機器學習課程(吳恩達)
訓練應當從相對較大的學習率開始。這是因為在開始時,初始的隨機權重遠離最優值。在訓練過程中,學習率應當下降,以允許細粒度的權重更新。
有很多方式可以為學習率設定初始值。一個簡單的方案就是嘗試一些不同的值,看看哪個值能夠讓損失函式最優,且不損失訓練速度。我們可以從 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)來做平緩化處理。
640?wx_fmt=png
使用 SMA 平緩化處理後的損失函式變化率
這樣看起來就好多了。在這個圖中,我們需要找到最小值位置。看起來,它接近於學習率為 0.01 這個位置。
實現
Jeremy Howard 和他在 USF 資料研究所的團隊開發了
fast.ai 提供了學習率搜尋器的一個實現。你只需要寫幾行程式碼就能繪製模型的損失函式-學習率的影象(來自 GitHub:plot_loss.py):
learn is an instance of Learner class 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.
Parameters:
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):如何在訓練過程中改變學習率。傳統的觀點是,隨著時間推移學習率要越來越低,而且有許多方法進行設定:例如損失函式停止改善時逐步進行學習率退火、指數學習率衰退、餘弦退火等。
我上面引用的論文描述了一種迴圈改變學習率的新方法,它能提升卷積神經網路在各種影象分類任務上的效能表現。
參考部落格:https://blog.csdn.net/sfM06sqVW55DFt1/article/details/78684355
GitHub 連結:https://gist.github.com/surmenok