從線性迴歸到梯度下降法詳細筆記

引言

我記得之前寫過一篇關於kaggle的一個小項目中有提到過梯度下降,當時感覺比較清晰,代碼也是能有直接現成的解,然後就這樣過了一年多,現在已經基本上忘得差不多了,而之前關於梯度下降也一直沒有仔細深究過,但它卻比較重要,不論是在機器學習還是深度學習,所以這裏嘗試重新撿起並做個筆記。

線性迴歸與最小二乘

我們先從線性迴歸開始談起,根據鄒博的機器學習課件,首先令y(i)=θTx1(i)+ε(i)x2(i)y^{\left( i \right)}=\theta ^Tx_1^{\left( i \right)}+\varepsilon ^{\left( i \right)}x_2^{\left( i \right)},其中x2(i)=1x_2^{\left( i \right)}=1,那麼上述表達式爲:

y(i)=θTx(i)+ε(i)y^{\left( i \right)}=\theta ^Tx^{\left( i \right)}+\varepsilon ^{\left( i \right)}

假定誤差ε(i)(1im)\varepsilon ^{\left( i \right)}\left( 1\leqslant i\leqslant m \right)是獨立同分布的,那麼由中心極限定理可以得到,這個誤差服從均值爲0,它的方差爲σ2\sigma^{2},則可以得到進一步的推導:
p(ϵ(i))=12πσexp((ϵ(i))22σ2)p(y(i)x(i);θ)=12πσexp((y(i)θTx(i))22σ2)L(θ)=i=1mp(y(i)x(i);θ)=i=1m12πσexp((y(i)θTx(i))22σ2)\begin{aligned} \\ p\left(\epsilon^{(i)}\right) &=\frac{1}{\sqrt{2 \pi} \sigma} \exp \left(-\frac{\left(\epsilon^{(i)}\right)^{2}}{2 \sigma^{2}}\right) \\ p\left(y^{(i)} | x^{(i)} ; \theta\right) &=\frac{1}{\sqrt{2 \pi} \sigma} \exp \left(-\frac{\left(y^{(i)}-\theta^{T} x^{(i)}\right)^{2}}{2 \sigma^{2}}\right) \\ L(\theta) &=\prod_{i=1}^{m} p\left(y^{(i)} | x^{(i)} ; \theta\right) \\ &=\prod_{i=1}^{m} \frac{1}{\sqrt{2 \pi} \sigma} \exp \left(-\frac{\left(y^{(i)}-\theta^{T} x^{(i)}\right)^{2}}{2 \sigma^{2}}\right) \end{aligned}

上述L(θ)L(\theta)叫做似然函數,如果我們要保證概率最合理,那麼就要保證似然函數最大,而在保證模型沒有變化的同時,我們可以對其取對數進行簡化步驟,那麼得到對數自然函數:

(θ)=logL(θ)=logi=1m12πσexp((y(i)θTx(i))22σ2)=i=1mlog12πσexp((y(i)θTx(i))22σ2)=mlog12πσ1σ212i=1m(y(i)θTx(i))2J(θ)=12i=1m(hθ(x(i))y(i))2\begin{aligned} \ell(\theta) &=\log L(\theta) \\ &=\log \prod_{i=1}^{m} \frac{1}{\sqrt{2 \pi} \sigma} \exp \left(-\frac{\left(y^{(i)}-\theta^{T} x^{(i)}\right)^{2}}{2 \sigma^{2}}\right) \\ &=\sum_{i=1}^{m} \log \frac{1}{\sqrt{2 \pi} \sigma} \exp \left(-\frac{\left(y^{(i)}-\theta^{T} x^{(i)}\right)^{2}}{2 \sigma^{2}}\right) \\=& m \log \frac{1}{\sqrt{2 \pi} \sigma}-\frac{1}{\sigma^{2}} \cdot \frac{1}{2} \sum_{i=1}^{m}\left(y^{(i)}-\theta^{T} x^{(i)}\right)^{2} \\ J(\theta) &=\frac{1}{2} \sum_{i=1}^{m}\left(h_{\theta}\left(x^{(i)}\right)-y^{(i)}\right)^{2} \end{aligned}

