機器學習系列 03:線性迴歸模型

  本內容將介紹機器學習中的 線性迴歸模型,及 Python 代碼實現。

  線性模型形式簡單、易於建模,但卻蘊涵這機器學習中一些重要的基本思想。許多功能更爲強大的非線性模型(nonlinear model)可在線性模型的基礎上通過引入層級結構或高維映射而得。

  線性迴歸意味着可以將輸入項分別乘以一些常量,再將結果加起來得到輸出。迴歸需要數值型數據,標稱型數據將被轉化成二值型數據。

一、線性迴歸模型

  給定一個數據集 D={(x(1),y(1)),(x(2),y(2)), ,(x(m),y(m))}D=\{(\mathbf{x}^{(1)},y^{(1)}),(\mathbf{x}^{(2)},y^{(2)}),\cdots,(\mathbf{x}^{(m)},y^{(m)})\},其中 x(i)=(x1(i);x2(i); ;xn(i))\mathbf{x}^{(i)}=(x_{1}^{(i)};x_{2}^{(i)};\cdots;x_{n}^{(i)})(表示 x\mathbf{x} 擁有 nn 個屬性),yiRy_i\in\mathbb{R}。線性迴歸(linear regression)試圖學得一個通過特徵值的線性組合以儘可能準確地預測輸出標記。學得的模型如下:

(1)f(x)=w1x1+w2x2++wnxn+b f(x)=w_1x_1+w_2x_2+\cdots+w_nx_n+b \tag{1}

或者

(2)f(x)=i=0nwixi f(x)=\sum_{i=0}^{n}w_ix_i \tag{2}

其中 x0=1x_0=1w0=bw_0=b。用向量形式寫成

(3)f(x)=wTx f(\mathbf{x})=\mathbf{w}^T\mathbf x \tag{3}

其中 w=(w0;w1; ;wn)\mathbf{w}=(w_0;w_1;\cdots;w_n)x=(x0;x1; ;xn)\mathbf{x}=(x_0;x_1;\cdots;x_n)x0=1x_0=1

  當 w\mathbf{w}(即w0,w1, ,wnw_0,w_1,\cdots,w_n) 學得之後,模型就得以確定。但是如何才能求得 w\mathbf{w} 呢?

  爲了便於理解,我們先考慮最簡單的情況:即輸入 xx 只有一個屬性。那麼線性迴歸試圖學得的模型如下:
(4)f(x)=wx+b f(x)=wx+b \tag{4}

對離散屬性,若屬性值間存在“序”關係,可以通過連續化將其轉化爲連續值;若屬性值間不存在序關係,則通常轉爲爲 kk 維向量。

  如何確定 wwbb 呢?一個常用的方法:找出使損失函數最小的 wwbb。下面我們就來介紹一下線性迴歸模型的損失函數。

二、損失函數

  線性迴歸模型最常用的損失函數是平方損失函數,即

(5)E(w,b)=i=1m12m(f(x(i))y(i))2=i=1m12m(wx(i)+by(i))2 E(w,b) = \sum_{i=1}^{m}\frac{1}{2m} (f(x^{(i)})-y^{(i)})^{2} = \sum_{i=1}^{m}\frac{1}{2m} (wx^{(i)}+b-y^{(i)})^2 \tag{5}

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

  爲了求得的線性迴歸模型更好地擬合數據集,我們需要試圖讓平方損失函數最小化,即

(6)minw,bi=1m12m(f(x(i))y(i))2=minw,bi=1m12m(wx(i)+by(i))2 min_{w,b} \sum_{i=1}^{m}\frac{1}{2m} (f(x^{(i)})-y^{(i)})^2 = min_{w,b} \sum_{i=1}^{m}\frac{1}{2m} (wx^{(i)}+b-y^{(i)})^2 \tag{6}


三、最小二乘法

  基於均方誤差最小化來進行模型求解的方法稱爲“最小二乘法”(least square method)。在線性迴歸中,最小二乘法是試圖找到一條直線,使所有樣本到直線上的歐式距離之和最小。

  求解 wwbb 使 E(w,b)E(w,b) 最小化的過程,稱爲線性迴歸模型的最小二乘“參數估計”(parameter estimation)。我們可將 E(w,b)E(w,b) 分別對 wwbb 求導,得到
(7)E(w,b)w=1m(i=1m(wx(i)+by(i))x(i))=1m(wi=1m(x(i))2i=1m(y(i)b)x(i)) \frac{\partial E(w,b)}{\partial w} = \frac{1}{m} \Big(\sum_{i=1}^{m}(wx^{(i)}+b-y^{(i)})x^{(i)}\Big)= \frac{1}{m} \Big(w\sum_{i=1}^{m}(x^{(i)})^2-\sum_{i=1}^m(y^{(i)}-b)x^{(i)}\Big) \tag{7}

