在上一篇文章 机器学习入门(二)线性回归 中我们介绍了线性回归,这一篇文章将介绍监督学习中的逻辑回归。
线性回归面对分类问题的缺陷
在生活中除了预测房价这种回归问题,还有像判断一封电子邮件是否是垃圾邮件;区别一个肿瘤是恶性的还是良性的的分类问题。这里以判断肿瘤的种类为例,看一下线性回归如何处理该问题。

如上图所示,横轴代表肿瘤的大小,纵轴表示 0.0 表示是良性肿瘤;1.0表示是恶性肿瘤。如果我们使用线性回归,可以让 y < 0.5 时,预测 y = 0;让 y >= 0.5 时,预测 y = 1。对于上图所示的数据,这样的一个线性模型似乎能很好地完成分类任务。
但是如果此时增加一个非常大的恶性肿瘤的数据,这里获取的直线如下。可以看到,在被新数据影响后,本因被判断为恶性肿瘤的点,此时在该条直线中的 y < 0.5,导致分类出现了问题。

从中可以看出线性回归模型,并不适合解决这样分类的问题。
逻辑回归
上面介绍的线性回归模型的缺陷,其原因主要是预测的值可以超越[0,1]的范围。因此我们引入一个新的模型,逻辑回归,该模型的输出变量范围始终在0和1之间。
常用的逻辑回归函数是 sigmond 函数,其函数公式为 <math xmlns="http://www.w3.org/1998/Math/MathML"> σ ( x ) = 1 1 + e − x \sigma(x) = \frac{1}{1 + e^{-x}} </math>σ(x)=1+e−x1,图形为:

需要注意,逻辑回归不是单独使用 sigmond 函数,而是回归函数 + sigmond 函数。在逻辑回归中,会把回归函数的输出(y)作为 sigmond 函数的输入(x)。由于 sigmond 函数:
- 当 x = 0 时, <math xmlns="http://www.w3.org/1998/Math/MathML"> σ ( x ) = 0.5 \sigma(x) = 0.5 </math>σ(x)=0.5
- 当 x > 0 时, <math xmlns="http://www.w3.org/1998/Math/MathML"> σ ( x ) > 0.5 \sigma(x) > 0.5 </math>σ(x)>0.5
- 当 x < 0 时, <math xmlns="http://www.w3.org/1998/Math/MathML"> σ ( x ) < 0.5 \sigma(x) < 0.5 </math>σ(x)<0.5
这样就避免了回归函数预测的值可以超越[0,1]的范围的问题。逻辑回归的效果如下所示:

而且对于数据呈现如下图这样的分布情况,我们只需要找到对应的二次回归曲线,就可以来适应该图形的椭圆边界。

注意:逻辑回归(Logistic Regression)虽然名称中包含"回归"二字,但它实际上是一种分类方法,主要用于二分类问题。
决策边界和代价函数
从上面的示例中,我们知道如果想要提高分类算法的准确率,就必须找到合适的决策边界,比如选择线性边界 y=ax+b 还是非线性边界 ax^2 + by^2 = c;以及决策边界对应的参数 a、b、c的值。
对于决策边界函数的选取,一般先使用线性边界,当数据复杂且线性边界表现不佳时,再通过可视化、降维等方法来找到需要的决策边界。
选择好决策边界函数后,就需要找出最适合的参数,让数据的拟合误差最小。在线性回归中,我们是使用代价函数来实现。逻辑回归中,也是一样的,区别是使用的代价函数是不一样的,具体可以看 逻辑回归算法详解
示例
这里以根据两场考试成绩来预测第三场考试是否及格为例:
假设决策边界是一条直线:
ini
# 训练数据
from sklearn.linear_model import LogisticRegression
# 定义数据
x = data.drop(['Pass'], axis = 1)
y = data.loc[:, 'Pass']
# 训练
LR = LogisticRegression()
LR.fit(x, y)
# x1 表示考试一;x2表示考试2
# 他们的边界关系满足 a * x1 + b * x2 + c = 0
x1 = data.loc[:, 'Exam1']
x2 = data.loc[:, 'Exam2']
c = LR.intercept_
a, b = LR.coef_[0][0], LR.coef_[0][1]
x2_new = -(c + a * x1) / b
fig3 = plt.figure()
plt.plot(x1, x2_new)
passed = plt.scatter(data.loc[:, 'Exam1'][mask], data.loc[:, 'Exam2'][mask])
failed = plt.scatter(data.loc[:, 'Exam1'][~mask], data.loc[:, 'Exam2'][~mask])
plt.title('Exam1-Exam2')
plt.xlabel('Exam1')
plt.ylabel('Exam2')
plt.legend((passed, failed), ('passed', 'failed'))
plt.show()
效果如下所示:

假设决策边界满足 a0 + a1 * x1 + a2 * x2 + a3 * x1 ^ 2 + a4 * x2 ^ 2 + a5 * x1 * x2 = 0
:
ini
# 创建新的数据
x1_2 = x1 * x1
x2_2 = x2 * x2
x1_x2 = x1 * x2
x_new_data = {'x1':x1, 'x2':x2, 'x1_2': x1_2, 'x2_2': x2_2, 'x1_x2': x1_x2}
x_new_data = pd.DataFrame(x_new_data)
# 再次训练模型
LR2 = LogisticRegression()
LR2.fit(x_new_data, y)
# 此时的边界函数满足 a0 + a1 * x1 + a2 * x2 + a3 * x1 ^ 2 + a4 * x2 ^ 2 + a5 * x1 * x2 = 0
a0 = LR2.intercept_
a1, a2, a3, a4, a5 = LR2.coef_[0][0], LR2.coef_[0][1], LR2.coef_[0][2], LR2.coef_[0][3], LR2.coef_[0][4]
x1_sort_data = x1.sort_values()
a = a4
b = a5 * x1_sort_data + a2
c = a0 + a1 * x1_sort_data + a3 * x1_sort_data * x1_sort_data
x2_new_boundary = (-b + np.sqrt(b * b - 4 * a * c)) / (2 * a)
fig4 = plt.figure()
passed = plt.scatter(data.loc[:, 'Exam1'][mask], data.loc[:, 'Exam2'][mask])
failed = plt.scatter(data.loc[:, 'Exam1'][~mask], data.loc[:, 'Exam2'][~mask])
plt.title('Exam1-Exam2')
plt.xlabel('Exam1')
plt.ylabel('Exam2')
plt.legend((passed, failed), ('passed', 'failed'))
plt.plot(x1_sort_data, x2_new_boundary)
plt.show()
效果如下所示:
