本文將從一個下山的場景開始,先提出梯度降低算法的基本思想,進而從數學上解釋梯度降低算法的原理,最後實現一個簡單的梯度降低算法的實例!python
梯度降低法的基本思想能夠類比爲一個下山的過程。假設這樣一個場景:一我的被困在山上,須要從山上下來(找到山的最低點,也就是山谷)。但此時山上的濃霧很大,致使可視度很低。所以,下山的路徑就沒法肯定,他必須利用本身周圍的信息去找到下山的路徑。這個時候,他就能夠利用梯度降低算法來幫助本身下山。具體來講就是,以他當前的所處的位置爲基準,尋找這個位置最陡峭的地方,而後朝着山的高度降低的地方走,同理,若是咱們的目標是上山,也就是爬到山頂,那麼此時應該是朝着最陡峭的方向往上走。而後每走一段距離,都反覆採用同一個方法,最後就能成功的抵達山谷。算法
咱們同時能夠假設這座山最陡峭的地方是沒法經過肉眼立馬觀察出來的,而是須要一個複雜的工具來測量,同時,這我的此時正好擁有測量出最陡峭方向的能力。因此,此人每走一段距離,都須要一段時間來測量所在位置最陡峭的方向,這是比較耗時的。那麼爲了在太陽下山以前到達山底,就要儘量的減小測量方向的次數。這是一個兩難的選擇,若是測量的頻繁,能夠保證下山的方向是絕對正確的,但又很是耗時,若是測量的過少,又有偏離軌道的風險。因此須要找到一個合適的測量方向的頻率,來確保下山的方向不錯誤,同時又不至於耗時太多!函數
梯度降低的基本過程就和下山的場景很相似。工具
首先,咱們有一個可微分的函數。這個函數就表明着一座山。咱們的目標就是找到這個函數的最小值,也就是山底。根據以前的場景假設,最快的下山的方式就是找到當前位置最陡峭的方向,而後沿着此方向向下走,對應到函數中,就是找到給定點的梯度 ,而後朝着梯度相反的方向,就能讓函數值降低的最快!由於梯度的方向就是函數之變化最快的方向(在後面會詳細解釋)
因此,咱們重複利用這個方法,反覆求取梯度,最後就能到達局部的最小值,這就相似於咱們下山的過程。而求取梯度就肯定了最陡峭的方向,也就是場景中測量方向的手段。那麼爲何梯度的方向就是最陡峭的方向呢?接下來,咱們從微分開始講起學習
看待微分的意義,能夠有不一樣的角度,最經常使用的兩種是:spa
函數的變化率
幾個微分的例子:3d
上面的例子都是單變量的微分,當一個函數有多個變量的時候,就有了多變量的微分,即分別對每一個變量進行求微分code
梯度實際上就是多變量微分的通常化。
下面這個例子:orm
咱們能夠看到,梯度就是分別對每一個變量進行微分,而後用逗號分割開,梯度是用<>包括起來,說明梯度其實一個向量。ip
梯度是微積分中一個很重要的概念,以前提到過梯度的意義
這也就說明了爲何咱們須要想方設法的求取梯度!咱們須要到達山底,就須要在每一步觀測到此時最陡峭的地方,梯度就恰巧告訴了咱們這個方向。梯度的方向是函數在給定點上升最快的方向,那麼梯度的反方向就是函數在給定點降低最快的方向,這正是咱們所須要的。因此咱們只要沿着梯度的方向一直走,就能走到局部的最低點!
上面咱們花了大量的篇幅介紹梯度降低算法的基本思想和場景假設,以及梯度的概念和思想。下面咱們就開始從數學上解釋梯度降低算法的計算過程和思想!
此公式的意義是:J是關於Θ的一個函數,咱們當前所處的位置爲Θ0點,要從這個點走到J的最小值點,也就是山底。首先咱們先肯定前進的方向,也就是梯度的反向,而後走一段距離的步長,也就是α,走完這個段步長,就到達了Θ1這個點!
下面就這個公式的幾個常見的疑問:
咱們已經基本瞭解了梯度降低算法的計算過程,那麼咱們就來看幾個梯度降低算法的小實例,首先從單變量的函數開始
咱們假設有一個單變量的函數
函數的微分
咱們假設有一個目標函數
如今要經過梯度降低法計算這個函數的最小值。咱們經過觀察就能發現最小值其實就是 (0,0)點。可是接下來,咱們會從梯度降低算法開始一步步計算到這個最小值!
咱們假設初始的起點爲: , 初始的學習率爲:
函數的梯度爲:
下面咱們將用python實現一個簡單的梯度降低算法。場景是一個簡單的線性迴歸的例子:假設如今咱們有一系列的點,以下圖所示
咱們將用梯度降低法來擬合出這條直線!
首先,咱們須要定義一個代價函數,在此咱們選用均方偏差代價函數
此公示中
h 是咱們的預測函數,根據每個輸入x,根據Θ 計算獲得預測的y值,即
咱們能夠根據代價函數看到,代價函數中的變量有兩個,因此是一個多變量的梯度降低問題,求解出代價函數的梯度,也就是分別對兩個變量進行微分
明確了代價函數和梯度,以及預測的函數形式。咱們就能夠開始編寫代碼了。但在這以前,須要說明一點,就是爲了方便代碼的編寫,咱們會將全部的公式都轉換爲矩陣的形式,python中計算矩陣是很是方便的,同時代碼也會變得很是的簡潔。
爲了轉換爲矩陣的計算,咱們觀察到預測函數的形式
咱們有兩個變量,爲了對這個公式進行矩陣化,咱們能夠給每個點x增長一維,這一維的值固定爲1,這一維將會乘到Θ0上。這樣就方便咱們統一矩陣化的計算
而後咱們將代價函數和梯度轉化爲矩陣向量相乘的形式
首先,咱們須要定義數據集和學習率
import numpy as np # Size of the points dataset. m = 20 # Points x-coordinate and dummy value (x0, x1). X0 = np.ones((m, 1)) X1 = np.arange(1, m+1).reshape(m, 1) X = np.hstack((X0, X1)) # Points y-coordinate y = np.array([ 3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12, 11, 13, 13, 16, 17, 18, 17, 19, 21 ]).reshape(m, 1) # The Learning Rate alpha. alpha = 0.01
接下來咱們以矩陣向量的形式定義代價函數和代價函數的梯度
def error_function(theta, X, y): '''Error function J definition.''' diff = np.dot(X, theta) - y return (1./2*m) * np.dot(np.transpose(diff), diff) def gradient_function(theta, X, y): '''Gradient of the function J definition.''' diff = np.dot(X, theta) - y return (1./m) * np.dot(np.transpose(X), diff)
最後就是算法的核心部分,梯度降低迭代計算
def gradient_descent(X, y, alpha): '''Perform gradient descent.''' theta = np.array([1, 1]).reshape(2, 1) gradient = gradient_function(theta, X, y) while not np.all(np.absolute(gradient) <= 1e-5): theta = theta - alpha * gradient gradient = gradient_function(theta, X, y) return theta
當梯度小於1e-5時,說明已經進入了比較平滑的狀態,相似於山谷的狀態,這時候再繼續迭代效果也不大了,因此這個時候能夠退出循環!
完整的代碼以下
import numpy as np # Size of the points dataset. m = 20 # Points x-coordinate and dummy value (x0, x1). X0 = np.ones((m, 1)) X1 = np.arange(1, m+1).reshape(m, 1) X = np.hstack((X0, X1)) # Points y-coordinate y = np.array([ 3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12, 11, 13, 13, 16, 17, 18, 17, 19, 21 ]).reshape(m, 1) # The Learning Rate alpha. alpha = 0.01 def error_function(theta, X, y): '''Error function J definition.''' diff = np.dot(X, theta) - y return (1./2*m) * np.dot(np.transpose(diff), diff) def gradient_function(theta, X, y): '''Gradient of the function J definition.''' diff = np.dot(X, theta) - y return (1./m) * np.dot(np.transpose(X), diff) def gradient_descent(X, y, alpha): '''Perform gradient descent.''' theta = np.array([1, 1]).reshape(2, 1) gradient = gradient_function(theta, X, y) while not np.all(np.absolute(gradient) <= 1e-5): theta = theta - alpha * gradient gradient = gradient_function(theta, X, y) return theta optimal = gradient_descent(X, y, alpha) print('optimal:', optimal) print('error function:', error_function(optimal, X, y)[0,0])
運行代碼,計算獲得的結果以下
所擬合出的直線以下
原文連接:https://www.jianshu.com/p/c7e642877b0e