机器学习系列 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

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