在之前的《常见线性回归模型》一文中,介绍了机器学习中比较简单但又非常常用的线性回归模型,今天来介绍另外一个模型:Logistic Regression,这又是机器学习中用的非常多的一个模型。虽然Logistic Regression(后简称LR)里面带了回归字样(Regression),但它实际是一个分类模型(关于回归和分类的区别见《机器学习介绍》),更准确的说是一个二分类模型(0、1或者true、false之类,当然通过一些手段也可以实现多分类),比如预测一份邮件是不是垃圾垃圾邮件、一个人是否患了某种病等。它的基本思想是:假设一份邮件为垃圾邮件的概率为$p$,我们根据一些特征计算出这个$p$,如果$p$大于0.5,那么就认为是垃圾邮件,否则就不是垃圾邮件。下面我们就看看是如何计算这个概率p的。

先回顾一下线性回归的公式:

$$ { \hat y=\omega_0+\omega_1x_1+...+\omega_px_p =\omega_0+X\omega } $$

线性回归预测出来的结果y属于整个实数域,而概率只能是0~1之间的数。有没有什么方法可以将整个实数域映射到0~1之间呢?当然有,Logit函数就可以,我们看下Logit函数:

$$ { logit(x) = \frac{1}{1+e^{-x}} } $$

我们看下函数图像:

Logit函数图像

从函数公式和图像可以看出来自变量x的定义域为整个实数域,而因变量的值则为0~1之间的实数,正好符合我们上面的要求。其实除了logit函数,还有一些其它函数也可以实现该功能,因为这些函数的图形都呈S型,所以也称之为Sigmoid(英文含义为“S型函数”)函数,我们看看除了logit函数之外的其他sigmoid函数:

sigmoid函数

当然最常用的sigmoid函数还是logit函数,所以很多地方提到sigmoid函数指的就是logit函数。

言归正传,现在我们已经解决了从实数域映射到0~1之间的问题了。然后我们将线性回归公式和logit函数合并一下:

$$ p = y(w,x) = \frac{1}{1+e^{-(\omega_0+X\omega)}} $$

$X$依旧是特征,所以现在和线性回归就一样了,我们只要根据已有的数据计算出$\omega_i$的值,也就是构建出了预测模型了。当有一条新的数据之后,我们带到上面的公式里面,计算出$y$的值,如果大于0.5就认为是某个类别,否则就属于另外一个类别。可以看到,LR模型是通过将线性模型与Sigmoid函数组合而实现的,这也是为什么里面带了回归的字样。

现在的问题就成了如何计算$\omega_i$了,也就是参数估计。常用的两种参数估计方法为:(普通)最小二乘法(Ordinary Least Squares, OLS)最大似然估计(Maximum Likehood Estimation, MLE)。前者基于距离,后者基于概率,当误差满足正态分布的时候,虽然两种估计原理和方式不一样,但结果是一样的。而之前线性回归模型中我们使用的就是最小二乘法,而LR是基于概率的,所以一般使用最大似然估计来计算参数$\omega_i$的值。如果你还不知道什么是最大似然估计,可以看下知乎上面马同学的回答:如何通俗地理解概率论中的极大似然估计法?

网上关于LR算法参数估计详细的数学推导很多,这里就简单推导一下。使用MLE做参数估计的核心就是计算似然函数。现在假设有n个样本,观测值分别为$y_1,y_2,…,y_n$($y_i$的取值为0或1)。设$p_i=p(y=1|x_i)$为给定$x_i$ (第i条样本或第i个观测值)的条件下得到$y_i=1$的条件概率;这样,在同样条件下得到结果$y_i=0$的条件概率为$p(y=0|x_i)=1-p_i$。从而,我们得到一个观测值的概率为:

$$ { p(y_i) = p_i^{y_i}(1-p_i)^{1-y_i} } $$

因为各项观测值相互独立,所以他们的联合分布就是各边缘分布的乘积:

$$ { L(\theta)=\prod_{i=1}^n p_i^{y_i}(1 - p_i)^{1-y_i} } $$

这就是我们要求的n个观测值的似然函数(前面加一个负号就是Logistic Regression的损失函数)。MLE就是要求出能够使这一似然函数值最大的参数估计。为了简化,一般将其转换为对数形式(这里不打公式了,直接截图之前在word里面打的公式了):

对数似然函数

接下来的问题就是求上面对数似然函数的极大值了。根据高数的理论,讨论就是求倒数,然后另倒数等于0,计算出来的点就是极值点了。我们分别对$\omega_0$和$\omega_i(i=1,...,n)$求偏导:

偏导数

