事先聲明,這篇博客的適用人羣是剛開始學習梯度下降的同學,有無深度學習基礎不是特別重要,重要的是有一顆學習探索的心~
關鍵字:momentum、函數求導、Python
一直以來,不是很懂隨機梯度下降中momentum的概念,後來又好好地研究了下momentum,順便總結下,這篇博客旨在讓各位看官以最快的速度弄懂momentum的含義,因而如下主要內容的梗概比較簡短,讓大家快速上手:
- 原理
- Python實現
原理
首先說一下原始的沒有momentum的梯度下降,最一開始的每次的迭代過程如下:
- 隨機抽取個樣本,,
- 計算梯度並更新參數爲如下公式:
經過上述操作,每次梯度更新的時候都會更新,這個恆定的值會帶來很多麻煩:比如說如果梯度下降過程中曲面太平坦,如果還按照上述步伐走,梯度下降地會比較緩慢例如下圖所示;
相反,如果梯度下降過程中太陡峭,那麼下降時就會一直震盪,例如下圖所示,
上述兩種情況都沒辦法達到穩定下降的目的。針對上述缺點,提出了momentum這個概念,在這裏不講述其物理意義,因爲很容易把人弄暈,到最後速度和位移都混在一起,現在只講原理,
還是按照上面的思路,不過這裏引入了這個概念,其實這個就是梯度的改變量, 迭代過程如下所示,
- 隨機抽取個樣本,,
- 計算梯度並更新參數爲如下公式:
經過上述操作, 梯度的變化量的推導公式如下所示,
第一次迭代公式如下,其中
第二次迭代公式如下,
…
通過第二個公式可以得到:如果本次和上次的梯度符號是相同的,那麼就能夠加速下降(幅度變大),就能夠解決原先下降太慢的問題;如果本次和上次的梯度符號是相反的,那麼這次就和上次相互抑制,減緩震盪。
經過上述操作,就能夠解決最一開始博主所討論的兩個問題了,是不是很棒!
Python實現
根據上述原理可以看出,使用momentum時需要保留之前輪的,這裏代表的是神經網絡的層的編號,這樣做是因爲神經網絡每一層的都是不相同的,而momentum需要針對每一層都做一個類似緩衝的操作,因而這時必須要添加一個靜態變量來保存上一輪的值,且這個靜態變量需要定義爲一個dict才能滿足上述需求,具體代碼如下:
import numpy as np
from collections import defaultdict
class WeightUpdate:
# 保存之前輪的狀態
CACHE = defaultdict()
def __init__(self, weight_update_param=None):
self.weight_update_func = None
self._generate_weight_update_func(weight_update_param)
def _generate_weight_update_func(self, weight_update_param=None):
if weight_update_param is None:
weight_update_param = {
"mode": "momentum",
"alpha": 0.01
}
self.weight_update_param = weight_update_param
# 方便拓展
if weight_update_param["mode"] == "momentum":
self.weight_update_func = self._momentum
else:
pass
def _momentum(self, W, delta_W, learning_rate, name=None):
"""Momentum weight update.
:param W:
:param delta_W:
:param learning_rate:
:return:
"""
if W.shape != delta_W.shape:
return None
if "alpha" not in self.weight_update_param:
return None
v = WeightUpdate.CACHE.get(name, np.zeros_like(W))
alpha = self.weight_update_param["alpha"]
WeightUpdate.CACHE[name] = -learning_rate * delta_W + alpha * v
W += WeightUpdate.CACHE[name]
return W
更多細節可以到github repo中查詢。