機器學習系列 05:Logistic 迴歸及 Python 實現

  本內容將介紹機器學習中的 Logistic 迴歸 及 Python 代碼實現,和 Softmax 迴歸

  Logistic 迴歸(logistic regression,也稱邏輯迴歸和對數機率迴歸)是一種經典的分類模型,屬於廣義的線性迴歸分析模型。雖然名稱中包含了“迴歸”,但是實際上它不是迴歸模型,而是分類模型。

一、Logistic 迴歸

  在閱讀本內容前,需要了解 線性迴歸模型 的基本概念。如果您還不瞭解,可以參閱 機器學習系列:線性迴歸模型

  在 機器學習系列:線性迴歸模型 中介紹瞭如何使用線性模型進行迴歸預測。但是否可以進行分類預測呢?

  考慮二分類任務,其輸出標記 y{0,1}y \in \{0,1\},線性迴歸模型產生的預測值 z=wTxz = \mathbf{w}^{T} \mathbf{x} 是實值,於是我們需要將實值 zz 轉換爲 {0,1}\{0,1\} 值。最理想的是“單位階躍函數”(unit-step function)

(1)y={0,z<00.5,z=01,z>0 y = \left \{ \begin{array}{cc} 0,\quad z < 0 \\ 0.5, \quad z = 0\\ 1, \quad z > 0 \end{array} \right. \tag{1}

即當 z>0z>0 時輸出 11(即正例),當 z<0z<0 是輸出 00(即反例),當 z=0z=0 時可輸出 0011,如圖-1所示。

圖-1 單位階躍函數與對數機率函數

  但從圖-1 可看出,單位階躍函數不連續。可以使用對數機率函數(logistic function)對其進行替換:

(2)g(z)=11+ez g(z) = \frac{1}{1+e^{-z}} \tag{2}

  從圖-1 可看出,對數機率函數是一種“sigmoid 函數”,它將 zz 值轉化爲一個接近 0011yy 值,並且其輸出值在 z=0z=0 附近變化很快。

  我們知道線性迴歸模型爲

(3)fw(x)=w0x0+w1x1++wnxn=j=0nwjxj=wTx f_{w}(x) = w_0x_0 + w_1x_1 + \cdots + w_nx_n = \sum_{j=0}^{n}w_jx_j = \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。將其代入式(2),得到

(4)fw(x)=11+ewTx f_{\mathbf{w}} (\mathbf{x}) = \frac{1}{1 + e^{-\mathbf w^{T} \mathbf{x}}} \tag{4}


二、損失函數

2.1 損失函數

  對於二分類問題,單個樣本的損失函數爲

(5)Cost(fw(x(i),y(i)))={log(fw(x)),y=1log(1fw(x)),y=0 Cost\left( f_{\bf w}(\mathbf{x}^{(i)},y^{(i)}) \right) = \left \{\begin{array}{cc} -log(f_{\bf w}(\mathbf x)),\quad y=1 \\ -log(1-f_{\bf w}(\mathbf{x})), \quad y=0 \end{array} \right. \tag{5}

等價於

(6)Cost(fw(x(i),y(i)))=ylog(fw(x))(1y)log(1fw(x)) Cost\left( f_{\bf w}(\mathbf x^{(i)},y^{(i)}) \right) = -ylog(f_{\bf w}(\mathbf x))-(1-y)log(1-f_{\bf w}(\mathbf x)) \tag{6}

其也稱作爲交叉熵代價函數

  對於訓練集所有樣本,其損失函數爲

(7)E(w)=1mi=1mCost(fw(x(i)),y(i)) E({\bf w}) = \frac{1}{m} \sum_{i=1}^{m} Cost\left(f_{\bf w}(\mathbf x^{(i)}),y^{(i)}\right) \tag{7}

  將式(6)代入式(7)得

(8)E(w)=1mi=1m(y(i)logfw(x(i))+(1y(i))log(1fw(x(i)))) E({\bf w}) = -\frac{1}{m} \sum_{i=1}^{m} \bigg(y^{(i)}logf_{\bf w}(\mathbf x^{(i)}) + (1-y^{(i)})log\Big(1-f_{\bf w}(\mathbf x^{(i)})\Big)\bigg) \tag{8}

  然後可以採用 梯度下降法(Gradient descent method) 或者 牛頓法(Newton method)求使 E(w)E({\bf w}) 取最小值的 w\mathbf{w}

  實際上,上面的 Cost(fw(x(i),y(i)))Cost\left( f_{\bf w}(\mathbf x^{(i)},y^{(i)}) \right)E(w)E({\bf w}) 是基於最大似然估計推導出來的,下面我們來了解一下具體過程。

2.2 最大似然估計

  fw(x)f_{\mathbf{w}} (\mathbf{x}) 函數的值表示樣本輸出爲 1 的概率,則樣本輸出爲 1 和 0 的概率分別爲

(9)P(y=1x;w)=fw(x) P(y=1|\mathbf{x};\mathbf{w}) = f_{\mathbf{w}} (\mathbf{x}) \tag{9}

(10)P(y=0x;w)=1fw(x) P(y=0|\mathbf{x};\mathbf{w}) = 1- f_{\mathbf{w}} (\mathbf{x}) \tag{10}

  將式(9)和式(10)可簡化爲

(11)P(yx;w)=(fw(x))y(1fw(x))(1y) P(y|\mathbf{x};\mathbf{w}) = (f_{\mathbf{w}}(\mathbf{x}))^{y} (1-f_{\mathbf{w}}(\mathbf{x}))^{(1-y)} \tag{11}

  則對應的似然函數爲

(12)L(w)=i=1mP(y(i)x(i);w) L(\mathbf{w}) = \prod_{i=1}^{m} P\left(y^{(i)}|\mathbf{x}^{(i)};\mathbf{w}\right) \tag{12}

(13)=i=1m(fw(x(i)))y(i)(1fw(x(i)))(1y(i)) = \prod_{i=1}^{m} \left(f_{\mathbf{w}}(\mathbf{x}^{(i)})\right)^{y^{(i)}} \left(1-f_{\mathbf{w}}(\mathbf{x}^{(i)})\right)^{(1-y^{(i)})} \tag{13}

  則對數似然函數爲

(14)l(w)=logL(w)=i=1m(y(i)logfw(x(i))+(1y(i))log(1fw(x(i)))) l(\mathbf{w}) = logL(\mathbf{w}) = \sum_{i=1}^{m} \bigg( y^{(i)}logf_{\mathbf{w}}(\mathbf{x}^{(i)})+\left(1-y^{(i)}\right)log\Big(1-f_{\mathbf{w}}(\mathbf{x}^{(i)})\Big)\bigg) \tag{14}

  最大似然估計就是要求得使 l(w)l(\mathbf{w}) 取最大值時的 w\mathbf{w},我們可以使用 梯度上升法 求得最優的 w\mathbf{w}

  對比式(8)和式(14),我們發現存在以下關係

(15)E(w)=1ml(w) E(\mathbf{w}) = -\frac{1}{m}l(\mathbf{w}) \tag{15}

  在求解 w\mathbf{w} 時,求解 l(w)l(\mathbf{w}) 的最大值或求解 E(w)E(\mathbf{w}) 的最小值,兩者實際上是一致的。

三、根據梯度下降法求解最優 w\mathbf{w}

  如果你還不瞭解梯度下降法,可以參閱 機器學習系列:梯度下降法及 Python 實現

  對 E(w)E(w) 中的每個 wjw_j 求偏導數(即梯度),得到(注意:這裏的 loglog 的底爲 ee

(16)E(wj)=E(w)wj=1mi=1m(fw(x(i))y(i)))xj(i) \nabla E(w_j) = \frac{\partial E(w)}{\partial w_j} = \frac{1}{m} \sum_{i=1}^{m} \left(f_{\mathbf{w}}(\mathbf{x}^{(i)}) - y^{(i)}) \right) x_j^{(i)} \tag{16}

E(wj)\nabla E(w_j) 的具體求解過程,在下面的 3.1 求解 E(wj)\nabla E(w_j) 的具體過程 會進行介紹。

  則 wjw_j 的迭代更新過程爲

(17)wj^=wjη1mi=1m(fw(x(i))y(i)))xj(i),j=0,1, ,n \hat{w_{j}} = w_{j} - \eta \frac{1}{m} \sum_{i=1}^{m} \left(f_{\mathbf{w}}(\mathbf{x}^{(i)}) - y^{(i)}) \right) x_j^{(i)},\quad j=0,1,\cdots,n \tag{17}

其中 η\eta 爲學習速率。

3.1 E(wj)\nabla E(w_j) 的具體求解過程

  如果你對 E(wj)\nabla E(w_j) 的具體求解過程很瞭解或者不感興趣,可以直接跳過這一部分。

  因爲 E(wj)\nabla E(w_j) 的求解需要用到 wjfw(x)\frac{\partial}{\partial w_j} f_{\mathbf{w}}(\mathbf{x})。先來看一下 wjfw(x)\frac{\partial}{\partial w_j} f_{\mathbf{w}}(\mathbf{x}) 的求解過程:

wjfw(x)=wj(11+ewTx) \frac{\partial}{\partial w_j} f_{\mathbf{w}}(\mathbf{x}) = \frac{\partial}{\partial w_j} \left(\frac{1}{1 + e^{-\mathbf w^{T} \mathbf{x}}}\right)

=1(1+ewTx)2wj(1+ewTx) = -\frac{1}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} \frac{\partial }{\partial w_j} (1 + e^{-\mathbf{w}^T \mathbf{x}})