拋開前面的常數項,最大似然函數就變成了求最小的J(θ)J(\theta),所以這個就是線性迴歸的誤差函數。一般如果是一維的線性迴歸,我們的損失函數可以寫爲:

L=i=0m(yiβ0β1xi)2L=\sum_{i=0}^m{\left( y_i-\beta _0-\beta _1x_i \right) ^2}

這裏可以引用兩種方法求解Loss,第一種便是最小二乘法,它的速度快,因爲是平方誤差,第二種就是梯度下降法。下圖爲運用最小二乘法求解的過程:

這張圖翻資料的時候發現是去年的推導,我開始沒看懂還懷疑了下這是我啥時候寫的,都忘了,所以這不就重新記個筆記。。。最小二乘法在這裏就是試圖找到一條直線,在二維平面上使所有樣本店距離直線的歐氏距離最小。簡易代碼爲:

import numpy as np
import matplotlib.pyplot as plt

def calcAB(x,y):
    n = len(x)
    sumX, sumY, sumXY, sumXX = 0, 0, 0, 0
    for i in range(0, n):
        sumX += x[i]
        sumY += y[i]
        sumXX += x[i] * x[i]
        sumXY += x[i] * y[i]
    a = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX)
    b = (sumXX * sumY - sumX * sumXY) / (n * sumXX - sumX * sumX)
    return a, b

xi = [1,2,3,4,5,6,7,8,9,10]
yi = [10,11.5,12,13,14.5,15.5,16.8,17.3,18,18.7]
a,b=calcAB(xi,yi)
print("y = %10.5fx + %10.5f" %(a,b))
x = np.linspace(0,10)
y = a * x + b
plt.plot(x,y)
plt.scatter(xi,yi)
plt.show()
# y =    0.98606x +    9.30667

在這裏插入圖片描述
如果是多元情況下,我們可以將J(θ)J(\theta)寫成矩陣形式,那麼XXmm行樣本nn列特徵標籤的矩陣,YYmm11列的數據,式子可變爲:

 J(θ)=12i=1m(hθ(x(i))y(i))2=12(XθY)T(XθY)\ J(\theta)=\frac{1}{2}\sum_{i=1}^{m}(h_\theta(x^{(i)})-y^{(i)})^2=\frac{1}{2}(X\theta-Y)^T(X\theta-Y)

這裏同樣可以用最小二乘,sklearn裏的linregression能直接擬合,同樣也能直接對上式寫出代碼,但我們會發現當樣本量大於1w以上的時候,最小二乘需要對原來的方程進行轉置,這會造成矩陣操作複雜的情況,所以這裏,就引出了第二種解決方案爲—— 梯度下降法。

梯度下降法原理

在機器學習算法中,在最小化損失函數時,可以通過梯度下降法來一步步的迭代求解,得到最小化的損失函數,和模型參數值。反過來,如果我們需要求解損失函數的最大值,這時就需要用梯度上升法來迭代了。那麼首先要明確的是,什麼是梯度呢?

梯度是一個向量,是一個n元函數f關於n個變量的偏導數,比如二元函數f的梯度爲(X(θ)X,Y(θ)Y)T\left( \frac{\partial X\left( \theta \right)}{\partial X},\frac{\partial Y\left( \theta \right)}{\partial Y} \right) ^T,簡稱爲grad f(x,y)或者f(x,y)▽f(x,y),而三元函數的梯度爲 (X(θ)X,Y(θ)Y,Z(θ)Z)T\left( \frac{\partial X\left( \theta \right)}{\partial X},\frac{\partial Y\left( \theta \right)}{\partial Y},\frac{\partial Z\left( \theta \right)}{\partial Z} \right) ^T然後要明白梯度的方向是函數ff增長最快的方向,梯度的反方向是ff降低最快的方向。

那麼,我們要使用梯度下降,其實對多元函數的偏導進行負方向上的行走,也就是說將該負方向可以分解到各個自變量的維度上,就可以得到下述經典的式子:

θ=θαJ(θ)θ \theta=\theta-\alpha \cdot \frac{\partial J(\theta)}{\partial \theta}

