線性迴歸:梯度下降法原理與實現
阿新 • • 發佈:2021-01-10
目錄
表示了移動的步長和方向,那麼可以有\(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 \] 並且儘可能小。又有:
\[\gamma∇f(w_0)=|\gamma||∇f(w_0)|\cos \alpha
\]
一、線性迴歸
關於線性迴歸的詳細介紹可以參見我的上一篇博文《線性迴歸:最小二乘法實現》。在《線性迴歸:最小二乘法實現》中我已經說明了線性迴歸模型建立的關鍵在於求解:
\[(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\)
梯度下降演算法的目的是讓優化函式\(f(w)\)儘快到達全域性最小值,所以便有了第一個條件: \[condition1:f(w)-f(w_0)=\eta\gamma∇f(w_0)≤0 \]
由於\(\eta\)是一個大於0的常數,所以可以暫不考慮。那麼就需要滿足\(\eta\gamma∇f(w_0)≤0\)
所以想要使\(\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()
均方誤差:
代價曲線:
擬合曲線: