以下內容為結合李沐老師的課程和教材補充的學習筆記,以及對課后練習的一些思考,自留回顧,也供同學之人交流參考。
本節課程地址:72 優化算法【動手學深度學習v2】_嗶哩嗶哩_bilibili
本節教材地址:11.9. Adadelta — 動手學深度學習 2.0.0 documentation
本節開源代碼:...>d2l-zh>pytorch>chapter_optimization>adadelta.ipynb
Adadelta
Adadelta是AdaGrad的另一種變體(?11.7節?), 主要區別在于前者減少了學習率適應坐標的數量。 此外,廣義上Adadelta被稱為沒有學習率,因為它使用變化量本身作為未來變化的校準。 Adadelta算法是在 (Zeiler, 2012)中提出的。
Adadelta算法
簡而言之,Adadelta使用兩個狀態變量,??用于存儲梯度二階導數的泄露平均值,?
?用于存儲模型本身中參數變化二階導數的泄露平均值。請注意,為了與其他出版物和實現的兼容性,我們使用作者的原始符號和命名(沒有其它真正理由讓大家使用不同的希臘變量來表示在動量法、AdaGrad、RMSProp和Adadelta中用于相同用途的參數)。
以下是Adadelta的技術細節。鑒于參數du jour是??,我們獲得了與?11.8節?類似的以下泄漏更新:
與?11.8節?的區別在于,我們使用重新縮放的梯度??執行更新,即
那么,調整后的梯度??是什么?我們可以按如下方式計算它:
其中?Δxt?1?是重新縮放梯度的平方???的泄漏平均值。我們將?
?初始化為0,然后在每個步驟中使用?
?更新它,即
和??(例如?
?這樣的小值)是為了保持數字穩定性而加入的。
代碼實現
Adadelta需要為每個變量維護兩個狀態變量,即??和?
?。這將產生以下實現。
%matplotlib inline
import torch
from d2l import torch as d2ldef init_adadelta_states(feature_dim):s_w, s_b = torch.zeros((feature_dim, 1)), torch.zeros(1)delta_w, delta_b = torch.zeros((feature_dim, 1)), torch.zeros(1)return ((s_w, delta_w), (s_b, delta_b))def adadelta(params, states, hyperparams):rho, eps = hyperparams['rho'], 1e-5for p, (s, delta) in zip(params, states):with torch.no_grad():# In-place updates via [:] 原地更新,不會干擾計算圖的結構s[:] = rho * s + (1 - rho) * torch.square(p.grad)g = (torch.sqrt(delta + eps) / torch.sqrt(s + eps)) * p.gradp[:] -= gdelta[:] = rho * delta + (1 - rho) * g * gp.grad.data.zero_()
對于每次參數更新,選擇??相當于10個半衰期。由此我們得到:
data_iter, feature_dim = d2l.get_data_ch11(batch_size=10)
d2l.train_ch11(adadelta, init_adadelta_states(feature_dim),{'rho': 0.9}, data_iter, feature_dim)
輸出結果:
loss: 0.244, 0.031 sec/epoch
為了簡潔實現,我們只需使用高級API中的Adadelta算法。
trainer = torch.optim.Adadelta
d2l.train_concise_ch11(trainer, {'rho': 0.9}, data_iter)
輸出結果:
loss: 0.243, 0.011 sec/epoch
小結
- Adadelta沒有學習率參數。相反,它使用參數本身的變化率來調整學習率。
- Adadelta需要兩個狀態變量來存儲梯度的二階導數和參數的變化。
- Adadelta使用泄漏的平均值來保持對適當統計數據的運行估計。
練習
- 調整?
?的值,會發生什么?
解:
類似于RMSProp的??,?
?之于Adadelta也是一個介于 0 和 1 之間的值,用于平衡歷史信息和當前信息的權重。
當??接近于1時,模型會更多地依賴于過去的梯度和更新量信息;
當??接近于0時,模型會更多地依賴于當前的梯度和更新量信息。
- 展示如何在不使用?
?的情況下實現算法。為什么這是個好主意?
解:
?是一個顯式的中間變量,不使用?
,即直接在更新變量時進行計算和累積量時進行計算,這樣做可以減少內存占用,但實際計算量是重復的,所以每epoch的計算用時增加了。
def adadelta_no_g(params, states, hyperparams):rho, eps = hyperparams['rho'], 1e-5for p, (s, delta) in zip(params, states):with torch.no_grad(): s[:] = rho * s + (1 - rho) * p.grad ** 2p[:] -= (torch.sqrt(delta + eps) / torch.sqrt(s + eps)) * p.graddelta[:] = rho * delta + (1 - rho) * ((torch.sqrt(delta + eps) / torch.sqrt(s + eps)) * p.grad) ** 2p.grad.data.zero_()
d2l.train_ch11(adadelta_no_g, init_adadelta_states(feature_dim),{'rho': 0.9}, data_iter, feature_dim)
輸出結果:
loss: 0.243, 0.016 sec/epoch
3. Adadelta真的是學習率為0嗎?能找到Adadelta無法解決的優化問題嗎?
解:
不是,Adadelta 是一種自適應學習率優化算法,它的學習率并不是固定的也不是為0,而是動態調整的,具體為:?
Adadelta 在處理非凸優化問題時可能會遇到困難。非凸優化問題的損失函數可能有多個局部最小值,Adadelta 可能會陷入局部最小值而無法找到全局最小值。
在某些情況下,梯度可能非常稀疏,即大部分梯度值為零,這時,Adadelta 的累積梯度平方?st?可能會變得非常小,導致學習率變得非常大。這可能會導致參數更新過大,從而破壞訓練過程。例如,在訓練稀疏數據集(如某些自然語言處理任務)時,Adadelta 可能會表現不佳。
4. 將Adadelta的收斂行為與AdaGrad和RMSProp進行比較。
解:
算法 | AdaGrad | RMSProp | Adadelta |
---|---|---|---|
收斂速度 | 在訓練初期,AdaGrad 的收斂速度較快,因為它能夠快速調整學習率以適應稀疏梯度。 | 在訓練初期,RMSProp 的收斂速度相對較快,因為它能夠更好地適應動態變化的梯度。 | 在訓練初期,Adadelta 的收斂速度可能較慢,因為它依賴于歷史信息來調整學習率。 |
后期表現 | 由于累積梯度平方不斷增大,學習率逐漸減小,最終可能趨近于零,導致訓練停止。 | 由于引入了衰減率 $\gamma$,RMSProp 能夠保持相對穩定的學習率,避免了學習率趨近于零的問題。 | Adadelta 能夠自適應地調整學習率,避免了手動設置全局學習率的問題。 |
適用場景 | 適用于稀疏梯度問題,如自然語言處理中的詞嵌入。 | 適用于非平穩優化問題,如深度神經網絡的訓練。 | 適用于非平穩優化問題,尤其是需要長期依賴歷史信息的任務。 |