=1(1+ewTx)2wj(ewTx) = -\frac{1}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} \frac{\partial }{\partial w_j} (e^{-\mathbf{w}^T \mathbf{x}})

=ewTx(1+ewTx)2wj(wTx) = -\frac{e^{-\mathbf{w}^T \mathbf{x}}}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} \frac{\partial }{\partial w_j} (-\mathbf{w}^T \mathbf{x})

=ewTx(1+ewTx)2wj(w0x0+w1x1++wnxn) = \frac{e^{-\mathbf{w}^T \mathbf{x}}}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} \frac{\partial }{\partial w_j} (w_0x_0 + w_1x_1 + \cdots + w_nx_n)

=ewTx(1+ewTx)2xj = \frac{e^{-\mathbf{w}^T \mathbf{x}}}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} x_j

=1(1+ewTx)ewTx(1+ewTx)xj = \frac{1}{(1 + e^{-\mathbf{w}^T \mathbf{x}})} \frac{e^{-\mathbf{w}^T \mathbf{x}}}{(1 + e^{-\mathbf{w}^T \mathbf{x}})} x_j

=fw(x)(1fw(x))xj = f_{\mathbf{w}}(\mathbf{x}) (1- f_{\mathbf{w}}(\mathbf{x})) x_j

  E(wj)\nabla E(w_j) 的求解過程:
E(wj)=E(w)wj \nabla E(w_j) = \frac{\partial E(w)}{\partial w_j}

=wj(1mi=1m(y(i)logfw(x(i))+(1y(i))log(1fw(x(i))))) = \frac{\partial}{\partial w_j} \Bigg(-\frac{1}{m} \sum_{i=1}^{m} \bigg(y^{(i)}logf_{\bf w}(\mathbf x^{(i)}) + (1-y^{(i)})log\Big(1-f_{\bf w}(\mathbf x^{(i)})\Big)\bigg)\Bigg)

=1mi=1m(y(i)wjlogfw(x(i))+(1y(i))wjlog(1fw(x(i)))) = -\frac{1}{m} \sum_{i=1}^{m} \left(y^{(i)}\frac{\partial}{\partial w_j} logf_{\bf w}(\mathbf x^{(i)}) + \left(1-y^{(i)}\right) \frac{\partial}{\partial w_j} log\left(1-f_{\bf w}(\mathbf x^{(i)})\right)\right)

=1mi=1m(y(i)1fw(x(i))1y(i)1fw(x(i)))wjfw(x(i)) = -\frac{1}{m} \sum_{i=1}^{m} \left( y^{(i)}\frac{1}{f_{\mathbf{w}}(\mathbf{x}^{(i)})} - \frac{1-y^{(i)}}{1-f_{\bf w}(\mathbf x^{(i)})} \right)\frac{\partial}{\partial w_j}f_{\mathbf{w}}(\mathbf{x}^{(i)})

=1mi=1m(y(i)(1fw(x(i)))(1y(i))fw(x(i)))xj(i) = -\frac{1}{m} \sum_{i=1}^{m} \left( y^{(i)} \left(1-f_{\bf w}(\mathbf x^{(i)})\right) - \left(1-y^{(i)}\right)f_{\mathbf{w}}(\mathbf{x}^{(i)}) \right) x_j^{(i)}

=1mi=1m(fw(x(i))y(i)))xj(i) = \frac{1}{m} \sum_{i=1}^{m} \left(f_{\mathbf{w}}(\mathbf{x}^{(i)}) - y^{(i)}) \right) x_j^{(i)}


四、Python 代碼實現

  下面使用批量梯度下降法擬合一個 Logistic 迴歸模型。代碼如下(Python 3.x):

import numpy as np
import matplotlib.pyplot as plt


class LogisticRegression:
    def __init__(self):
        self.weights = None
        pass

    def __str__(self):
        return 'weights: {}'.format(self.weights)

    def _sigmoid(self, inx):
        """
        計算公式:1/(1 + exp(-inx))
        """
        return 1.0/(1+np.exp(-inx))

    def train(self, input_data, label_data, learning_rate, iteration):
        """
        進行模型訓練

        :param input_data: 訓練數據,特徵值
        :param label_data: 訓練數據,標籤值
        :param learning_rate: 熟悉速率
        :param iteration: 迭代次數
        """
        # 使用 np.mat() 將 list 數據變更爲 matrix,函數 transpose() 進行轉置操作
        input_data_mat = np.mat(input_data)
        label_data_mat = np.mat(label_data).transpose()
        m, n = np.shape(input_data_mat)
        # 初始化 weights 爲 1
        self.weights = np.ones((n, 1))
        # 使用批量梯度下降法進行訓練
        for i in range(iteration):
            # 計算預測輸出
            h = self._sigmoid(input_data_mat * self.weights)
            # 計算損失
            error = h - label_data_mat
            # 更新權值(閱讀時,需要對矩陣操作有一定了解)
            self.weights -= (learning_rate * input_data_mat.transpose() * error)

    def get_weights(self):
        return self.weights


def load_data_set(file_name):
    """
    從文件中獲取數據集

    :param file_name: 文件名
    :return: 返回從文件中獲取的數據集
            input_data 存儲特徵值,label_data 存儲標籤值
    """
    input_data, label_data = [], []
    fr = open(file_name)
    for line in fr.readlines():
        cur_line = line.strip().split()
        # 在每列數據的第一列添加 1.0,供計算偏置 b 時使用
        input_data.append([1.0, float(cur_line[0]), float(cur_line[1])])
        label_data.append(int(cur_line[2]))
    return input_data, label_data


def plot_best_fit(input_data, label_data, weights):
    input_data_arr = np.array(input_data)
    x_cord_01, y_cord_01, x_cord_02, y_cord_02 = [], [], [], []
    for i in range(len(input_data)):
        if label_data[i] == 1:
            x_cord_01.append(input_data_arr[i][1])
            y_cord_01.append(input_data_arr[i][2])
        else:
            x_cord_02.append(input_data_arr[i][1])
            y_cord_02.append(input_data_arr[i][2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(x_cord_01, y_cord_01, s=30, c='red', marker='s')
    ax.scatter(x_cord_02, y_cord_02, s=30, c='green')
    x = np.arange(-3.0, 3.0, 0.1)
    y = (-weights[0] - weights[1]*x)/weights[2]
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()


def test_logistic_regression():
    # 測試 Logistic regression model,並且繪製出擬合圖形
    input_data, label_data = load_data_set('testSet.txt')
    logistic_regression = LogisticRegression()
    logistic_regression.train(input_data, label_data, 0.001, 500)
    print(logistic_regression)
    plot_best_fit(input_data, label_data, logistic_regression.get_weights())


if __name__ == "__main__":
    test_logistic_regression()

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

weights: [[ 4.12414349]
 [ 0.48007329]
 [-0.6168482 ]]

五、多分類

  上面主要介紹了 Logistic 迴歸用於解決二分類問題。實際上,可以對 Logistic 迴歸進行擴展,用於解決多分類問題。下面將介紹兩種方法。

5.1 多個 Logistic 迴歸

  將多分類任務拆分爲若干個二分類任務進行求解。具體來說,先對問題進行拆分,然後爲拆出的每個二分類任務訓練一個分類器;在預測時,對這些分類器的預測結果進行集成以獲得最終的多分類結果。最常用的拆分策略爲:“一對一”(One vs One)、“一對其餘”(One vs Rest)和“多對多”(Many vs Many)。

  假設數據集有 kk 個類別,“一對一”將爲任意兩個類別訓練一個分類器,將存在 k(k1)k(k-1) 個分類器。在預測時,將得到 k(k1)k(k-1) 個分類結果,然後通過投票得到最終的分類結果(即預測最多的類別作爲最終的結果)。

  “一對其餘”將一個類別作爲正例,其他類別作爲反例,將存在 kk 個分類器。在預測時,根據這 kk 個分類器可以得到每個類別的概率,最後我們選擇概率值最大的類別作爲最終的分類結果。

5.2 多項 Logistic 迴歸

  多項 Logistic 迴歸 也稱爲 Softmax 迴歸

  假設離散型隨機變量 yy 的取值集合是 {1,2, ,k}\{1,2,\cdots,k\},那麼樣本輸出爲 ll 概率爲

(18)P(y=lx)=ewlxl=1kewlx,l=1,2, ,k P(y=l|x) = \frac{e^{\mathbf{w}_l \mathbf{x}}}{\sum_{l=1}^{k} e^{\mathbf{w}_l\mathbf{x}}}, \quad l=1,2,\cdots,k \tag{18}

  參照二分類,可知損失函數爲

(19)E(w)=1mi=1mj=1k1{y(i)=j}logewlx(i)l=1kewlx(i) E(\mathbf{w}) = -\frac{1}{m} \sum_{i=1}^{m} \sum_{j=1}^{k} 1\{y^{(i)}=j\}log\frac{e^{\mathbf{w}_l \mathbf{x}^{(i)}}}{\sum_{l=1}^{k} e^{\mathbf{w}_l\mathbf{x}^{(i)}}} \tag{19}

其中,mm 表示樣本個數,kk 表示類別的個數;1{y(i)=j}1\{y^{(i)} = j\} 函數表示:當 y(i)=jy^{(i)} = j 時,函數值爲 1,否則爲 0。

  然後參照上面的梯度下降法求出各個最優 wl\mathbf{w}_{l}。這裏就不再詳細介紹求解過程了。

5.3 選擇原則

  解決多分類問題時,選擇上面介紹的兩種方法的具體原則:

  • 如果各類別之間是互斥的,適合選擇使用 softmax 迴歸分類器;

  • 如果各類別之間不完全互斥,適合選擇使用多個 Logistic 迴歸分類器。


參考:
[1] 周志華《機器學習》
[2] 李航《統計學習方法》
[3] 《機器學習實戰》
[4] https://www.cnblogs.com/alfred2017/p/6627824.html
[5] https://blog.csdn.net/u011734144/article/details/79717470

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