梯度下降 Momentum

2021-08-08 03:42:10 字數 3937 閱讀 3503

總結:

梯度下降演算法中,學習率太大,函式無法收斂,甚至發散,如下圖。學習率足夠小,理論上是可以達到區域性最優值的(非凸函式不能保證達到全域性最優),但學習率太小卻使得學習過程過於緩慢,合適的學習率應該是能在保證收斂的前提下,能盡快收斂。對於深度網路中,引數眾多,引數值初始位置隨機,同樣大小的學習率,對於某些引數可能合適,對另外一些引數可能偏小(學習過程緩慢),對另外一些引數可能太大(無法收斂,甚至發散),而學習率一般而言對所有引數都是固定的,所以無法同時滿足所有引數的要求。通過引入momentum可以讓那些因學習率太大而來回擺動的引數,梯度能前後抵消,從而阻止發散。

改進:不能的引數,設定不同的學習率??nesterov演算法...

怎麼避免區域性最優??

為了保證完整性,今天再扯一下另外乙個在梯度下降中十分重要的東西,那就是衝量——momentum。

這是乙個十分神秘的變數,我也只能以最簡單的方式理解它,於是在這裡班門弄斧了。正如它的中文名字一樣,在優化求解的過程中,衝量扮演了對之前優化量的持續發威的推動劑。乙個已經完成的梯度+步長的組合不會立刻消失,只是會以一定的形式衰減,剩下的能量將繼續發揮餘熱。我們先不加解釋的給出基於衝量的梯度下降的**:

def momentum(x_start, step, g, discount = 0.7):   

x = np.array(x_start, dtype='float64')

pre_grad = np.zeros_like(x)

for i in range(50):

grad = g(x)

pre_grad = pre_grad * discount + grad

x -= pre_grad * step

print '[ epoch ] grad = , x = '.format(i, grad, x)

if abs(sum(grad)) < 1e-6:

break;

return x

可以看出這個演算法和之前的梯度下降法相比,唯一不同的就是多了乙個pre_grad*discount,這就是衝量發揮餘熱的地方。

那麼衝量究竟有什麼作用呢?今天主要扯它其中的乙個作用,那就是幫助你穿越「山谷」。怎麼來理解穿越「山谷」呢?先來乙個待優化函式。這次的問題相對複雜些,是乙個二元二次函式:

def f(x):

return x[0] * x[0] + 50 * x[1] * x[1]

def g(x):

return np.array([2 * x[0], 100 * x[1]])

xi = np.linspace(-200,200,1000)

yi = np.linspace(-100,100,1000)

x,y = np.meshgrid(xi, yi)

z = x * x + 50 * y * y

上面這個函式在等高線圖上是這樣的:

其中中心的藍色點表示了最優值。我們根據這個圖發揮下想象,這個函式在y軸十分陡峭,在x軸相對平緩些。好了話說完我們趕緊拿樸素梯度下降來嘗試下:

gd([150,75], 0.016, g)

經過50輪的迭代,他的優化過程圖如下所示:

可以看出我們從某個點出發,整體趨勢向著最優點前進,這個是沒有問題的,但是前進的速度似乎有點乏力,是不是步長又設小了?有了之前的經歷,這一回我們在設定步長時變得小心了許多:

res, x_arr = gd([150,75], 0.019, g)

contour(x,y,z, x_arr)

好像成效不是很明顯啊,而且優化的過程中左右來回抖是怎麼回事?看著這個曲線讓我想起了乙個極限運動:

沒錯,其實演算法眼中的這個函式很這張圖很像,而演算法也果然沒有讓大家「失望」,選擇了一條艱難的道路進行優化——就像從一邊的高台滑下,然後滑到另一邊,這樣艱難地前進。沒辦法,這就是梯度下降法。在它的眼中,這樣走是最快的,而事實上,每個優化點所對應的梯度方向也確實是那個方向。

大神們這時可能會聊起特徵值的問題,關於這些問題以後再說。好吧,現在我們只能繼續挑步長,說不定步長再大點,「滑板少年」還能再快點呢!

res, x_arr = gd([150,75], 0.02, g)

contour(x,y,z, x_arr)

好吧……我們的滑板少年已經徹底玩脫了……這已經是我們能設的最大的步長了(上一次關於步長和函式之間的關係在這裡依然受用),再設大些我們的滑板少年就飛出去了。對於這個問題,由於兩個座標軸方向的函式屬性不同,為了防止在優化的過程中發散,步長只能夠根據最陡峭的方向設定。當然,解決快速收斂這個問題還有其他的辦法,這裡我們看看衝量如何搞定這位滑板少年。

