算法學習筆記——梯度降低法原理及其代碼實現

梯度降低法原理以及代碼實現

本篇博客承接本人上一篇關於逐步迴歸算法的引伸,本篇將開始整理梯度降低算法的相關知識。梯度降低,gradient descent(以後將簡稱GD),是一種經過迭代找最優的方式一步步找到損失函數最小值的算法,基本算法思路可總結爲以下幾點:python

(1) 隨機設置一個初始值算法

(2) 計算損失函數的梯度dom

(3) 設置步長,步長的長短將會決定梯度降低的速度和準確度,以後會詳細展開函數

(4) 將初值減去步長乘以梯度,更新初值,而後將這一過程不斷迭代優化

1. 原理講解——二維平面情形

仍是老規矩,先從二維平面場景開始,由易到難,步步深刻,先上圖:lua

爲了方便思考,咱們求解的是對上述函數圖像的任意一個點來講,沿着哪一個方向走可以使得該函數的y上升幅度最大,那麼逆向思惟,該方向一旦求得,該方向的反方向不就是使得y降低速度最快的方向了嗎?好,沿着這個思路繼續,因爲這個方向是咱們要求的,不妨設該方向與x軸的夾角爲,如今咱們沿着這個方向走的距離,將該向量正交分解可知,它只有在水平方向上的份量纔會提供使得函數值發生變化的效果,豎直方向上份量不產生任何效果,因而可得如下函數值變化量函數:spa

顯然,當  爲0,也就是水平正方向時,可以使得該函數值上升最快,也就是說水平負方向可以使得該函數值降低最快,至此,二維平面上的使得函數值降低最快的方向咱們就找到了:code

2. 原理講解——三維空間情形

現將該問題拓展至三維空間上,由以前的證實可知,對於函數來講,某向量在y軸上的份量不會提供使得函數值發生變化的效果,那麼將此結論擴展,對於函數來講,某向量在z軸上的份量也不會提供使得函數值發生變化的效果,所以,咱們能夠這樣說,該問題的最終解必落在xoy平面內,而與z軸無關!現正式開始證實blog

爲xoy平面上的一個向量,該向量與x軸的夾角爲,則它在x軸和y軸上的份量爲:get

x軸和y軸的變化共同引發了z軸方向上的變化,由此可得:

利用向量的思想,將上式表示成兩個向量內積的結果,可得:

第一個向量其實就是向量,而第二個向量就是函數在某點上的梯度,所以上式最終就可轉換成:

設上式中兩個向量的夾角爲α,根據向量內積公式,又可表示成:

該式中,當α爲0時達到最大值,也就是說,與梯度相同的方向是使得函數值上升最快的方向,換句話來講,與梯度相反的方向是使得函數值降低最快的方向!!!

3. 原理講解——高維空間情形

結合上一篇博客中提到的高維空間下的SSE的計算,咱們直接引用以前的結論:

若採用梯度降低法求解最小值,只需沿着上式的方向進行試探便可

4. 代碼實現

目前常見的求解梯度降低法的方式有三種,BGD,Batch Gradient Descent,批量梯度降低法,SGD,Stochastic Gradient Descent,隨機梯度降低法,以及MBGD,Mini-Batch Gradient Descent,小批量梯度降低法。因爲最爲普通的批量梯度降低法是每次迭代都是對於全量的數據來講的,若初值選的不巧就比較容易陷入一個局部(local)最優而不是全局(global)最優的解中去,所以出現了一個更爲優化的算法來解決這個問題,那就是隨機梯度降低法,在SGD中,每一個循環只隨機選取一個樣本進行迭代,所以對於BGD來講,訓練模型的速度明顯快上了許多,但因爲每次僅僅選用一個樣本,樣本量不夠,所以不可以很快收斂到最優解,值得一提的是,因爲該算法的隨機性,在面對非凸函數時,能很好地避免陷入局部最優的狀況,所以比BGD效果更好;而後這兩種算法都比較極端,要麼每次選擇所有,要麼每次只選一個,有沒有一種折中的算法呢?答案是確定的,那就是MBGD,小批量梯度降低法,每次選擇的樣本量是在整體中進行一次抽樣,通常以比例的方式抽取最佳。

4.1 關於步長大小的選擇

雖然說在實際寫代碼的時候,步長是咱們本身設定的,但對於步長究竟選擇多少合適,主要有兩點考量。

1. 步長太小會致使一步步迭代到最優值的過程會很是長,不過結果會較爲精確,須要注意的是,假若迭代次數不夠,就會致使最終結果每每不是最優解

2. 步長過長雖然可使得迭代過程縮短,可是對於精度丟失會比較大,而且最終解頗有可能會在最有點兩側來回跳動,致使始終接近不了最優解

4.2 三種算法代碼實現

咱們使用python將這三種算法寫進一個包中,功能是使用梯度降低法來糾結迴歸方程SSE的最小值,具體代碼以下所示:

import numpy as np
import pandas as pd

class regression:
    
    def __init__(self, data, intercept = True):
        self.X = np.mat(data.iloc[:,:-1].values)
        self.Y = np.mat(data.iloc[:,-1].values).T
        self.data = data
        self.intercept = intercept
    def BGDfit(self, lam, iternum):
        #This function aims to use batch gradient descent algorithm to get the minimum value
        theta = np.mat(np.zeros((self.X.shape[1], 1)))
        for _ in range(iternum):
            grad =  2 / self.X.shape[0] * (self.X.T * (self.X * theta - self.Y))
            theta -= lam * grad
        return np.ravel(theta)
        
    def SGDfit(self, lam, iternum):
        #This function aims to use stochastic gradient descent algorithm to get the minimum value
        theta = np.mat(np.zeros((self.X.shape[1], 1)))
        for _ in range(iternum):
            rnd_num = np.random.randint(self.X.shape[0])
            grad =  2 / self.X.shape[0] * (self.X[rnd_num].T * (self.X[rnd_num] * theta - self.Y[rnd_num]))
            theta -= lam * grad
        return np.ravel(theta)
        
    def MBGDfit(self, lam, iternum):
        #This function aims to use mini-batch gradient descent algorithm to get the minimum value
        theta = np.mat(np.zeros((self.X.shape[1], 1)))
        temp_X = pd.DataFrame(self.X)
        for _ in range(iternum):
            X_index = temp_X.sample(frac = 0.1, replace = False).index
            X_mini = self.X[X_index, :]
            Y_mini = self.Y[X_index]
            grad =  2 / X_mini.shape[0] * (X_mini.T * (X_mini * theta - Y_mini))
            theta -= lam * grad
        return np.ravel(theta)
        
    def evaluate(self, coef):
        #This function aims to calculate SSE and R2 in order to evaluate whether the model is accurate or not
        Y_pred = self.X * np.mat(coef).T
        SSE = np.power(np.ravel(self.Y) - np.ravel(Y_pred), 2).sum()
        SST = np.power(np.ravel(self.Y) - np.ravel(self.Y).mean(), 2).sum()
        R2 = 1 - SSE / SST
        return SSE, R2
相關文章
相關標籤/搜索