梯度下降法python
A. 一文搞懂梯度下降&反向傳播
如果把神經網路模型比作一個黑箱,把模型參數比作黑箱上面一個個小旋鈕,那麼根據通用近似理論(universal approximation theorem),只要黑箱上的旋鈕數量足夠多,而且每個旋鈕都被調節到合適的位置,那這個模型就可以實現近乎任意功能(可以逼近任意的數學模型)。
顯然,這些旋鈕(參數)不是由人工調節的,所謂的機器學習,就是通過程序來自動調節這些參數。神經網路不僅參數眾多(少則十幾萬,多則上億),而且網路是由線性層和非線性層交替疊加而成,上層參數的變化會對下層的輸出產生非線性的影響,因此,早期的神經網路流派一度無法往多層方向發展,因為他們找不到能用於任意多層網路的、簡潔的自動調節參數的方法。
直到上世紀80年代,祖師爺辛頓發明了反向傳播演算法,用輸出誤差的均方差(就是loss值)一層一層遞進地反饋到各層神經網路,用梯度下降法來調節每層網路的參數。至此,神經網路才得以開始它的深度之旅。
本文用python自己動手實現梯度下降和反向傳播演算法。 請點擊這里 到Github上查看源碼。
梯度下降法是一種將輸出誤差反饋到神經網路並自動調節參數的方法,它通過計算輸出誤差的loss值( J )對參數 W 的導數,並沿著導數的反方向來調節 W ,經過多次這樣的操作,就能將輸出誤差減小到最小值,即曲線的最低點。
雖然Tensorflow、Pytorch這些框架都實現了自動求導的功能,但為了徹底理解參數調節的過程,還是有必要自己動手實現梯度下降和反向傳播演算法。我相信你和我一樣,已經忘了之前學的微積分知識,因此,到可汗學院復習下 Calculus
和 Multivariable Calculus 是個不錯的方法,或是拜讀 這篇關於神經網路矩陣微積分的文章 。
Figure2是求導的基本公式,其中最重要的是 Chain Rule ,它通過引入中間變數,將「 y 對 x 求導」的過程轉換為「 y 對中間變數 u 求導,再乘以 u 對 x 求導」,這樣就將一個復雜的函數鏈求導簡化為多個簡單函數求導。
如果你不想涉及這些求導的細節,可以跳過具體的計算,領會其思想就好。
對於神經網路模型: Linear -> ReLu -> Linear -> MSE(Loss function) 來說,反向傳播就是根據鏈式法則對 求導,用輸出誤差的均方差(MSE)對模型的輸出求導,並將導數傳回上一層神經網路,用於它們來對 w 、 b 和 x (上上層的輸出)求導,再將 x 的導數傳回到它的上一層神經網路,由此將輸出誤差的均方差通過遞進的方式反饋到各神經網路層。
對於 求導的第一步是為這個函數鏈引入中間變數:
接著第二步是對各中間變數求導,最後才是將這些導數乘起來。
首先,反向傳播的起點是對loss function求導,即 。 :
mse_grad()之所以用unsqueeze(-1)給導數增加一個維度,是為了讓導數的shape和tensor shape保持一致。
linear層的反向傳播是對 求導,它也是一個函數鏈,也要先對中間變數求導再將所有導數相乘:
這些中間變數的導數分別是:
對向量 求導,指的是對向量所有的標量求偏導( ),即: ,這個橫向量也稱為y的梯度。
這里 ,是一個向量,因此, 求導,指的是y的所有標量(y_1, y_2, ..., y_n)對向量x求偏導,即:
。
這個矩陣稱為雅克比矩陣,它是個對角矩陣,因為 ,因此 。
同理, 。
因此,所有中間導數相乘的結果:
lin_grad() 中的inp.g、w.g和b.g分別是求 的導數,以inp.g為例,它等於 ,且需要乘以前面各層的導數,即 outp.g @ w.t() ,之所以要用點積運算符(@)而不是標量相乘,是為了讓它的導數shape和tensor shape保持一致。同理,w.g和b.g也是根據相同邏輯來計算的。
ReLu層的求導相對來說就簡單多了,當輸入 <= 0時,導數為0,當輸入 > 0時,導數為1。
求導運算終於結束了,接下來就是驗證我們的反向傳播是否正確。驗證方法是將forward_backward()計算的導數和Pytorch自動微分得到的導數相比較,如果它們相近,就認為我們的反向傳播演算法是正確的。
首先,將計算好的參數導數保存到w1g、b1g、w2g和b2g中,再用Pytorch的自動微分來求w11、b11、w22和b22的導數。
最後,用np.allclose()來比較導數間的差異,如果有任何一個導數不相近,assert就會報錯。結果證明,我們自己動手實現的演算法是正確的。
反向傳播是遵循鏈式法則的,它將前向傳播的輸出作為輸入,輸入作為輸出,通過遞進的方式將求導這個動作從後向前傳遞回各層。神經網路參數的求導需要進行矩陣微積分計算,根據這些導數的反方向來調節參數,就可以讓模型的輸出誤差的優化到最小值。
歡迎關注和點贊,你的鼓勵將是我創作的動力
B. Python實現 近端梯度下降法、BCD、ADMM解LASSO問題
近端梯度下降法、BCD、ADMM解LASSO問題
數據處理部分,我們先簡單介紹三種方法:近端梯度下降法、BCD塊坐標下降法、ADMM交替方向乘子法。它們分別應用於不同場景的優化問題,特別是LASSO問題。
近端梯度下降法(Proximal Gradient Descent)的核心在於其能夠處理具有非光滑部分的優化問題,尤其在LASSO問題中,其L1范數懲罰項導致目標函數不光滑。近端梯度下降法結合了梯度下降和proximal操作,以實現對非光滑部分的處理,從而求解最優解。在LASSO問題中,通過迭代更新參數,近端梯度下降法可以有效尋找到最優解。
接著,我們轉向BCD塊坐標下降法。這種方法通過將優化問題分解為多個子問題,分別在不同坐標上進行優化。對於LASSO問題,BCD通過交替地優化不同變數,從而逐步逼近全局最優解。推導過程見文章《BCD演算法解LASSO問題》[1]。
最後,我們介紹ADMM交替方向乘子法。ADMM是一種用於分解復雜優化問題的演算法,將原問題分解為多個子問題並交替求解。在LASSO問題中,ADMM通過引入乘子來處理約束條件,進而實現對目標函數的優化。推導過程可參照文章《ADMM演算法解Fused LASSO問題》[2]。
總之,這三種方法在解決LASSO問題時各具優勢,近端梯度下降法擅長處理非光滑問題,BCD塊坐標下降法通過分解問題進行優化,而ADMM則利用分解和乘子策略有效處理約束問題。它們在實際應用中各有側重,可根據具體問題選擇適用的方法。