其中α\alpha是學習因子,J(θ)θ\frac{\partial J\left( \theta \right)}{\partial \theta}是梯度,可以寫爲f(θ)▽f(\theta)。對於梯度下降,我想都應該看過下面這張很經典的圖:
在這裏插入圖片描述
這個就是一個多元函數的可視化,我們可以看到兩條路徑都尋優到了最小值。而這個方向是怎麼確定的呢?更進一步的,關於怎麼確定該下降方向,可以根據泰勒展開公式可以分析得到,當其值等於其負梯度方向時,在某一範圍內下降速度最快。同樣也可以根據方向導數來確定:
在這裏插入圖片描述
f(x0)=limΔx0ΔyΔx=limΔx0f(x0+Δx)f(x0)Δxf^{\prime}\left(x_{0}\right)=\lim _{\Delta x \rightarrow 0} \frac{\Delta y}{\Delta x}=\lim _{\Delta x \rightarrow 0} \frac{f\left(x_{0}+\Delta x\right)-f\left(x_{0}\right)}{\Delta x}

很顯然切線方向即是梯度方向,而在多元情況下,我們都學過一個定理:在一平面中,任意一向量都可以用兩個不共線的基向量表示,也就是說任意一方向上的變化,都可以分解到x和y兩個方向上。

那麼上式可以改成:

limΔu0ΔzΔu=limΔu0f(x+Δucosα,y+Δusinα)f(x,y)Δu\lim _{\Delta u \rightarrow 0} \frac{\Delta z}{\Delta u}=\lim _{\Delta u \rightarrow 0} \frac{f(x+\Delta u * \cos \alpha, y+\Delta u * \sin \alpha)-f(x, y)}{\Delta u}

我們記爲DufD_uf,進一步化簡爲:

Duf=fx(x,y)cosθ+fy(x,y)sinθ=[fx(x,y)fy(x,y)][cosθsinθ]D_{u} f=f_{x}(x, y) \cos \theta+f_{y}(x, y) \sin \theta=\left[f_{x}(x, y) \quad f_{y}(x, y)\right]\left[\begin{array}{c}{\cos \theta} \\ {\sin \theta}\end{array}\right]

A=(fx(x,y),fy(x,y)),I=(cosα,sinα)\vec{A}=\left(f_{x}(x, y), f_{y}(x, y)\right), \vec{I}=(\cos \alpha, \sin \alpha),則能化簡成最終式爲:

Duf=A×I=AIcosαD_{u} f=\mathbf{A} \times \mathbf{I}=|\mathbf{A}||\mathbf{I}| \cos \alpha

其中A|A|I|I|已經轉變成標量。那麼只要cosacosa爲0時,方向導數DufD_uf達到最大,此時的方向導數即爲梯度,同樣可證得。

關於泰勒的推導,可以看 梯度下降法的推導(非常詳細、易懂的推導)

所以,總結梯度下降法的步驟:

  • 初始化θ(隨機初始化)
  • 迭代,新的θ能夠使得J(θ)更小
  • 如果J(θ)無法繼續減少或者達到循環上界次數,退出。
    θj:=θjαθjJ(θ)\theta_{j}:=\theta_{j}-\alpha \frac{\partial}{\partial \theta_{j}} J(\theta)

其中a爲步長或學習率

梯度下降法實踐

所以,我們可以用梯度下降法來解決線性迴歸的損失函數,根據鄒博的課件爲:

θjJ(θ)=θj12(hθ(x)y)2=212(hθ(x)y)θj(hθ(x)y)=(hθ(x)y)θj(i=0nθixiy)=(hθ(x)y)xj\begin{aligned} \frac{\partial}{\partial \theta_{j}} J(\theta) &=\frac{\partial}{\partial \theta_{j}} \frac{1}{2}\left(h_{\theta}(x)-y\right)^{2} \\ &=2 \cdot \frac{1}{2}\left(h_{\theta}(x)-y\right) \cdot \frac{\partial}{\partial \theta_{j}}\left(h_{\theta}(x)-y\right) \\ &=\left(h_{\theta}(x)-y\right) \cdot \frac{\partial}{\partial \theta_{j}}\left(\sum_{i=0}^{n} \theta_{i} x_{i}-y\right) \\ &=\left(h_{\theta}(x)-y\right) x_{j} \end{aligned}

我們就算法描述和凸函數極值得到如下需要重複迭代的式子:

θj:=θj+αi=1m(y(i)hθ(x(i)))xj(i)\theta_{j}:=\theta_{j}+\alpha \sum_{i=1}^{m}\left(y^{(i)}-h_{\theta}\left(x^{(i)}\right)\right) x_{j}^{(i)}

我們再來換一個視角,如何確定α\alpha的值呢?記當前點爲xkx_k ,當前搜索方向爲dkd_k (如:負梯度方向),因爲學習率αα是待考察的對象,因此,將下列函數f(xk+αdk)f(x_k +αd_k )看做是關於αα的函數h(α),那麼原式變爲:

h(α)=f(xk+αdk),α>0h(\alpha)=f\left(x_{k}+\alpha d_{k}\right), \quad \alpha>0

  • α=0α=0時,h(0)=f(xk)h(0)=f(x_k )
  • 導數 h(α)=f(xk+αdk)Tdk\nabla h(\alpha)=\nabla f\left(x_{k}+\alpha d_{k}\right)^{T} d_{k}

因爲梯度下降是尋找f(x)f(x)的最小值,那麼,在xkx_kdkd_k都給定的情況下,α\alpha的求解爲:

α=argminα>0h(α)=argminα>0f(xk+αdk)\alpha=\arg \min _{\alpha>0} h(\alpha)=\arg \min _{\alpha>0} f\left(x_{k}+\alpha d_{k}\right)

這裏鄒博的課件中給出了兩種方法求解相關的係數,分別是線性搜索(Line Search)和回溯線性搜索(Backing Line Search),而回溯裏面還有插值的搜索方式,中間的推導可以去看鄒博的課件或者是視頻,這裏就不推了,其實我也沒太看懂。。我們可以得到結論,回溯的方式在前期迭代次數不高情況下效率要優於插值法,而越晚後,插值的效率比回溯的越高,這算得上是一種調優,那麼問題來了,在使用梯度下降時,需要進行調優。哪些地方需要調優呢?

  1. 算法的步長選擇。步長取值取決於數據樣本,可以多取一些值,從大到小,分別運行算法,看看迭代效果,如果損失函數在變小,說明取值有效,否則要增大步長。步長太大,會導致迭代過快,甚至有可能錯過最優解。步長太小,迭代速度太慢,很長時間算法都不能結束。所以算法的步長需要多次運行後才能得到一個較爲優的值。
  2. 算法參數的初始值選擇。初始值不同,獲得的最小值也有可能不同,因此梯度下降求得的只是局部最小值;當然如果損失函數是凸函數則一定是最優解。由於有局部最優解的風險,需要多次用不同初始值運行算法,關鍵損失函數的最小值,選擇損失函數最小化的初值。
  3. 歸一化。由於樣本不同特徵的取值範圍不一樣,可能導致迭代很慢,爲了減少特徵取值的影響,可以對特徵數據歸一化。

相應的,我們能得到三種梯度下降法的變化形式,分別爲批量梯度下降法(Batch Gradient Descent),隨機梯度下降法(Stochastic Gradient Descent),小批量梯度下降法(Mini-batch Gradient Descent)。

這三種的公式與推導可以看梯度下降(Gradient Descent)小結

代碼可以看梯度下降算法及python實現(學習筆記)

我們可以看一下梯度的迭代過程爲:

from scipy import stats
import matplotlib.pyplot as plt

# 構造訓練數據
x = np.arange(0., 10., 0.2)
m = len(x)  # 訓練數據點數目
print(m)
x0 = np.full(m, 1.0)
input_data = np.vstack([x0, x]).T  # 將偏置b作爲權向量的第一個分量
target_data = 2 * x + 5 + np.random.randn(m)

# 兩種終止條件
loop_max = 10000  # 最大迭代次數(防止死循環)
epsilon = 1e-3

# 初始化權值
np.random.seed(0)
theta = np.random.randn(2)

alpha = 0.001  # 步長(注意取值過大會導致振盪即不收斂,過小收斂速度變慢)
diff = 0.
error = np.zeros(2)
count = 0  # 循環次數
finish = 0  # 終止標誌

