機器學習課程也上了一段時間了,今天就帶大家從 0 開始手把手用 Python 實現第一個機器學習算法:單變量梯度下降(Gradient Descent)!
我們從一個小例子開始一步步學習這個經典的算法。
一、如何最快下山?
在學習算法之前先來看一個日常生活的例子:下山。想象一下你出去旅遊爬山,爬到山頂後已經傍晚了,很快太陽就會落山,所以你必須想辦法儘快下山,然後去喫海底撈。
那最快的下山方法是什麼呢?沒錯就是縮成一個球,然後從最陡的方向直接滾下去,可是我們是人不是球,不能直接滾下去,但是可以借鑑這種方式,改變一下策略。
要想最快下山,其實只需要循環執行以下 3 步驟:
- 環顧周圍找到最陡的一段路
- 在最陡的一段路上走一段距離
- 重複以上步驟直到山底
假設你擁有找到目前所在位置最陡路線的能力,那麼你只需要重複以上步驟就能以最短時間,最短路程下到山底去喫海底撈啦!
這就是梯度下降法的現實例子,下面來正式學習下梯度下降法的基本思想。
二、梯度下降法基本思想
數學上的梯度下降法步驟跟下山的例子一模一樣,只不過換成了數學公式具體表達出來而已,這裏用個函數圖像來形象解釋下:
把這個函數圖像想象成一座山,此刻你正在山頂,並且要尋找最快的下山路線,那麼按照上面講的下山 3 步驟,你要做的就是找最陡的下山方向,然後走一段路,再找最陡的下山方向,再走一段路,以此類推,最後就得到上面的這條下山路線。
2.1 算法解決什麼問題?
我們爲了儘快下山,用了梯度下山法;那麼對應到數學曲線上,對一個函數應用梯度下降法,就是爲了最快地求出函數的全局最小值或者局部最小值;再對應到機器學習問題上,梯度下降法就是爲了儘快求出模型代價函數最小值,進而得到模型參數;
所以梯度下降法要解決的問題就是:以最快速度求函數最小值。
2.2 如何用數學公式表達?
先來用公式表達出下山的步驟:
與下山公式一樣,一行公式即可寫出梯度下降法公式:
我們來對照下山的例子,詳細解釋這個公式:
- 等式左邊的 是下一時刻我在山頂的位置:下一函數值
- 等式右邊的 是當前時刻我在山頂的位置:當前函數值
- 學習率 :算法迭代步長
- 是最快的下山方向:當前時刻函數值下降最快的方向
- 是當前時刻的打算邁出的距離:算法當前迭代下降的距離
- 可以理解爲你要下的那座山:算法執行的函數
這裏的 在算法中叫做學習率,雖然從名字上不好理解,不過它的作用就是控制算法每次迭代下降的步長,也就是每次下山打算邁開多大步。
你可能疑惑的是下山方向爲何加負號,其實在數學上,這個公式 的意思是求偏導數,也叫做求梯度,函數梯度是函數值增加速度最快的方向,而這裏加上一個負號就表示函數值下降最快的方向,也就表示下山速度最快(最陡)的方向。
但是學習率和梯度都不是當前時刻函數值要減少的量,當前函數下降的量等於這兩者的乘積 ,一定不要誤以爲學習率就是當前函數下降的距離,它只是一種度量方式,可以理解爲一個尺度,不是實際的值。
讓我們再從實際的梯度下降曲線中直觀的看下算法的迭代過程。
三、梯度下降法的直觀理解
下面這個例子可以很好地解釋單變量()梯度下降的過程:
算法從曲線的右上角紫色的數據點開始迭代下降,最終下降到底部綠色點找到函數最小值,算法結束,來詳細分析一次下降的過程:
- 計算第一個數據點處的梯度(斜率或偏導數),這裏梯度大於 0
- 計算下一函數值:
- 重複以上步驟,直到底部綠色點梯度爲 0
這裏簡單說下學習率 對算法的影響:
- 如果 $ \alpha $ 太小,每次更新的步長很小,導致要很多步才能才能到達最優點;
- 如果 太大,每次更新步長很大,在快接近最優點時,容易因爲步長過大錯過最優點,最終導致算法無法收斂,甚至發散;
雖然學習率會影響迭代步長,那是否需要我們每次手動更新學習率呢?
不需要!因爲迭代的步長每次都會自動減小!隨着數據點越來越靠近最低點,在該點處的斜率越來越小,即梯度值 越來越小,而 ,所以兩者相乘後 也越來越小,進一步導致 值減小的越來越慢。
當算法最後迭代到最低點時,綠色點處斜率爲 0,即梯度爲 0,此時梯度下降公式將不再變化:
所以算法認爲已經找到最優值,不再迭代下降,算法至此結束。
這個例子是從右向左迭代下降,如果起始數據點是在左邊,算法是否能正常運行呢?完全沒問題,唯一的不同處是梯度小於 0,公式裏面負號變爲正號,你可以試着自己分析下。
四、梯度下降法擬合函數直線
理論介紹完了,下面進入實戰部分,登龍手把手用 Python 帶你實現一個梯度下降法,並用這個的算法來擬合下面的數據點(人口 - 利潤):
因爲篇幅限制,這裏只講解我認爲比較關鍵的代碼,其他比較基礎的加載數據,導包就不介紹了,文末有完整代碼,裏面的註釋非常詳細,建議下載食用,有收穫記得回來給我個 Star 哦!
4.1 模型選擇
這個數據集比較簡單,只有一個人口特徵,但是爲了方便代碼計算我們人爲增加一個特徵 ,並且使用線性迴歸的函數模型:
那先來定義這兩個參數:
# X.shape[1] = 2,表示參數數量 n = 2
# theta = [theta_0 = 0, theta_1 = 0]
theta = np.zeros(X.shape[1])
我們的最終目的就是找出最優的參數(,),使得直線擬合的總均方誤差達到最小,那如何表示均方誤差呢?這就需要代價函數登場了。
4.2 代價函數
代價函數顧名思義就是每組參數所對應的擬合誤差量,要想擬合數據集的效果最好,那就要求參數所對應的代價函數取最小值,這裏的我選擇常用的均方誤差來作爲代價函數:
這個代價函數計算的是一組參數(,)擬合的數據預測值與真實值均方誤差,看下這個函數如何用代碼寫出來,這裏要用點線性代數的知識:
# Cost Function
# X: R(m * n) 特徵矩陣
# y: R(m * 1) 標籤值矩陣
# theta: R(n) 線性迴歸參數
def cost_function(theta, X, y):
# m 爲樣本數
m = X.shape[0]
# 誤差 = theta * x - y
inner = X @ theta - y
# 將向量的平方計算轉換爲:列向量的轉置 * 列向量
square_sum = inner.T @ inner
# 縮小成本量大小,這裏無特殊含義
cost = square_sum / (2 * m)
# 返回 theta 參數對應的成本量
return cost;
我們的梯度下降法就是應用在這個代價函數上,來尋找代價函數的最小值,進而找到取最小值時對應的參數 (,)。
4.3 計算梯度
梯度下降需要用到某點的梯度,即導數,看下求梯度的代碼:
# 計算偏導數
def gradient(theta, X, y):
# 樣本數量
m = X.shape[0]
# 用向量計算複合導數
inner = X.T @ (X @ theta - y)
# 不要忘記結果要除以 m
return inner / m
這個其實也不難,就是代價函數對 進行復合求導:
因爲上面的代碼是用向量表示的,一列向量裏面包含所有參數,所以對包含參數的向量進行乘積,就相當於公式裏面的求和符號了,而且使用向量計算,順序會有點不一樣,這裏就不詳細展開講了,暫時能理解就可以。
4.4 執行批量梯度下降算法
準備就緒,下面就到了最重要的部分,批量梯度下降法的邏輯代碼,其實也很簡單,就是執行一個循環 =_=:
# 批量梯度下降法
# epoch: 下降迭代次數 500
# alpha: 初始學習率 0.01
def batch_gradient_decent(theta, X, y, epoch, alpha = 0.01):
# 計算初始成本:theta 都爲 0
cost_data = [cost_function(theta, X, y)]
# 創建新的 theta 變量,不與原來的混淆
_theta = theta.copy()
# 迭代下降 500 次
for _ in range(epoch):
# 核心公式:theta = theta - 學習率 * 梯度
_theta = _theta - alpha * gradient(_theta, X, y)
# 保存每次計算的代價數據用於後續分析
cost_data.append(cost_function(_theta, X, y))
# 返回最終的模型參數和代價數據
return _theta, cost_data
傳入的參數我們之前都介紹過了,再複習下:
- theta:待預測的直線參數
- X:樣本橫座標值
- y:樣本縱座標值
- epoch:迭代下降次數
- alpha:學習率
最終返回的結果是迭代 500 次後對原始數據擬合誤差最小的參數 以及每次計算的代價數據 。
補充下:這裏批量的意思是每次迭代都計算全部樣本的均方誤差,不是一次計算一個樣本,只是我們常常省略批量這兩個字,直接叫梯度下降法。
4.5 測試擬合結果
我們來調用上面的批量梯度下降法,看下預測的參數:
epoch = 500
final_theta, cost_data = batch_gradient_decent(theta, X, y, epoch)
final_theta
輸出 :
array([-2.28286727, 1.03099898])
來可視化代價數據看下是否連續下降:
cost_data
代價函數隨着迭代次數變多逐漸減小,直到 4.713809 基本不變,至此我們已經找到了我們認爲的最優的擬合數據的直線模型參數,因爲代價函數取得了最小值。
那來看下擬合的效果,看起來還不錯:
五、總結
以上就是我對單變量梯度下降法的基本理解,還有很多不足,希望大家多多指正,文中部分代碼用到微積分和線性代數的知識,建議回頭複習下,可以更好的理解算法 _!
另外,關於多變量的梯度下降法我也寫了點自己的總結:從 0 開始機器學習 - 一文入門多維特徵梯度下降法!,原理幾乎相同,強烈推薦實踐一下。
文中項目超詳細註釋完整代碼:AI-Notes,學會了記得回來給我個 Star 哈。
本文原創首發於 同名微信公號「登龍」,微信搜索關注回覆「1024」你懂的!