很自然地,我們在想,要是少年能把行動的力量集中在往前走而不是兩邊晃就好了。這個想法分兩個步驟:首先是集中力量向前走,然後是盡量不要在兩邊晃。這時候,我們的衝量就閃亮登場了。我們發現滑板少年每一次的行動只會在以下三個方向進行:

我們可以想象到,當使用了衝量後,實際上沿-y和+y方向的兩個力可以相互抵消,而-x方向的力則會一直加強,這樣滑板少年會在y方向打轉,但是y方向的力量會越來越小,但是他在-x方向的速度會比之前快不少!

好了,那我們看看加了衝量技能的滑板少年的實際表現:

momentum([150,75], 0.016, g)

總算沒有讓大家失望,儘管滑板少年還是很貪玩,但是在50輪迭代後,他還是來到了最優點附近。可以說是基本完成了我們的任務吧。當然由於衝量的問題,前面幾輪迭代他在y軸上玩得似乎比以前還歡樂,這個問題我們後面會提。但不管怎麼說,總算完成目標了。

後來,又有高人發明了解決前面衝量沒有解決的問題的演算法,乾脆不讓滑板少年愉快地玩耍了,也就是傳說中的nesterov演算法。這裡就不細說了,有時間詳細聊下。直接給出**和結果:

def nesterov(x_start, step, g, discount = 0.7):   

x = np.array(x_start, dtype='float64')

pre_grad = np.zeros_like(x)

for i in range(50):

x_future = x - step * discount * pre_grad

grad = g(x_future)

pre_grad = pre_grad * 0.7 + grad

x -= pre_grad * step

print '[ epoch ] grad = , x = '.format(i, grad, x)

if abs(sum(grad)) < 1e-6:

break;

return x

nesterov([150,75], 0.012, g)

好了,滑板少年已經哭暈在廁所……

費了這麼多話,我們總算把穿越「山谷」這件事情說完了,下面還要說乙個數值上的事情。在cnn的訓練中,我們的開山祖師已經給了我們衝量的建議配置——0.9(剛才的例子全部是0.7),那麼0.9的衝量有多大量呢?終於要來點公式了……

我們用g表示每一輪的更新量,g表示當前一步的梯度量(方向*步長),t表示迭代輪數,表示衝量的衰減程度,那麼對於時刻t的梯度更新量有:

那麼我們可以計算下對於梯度g0對從g0到gt的總貢獻量為

我們發現它的貢獻是乙個等比數列,如果=0.9,那麼跟據等比數列的極限運算方法,我們知道在極限狀態下,它一共貢獻了自身10倍的能量。如果=0.99呢?那就是100倍了。

那麼在實際中我們需要多少倍的能量呢?

本文相關**詳見:

等比數列的極限運算方法,我們知道在極限狀態下,它一共貢獻了自身10倍的能量。如果

=0.99呢?那就是100倍了。

動量梯度下降(momentum)

動量梯度下降法是對梯度下降法的改良版本,通常來說優化效果好於梯度下降法。對梯度下降法不熟悉的可以參考梯度下降法,理解梯度下降法是理解動量梯度下降法的前提,除此之外要搞懂動量梯度下降法需要知道原始方法在實際應用中的不足之處,動量梯度下降法怎樣改善了原來方法的不足以及其具體的實現演算法。依次從以下幾個方...

動量梯度下降法 Momentum

動量梯度下降法是對梯度下降法的一種優化演算法,該方法學習率可以選擇更大的值,函式的收斂速度也更快。梯度下降法就像下面這張圖,通過不斷的更新 w與b,從而讓函式移動到紅點,但是要到達最優解,需要我們不斷的迭代或者調整學習率來達到最後到達最優解的目的。但是調大學習率會導致每一次迭代的步長過大,也就是擺動...

指數加權平均和momentum梯度下降

開頭先以乙個例子引入指數加權平均 假設以下是倫敦某年365天的天氣 藍點是每一天的溫度,vt表示每一天指數加權後的溫度,連線這些點後,也就有了這條紅色的彎曲的線,為什麼我們要使用指數加權呢?我的理解是讓連線的曲線更加平滑,讓每天溫度的變化幅度變小一點,因為每一天的加權溫度,總是會加上前一天的溫度。1...