while count < loop_max:
    count += 1

    # 標準梯度下降是在權值更新前對所有樣例彙總誤差,而隨機梯度下降的權值是通過考查某個訓練樣例來更新的
    # 在標準梯度下降中,權值更新的每一步對多個樣例求和,需要更多的計算
    sum_m = np.zeros(2)
    for i in range(m):
        dif = (np.dot(theta, input_data[i]) - target_data[i]) * input_data[i]
        sum_m = sum_m + dif  # 當alpha取值過大時,sum_m會在迭代過程中會溢出

    theta = theta - alpha * sum_m  # 注意步長alpha的取值,過大會導致振盪
    # theta = theta - 0.005 * sum_m      # alpha取0.005時產生振盪,需要將alpha調小

    # 判斷是否已收斂
    if np.linalg.norm(theta - error) < epsilon:
        finish = 1
        break
    else:
        error = theta
    print('loop count = %d' % count, '\tw:',theta)
print('loop count = %d' % count, '\tw:',theta)

# check with scipy linear regression
slope, intercept, r_value, p_value, slope_std_error = stats.linregress(x, target_data)
print('intercept = %s slope = %s' % (intercept, slope))

plt.plot(x, target_data, 'g*')
plt.plot(x, theta[1] * x + theta[0], 'r')
plt.show()

"""
......
loop count = 297 	w: [5.41376264 1.96101475]
loop count = 298 	w: [5.41476069 1.96086235]
loop count = 299 	w: [5.41574617 1.96071186]
intercept = 5.493050963693186 slope = 1.9489071170997092
"""

在這裏插入圖片描述

梯度下降和牛頓法的區別

這裏不做太具體的推導,可以看最後的鏈接。

f(x)f(x)二階導連續,將f(x)f(x)xkx_k 處Taylor展開爲:
φ(x)=f(xk)+f(xk)(xxk)+12f(xk)(xxk)2+R2(x) \varphi(x)=f\left(x_{k}\right)+f^{\prime}\left(x_{k}\right)\left(x-x_{k}\right)+\frac{1}{2} f^{\prime \prime}\left(x_{k}\right)\left(x-x_{k}\right)^{2}+R_{2}(x)
其中R2是餘項,那麼對其進行求導,得到:

φ(x)f(xk)+f(xk)(xxk)\varphi^{\prime}(x) \approx f^{\prime}\left(x_{k}\right)+f^{\prime \prime}\left(x_{k}\right)\left(x-x_{k}\right)

進行化簡,則:
xk+1=xkf(xk)f(xk)x_{k+1}=x_{k}-\frac{f^{\prime}\left(x_{k}\right)}{f^{\prime \prime}\left(x_{k}\right)}

上述迭代公式,即牛頓法,該方法可以直接推廣到多維:用方向導數代替一階導,用Hessian矩陣代替二階導,所以它的方向,是一條曲線:
在這裏插入圖片描述

梯度下降法和牛頓法相比,兩者都是迭代求解,不過梯度下降法是梯度求解,而牛頓法是用二階的海森矩陣的逆矩陣求解。相對而言,使用牛頓法收斂更快(迭代更少次數)。但是每次迭代的時間比梯度下降法長。

上圖也是一張很經典的圖,關於最小化一個目標方程的例子,紅色曲線是利用牛頓法迭代求解,綠色曲線是利用梯度下降法求解。當然同樣還有修正牛頓、擬牛頓法等牛頓法:

在這裏插入圖片描述
關於牛頓法的特點以及擬牛頓法的推導步驟可以在我最後給的鏈接中有提及,我這裏算是隻做了一個初步概括。

總結

花了幾天大概整理了下,如果有錯後續會進行修正,好像很久沒敲這麼多公式了。今年有一直在兜兜轉轉卻又回到了原地的感覺,雖然並不是停滯不前,生活算過得比去年更加豐富,這個應該到時候年終總結會去回憶一下相關的變化,但確實感觸最深的是,計劃趕不上變化,很多時候都太盲目了,我希望明年能制定一個明確的方向,這也是我需要真正投入精力與時間去做的事情,節約一點浪費的時間,多留點精力寫寫博客也好,或許吧。


參考與推薦

[1]. 梯度下降(Gradient Descent)小結

[2]. 鄒博的機器學習課件與視頻

[3]. 梯度下降法、牛頓法和擬牛頓法

[4]. 梯度下降算法及python實現(學習筆記)

[5]. 機器學習算法入門之(一) 梯度下降法實現線性迴歸

[6]. 淺談對梯度下降法的理解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章