本内容将介绍机器学习中的 Logistic 回归 及 Python 代码实现,和 Softmax 回归。
Logistic 回归(logistic regression,也称逻辑回归和对数几率回归)是一种经典的分类模型,属于广义的线性回归分析模型。虽然名称中包含了“回归”,但是实际上它不是回归模型,而是分类模型。
在阅读本内容前,需要了解 线性回归模型 的基本概念。如果您还不了解,可以参阅 机器学习系列:线性回归模型。
在 机器学习系列:线性回归模型 中介绍了如何使用线性模型进行回归预测。但是否可以进行分类预测呢?
考虑二分类任务,其输出标记 y ∈ { 0 , 1 } y \in \{0,1\} y∈{0,1},线性回归模型产生的预测值 z = w T x z = \mathbf{w}^{T} \mathbf{x} z=wTx 是实值,于是我们需要将实值 z z z 转换为 { 0 , 1 } \{0,1\} {0,1} 值。最理想的是“单位阶跃函数”(unit-step function)
(1) y = { 0 , z < 0 0.5 , z = 0 1 , 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} y=⎩⎨⎧0,z<00.5,z=01,z>0(1)
即当 z > 0 z>0 z>0 时输出 1 1 1(即正例),当 z < 0 z<0 z<0 是输出 0 0 0(即反例),当 z = 0 z=0 z=0 时可输出 0 0 0 或 1 1 1,如图-1所示。
但从图-1 可看出,单位阶跃函数不连续。可以使用对数几率函数(logistic function)对其进行替换:
(2) g ( z ) = 1 1 + e − z g(z) = \frac{1}{1+e^{-z}} \tag{2} g(z)=1+e−z1(2)
从图-1 可看出,对数几率函数是一种“sigmoid 函数”,它将 z z z 值转化为一个接近 0 0 0 或 1 1 1 的 y y y 值,并且其输出值在 z = 0 z=0 z=0 附近变化很快。
我们知道线性回归模型为
(3) f w ( x ) = w 0 x 0 + w 1 x 1 + ⋯ + w n x n = ∑ j = 0 n w j x j = w T x 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} fw(x)=w0x0+w1x1+⋯+wnxn=j=0∑nwjxj=wTx(3)
其中 w = ( w 0 ; w 1 ; ⋯   ; w n ) \mathbf{w}=(w_0;w_1;\cdots;w_n) w=(w0;w1;⋯;wn), x = ( x 0 ; x 1 ; ⋯   ; x n ) \mathbf{x}=(x_0;x_1;\cdots;x_n) x=(x0;x1;⋯;xn), x 0 = 1 x_0=1 x0=1。将其代入式(2),得到
(4) f w ( x ) = 1 1 + e − w T x f_{\mathbf{w}} (\mathbf{x}) = \frac{1}{1 + e^{-\mathbf w^{T} \mathbf{x}}} \tag{4} fw(x)=1+e−wTx1(4)
对于二分类问题,单个样本的损失函数为
(5) C o s t ( f w ( x ( i ) , y ( i ) ) ) = { − l o g ( f w ( x ) ) , y = 1 − l o g ( 1 − f w ( 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} Cost(fw(x(i),y(i)))={−log(fw(x)),y=1−log(1−fw(x)),y=0(5)
等价于
(6) C o s t ( f w ( x ( i ) , y ( i ) ) ) = − y l o g ( f w ( x ) ) − ( 1 − y ) l o g ( 1 − f w ( 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} Cost(fw(x(i),y(i)))=−ylog(fw(x))−(1−y)log(1−fw(x))(6)
其也称作为交叉熵代价函数。
对于训练集所有样本,其损失函数为
(7) E ( w ) = 1 m ∑ i = 1 m C o s t ( f w ( 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} E(w)=m1i=1∑mCost(fw(x(i)),y(i))(7)
将式(6)代入式(7)得
(8) E ( w ) = − 1 m ∑ i = 1 m ( y ( i ) l o g f w ( x ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − f w ( 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} E(w)=−m1i=1∑m(y(i)logfw(x(i))+(1−y(i))log(1−fw(x(i))))(8)
然后可以采用 梯度下降法(Gradient descent method) 或者 牛顿法(Newton method)求使 E ( w ) E({\bf w}) E(w) 取最小值的 w \mathbf{w} w 。
实际上,上面的 C o s t ( f w ( x ( i ) , y ( i ) ) ) Cost\left( f_{\bf w}(\mathbf x^{(i)},y^{(i)}) \right) Cost(fw(x(i),y(i))) 和 E ( w ) E({\bf w}) E(w) 是基于最大似然估计推导出来的,下面我们来了解一下具体过程。
f w ( x ) f_{\mathbf{w}} (\mathbf{x}) fw(x) 函数的值表示样本输出为 1 的概率,则样本输出为 1 和 0 的概率分别为
(9) P ( y = 1 ∣ x ; w ) = f w ( x ) P(y=1|\mathbf{x};\mathbf{w}) = f_{\mathbf{w}} (\mathbf{x}) \tag{9} P(y=1∣x;w)=fw(x)(9)
(10) P ( y = 0 ∣ x ; w ) = 1 − f w ( x ) P(y=0|\mathbf{x};\mathbf{w}) = 1- f_{\mathbf{w}} (\mathbf{x}) \tag{10} P(y=0∣x;w)=1−fw(x)(10)
将式(9)和式(10)可简化为
(11) P ( y ∣ x ; w ) = ( f w ( x ) ) y ( 1 − f w ( x ) ) ( 1 − y ) P(y|\mathbf{x};\mathbf{w}) = (f_{\mathbf{w}}(\mathbf{x}))^{y} (1-f_{\mathbf{w}}(\mathbf{x}))^{(1-y)} \tag{11} P(y∣x;w)=(fw(x))y(1−fw(x))(1−y)(11)
则对应的似然函数为
(12) L ( w ) = ∏ i = 1 m P ( y ( i ) ∣ x ( i ) ; w ) L(\mathbf{w}) = \prod_{i=1}^{m} P\left(y^{(i)}|\mathbf{x}^{(i)};\mathbf{w}\right) \tag{12} L(w)=i=1∏mP(y(i)∣x(i);w)(12)
(13) = ∏ i = 1 m ( f w ( x ( i ) ) ) y ( i ) ( 1 − f w ( x ( i ) ) ) ( 1 − y ( 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} =i=1∏m(fw(x(i)))y(i)(1−fw(x(i)))(1−y(i))(13)
则对数似然函数为
(14) l ( w ) = l o g L ( w ) = ∑ i = 1 m ( y ( i ) l o g f w ( x ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − f w ( 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)=logL(w)=i=1∑m(y(i)logfw(x(i))+(1−y(i))log(1−fw(x(i))))(14)
最大似然估计就是要求得使 l ( w ) l(\mathbf{w}) l(w) 取最大值时的 w \mathbf{w} w,我们可以使用 梯度上升法 求得最优的 w \mathbf{w} w。
对比式(8)和式(14),我们发现存在以下关系
(15) E ( w ) = − 1 m l ( w ) E(\mathbf{w}) = -\frac{1}{m}l(\mathbf{w}) \tag{15} E(w)=−m1l(w)(15)
在求解 w \mathbf{w} w 时,求解 l ( w ) l(\mathbf{w}) l(w) 的最大值或求解 E ( w ) E(\mathbf{w}) E(w) 的最小值,两者实际上是一致的。
如果你还不了解梯度下降法,可以参阅 机器学习系列:梯度下降法及 Python 实现。
对 E ( w ) E(w) E(w) 中的每个 w j w_j wj 求偏导数(即梯度),得到(注意:这里的 l o g log log 的底为 e e e)
(16) ∇ E ( w j ) = ∂ E ( w ) ∂ w j = 1 m ∑ i = 1 m ( f w ( x ( i ) ) − y ( i ) ) ) x j ( 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)=∂wj∂E(w)=m1i=1∑m(fw(x(i))−y(i)))xj(i)(16)
∇ E ( w j ) \nabla E(w_j) ∇E(wj) 的具体求解过程,在下面的 3.1 求解 ∇ E ( w j ) \nabla E(w_j) ∇E(wj) 的具体过程 会进行介绍。
则 w j w_j wj 的迭代更新过程为
(17) w j ^ = w j − η 1 m ∑ i = 1 m ( f w ( x ( i ) ) − y ( i ) ) ) x j ( 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} wj^=wj−ηm1i=1∑m(fw(x(i))−y(i)))xj(i),j=0,1,⋯,n(17)
其中 η \eta η 为学习速率。
如果你对 ∇ E ( w j ) \nabla E(w_j) ∇E(wj) 的具体求解过程很了解或者不感兴趣,可以直接跳过这一部分。
因为 ∇ E ( w j ) \nabla E(w_j) ∇E(wj) 的求解需要用到 ∂ ∂ w j f w ( x ) \frac{\partial}{\partial w_j} f_{\mathbf{w}}(\mathbf{x}) ∂wj∂fw(x)。先来看一下 ∂ ∂ w j f w ( x ) \frac{\partial}{\partial w_j} f_{\mathbf{w}}(\mathbf{x}) ∂wj∂fw(x) 的求解过程:
∂ ∂ w j f w ( x ) = ∂ ∂ w j ( 1 1 + e − w T x ) \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) ∂wj∂fw(x)=∂wj∂(1+e−wTx1)
= − 1 ( 1 + e − w T x ) 2 ∂ ∂ w j ( 1 + e − w T x ) = -\frac{1}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} \frac{\partial }{\partial w_j} (1 + e^{-\mathbf{w}^T \mathbf{x}}) =−(1+e−wTx)21∂wj∂(1+e−wTx)
= − 1 ( 1 + e − w T x ) 2 ∂ ∂ w j ( e − w T x ) = -\frac{1}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} \frac{\partial }{\partial w_j} (e^{-\mathbf{w}^T \mathbf{x}}) =−(1+e−wTx)21∂wj∂(e−wTx)
= − e − w T x ( 1 + e − w T x ) 2 ∂ ∂ w j ( − w T x ) = -\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}) =−(1+e−wTx)2e−wTx∂wj∂(−wTx)
= e − w T x ( 1 + e − w T x ) 2 ∂ ∂ w j ( w 0 x 0 + w 1 x 1 + ⋯ + w n x n ) = \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) =(1+e−wTx)2e−wTx∂wj∂(w0x0+w1x1+⋯+wnxn)
= e − w T x ( 1 + e − w T x ) 2 x j = \frac{e^{-\mathbf{w}^T \mathbf{x}}}{(1 + e^{-\mathbf{w}^T \mathbf{x}})^{2}} x_j =(1+e−wTx)2e−wTxxj
= 1 ( 1 + e − w T x ) e − w T x ( 1 + e − w T x ) x j = \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 =(1+e−wTx)1(1+e−wTx)e−wTxxj
= f w ( x ) ( 1 − f w ( x ) ) x j = f_{\mathbf{w}}(\mathbf{x}) (1- f_{\mathbf{w}}(\mathbf{x})) x_j =fw(x)(1−fw(x))xj
∇ E ( w j ) \nabla E(w_j) ∇E(wj) 的求解过程:
∇ E ( w j ) = ∂ E ( w ) ∂ w j \nabla E(w_j) = \frac{\partial E(w)}{\partial w_j} ∇E(wj)=∂wj∂E(w)
= ∂ ∂ w j ( − 1 m ∑ i = 1 m ( y ( i ) l o g f w ( x ( i ) ) + ( 1 − y ( i ) ) l o g ( 1 − f w ( 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) =∂wj∂(−m1i=1∑m(y(i)logfw(x(i))+(1−y(i))log(1−fw(x(i)))))
= − 1 m ∑ i = 1 m ( y ( i ) ∂ ∂ w j l o g f w ( x ( i ) ) + ( 1 − y ( i ) ) ∂ ∂ w j l o g ( 1 − f w ( 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) =−m1i=1∑m(y(i)∂wj∂logfw(x(i))+(1−y(i))∂wj∂log(1−fw(x(i))))
= − 1 m ∑ i = 1 m ( y ( i ) 1 f w ( x ( i ) ) − 1 − y ( i ) 1 − f w ( x ( i ) ) ) ∂ ∂ w j f w ( 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)}) =−m1i=1∑m(y(i)fw(x(i))1−1−fw(x(i))1−y(i))∂wj∂fw(x(i))
= − 1 m ∑ i = 1 m ( y ( i ) ( 1 − f w ( x ( i ) ) ) − ( 1 − y ( i ) ) f w ( x ( i ) ) ) x j ( 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)} =−m1i=1∑m(y(i)(1−fw(x(i)))−(1−y(i))fw(x(i)))xj(i)
= 1 m ∑ i = 1 m ( f w ( x ( i ) ) − y ( i ) ) ) x j ( i ) = \frac{1}{m} \sum_{i=1}^{m} \left(f_{\mathbf{w}}(\mathbf{x}^{(i)}) - y^{(i)}) \right) x_j^{(i)} =m1i=1∑m(fw(x(i))−y(i)))xj(i)
下面使用批量梯度下降法拟合一个 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 回归进行扩展,用于解决多分类问题。下面将介绍两种方法。
将多分类任务拆分为若干个二分类任务进行求解。具体来说,先对问题进行拆分,然后为拆出的每个二分类任务训练一个分类器;在预测时,对这些分类器的预测结果进行集成以获得最终的多分类结果。最常用的拆分策略为:“一对一”(One vs One)、“一对其余”(One vs Rest)和“多对多”(Many vs Many)。
假设数据集有 k k k 个类别,“一对一”将为任意两个类别训练一个分类器,将存在 k ( k − 1 ) k(k-1) k(k−1) 个分类器。在预测时,将得到 k ( k − 1 ) k(k-1) k(k−1) 个分类结果,然后通过投票得到最终的分类结果(即预测最多的类别作为最终的结果)。
“一对其余”将一个类别作为正例,其他类别作为反例,将存在 k k k 个分类器。在预测时,根据这 k k k 个分类器可以得到每个类别的概率,最后我们选择概率值最大的类别作为最终的分类结果。
多项 Logistic 回归 也称为 Softmax 回归。
假设离散型随机变量 y y y 的取值集合是 { 1 , 2 , ⋯   , k } \{1,2,\cdots,k\} {1,2,⋯,k},那么样本输出为 l l l 概率为
(18) P ( y = l ∣ x ) = e w l x ∑ l = 1 k e w l x , 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} P(y=l∣x)=∑l=1kewlxewlx,l=1,2,⋯,k(18)
参照二分类,可知损失函数为
(19) E ( w ) = − 1 m ∑ i = 1 m ∑ j = 1 k 1 { y ( i ) = j } l o g e w l x ( i ) ∑ l = 1 k e w l x ( 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} E(w)=−m1i=1∑mj=1∑k1{y(i)=j}log∑l=1kewlx(i)ewlx(i)(19)
其中, m m m 表示样本个数, k k k 表示类别的个数; 1 { y ( i ) = j } 1\{y^{(i)} = j\} 1{y(i)=j} 函数表示:当 y ( i ) = j y^{(i)} = j y(i)=j 时,函数值为 1,否则为 0。
然后参照上面的梯度下降法求出各个最优 w l \mathbf{w}_{l} wl。这里就不再详细介绍求解过程了。
解决多分类问题时,选择上面介绍的两种方法的具体原则:
如果各类别之间是互斥的,适合选择使用 softmax 回归分类器;
如果各类别之间不完全互斥,适合选择使用多个 Logistic 回归分类器。
参考:
[1] 周志华《机器学习》
[2] 李航《统计学习方法》
[3] 《机器学习实战》
[4] https://www.cnblogs.com/alfred2017/p/6627824.html
[5] https://blog.csdn.net/u011734144/article/details/79717470