上面两个式子称为似然方程(likehood equations)。如果模型中有$n$个自变量,那么就有$n+1$个联立方程来估计$\omega_0$和$\omega_i(i=1,...,n)$的值。在线性回归中,似然方程是通过把偏差平方和分别对$\omega_0$和$\omega_i(i=1,...,n)$求偏导所得到的(即之前说的OLS),它对于未知参数都是线性的,因此很容易求解。但对于LR,上面两个似然方程都是$\omega_0$和$\omega_i(i=1,...,n)$的非线性函数,所以求解十分困难。对于这种非线性函数的极值问题,一般是通过迭代的方式求解的,最常用的就是梯度上升(求极大值)和梯度下降(求极小值)算法了。这里我们是要求最大值。而梯度上升的基本思想就是:要找某函数的最大值,最好的方法是沿着该函数的梯度方向探查,即沿着梯度的方向移动,每移动一次称之为一次迭代。梯度记为$\nabla$,$f(\omega,\omega_0)$的梯度为:

梯度

沿梯度方向每次移动的大小称之为步长,记为$\alpha$。这样梯度上升算法的迭代公式为:

$$ { \omega := \omega + \alpha \nabla_\omega f(\omega) } $$

我们不停的迭代该公式,直到达到某个停止条件位置迭代次数到达某个指定值。此时的$\omega$和$\omega_0$就是我们要求的回归系数和截距。

原生的Logistic Regression模型是处理二分类问题的,但通过一些技术手段也可以处理多分类,目前LR一般有三种类型:

  • Binary Logistic Regression:即原生的二分类模型。
  • Multinomial Logistic Regression:多分类模型,即要预测的目标变量多于两个。
  • Ordinal Logistic Regression:序数多分类模型,目标变量多于两个,且有序,比如1到5.

另外简单总结下LR模型的优缺点:

优点

  • 高效、直观易懂,容易实现、结果容易解释,不需要耗费非常高的计算能力;
  • 不需要对特征做归一化(一把基于概率的模型不需要做归一化,而基于距离的模型需要做归一化);
  • 给出了观测值的概率,这一点在实际应用中非常重要,像很多评分模型的分数就是基于概率计算的。

缺点

  • 不太能处理特征特别多的场景,容易过拟合;
  • 如果特征和目标变量相关性很小的话,模型性能也不好;
  • 如果特征之间存在相关性的话,模型性能表现不好。

以上便是Logistic Regression的理论知识,实际中我们基本上不会自己实现LR模型,都是使用各种库,所以那些优(fan)美(ren)的数学公式的计算对我们都是不可见的。

最后我们使用scikit-learn这个Python的机器学习库中提供的LR模型做一个简单的二分类预测:有一个糖尿病的数据,数据中前8列为特征变量,最后一列为目标变量。我们按照通用的套路构建模型:

  1. 读入数据集,划分训练集和测试集;
  2. 划分训练集和测试集;
  3. 创建并训练模型;
  4. 在测试集上面进行预测;
  5. 探索模型性能

需要注意的是,第5步探索模型性能的时候用到了机器学习里面的一些专业东西,比如准确率、精确度、召回率等,如果你还不了解这些的含义,可以阅读我之前的文章《confusion matrix,precision,recall,F1-score,ROC,AUC,p-value概念总结》。

下面是完整的代码:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
import matplotlib.pyplot as plt


# 读入数据
df = pd.read_csv("https://raw.githubusercontent.com/niyanchun/AI_Learning/master/SampleData/diabetes.csv")

X = df.values[:, :-1]
y = df.values[:, -1]

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25,random_state=1024)

# 创建模型并训练
lr = LogisticRegression()
lr.fit(X_train, y_train)

# 在测试集上面预测
predictions = lr.predict(X_test)

# 求混淆矩阵、准确率、精确率、召回率
print("Confusion Matrix: ", metrics.confusion_matrix(y_test, predictions))
print("Accuracy: ", metrics.accuracy_score(y_test, predictions))
print("Precision: ", metrics.precision_score(y_test,predictions))
print("Recall: ", metrics.recall_score(y_test, predictions))

# 画ROC图
pred_proba = lr.predict_proba(X_test)[::,1]
fpr, tpr, _ = metrics.roc_curve(y_test, pred_proba)
auc = metrics.roc_auc_score(y_test, pred_proba)
plt.plot(fpr,tpr,label="data 1, auc="+str(auc))
plt.legend(loc=4)
plt.show()

ROC图:

ROC图

这是总结的之前自己做的一些笔记,当时查阅的资料已经不记得有哪些已经出处了,本文就不附References了。