1. 程式人生 > >機器學習筆記——梯度下降(Gradient Descent)

機器學習筆記——梯度下降(Gradient Descent)

梯度下降演算法(Gradient Descent)

在所有的機器學習演算法中,並不是每一個演算法都能像之前的線性迴歸演算法一樣直接通過數學推導就可以得到一個具體的計算公式,而再更多的時候我們是通過基於搜尋的方式來求得最優解的,這也是梯度下降法所存在的意義。

  • 不是一個機器學習演算法
  • 是一種基於搜尋的最優化方法
  • 作用:最小化一個損失函式
  • 梯度上升法:最大化一個效用函式

1 梯度下降法

1.1 什麼梯度下降法?


圖中任意一點的斜率我們可以用 d

J d θ \frac{dJ}{d\theta} 來表示,而斜率的正負更可以表示增大或減小的放下,若斜率為正則沿 x
x
正方向增大,若斜率為負則沿 x x 正方向減小,當我們設定一個步長 η \eta ,以及一個起始點 (
J , θ ) (J,\theta)
,每次向 J J 減小的方向移動一個步長,則有:
J ( θ η ) J ( θ ) η d J d θ J(\theta-\eta) \approx J(\theta) -\eta\frac{dJ}{d\theta}
這樣通過搜尋的方法就可以找到一個最小的 J J ,而對於某些函式來說並不是都存在一個唯一的極值點,例如下圖中的情況:

函式 f f 存在一個區域性最優解和一個全域性最優解,按照梯度下降演算法,如果我們的起始點選在了最右邊的紅點處的話那麼這樣尋找到得只是一個區域性最優解。為了解決這個問題,我們通常的做法是多次執行,隨機化初始點。因此我們可以知道梯度下降演算法中的初始點也是一個超引數。

1.2 關於引數 η \eta

  • η \eta 稱為學習率(Learning Rate)
  • η \eta 的取值影響獲得最優解的速度
  • η \eta 取值不合適甚至得不到最優解
  • η \eta 是梯度下降演算法的一個超引數

1.3 程式碼實現梯度下降演算法

import numpy as np
import matplotlib.pyplot as plt
plot_x = np.linspace(-1,6,141)
# print(plot_x)
plot_y = (plot_x - 2.5) ** 2 -1

plt.plot(plot_x,plot_y)
# plt.show()

def dJ(theta):
    return 2 * (theta - 2.5)
def J(theta):
    return (theta - 2.5) ** 2 -1

theta = 0.0
eta = 0.1
epsilon = 1e-8
theta_history = [theta]
while True:
    gradieent = dJ(theta)
    last_theta = theta
    theta = theta - eta * gradieent
    theta_history.append(theta)

    if(abs(J(theta) -J(last_theta)) < epsilon):
        break

plt.plot(np.array(theta_history), J(np.array(theta_history)), color = 'r', marker = '+')
plt.show()

print(theta)
print(J(theta))
#**************************執行結果***************************************
#                      2.499891109642585
#                     -0.99999998814289

圖1.3-1
顯然我們實現的梯度下降演算法已經達到了我們想要的效果,下面我們對上面的程式碼進行封裝,再看看不同的 θ \theta 取值對演算法效果的影響:

import numpy as np
import matplotlib.pyplot as plt
plot_x = np.linspace(-1,6,141)

def dJ(theta):
    return 2 * (theta - 2.5)

def J(theta):
    return (theta - 2.5) ** 2 -1

def gradient_descent(initial_theta, eta, epsilon=1e-8):
    theta = initial_theta
    theta_history.append(initial_theta)
    while True:
        gradieent = dJ(theta)
        last_theta = theta
        theta = theta - eta * gradieent
        theta_history.append(theta)

        if (abs(J(theta) - J(last_theta)) < epsilon):
            break

def plot_theta_history():
    plt.plot(plot_x, J(plot_x))
    plt.plot(np.array(theta_history), J(np.array(theta_history)), color='r', marker='+')
    plt.show()

eta = 0.01
theta_history = []
gradient_descent(0.0, eta)
plot_theta_history()
#***************************************執行結果************************
#            如下圖


相比我們將 θ \theta 設定為0.1,此時的 θ = 0.01 \theta = 0.01 顯然此時已經近乎擬合了曲線。如果我們把 θ \theta 的取值設定的更大一點呢?

eta = 0.8
theta_history = []
gradient_descent(0.0, eta)
plot_theta_history()


雖然此時我們的梯度下降演算法並沒有很好的擬合我們的曲線,但是依然有效,依舊找到了最優解,因此我們知道, θ \theta 的值只要不超過某個限定的值就是有效的。
下面我們試著將 θ \theta 的值設定為1.1
此時編譯器直接報錯

OverflowError: (34, 'Result too large')

此時因為我們選取的 η \eta 值過大,是的我們無法求得最優解,我我們修改一下程式碼,用matplot畫出直觀的圖示來看看究竟發生了什麼。
首先我們將gradient_descent函式中新增一個n_iterations變數來控制迴圈次數,再改變while迴圈的條件

ef gradient_descent(initial_theta,eta, n_iterations = 1e4,  epsilon=1e-8):
    theta = initial_theta
    theta_history.append(initial_theta)
    i_iter = 0
    while i_iter < n_iterations:
        gradieent = dJ(theta)
        last_theta = theta
        theta = theta - eta * gradieent
        theta_history.append(theta)

        if (abs(J(theta) - J(last_theta)) < epsilon):
            break
        i_iter += 1

我們讓while迴圈執行10次檢視結果

eta = 1.1
theta_history = []
gradient_descent(0.0,  eta,n_iterations=10)
plot_theta_history()


從結果中我們可以明顯看到, θ \theta 取值過大, J ( θ ) J(\theta) 的值也隨之越來越大,而且我們這裡只迴圈了10次,在之前我們的while迴圈直接等於true,一直在死迴圈,所以才會報OverflowError: (34, 'Result too large')的錯誤!

2 多元線性迴歸中的梯度下降演算法


在引數只有一個的情況下,我們知道斜率對應於 J J 增大的方向,而在多個引數的情況下梯度 J \nabla J 表示 J J 增大最快的方向
在之前的多元線性迴歸演算法中我們的目標函式為
i = 1 m ( y ( i ) y ^ ( i ) ) 2 \sum_{i=1}^m{(y^{(i)}-\hat{y}^{(i)})^2}
y ^ ( i ) = θ 0 + θ 0 x 1 ( i ) + θ 0 x 2 ( i ) + + θ 0 x n ( i ) \hat{y}^{(i)}=\theta_0+\theta_0x_1^{(i)}+\theta_0x_2^{(i)}+\cdots+\theta_0x_n^{(i)}
所以也就是求
i = 1 m ( y ( i ) θ 0 θ 0 x 1 ( i ) θ 0 x 2 ( i ) θ 0 x n ( i ) ) 2 \sum_{i=1}^m{(y^{(i)}-\theta_0-\theta_0x_1^{(i)}-\theta_0x_2^{(i)}-\cdots-\theta_0x_n^{(i)})^2}
儘可能小。
下面我們來求這一函式的梯度: