本内容将介绍机器学习中的 Logistic 回归 及 Python 代码实现,和 Softmax 回归。
Logistic 回归(logistic regression,也称逻辑回归和对数几率回归)是一种经典的分类模型,属于广义的线性回归分析模型。虽然名称中包含了“回归”,但是实际上它不是回归模型,而是分类模型。
一、Logistic 回归
在阅读本内容前,需要了解 线性回归模型 的基本概念。如果您还不了解,可以参阅 机器学习系列:线性回归模型。
在 机器学习系列:线性回归模型 中介绍了如何使用线性模型进行回归预测。但是否可以进行分类预测呢?
,线性回归模型产生的预测值 是实值,于是我们需要将实值 转换为
即当 时输出 (即正例),当 是输出 (即反例),当 时可输出 或 ,如图-1所示。
图-1 单位阶跃函数与对数几率函数
但从图-1 可看出,单位阶跃函数不连续。可以使用对数几率函数(logistic function)对其进行替换:
值转化为一个接近 或 的 值,并且其输出值在
我们知道线性回归模型为
其中 ,,。将其代入式(2),得到
二、损失函数
2.1 损失函数
对于二分类问题,单个样本的损失函数为
等价于
其也称作为交叉熵代价函数。
对于训练集所有样本,其损失函数为
将式(6)代入式(7)得
然后可以采用 梯度下降法(Gradient descent method) 或者 牛顿法(Newton method)求使 取最小值的
和
2.2 最大似然估计
将式(9)和式(10)可简化为
则对应的似然函数为
则对数似然函数为
取最大值时的 ,我们可以使用 梯度上升法 求得最优的 。
对比式(8)和式(14),我们发现存在以下关系
时,求解 的最大值或求解 的最小值,两者实际上是一致的。
三、根据梯度下降法求解最优
如果你还不了解梯度下降法,可以参阅 机器学习系列:梯度下降法及 Python 实现。
中的每个 求偏导数(即梯度),得到(注意:这里的 的底为 )
的具体求解过程,在下面的 3.1 求解 会进行介绍。
其中
3.1
的求解需要用到 。先来看一下
的求解过程:
四、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)。
个类别,“一对一”将为任意两个类别训练一个分类器,将存在 个分类器。在预测时,将得到
个分类器。在预测时,根据这
5.2 多项 Logistic 回归
多项 Logistic 回归 也称为 Softmax 回归。
的取值集合是 ,那么样本输出为
参照二分类,可知损失函数为
其中, 表示样本个数, 表示类别的个数; 函数表示:当
。这里就不再详细介绍求解过程了。
5.3 选择原则
解决多分类问题时,选择上面介绍的两种方法的具体原则:
- 如果各类别之间是互斥的,适合选择使用 softmax 回归分类器;
- 如果各类别之间不完全互斥,适合选择使用多个 Logistic 回归分类器。
参考:
[1] 周志华《机器学习》
[2] 李航《统计学习方法》
[3] 《机器学习实战》