(8)E(w,b)b=1m(i=1m(wx(i)+by(i)))=1m(mbi=1m(y(i)wx(i))) \frac{\partial E(w,b)}{\partial b}= \frac{1}{m} \Big(\sum_{i=1}^{m}(wx^{(i)}+b-y^{(i)})\Big) = \frac{1}{m} \Big(mb-\sum_{i=1}^{m}(y^{(i)}-wx^{(i)})\Big) \tag{8}

然後令上面式(7)和式(8)求得的兩個偏導數爲零,可求得 wwbb 最優解的閉式解

(9)w=i=1my(i)(x(i)xˉ)i=1m(x(i))21m(i=1mx(i))2 w = \frac{\sum_{i=1}^{m}y^{(i)}(x^{(i)}-\bar x)}{\sum_{i=1}^{m}(x^{(i)})^2-\frac{1}{m}\Big(\sum_{i=1}^{m}x^{(i)}\Big)^2} \tag{9}

(10)b=1mi=1m(y(i)wx(i)) b=\frac{1}{m}\sum_{i=1}^{m}(y^{(i)}-wx^{(i)}) \tag{10}

  上面我們只討論了只有一種屬性的情況,即單變量線性迴歸(也稱一元線性迴歸)。更一般的情況,輸入 xxnn 個屬性。此時試圖學得

(11)f(x)=wTx f(\mathbf{x})=\mathbf{w}^T\mathbf{x} \tag{11}

這稱爲多變量線性迴歸(也稱多元線性迴歸)。

  同樣,可利用最小二乘法來對 w\mathbf{w} 進行估計。把數據集 DD 表示爲一個 m×(n+1)m\times (n+1) 大小的矩陣 X\mathbf{X},即
(12)X=[x1(1)x2(1)xn(1)1x1(2)x2(2)xn(2)1x1(m)x2(m)xn(m)1]=[(x(1))T1(x(2))T1(x(m))T1] \mathbf{X} = \begin{bmatrix} x_1^{(1)} & x_2^{(1)} & \cdots & x_n^{(1)} & 1\\ x_1^{(2)} & x_2^{(2)} & \cdots & x_n^{(2)} & 1\\ \vdots & \vdots & \ddots & \vdots & \vdots \\ x_1^{(m)} & x_2^{(m)} & \cdots & x_n^{(m)} & 1 \\ \end{bmatrix} = \begin{bmatrix} (x^{(1)})^{T} & 1\\ (x^{(2)})^{T} & 1\\ \vdots & \vdots \\ (x^{(m)})^{T} & 1 \\ \end{bmatrix} \tag{12}

再把所有標記也寫成向量形式 y=(y1;y2; ,ym)\mathbf{y}=(y_1;y_2;\cdots,y_m),則有

(13)E(w)=12m(yXw)T(yXw) E(\mathbf{w}) = \frac{1}{2m} (\mathbf{y}-\mathbf{X}\mathbf{w})^{T}(\mathbf{y}-\mathbf{X}\mathbf{w}) \tag{13}

E(w)E(\mathbf{w})w\mathbf{w} 求導得到

(14)E(w)w=1mXT(Xwy) \frac{\partial E(\mathbf{w})}{\partial \mathbf{w}} = \frac{1}{m} \mathbf{X}^{T}(\mathbf{X}\mathbf{w}-\mathbf{y}) \tag{14}

令上式爲零,可求得 w\mathbf{w} 的解

(15)w=(XTX)1XTy \mathbf{w} = (\mathbf{X}^{T}\mathbf{X})^{-1}\mathbf{X}^{T}\mathbf{y} \tag{15}

  注意,上述公式中包含 (XTX)1(\mathbf{X}^{T}\mathbf{X})^{-1},也就是需要對矩陣求逆,因此這個方程只在逆矩陣存在的時候適用。然而,在現實任務中 XTX\mathbf{X}^{T}\mathbf{X} 往往不是滿秩矩陣,即不可逆。在這種情況下,我們可以使用梯度下降法求得 w\mathbf{w}

四、梯度下降法

4.1 算法介紹

  梯度下降法(Gradient descent)是一個一階最優化算法,通常也稱爲最速下降法。要使用梯度下降法找一個一個函數的局部極小值,必須向函數上當前點對應梯度的反方向的規定步長距離點進行迭代搜索。

  首先,我們爲 w\mathbf{w} 初始化一個隨機值,然後每次向 E(w)E(\mathbf{w}) 的梯度的相反方向來修改 w\mathbf{w}。經過數次迭代後最終達到函數最小值點。那麼,梯度下降法的公式

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

