最近被問機器學習是怎麼訓練的,那麼就順便再深入理解梯度下降算法。
一、導數
首先我們需要複習一下導數的知識。導數是什麼?一個導數是描述一個函數上的一個點在自變量增大時,因變量的變化率。在這裏我討論導數的對象爲二維空間上的函數。我們用極限來表達導數,當左右側導數相等時,這個點才存在導數。
及
這裏強調一下導數的方向。用一個二次函數爲例子,這個函數的導函數就是。在區間中,是隨着的增加而減少的,說明這時函數上的每個點都是負增長(沿正方向),這就是爲什麼導函數這時。
二、偏導數
將導數從二維空間推廣到多維空間,產生了多元函數的導數,就是偏導數。以二元函數爲例,如果只有自變量變化,而另一個自變量固定不變(看成常量),那麼這就是的一元函數,這個函數對x的導數就是二元函數z對於x的偏導數(對y的偏導數同理可得)。與導數相似,偏導數的方向也是對應自變量增大(座標軸正方向)的方向,在其他自變量不變的情況下表達因變量的變化率。
三、方向導數
導數與偏導數反映的是函數沿座標軸方向的變化率。那麼我們如何知道函數沿任意方向的變化率呢?那麼這就產生了方向導數。以二元函數爲例,在點可微分,那麼函數在該點沿任意方向的方向導數存在,其中和分別是方向與x軸和y軸的夾角。這樣我們就可以得到在所有自變量都發生一定改變的方向下,因變量的變化率。
四、梯度
梯度的本意是一個向量,表示某一函數在該點處的方向導數沿着該方向取得最大值,即函數在該點處沿着該方向(此梯度的方向)變化最快,變化率最大(爲該梯度的模)。換句話說,梯度就是使一個點的方向導數達到最大的方向。那麼我們怎麼求出某個點的梯度?我們還是以二元函數爲例。
根據方向導數的定義,我們可以知道,一個方向導數是函數的各個偏導數與對應夾角餘弦值的組合。更加直觀的講,就是先將偏導數作用到各個對應基向量(單位向量),再通過類似平行四邊形法則合成的向量,就是梯度的方向。可以理解爲,梯度的方向更偏向偏導數更大的基方向。如圖例所示,假設i方向的偏導數更大,所以紅色即爲梯度方向。
將梯度推廣到多元函數中,我們就可以得到一個向量,表示對應點的梯度。
五、梯度下降
機器學習的本質目標就是使損失函數達到全局最小,我們就可以利用函數的梯度來求解。因爲梯度是一個有大小與方向的向量,所以我們直接用矩陣運算來表示這個算法。以二元函數爲例,假設有原函數,即我們需要擬合的函數h,我們將函數的輸入規定爲(shape=[3,1]),那麼參數向量就是(shape=[3,1])。那麼就是輸入的m個樣本(shape=[m,3]),爲擬合值(shape=[m,1]),爲真實值(shape=[m,1])。
用均方差作損失函數,這個函數的自變量是原函數的參數,因爲X是已知的樣本值,m是樣本數。這裏涉及矩陣函數求導的問題,具體方法自行查詢。
迭代訓練開始之前,我們初始化,有以下迭代公式。其中,就是學習率。本質上梯度還是自變量增大的方向,只是每個自變量增大的幅度不同。注意上面偏導數的寫法,是h-y,這樣纔是當偏導數>0的時候,因變量爲正增長,所以要將對應變小。
六、代碼實現
在這裏我用python代碼模擬梯度下降是如何一步一步到達函數的全局最小值點。
import numpy as np
# y = 1 + 2x1 + 3x2
x = np.random.random_integers(1,100,[10,3])
x[:,0] = 1
theta = np.zeros([3])
theta_real = np.asarray([1,2,3])
def PartialTheta0(x,theta):
J = 0
for i in range(len(x)):
J += (np.matmul(theta,x[i]) - np.matmul(theta_real,x[i]))
return J / len(x)
def PartialTheta1(x,theta):
J = 0
for i in range(len(x)):
J += ((np.matmul(theta,x[i]) - np.matmul(theta_real,x[i])) * x[i][1])
return J / len(x)
def PartialTheta2(x,theta):
J = 0
for i in range(len(x)):
J += ((np.matmul(theta, x[i]) - np.matmul(theta_real, x[i])) * x[i][2])
return J / len(x)
for _ in range(100000):
theta -= 0.000001 * np.asarray([PartialTheta0(x,theta),PartialTheta1(x,theta),PartialTheta2(x,theta)])
# theta -= 0.000001 * np.matmul(x.transpose(),np.matmul(x,theta)-np.matmul(x,theta_real)) / len(x)
print(theta)
通過運行輸出可以看到,參數在一步步靠近真實值,說明算法是有效的。