1. 程式人生 > 實用技巧 >線性迴歸:梯度下降法原理與實現

線性迴歸:梯度下降法原理與實現

目錄

一、線性迴歸

  關於線性迴歸的詳細介紹可以參見我的上一篇博文《線性迴歸:最小二乘法實現》。在《線性迴歸:最小二乘法實現》中我已經說明了線性迴歸模型建立的關鍵在於求解:

\[(w^*, b^*)=\arg\min\sum^{m}_{i=1}{{(f(x_i)-y_i)^2}} \]

  這裡再介紹另一種求解演算法:梯度下降法。

二、梯度下降法的數學原理

  假設有以下問題:

\[w=\arg\min f(w) \]

  通過泰勒一階展開則有:

\[f(w)=f(w_0)+(w-w_0)∇f(w_0) \]

  圖示如下:

  這裡的\(w-w_0\)

表示了移動的步長和方向,那麼可以有\(w-w_0=\eta\gamma\)\(\eta\)為實數,表示移動的步長,\(\gamma\)為一個單位向量,表示步長移動的方向。\(f(w_0)+(w-w_0)∇f(w_0)\)則是\(f(w)\)\(w\)處的鄰近估計,此處要求\(\eta\)較小,否則將導致估計的精確度降低。
  梯度下降演算法的目的是讓優化函式\(f(w)\)儘快到達全域性最小值,所以便有了第一個條件:

\[condition1:f(w)-f(w_0)=\eta\gamma∇f(w_0)≤0 \]

  由於\(\eta\)是一個大於0的常數,所以可以暫不考慮。那麼就需要滿足\(\eta\gamma∇f(w_0)≤0\)

並且儘可能小。又有:

\[\gamma∇f(w_0)=|\gamma||∇f(w_0)|\cos \alpha \]

所以想要使\(\eta\gamma∇f(w_0)≤0\)並且儘可能小則需要滿足條件:

\[condition2:\cos \alpha=-1 \]

  換句話說,也就是要求單位向量\(\gamma\)\(∇f(w_0)\)方向完全相反,所以便可以得到以下結論:

\[condition3:\gamma=\frac{-∇f(w_0)}{|∇f(w_0)|} \]

  將\(condition3\)帶入\(w-w_0=\eta\gamma\),則有:

\[condition4:w-w_0=\eta\frac{-∇f(w_0)}{|∇f(w_0)|} \]

  由於\(\eta\)\(|∇f(w_0)|\)都是大於0的實數,所以可以合併為新的\(\eta^*=\frac{\eta}{|∇f(w_0)|}\),再對等式進行移項便可以得到:

\[condition5:w=w_0-\eta∇f(w_0) \]

  \(condition5\)便是梯度下降演算法中\(f(w)\)的引數\(w\)的更新公式。同時這也解釋了為什麼需要以梯度的反方向來更新權重。

三、梯度下降法優化

  下面就用梯度下降演算法來對線性迴歸模型進行優化。這裡將線性迴歸模型的代價函式E寫為如下形式(方便運算):

\[E=\frac{1}{2m}\sum^{m}_{i=1}{(f(x_i)-y_i)^2} \]

對其求導:

\[\frac{\partial E}{\partial w}=\frac{1}{m}\sum^{m}_{i=1}{(wx_i-y_i)\frac{\partial (wx_i-y_i)}{\partial w}}=\frac{1}{m}\sum^{m}_{i=1}{(wx_i-y_i)x_i} \]

  由前面的\(condition5\)可以得到權重的更新公式:\(w^*=w+\Delta w\),此處的\(\Delta w=-\eta∇f(w_0)\)。所以最終可以得到權重的更新公式為:

\[w^*=w+\frac{\eta}{m}\sum^{m}_{i=1}{(y_i-wx_i)x_i} \]

四、Python實現

  由之前推匯出的更新公式可以實現出以下擬合算法:

def _gradient_descent(self, X, y):
	for i in range(max_iter):
		delta = y - self._linear_func(X)
		self.W[0] += self.eta * sum(delta) / X.shape[0] # 第一列全部為1
		self.W[1:] += self.eta * (delta @ X) / X.shape[0]

  匯入波士頓資料集進行測試:

if __name__ == "__main__":
    from sklearn import datasets
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import MinMaxScaler
    boston = datasets.load_boston()
    X = boston.data
    y = boston.target
    scaler = MinMaxScaler().fit(X)
    X = scaler.transform(X)
    X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, test_size=0.3)
    lr = LinearRegression().fit(X_train, y_train)
    y_pred = lr.predict(X_test)
    from sklearn.metrics import mean_squared_error
    print(mean_squared_error(y_test, y_pred))
    plt.figure()
    plt.plot(range(len(y_test)), y_test)
    plt.plot(range(len(y_pred)), y_pred)
    plt.legend(["test", "pred"])
    plt.show()

均方誤差:

代價曲線:

擬合曲線: