機器學習系列 04:梯度下降法及 Python 實現

  本內容將介紹 梯度下降法 以及其三種類型( 批量梯度下降法(BGD)隨機梯度下降法(SGD)小批量梯度下降法(MBGD))。最後將給出使用隨機梯度下降法擬合一個線性迴歸模型的 Python 代碼。

  在機器學習中,爲了學得一個能夠較好反映實際的模型,通常的方法:針對模型構建損失函數,然後使得損失函數最小化。在實際任務中,梯度下降法 是使用較多的一種方法。

一、梯度下降法

  梯度下降法(Gradient descent)是一個一階最優化算法,是求解無約束最優化問題的常用算法,通常也稱爲最速下降法。要使用梯度下降法找一個函數的局部極小值,必須向函數上當前點對應梯度的反方向的規定步長距離點進行迭代搜索。如果相反地向梯度正方向迭代進行搜索,則會接近函數的局部極大值,這個過程被稱爲梯度上升法

  更直觀的理解:比如當前我們站在山上的某一點,環顧四周找到最陡的一個坡,沿此方向向下走一步;走到新的點後,再找到最陡的坡,向下走一步,按照此方法不斷的往下走,直到走到“最低點”。

  假設損失函數爲 E(w)E(w),則參數 ww 的更新迭代公式爲

(1)w^=wηE(w) \hat{w} = w - \eta \nabla E(w) \tag{1}

其中,w^\hat{w} 表示新求得的參數 ww(與原來的 ww 進行區別),η\eta 爲學習速率,E(w)\nabla E(w) 表示 E(w)E(w) 的梯度。

  在實際任務中,使用梯度下降法的流程如下:

  • 首先對參數 ww 進行隨機初始化,一般初始化爲一個全零向量。
  • 然後不斷採用式(1)迭代更新參數 ww,直到收斂或者達到設定的迭代次數。

  下面我們用線性迴歸模型來具體說明實際使用梯度下降法的過程。假設需要擬合的函數 fw(x)f_w(x) 爲線性迴歸模型(如果不理解線性迴歸模型,可以參閱這裏),即

(2)fw(x)=w0x0+w1x1++wnxn=j=0nwjxj f_{w}(x) = w_0x_0 + w_1x_1 + \cdots + w_nx_n = \sum_{j=0}^{n}w_jx_j \tag{2}

其中,nn 爲特徵數量,xjx_j 爲特徵值,x0=1x_0 = 1wjw_j 爲需要求得的參數。損失函數 E(w)E(w) 爲平方損失函數,即

(3)E(w)=12mi=1m(fw(x(i))y(i))2 E(w) = \frac{1}{2m} \sum_{i=1}^{m} (f_w(x^{(i)}) - y^{(i)})^2 \tag{3}

其中,mm 爲樣本數量。這裏乘以 12\frac{1}{2} 是爲了後面計算更加方便,實際上是否乘以 12\frac{1}{2} ,對最終的最優解都不會產生影響。

  對 E(w)E(w) 中的每個 wjw_j 求偏導數(即梯度),得到

E(wj)=E(w)wj=wj12mi=1m(fw(x(i))y(i))2 \nabla E(w_j) = \frac{\partial E(w)}{\partial w_j} = \frac{\partial}{\partial w_j} \frac{1}{2m} \sum_{i=1}^{m} (f_w(x^{(i)}) - y^{(i)})^2

=12mi=1mwj(fw(x(i))y(i))2 = \frac{1}{2m} \sum_{i=1}^{m} \frac{\partial}{\partial w_j} (f_w(x^{(i)}) - y^{(i)})^2

=12mi=1m2(fw(x(i))y(i))wj(fw(x(i))y(i)) = \frac{1}{2m} \sum_{i=1}^{m} 2(f_w(x^{(i)}) - y^{(i)}) \frac{\partial}{\partial w_j} (f_w(x^{(i)}) - y^{(i)})

(4)=1mi=1m(fw(x(i))y(i))xji = \frac{1}{m} \sum_{i=1}^{m}(f_w(x^{(i)})-y^{(i)})x_{j}^{i} \tag{4}

  根據式(1)和式(4)可知,參數 wjw_j 的迭代更新公式爲

(5)wj^=wjη1mi=1m(fw(x(i))y(i))xji \hat{w_j} = w_j - \eta \frac{1}{m} \sum_{i=1}^{m}(f_w(x^{(i)})-y^{(i)})x_{j}^{i} \tag{5}

其中,η\eta 爲學習速率。

  通過式(5)不斷地迭代更新參數 ww,將獲得 ww 的最優解或者最優解附近的值。

二、梯度下降法的種類

  根據式(5)中的 mm 取值(即選擇的樣本數量)的不同,梯度下降法分爲 批量梯度下降法(BGD)隨機梯度下降法(SGD)小批量梯度下降法(MBGD)

2.1 批量梯度下降法

  當 mm 爲測試集中全部樣本數量時,稱爲 批量梯度下降法(Batch Gradient Descent,BGD)。從式(5)可以看到,每迭代一次,會用到訓練集中的所有樣本,這樣可以得到一個全局最優解;但是當 mm 很大時,計算量將會很大,迭代的速度會比較慢。

2.2 隨機梯度下降法

  當 mm 爲 1 時(即每次只選擇一個樣本),稱爲 隨機梯度下降法(Stochastic Gradient Descent,SGD)。即每次只選擇一個樣本來迭代更新參數 ww,在樣本數量很大的情況下,可能只需要用到一部分樣本就可以求得參數 ww 的最優解。因此隨機梯度下降法相比批量梯度下降法,在計算量上會大大減少。

  當然,隨機梯度下降法同樣也存在一些缺點:相比批量梯度下降法,其噪音比較多;因此隨機梯度下降法每次迭代時,並不都是向着整體最優化方向更新。但是大的整體方向是向着最優解的。

2.3 小批量梯度下降法

  當 mm 爲遠小於測試集中全部樣本數量並且大於 1 時,稱爲 小批量梯度下降法(mini-batch Gradient Descent,MBGD)。比如測試集中全部樣本數量爲 10000,mm 爲 20。在實際任務中,通常使用交叉驗證法來選取 mm

總結:當測試集中樣本數量比較少時,我們完全可以使用批量梯度下降法來迭代更新參數 ww。但是當樣本數量比較多時,我們可以選擇使用隨機梯度下降法或者小批量梯度下降法。

2.4 代碼實現

  下面是使用隨機梯度下降法擬合一個線性迴歸模型的代碼(Python 3.x):

import matplotlib.pyplot as plt
import numpy as np


class LinearRegression:
    """
    初始化線性迴歸模型,並初始化權值(weights)和偏置(bias)。

    feature_num:feature 數量
    """
    def __init__(self, feature_num):
        self.feature_num = feature_num
        self.weights = np.zeros(feature_num)

    def __str__(self):
        return 'Weights: %s\n' % self.weights

    """
    進行模型訓練

    features:訓練數據中的特徵值(格式:(x0, x1, ... xn))
    labels:訓練數據中的標籤值
    learning_rate:學習速率
    threshold_values:損失閾值
    """
    def train(self, features, labels, learning_rate, threshold_value):
        cost01 = 0.0
        # 迭代更新參數 weights
        while True:
            for i in range(len(features)):
                # 每次使用一個樣本更新參數 weights,即 SGD
                output = self.predict(features[i])
                delta = output - labels[i]
                # 梯度爲 delta * features[i][j]
                for j in range(self.feature_num):
                    self.weights[j] -= learning_rate * delta * features[i][j]

            # 計算損失
            cost02 = 0.0
            for i in range(len(features)):
                cost02 += self.predict(features[i])

            # 當前後兩次的損失小於設定的閾值時,停止訓練
            if abs(cost02 - cost01) < threshold_value:
                break
            else:
                cost01 = cost02

    """
    進行預測
    """
    def predict(self, feature):
        return np.dot(feature, self.weights)


def get_training_data():
    x = [(1., 1.), (1., 2.), (1., 4.5), (1., 5.), (1., 9.), (1., 11.)]
    y = [5, 6, 12, 14, 21.5, 25]
    return x, y


if __name__ == "__main__":
    linear_regression = LinearRegression(2)
    features, labels = get_training_data()
    linear_regression.train(features, labels, 0.01, 0.0001)
    print(linear_regression)

    # 繪圖
    xArr = [x[1] for x in features]
    yArr = [linear_regression.predict(x) for x in features]
    plt.plot(xArr, labels, 'bx', xArr, yArr, 'r')
    plt.show()

運行以上代碼後,將打印如下信息及如下圖形:

Weights: [2.97224167 1.99106495]

輸入

參考:
[1] https://zh.wikipedia.org/wiki/梯度下降法
[2] https://blog.csdn.net/cs24k1993/article/details/79120579
[3] https://blog.csdn.net/yhao2014/article/details/51554910

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