在上一篇博客中,我們學習了線性回歸的基本概念、損失函數(如MSE)以及最小二乘法。最小二乘法通過求解解析解(直接計算出最優參數)的方式得到線性回歸模型,但它有一個明顯的局限:當特征數量很多時,計算過程會非常復雜(涉及矩陣求逆等操作)。今天我們來學習另一種更通用、更適合大規模數據的參數優化方法——梯度下降。
一、什么是梯度下降?
梯度下降(Gradient Descent)是一種迭代優化算法,核心思想是:通過不斷地沿著損失函數"下降最快"的方向調整參數,最終找到損失函數的最小值(或近似最小值)。
我們可以用一個生活中的例子理解:假設你站在一座山上,周圍被大霧籠罩,你看不見山腳在哪里,但你想以最快的速度走到山腳下。此時,你能做的最合理的選擇就是:先感受一下腳下的地面哪個方向坡度最陡且向下,然后沿著那個方向走一步;走到新的位置后,再重復這個過程——感受坡度最陡的向下方向,再走一步;直到你感覺自己已經走到了最低點(腳下各個方向都不再向下傾斜)。
這個過程就是梯度下降的直觀體現:
- 這座山就是我們的損失函數
- 你的位置代表當前參數值
- 你感受到的"坡度最陡的向下方向"就是負梯度方向
- 你每次走的"一步"的長度就是學習率
- 最終到達的"山腳下"就是損失函數的最小值點
在線性回歸中,我們的目標是找到最優參數(如權重w),使得損失函數L(w)達到最小值。梯度下降的作用就是幫助我們一步步調整這些參數,最終找到讓損失函數最小的參數值。
二、梯度下降的基本步驟
梯度下降的過程可以總結為4個核心步驟,我們以單特征且不含偏置項的線性回歸模型y = wx為例(即b=0,損失函數使用MSE),逐步說明:
步驟1:初始化參數
首先需要給參數w設定初始值。初始值可以是任意的(比如隨機值、0或1),因為梯度下降會通過迭代不斷優化它。
為什么初始值可以任意選擇?因為梯度下降是一個迭代優化的過程,無論從哪個點開始,只要迭代次數足夠多且學習率合適,最終都會收斂到損失函數的最小值附近。
例如:我們可以簡單地將初始值設為w = 0,然后開始優化過程。
步驟2:計算損失函數的梯度
“梯度"在單參數情況下就是損失函數對該參數的導數,它表示損失函數在當前參數位置的"變化率"和"變化方向”。
對單特征且b=0的模型y = wx,我們只需要計算一個導數:
- 損失函數L對w的導數:?L/?w(表示當w變化時,損失函數L的變化率)
這個導數就是"梯度",它指向損失函數增長最快的方向。這很重要:梯度指向的是損失函數值上升最快的方向,所以要讓損失函數減小,我們需要向相反的方向移動。
步驟3:更新參數
為了讓損失函數減小,我們需要沿著梯度的反方向(即負梯度方向)調整參數。更新公式為:
w = w - α · (?L/?w)
其中α是"學習率"(后面會詳細解釋),它控制參數更新的"步長"。
為什么是減去梯度而不是加上?因為梯度指向損失函數增大的方向,所以減去梯度就意味著向損失函數減小的方向移動,這正是我們想要的。
步驟4:重復迭代,直到收斂
重復步驟2和步驟3:每次計算當前參數的梯度,然后沿負梯度方向更新參數。當滿足以下條件之一時,停止迭代(即"收斂"):
- 梯度的絕對值接近0(此時損失函數變化很小,接近最小值);
- 損失函數L(w)的變化量小于某個閾值(比如連續兩次迭代的損失差小于10??);
- 達到預設的最大迭代次數(防止無限循環)。
"收斂"這個詞可以理解為:參數值已經穩定下來,繼續迭代也不會有明顯變化,此時我們可以認為找到了最優參數。
三、梯度下降的公式推導(單特征且b=0)
要實現梯度下降,核心是求出損失函數對參數w的導數。我們以MSE損失函數為例(且b=0),詳細推導?L/?w的計算過程,每一步都會給出詳細說明。
已知條件
-
模型:y_pred = wx(預測值,因b=0,無偏置項)
-
真實值:y
-
損失函數(MSE):
L(w) = (1/2n)Σ(y? - y_pred,?)2 = (1/2n)Σ(y? - wx?)2
(注:公式中加入1/2是為了后續求導時抵消平方項的系數2,使計算更簡潔,不影響最終結果)
推導?L/?w(損失函數對w的導數)
-
先對單個樣本的損失求導:
單個樣本的損失為l? = (1/2)(y? - wx?)2,對w求導:?l?/?w = 2 · (1/2)(y? - wx?) · (-x?) = -(y? - wx?)x?
這里用到了復合函數求導法則(鏈式法則):首先對平方項求導得到2·(1/2)(…),然后對括號內的內容求導,由于我們是對w求導,所以(wx?)對w的導數是x?,前面有個負號,所以整體是-(y? - wx?)x?。
-
對所有樣本的損失求和后求導:
總損失L是所有單個樣本損失的平均值:L = (1/n)Σl?,因此:?L/?w = (1/n)Σ(?l?/?w) = (1/n)Σ[-(y? - wx?)x?] = -(1/n)Σ(y? - y_pred,?)x?
這一步的含義是:總損失對w的導數等于所有單個樣本損失對w的導數的平均值。
最終更新公式
將上面得到的導數代入參數更新公式(參數 = 參數 - 學習率 × 導數),得到:
w = w + α · (1/n)Σ(y? - y_pred,?)x?
(注:負負得正,公式中的減號變為加號)
這個公式的含義是:
- 如果預測值y_pred,?小于真實值y?(即y? - y_pred,?為正),則w會增大;反之則減小。
- 增大或減小的幅度取決于三個因素:誤差大小(y? - y_pred,?)、特征值x?的大小和學習率α。
- 特征值x?越大,相同誤差下w的更新幅度也越大,這體現了特征對參數調整的影響。
四、學習率(α)的作用
學習率(Learning Rate)是梯度下降中最重要的超參數(需要人工設定的參數),它控制參數更新的"步長"。我們繼續用"下山"的例子來理解:
- 如果學習率α太小:就像每次只邁一小步下山,雖然安全,但需要走很多步才能到達山腳(迭代次數多,效率低)。
- 如果學習率α太大:就像每次邁一大步下山,可能會直接跨過山腳,甚至走到對面的山坡上(跳過最小值,甚至導致損失函數越來越大,無法收斂)。
- 合適的學習率:步長適中,能快速逼近最小值,既不會太慢也不會跳過。
實際應用中,學習率通常需要通過嘗試確定,常見的初始值有0.1、0.01、0.001等。一種常用的策略是"學習率衰減":隨著迭代次數增加,逐漸減小學習率,這樣在開始時可以快速接近最小值,后期可以精細調整。
舉個形象的例子:假設你在下山,開始時你離山腳很遠,可以大踏步前進(較大的學習率);當快到山腳時,你會放慢腳步,小步移動(較小的學習率),以免走過頭。
完整示例(手動實現梯度下降,單特征,b=0)
import numpy as np
import matplotlib.pyplot as plt # 可視化# 創建數據 植物的溫度、和生長高度 [[20,10],[22,10],[27,12],[25,16]]
data =np.array([[20,10],[22,10],[27,12],[25,16]])
# 劃分
x=data[:,0]
y=data[:,1]
print(x)
print(y)# 創建一個模型
def model(x,w):return x*w# 定義損失函數
# def loss(y_pred,y):
# return np.sum((y_pred-y)**2)/len(y)# 手動將損失函數展開 便于下面寫梯度函數
def loss(w):return 2238*(w**2) - 1144*w + 600# 梯度函數 即,將損失函數求導
def gradient(w):return 2*2238*w - 1144# 梯度下降 給定初始系數w 迭代100次 優化w
w=0
learning_rate = 1e-5 # 降低學習率避免溢出
for i in range(100):w=w-learning_rate*gradient(w)print('e:',loss(w),'w:',w)# 繪制損失函數
plt.plot(np.linspace(0,1,100),loss(np.linspace(0,1,100)))# 繪制模型
def draw_line(w):point_x = np.linspace(0, 30, 100)point_y = model(point_x, w)plt.plot(point_x, point_y, label=f'Fitted line (w={w:.4f})')plt.scatter(x, y, color='red', label='Data points')plt.legend()plt.xlabel("Temperature")plt.ylabel("Height")plt.title("Linear Regression via Gradient Descent")plt.grid(True)plt.show()# draw_line(w)
五、多特征的梯度下降(以2個特征為例)
現實中,我們遇到的問題往往有多個特征(比如用"面積"和"房間數"預測房價)。下面我們推導2個特征的線性回歸模型的梯度下降公式,方法與單特征類似,但需要考慮更多參數。
模型與損失函數
-
2個特征的模型:y_pred = w?x? + w?x?(x?、x?是兩個特征,w?、w?是對應的權重,因b=0,無偏置項)
-
損失函數(MSE):
L(w?,w?) = (1/2n)Σ(y? - (w?x?,? + w?x?,?))2
推導各參數的偏導數
與單特征思路一致,我們分別對w?、w?求偏導:
-
對w?的偏導:
?L/?w? = -(1/n)Σ(y? - y_pred,?)x?,?
推導過程與單特征中w的導數完全相同,只是這里特征是x?,所以最后乘以x?,?。
-
對w?的偏導:
?L/?w? = -(1/n)Σ(y? - y_pred,?)x?,?
同理,這里特征是x?,所以最后乘以x?,?。
參數更新公式
將上述偏導數代入更新公式,得到:
w? = w? + α · (1/n)Σ(y? - y_pred,?)x?,?
w? = w? + α · (1/n)Σ(y? - y_pred,?)x?,?
多特征的擴展規律
從2個特征的推導可以看出,梯度下降的公式可以很容易擴展到k個特征的情況:
-
模型:y_pred = w?x? + w?x? + … + w?x?
-
對第j個權重w?的更新公式:
w? = w? + α · (1/n)Σ(y? - y_pred,?)x?,?
(x?,?表示第i個樣本的第j個特征值)
這個規律非常重要,它告訴我們:無論有多少個特征,梯度下降的更新規則都是相似的——每個權重w?的更新量都與對應特征x?和誤差(y? - y_pred,?)的乘積有關。
完整示例(手動實現梯度下降,兩個特征,b=0)
import numpy as np
import matplotlib.pyplot as plt
# 如果使用中文顯示,建議添加以下配置
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號# 創建數據 [[1,1,3],[2,1,4],[1,2,5],[2,2,6]]
data = np.array([[1,1,3],[2,1,4],[1,2,5],[2,2,6]])
# 劃分
x=data[:,:-1]
y=data[:,-1]
print(x)
print(y)# 創建模型
def model(x,w):return np.sum(x*w)# 創建損失函數
# def loss(w,x):# return np.sum((np.sum(x*w,axis=1)-y)**2)# return np.sum((model(x,w)-y)**2)
def loss(w1,w2):return 5*w1**2 + 5*w2**2 +9*w1*w2 -28*w1-29*w2 +43# 創建梯度函數
def gradient_w1(w1,w2):return 10*w1+9*w2-28def gradient_w2(w1,w2):return 9*w1+10*w2-29# 初始化w1,w2
w1=0
w2=0# 迭代100次 優化w1,w2
for i in range(100):w1,w2=w1-0.01*gradient_w1(w1,w2),w2-0.01*gradient_w2(w1,w2)print('e:',loss(w1,w2),'w1:',w1,'w2:',w2)# # 繪制模型 沒寫出來(所以注釋了)
# def draw_line(w1,w2):
# point_x=np.linspace(0,5,100)
# point_y=model(point_x,w1,w2)
# plt.plot(point_x,point_y)# draw_line(w1,w2)
六、梯度下降與最小二乘法的對比
特點 | 梯度下降 | 最小二乘法 |
---|---|---|
本質 | 迭代優化(數值解) | 直接求解方程(解析解) |
計算復雜度 | 低(適合大規模數據/多特征) | 高(涉及矩陣求逆) |
適用性 | 幾乎所有損失函數 | 僅適用于凸函數且有解析解 |
超參數依賴 | 需要調整學習率等 | 無需超參數 |
內存需求 | 低(可分批處理數據) | 高(需要一次性加載所有數據) |
簡單來說,當特征數量較少時,最小二乘法可能更簡單直接;但當特征數量很多(比如超過1000個)時,梯度下降通常是更好的選擇。
總結
梯度下降是機器學習中最基礎也最常用的優化算法,它通過"沿損失函數負梯度方向迭代更新參數"的方式,找到使損失最小的參數值。與最小二乘法相比,梯度下降更適合處理大規模數據和復雜模型。
本文我們從概念、步驟、公式推導(單特征且b=0和雙特征)、學習率作用等方面詳細講解了梯度下降,希望能幫助你理解其核心邏輯。掌握梯度下降不僅對理解線性回歸至關重要,也是學習更復雜機器學習算法(如神經網絡)的基礎。
下一篇博客中,我們將通過實際案例演示如何用梯度下降實現線性回歸,進一步加深理解。