本文目錄一覽:
- 1、一文搞懂梯度下降&反向傳播
- 2、Python實現簡單多線程任務隊列
- 3、如何用 python 實現帶隨機梯度下降的線性回歸
- 4、python怎麼實現邏輯回歸的梯度下降和梯度上升法有區別嗎
- 5、梯度下降使用Python和NumPy問題,怎麼解決
一文搞懂梯度下降&反向傳播
如果把神經網路模型比作一個黑箱,把模型參數比作黑箱上面一個個小旋鈕,那麼根據通用近似理論(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就會報錯。結果證明,我們自己動手實現的演算法是正確的。
反向傳播是遵循鏈式法則的,它將前向傳播的輸出作為輸入,輸入作為輸出,通過遞進的方式將求導這個動作從後向前傳遞迴各層。神經網路參數的求導需要進行矩陣微積分計算,根據這些導數的反方向來調節參數,就可以讓模型的輸出誤差的優化到最小值。
歡迎關注和點贊,你的鼓勵將是我創作的動力
Python實現簡單多線程任務隊列
Python實現簡單多線程任務隊列
最近我在用梯度下降演算法繪製神經網路的數據時,遇到了一些演算法性能的問題。梯度下降演算法的代碼如下(偽代碼):
defgradient_descent(): # the gradient descent code plotly.write(X, Y)
一般來說,當網路請求 plot.ly 繪圖時會阻塞等待返回,於是也會影響到其他的梯度下降函數的執行速度。
一種解決辦法是每調用一次 plotly.write 函數就開啟一個新的線程,但是這種方法感覺不是很好。 我不想用一個像 cerely(一種分散式任務隊列)一樣大而全的任務隊列框架,因為框架對於我的這點需求來說太重了,並且我的繪圖也並不需要 redis 來持久化數據。
那用什麼辦法解決呢?我在 python 中寫了一個很小的任務隊列,它可以在一個單獨的線程中調用 plotly.write函數。下面是程序代碼。
fromthreadingimportThreadimportQueueimporttime classTaskQueue(Queue.Queue):
首先我們繼承 Queue.Queue 類。從 Queue.Queue 類可以繼承 get 和 put 方法,以及隊列的行為。
def__init__(self, num_workers=1): Queue.Queue.__init__(self) self.num_workers=num_workers self.start_workers()
初始化的時候,我們可以不用考慮工作線程的數量。
defadd_task(self, task,*args,**kwargs): args=argsor() kwargs=kwargsor{} self.put((task, args, kwargs))
我們把 task, args, kwargs 以元組的形式存儲在隊列中。*args 可以傳遞數量不等的參數,**kwargs 可以傳遞命名參數。
defstart_workers(self): foriinrange(self.num_workers): t=Thread(target=self.worker) t.daemon=True t.start()
我們為每個 worker 創建一個線程,然後在後台刪除。
下面是 worker 函數的代碼:
defworker(self): whileTrue: tupl=self.get() item, args, kwargs=self.get() item(*args,**kwargs) self.task_done()
worker 函數獲取隊列頂端的任務,並根據輸入參數運行,除此之外,沒有其他的功能。下面是隊列的代碼:
我們可以通過下面的代碼測試:
defblokkah(*args,**kwargs): time.sleep(5) print「Blokkah mofo!」 q=TaskQueue(num_workers=5) foriteminrange(1): q.add_task(blokkah) q.join()# wait for all the tasks to finish. print「Alldone!」
Blokkah 是我們要做的任務名稱。隊列已經緩存在內存中,並且沒有執行很多任務。下面的步驟是把主隊列當做單獨的進程來運行,這樣主程序退出以及執行資料庫持久化時,隊列任務不會停止運行。但是這個例子很好地展示了如何從一個很簡單的小任務寫成像工作隊列這樣複雜的程序。
defgradient_descent(): # the gradient descent code queue.add_task(plotly.write, x=X, y=Y)
修改之後,我的梯度下降演算法工作效率似乎更高了。如果你很感興趣的話,可以參考下面的代碼。fromthreadingimportThreadimportQueueimporttime classTaskQueue(Queue.Queue): def__init__(self, num_workers=1):Queue.Queue.__init__(self)self.num_workers=num_workersself.start_workers() defadd_task(self, task,*args,**kwargs):args=argsor()kwargs=kwargsor{}self.put((task, args, kwargs)) defstart_workers(self):foriinrange(self.num_workers):t=Thread(target=self.worker)t.daemon=Truet.start() defworker(self):whileTrue:tupl=self.get()item, args, kwargs=self.get()item(*args,**kwargs)self.task_done() deftests():defblokkah(*args,**kwargs):time.sleep(5)print”Blokkah mofo!” q=TaskQueue(num_workers=5) foriteminrange(10):q.add_task(blokkah) q.join()# block until all tasks are doneprint”All done!” if__name__==”__main__”:tests()
如何用 python 實現帶隨機梯度下降的線性回歸
線性回歸是一種用於預測真實值的方法。讓人困惑的是,這些需要預測真實值的問題被稱為回歸問題(regression problems)。線性回歸是一種用直線對輸入輸出值進行建模的方法。在超過二維的空間里,這條直線被想像成一個平面或者超平面(hyperplane)。預測即是通過對輸入值的組合對輸出值進行預判。
python怎麼實現邏輯回歸的梯度下降和梯度上升法有區別嗎
多數函數解不出導數得0的解析解.梯度下降法是種數值演算法,一般可以用計算機求出很好的近似解
梯度下降使用Python和NumPy問題,怎麼解決
它遵循LMS(Least Mean Square是)準則,該準則是通過使似然函數最大推導得出,即得出的參數使得樣本數據集出現的概率最大。常用的迭代方法有兩種:批量梯度下降法(Batch Gradient Descent)和隨機梯度下降法(Stochastic Gradient Descent)。梯度下降演算法對局部極值敏感,但是對於線性回歸問題只有整體極值,沒有局部極值,所以在這種情況下,演算法總是收斂的。對於隨機梯度下降演算法,其收斂速度要快於批量梯度下降演算法,但是它在最小值附近震蕩的幅度較大,所以可能不會收斂於true minimum
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/244250.html