其中 η\eta 是學習速率,\nabla 是梯度算子,這裏的 E(w)\nabla E(\mathbf{w}) 的值實際上就是上面的式(14),可得到 w\mathbf{w} 的更新公式

(17)w^=wη1mXT(Xwy)=w+η1mXT(yXw) \mathbf{\hat w} = \mathbf{w}-\eta \frac{1}{m} \mathbf{X}^{T}(\mathbf{X}\mathbf{w}-\mathbf{y}) = \mathbf{w} + \eta \frac{1}{m} \mathbf{X}^{T}(\mathbf{y} - \mathbf{X}\mathbf{w}) \tag{17}

  在上面的公式中,輸入和輸出分別是以矩陣和向量的方式表式的,看起來可能有些不好理解。下面我們換一種方式來表示
(18)E(w)=i=1m12m(f(x(i))y(i))2=i=1m12m(wTx(i)y(i))2 E(\mathbf{w}) = \sum_{i=1}^{m} \frac{1}{2m} (f(x^{(i)})-y^{(i)})^2 = \sum_{i=1}^{m}\frac{1}{2m} (\mathbf{w}^T\mathbf{x}^{(i)} - y^{(i)})^2 \tag{18}
E(w)E(\mathbf{w}) 求導得到
(19)E(w)=1mi=1m(f(x(i))y(i))x(i) \nabla E(\mathbf{w}) = \frac{1}{m} \sum_{i=1}^{m}(f(x^{(i)}) - y^{(i)})\mathbf{x}^{(i)} \tag{19}
可得到 w\mathbf{w} 的更新公式
(20)w^=wη1mi=1m(f(x(i))y(i))x(i)=w+η1mi=1m(y(i)f(x(i))x(i) \mathbf{\hat w} = \mathbf{w} - \eta \frac{1}{m} \sum_{i=1}^{m}(f(x^{(i)}) - y^{(i)})\mathbf{x}^{(i)} = \mathbf{w} + \eta \frac{1}{m} \sum_{i=1}^{m}(y^{(i)} - f(x^{(i)})) \mathbf{x}^{(i)} \tag{20}
  關於梯度下降法詳細的介紹,可以參閱這裏

4.2 代碼實現

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

import numpy as np
import matplotlib.pyplot as plt


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

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

    def __str__(self):
        return 'weights: %s, bias = %f\n' % (self.weights, self.bias)

    def _process_data(self, datas):
        x_arr = np.array([x[0] for x in datas])
        y_arr = np.array([x[1] for x in datas])
        return x_arr, y_arr

    """
    更新權值
    """
    def _update_weights(self, feature, output, label, learning_rate):
        delta = label - output
        self.weights += learning_rate * delta * feature
        self.bias += learning_rate * delta

    def _one_iteration(self, features, labels, learning_rate):
        samples = zip(features, labels)
        for (feature, label) in samples:
            output = self.predict(feature)
            # 使用隨機梯度下降法更新權值,即每次只使用一個樣本
            self._update_weights(feature, output, label, learning_rate)

    """
    進行模型訓練
    
    train_datas:訓練數據,輸入格式[[輸入], 輸出]
    iteration:訓練迭代次數
    learning_rate:學習速率
    """
    def train(self, train_datas, iteration, learning_rate=0.01):
        # 對訓練數據進行處理
        features, labels = self._process_data(train_datas)

        # 進行 iteration 次迭代訓練
        for i in range(iteration):
            self._one_iteration(features, labels, learning_rate)

    """
    進行預測
    """
    def predict(self, feature):
        return sum(feature* self.weights) + self.bias


def get_training_datas():
    datas = [[[1], 5], [[2], 6], [[5], 14], [[4.5], 12], [[9], 21.5], [[11], 25]]
    return datas


if __name__ == "__main__":
    # 獲取訓練數據
    datas = get_training_datas()

    # 訓練獲取線性迴歸模型
    linear_regression = LinearRegression(1)
    linear_regression.train(datas, 100, 0.01)
    print(linear_regression)

    # 繪圖
    xArr = np.array([x[0] for x in datas])
    yArr = np.array([x[1] for x in datas])
    yArr_output = np.array([linear_regression.predict(x) for x in xArr])
    plt.plot(xArr, yArr, 'bx', xArr, yArr_output, 'r')
    plt.show()

運行以上代碼,將繪製出如下圖形:
在這裏插入圖片描述

參考:
[1] 周志華《機器學習》
[2] 《機器學習實戰》
[3] https://www.zybuluo.com/hanbingtao/note/448086

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