梯度下降

原理

機(jī)器學(xué)習(xí)就是定義一個f(x),找到f(x)的最佳參數(shù),讓的過程。那么如何找到這個最佳參數(shù)呢?

梯度學(xué)習(xí)就是應(yīng)用最廣泛的一種方法。

為什么需要梯度下降

每個模型都有自己的損失函數(shù),訓(xùn)練一個模型的過程,就是找到使損失函數(shù)最小的最佳參數(shù)的過程。在簡單的線性回歸中,我們通過最小二乘法來求解參數(shù);但是一般損失函數(shù)都是比較復(fù)雜的,很難通過求解得到。這時候,我們就可以通過梯度下降去求解。

梯度下降算法作為一個聰明很多的算法,抓住了參數(shù)與損失值之間的導(dǎo)數(shù),也就是能夠計算梯度(gradient),通過導(dǎo)數(shù)告訴我們此時此刻某參數(shù)應(yīng)該朝什么方向,以怎樣的速度運(yùn)動,能安全高效降低損失值,朝最小損失值靠攏

什么是梯度

多元函數(shù)的導(dǎo)數(shù)就是梯度,對每個變量進(jìn)行微分,然后用逗號分隔,因此梯度是一個向量,代表了下降的方向。

\Delta L = (\frac{\partial L}{\partial a},\frac{\partial L}{\partial b})

假設(shè)有個二元函數(shù)f(x) = x_1^2+x_1x_2-3x_2,求偏導(dǎo)為
\Delta f =(\frac{\partial f}{\partial x_1},\frac{\partial f}{\partial x_2})=(2x_1+x_2, x_1-3)

那么在點(diǎn)(1,2),梯度\Delta = (4, -2)

在單變量函數(shù)中,梯度代表函數(shù)的微分,代表著函數(shù)在某個點(diǎn)的斜率;

對于多變量函數(shù),梯度代表是一個向量,代表在給定點(diǎn)上升最快的方向

梯度指向誤差值增加最快的方向。我們的目標(biāo)是找到損失函數(shù)(也就是誤差)最小對應(yīng)的參數(shù),因此我們需要沿著反梯度的方向進(jìn)行搜索。

理解梯度下降

梯度下降就是從群山中山頂找一條最短的路走到山谷最低的地方。

一個人被困在山上,需要從山頂?shù)缴焦?。但此時霧很大,看不清下山的路徑。他必須利用自己周圍的信息去找到下山的路徑。這個時候,他就可以利用梯度下降算法來幫助自己下山。具體來說就是,以他當(dāng)前的所處的位置為基準(zhǔn),隨機(jī)選擇一個方向,然后每次邁步都選擇最陡的方向。然后每走一段距離,都反復(fù)采用同一個方法:如果發(fā)現(xiàn)腳下的路是下坡,就順著最陡的方向走一步,如果發(fā)現(xiàn)腳下的路是上坡,就逆著方向走一步,最后就能成功的抵達(dá)山谷。

image.png

從數(shù)學(xué)的角度出發(fā),針對損失函數(shù)L,可以證明按照負(fù)梯度的方向移動,損失函數(shù)最終能夠達(dá)到一個最小值。

那么我們就可以得到損失函數(shù)值(也就是下一步的落腳點(diǎn))的迭代公式:

(a_{k+1},b_{k+1} =(a_k-\eta \frac{\partial L}{\partial a},b_k-\eta \frac{\partial L}{\partial b}) )

針對于上述公式,有一些常見的問題:

為什么要梯度要乘以一個負(fù)號?

我們已經(jīng)知道:梯度的方向就是損失函數(shù)值在此點(diǎn)上升最快的方向,是損失增大的區(qū)域,而我們要使損失最小,因此就要逆著梯度方向走,自然就是負(fù)的梯度的方向,所以此處需要加上負(fù)號

關(guān)于參數(shù) :

我們已經(jīng)知道,梯度對應(yīng)的是下山的方向,而參數(shù) 對應(yīng)的是步伐的長度。在學(xué)術(shù)上,我們稱之為“學(xué)習(xí)率”(learning rate),是模型訓(xùn)練時的一個很重要的超參數(shù),能直接影響算法的正確性和效率

  • 首先,學(xué)習(xí)率不能太大。因此從數(shù)學(xué)角度上來說,一階泰勒公式只是一個近似的公式,只有在學(xué)習(xí)率很小,也就是很小時才成立。并且從直觀上來說,如果學(xué)習(xí)率太大,那么有可能會“邁過”最低點(diǎn),從而發(fā)生“搖擺”的現(xiàn)象(不收斂),無法得到最低點(diǎn)
  • 其次,學(xué)習(xí)率又不能太小。如果太小,會導(dǎo)致每次迭代時,參數(shù)幾乎不變化,收斂學(xué)習(xí)速度變慢,使得算法的效率降低,需要很長時間才能達(dá)到最低點(diǎn)。

缺點(diǎn):

梯度算法只能達(dá)到局部最優(yōu)解,不是全局最優(yōu)解。

那么對應(yīng)的解決方案如下:首先隨機(jī)產(chǎn)生多個初始參數(shù)集,即多組;然后分別對每個初始參數(shù)集使用梯度下降法,直到函數(shù)值收斂于某個值;最后從這些值中找出最小值,這個找到的最小值被當(dāng)作函數(shù)的最小值。當(dāng)然這種方式不一定能找到全局最優(yōu)解,但是起碼能找到較好的。

對于梯度下降來說,初始點(diǎn)的位置,也是一個超參數(shù)。

線性回歸梯度下降代碼

def fit_gd(self, X_train, y_train, eta=0.01, n_iters=1e4):
    """根據(jù)訓(xùn)練數(shù)據(jù)集X_train, y_train, 使用梯度下降法訓(xùn)練Linear Regression模型"""
    assert X_train.shape[0] == y_train.shape[0], \
        "the size of X_train must be equal to the size of y_train"

    def J(theta, X_b, y):
        try:
            return np.sum((y - X_b.dot(theta)) ** 2) / len(y)
        except:
            return float('inf')
        
    def dJ(theta, X_b, y):
        return X_b.T.dot(X_b.dot(theta) - y) * 2. / len(y)

    def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):

        theta = initial_theta
        cur_iter = 0

        while cur_iter < n_iters:
            gradient = dJ(theta, X_b, y)
            last_theta = theta
            theta = theta - eta * gradient
            if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
                break

            cur_iter += 1

        return theta

    X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
    initial_theta = np.zeros(X_b.shape[1])
    self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)

    self.intercept_ = self._theta[0]
    self.coef_ = self._theta[1:]

    return self

然后使用向量化的方式編寫代碼,但是發(fā)現(xiàn)在真實數(shù)據(jù)中效果比較差,這是因為數(shù)據(jù)的規(guī)模不一樣,因此在梯度下降之前需要使用歸一化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容