逻辑回归

| |

程序实现

简单示例

一个简单的示例,从吴恩达那边复制过来的:

python

import numpy as np
X = np.array([[0.5, 1.5], [1,1], [1.5, 0.5], [3, 0.5], [2, 2], [1, 2.5]])
y = np.array([0, 0, 0, 1, 1, 1])

from sklearn.linear_model import LogisticRegression
lr_model = LogisticRegression()
lr_model.fit(X, y)
y_pred = lr_model.predict(X)

print("Prediction on training set:", y_pred)
print("Accuracy on training set:", lr_model.score(X, y))

威斯康辛州乳腺癌数据集

二分类模型,检测乳腺癌为恶性/良性(1/0),参阅前面的博客

程序实现

python

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# from sklearn.metrics import  accuracy_score
cancer = load_breast_cancer()
x_train, x_test, y_train, y_test = train_test_split(cancer.data,cancer.target, random_state=2)

log_reg = LogisticRegression(solver="liblinear")
log_reg.fit(x_train, y_train)

y_predict = log_reg.predict(x_test)
print("真实标签为:",y_test)
print("预测结果为:",y_predict)
# accurcy = accuracy_score(y_test, y_predict)
# print(accurcy)

真实标签为: [1 1 1 0 1 0 1 1 1 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 1 1
0 1 1 0 0 1 1 0 1 1 1 0 0 1 0 1 1 1 0 1 1 0 1 1 0 1 0 0 1 0 0 1 0 0 0 1 0
1 0 1 1 1 0 0 0 0 1 1 1 1 1 1 0 1 1 1 0 0 1 0 0 1 1 1 0 0 0 1 1 1 1 1 1 0
0 0 0 1 1 0 0 0 1 1 0 1 1 0 0 1 0 1 1 0 0 1 0 0 1 1 1 1 1 1 1 1]
预测结果为: [1 1 1 0 0 0 1 1 1 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 1 1
0 1 1 0 0 1 1 0 1 1 1 0 0 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 1 0 0 0 1 0 0 1 0
1 0 1 1 0 0 0 0 0 1 1 0 1 1 1 0 1 1 1 0 0 1 0 0 1 1 1 0 0 0 1 1 1 1 1 1 0
0 1 0 1 1 0 0 1 1 1 0 1 1 0 0 1 0 1 1 0 0 1 0 0 1 1 1 1 1 1 1 1]

函数原型及参数解释

class sklearn.linear_model.LogisticRegression(penalty='l2', *, dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver='lbfgs', max_iter=100, multi_class='auto', verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)
参数 penalty {‘L1’, ‘L2’, ‘elasticnet’, ‘none’}, default=’L2’ 用于指定处罚中使用的规范。'newton-cg','sag'和'lbfgs'求解器仅支持L2惩罚。仅“ saga”求解器支持“ elasticnet”。如果为“ none”(liblinear求解器不支持),则不应用任何正则化.
参数 C float, default=1.0 正则强度的倒数;必须为正浮点数。与支持向量机一样,较小的值指定更强的正则化。
参数 solver {‘lbfgs’, ‘liblinear’, ‘newton-cg’, ‘newton-cholesky’, ‘sag’, ‘saga’}, default=’lbfgs’
用于优化问题的算法。
- 对于小型数据集,“ liblinear”是一个不错的选择,而对于大型数据集,“ sag”和“ saga”更快。

参数 multi_class {‘auto’, ‘ovr’, ‘multinomial’}, default=’auto’
如果选择的选项是“ ovr”,则每个标签都看做二分类问题。对于“multinomial”,即使数据是二分类的,损失最小是多项式损失拟合整个概率分布。当solver ='liblinear' 时, 'multinomial' 不可用。如果数据是二分类的,或者如果Solver ='liblinear',则'auto'选择'ovr',否则选择'multinomial'。

softmax多分类实现

PYTHON

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
iris = load_iris()
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)

# 参照上面的函数参数解释,可以理解下面的参数设置
softmax_reg = LogisticRegression(multi_class="multinomial",solver="lbfgs", C=10,max_iter=1000)
softmax_reg.fit(x_train, y_train)

y_predict = softmax_reg.predict(x_test)
accurcy = np.sum(y_predict == y_test) / len(x_test)
print(accurcy)

混淆矩阵

python

# 计算混淆矩阵
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, y_predict)

# 可视化混淆矩阵
from sklearn.metrics import plot_confusion_matrix
import matplotlib.pyplot as plt  
plot_confusion_matrix(log_reg,x_test, y_test)
plt.show() 

分类指标计算

accuracy_score

sklearn.metrics.accuracy_score(y_true, y_pred, *, normalize=True, sample_weight=None)
关注参数: normalize bool, optional (default=True)
如果为true则返回准确率,如果为false则返回准确的数量

python

from sklearn.metrics import  accuracy_score
accurcy = accuracy_score(y_test, y_predict)
print(accurcy)

precision_score

sklearn.metrics.precision_score(y_true, y_pred, *, labels=None, pos_label=1, average='binary', sample_weight=None, zero_division='warn')
关注参数: average : {‘micro’, ‘macro’, ‘samples’, ‘weighted’, ‘binary’} or None, default=’binary’ 多类或 者多标签目标需要这个参数. 如果为None,每个类别的分数将会返回. 否则,它决定了数据的平均值类型. - ‘binary’: 仅报告由pos_label指定的类的结果. 这仅适用于目标(y_{true, pred})是二进制的情况. - ‘micro’: 通过计算总的真正性、假负性和假正性来全局计算指标.微平均,是指计算多分类指标时赋予所有类别的每个样本相同的权重,将所有样本合在一起计算各个指标。 - ‘macro’: 为每个标签计算指标,找到它们未加权的均值. 它不考虑标签数量不平衡的情况.宏平均,是指在计算均值时使每个类别具有相同的权重,最后结果是每个类别的指标的算术平均值。 - ‘weighted’: 为每个标签计算指标,并通过各类占比找到它们的加权均值(每个标签的正例数).它解决了’macro’的标签不平衡问题;它可以产生不在精确率和召回率之间的F-score. - ‘samples’: 为每个实例计算指标,找到它们的均值(只在多标签分类的时候有意义,并且和函数accuracy_score不同).

函数返回:
precision: 浮点数(如果average不是None) 或浮点数数组, shape =[唯一标签的数量]
二分类中正类的精确率或者在多分类任务中每个类的精确率的加权平均.
官方示例如下:

python

>>> import numpy as np
>>> from sklearn.metrics import precision_score
>>> y_true = [0, 1, 2, 0, 1, 2]
>>> y_pred = [0, 2, 1, 0, 0, 1]
>>> precision_score(y_true, y_pred, average='macro')
0.22...
>>> precision_score(y_true, y_pred, average='micro')
0.33...
>>> precision_score(y_true, y_pred, average='weighted')
0.22...
>>> precision_score(y_true, y_pred, average=None)
array([0.66..., 0.        , 0.        ])
>>> y_pred = [0, 0, 0, 0, 0, 0]
>>> precision_score(y_true, y_pred, average=None)
array([0.33..., 0.        , 0.        ])
>>> precision_score(y_true, y_pred, average=None, zero_division=1)
array([0.33..., 1.        , 1.        ])
>>> precision_score(y_true, y_pred, average=None, zero_division=np.nan)
array([0.33...,        nan,        nan])

>>> # multilabel classification
>>> y_true = [[0, 0, 0], [1, 1, 1], [0, 1, 1]]
>>> y_pred = [[0, 0, 0], [1, 1, 1], [1, 1, 0]]
>>> precision_score(y_true, y_pred, average=None)
array([0.5, 1. , 1. ])

recall_score

sklearn.metrics.recall_score(y_true, y_pred, *, labels=None, pos_label=1, average='binary', sample_weight=None, zero_division='warn')
与上面的precision_score基本一致,不再赘述。 函数返回值:
recall: 浮点数(如果average不是None) 或者浮点数数组,shape = [唯一标签的数量]
二分类中正类的召回率或者多分类任务中每个类别召回率的加权平均值.

f1_score

sklearn.metrics.f1_score(y_true, y_pred, *, labels=None, pos_label=1, average='binary', sample_weight=None, zero_division='warn')
函数返回值:
f1_score : 浮点数或者是浮点数数组,shape=[唯一标签的数量]
二分类中的正类的F1 score或者是多分类任务中每个类别F1 score的加权平均.

分类模型报告

sklearn.metrics.classification_report(y_true, y_pred, *, labels=None, target_names=None, sample_weight=None, digits=2, output_dict=False, zero_division='warn')
建立一个显示主要分类指标的文本报告。

python

>>> from sklearn.metrics import classification_report
>>> print(classification_report(y_test, y_predict, target_names=cancer.target_names))   #target_names:['malignant' 'benign']
              precision    recall  f1-score   support

   malignant       0.91      0.93      0.92        56
      benign       0.95      0.94      0.95        87

    accuracy                           0.94       143
   macro avg       0.93      0.94      0.93       143
weighted avg       0.94      0.94      0.94       143

ROC与AUC

sklearn.metrics.roc_curve(y_true, y_score, *, pos_label=None, sample_weight=None, drop_intermediate=True)
其中 y_score 含义是每个样本预测的概率值
sklearn.metrics.roc_auc_score(y_true, y_score, *, average='macro', sample_weight=None, max_fpr=None, multi_class='raise', labels=None)

python

# 承接上面的程序
from sklearn.metrics import roc_curve, roc_auc_score
print("AUC指标:", roc_auc_score(y_test, log_reg.predict(x_test)))

y_score = log_reg.predict_proba(x_test)[:, 1]     #含义是获取预测为正例的概率值
fpr, tpr, thresholds = roc_curve(y_test, y_score)
auc_score = roc_auc_score(y_test, y_score)

# 绘制ROC曲线:
plt.plot(fpr, tpr, label=f'AUC = {auc_score:.2f}')  # 绘制ROC曲线,标注AUC的值
# 随机分类器FPR=TPR。随机分类器的性能通常表示为ROC曲线上的对角线
plt.plot([0, 1], [0, 1], linestyle='--', color='r', label='Random Classifier')  # 绘制随机分类器的ROC曲线
plt.xlabel('False Positive Rate')  # x轴标签为FPR
plt.ylabel('True Positive Rate')   # y轴标签为TPR
plt.title('ROC Curve')
plt.legend()
plt.show()

输出结果: (图片丢失。 )

逻辑回归原理

逻辑回归是 分类模型 ,不是回归模型。
逻辑回归,Logistic Regression。
sigmoid函数(有译法为:逻辑函数)

$$g(z) = \frac{1}{1+e^{-z}} \tag{1}$$

逻辑回归模型:

$$ f_{\mathbf{w},b}(\mathbf{x}^{(i)}) = g(\mathbf{w} \cdot \mathbf{x}^{(i)} + b ) \tag{2} $$

其中$g(z) = \frac{1}{1+e^{-z}}$, $\mathbf{w} \cdot \mathbf{x}$ 是向量的内积,

$$ \mathbf{w} \cdot \mathbf{x} = w_0 x_0 + w_1 x_1 $$

为了从逻辑回归模型中获得预测值 ($y=0$ or $y=1$) 我们采用下面的启发式方法:

$$ \begin{cases} 若 f_{\mathbf{w},b}(x) >= 0.5, 则 y=1 \\ 若 f_{\mathbf{w},b}(x) < 0.5, 则 y=0 \end{cases} $$

对于sigmoid函数,当$z >=0$ 时 $g(z) >= 0.5$

又 $z = \mathbf{w} \cdot \mathbf{x} + b$,故有:

$$ \begin{cases} 当\mathbf{w} \cdot \mathbf{x} + b >= 0 时,模型输出y=1 \\ 当\mathbf{w} \cdot \mathbf{x} + b < 0 时,模型输出 y=0 \end{cases} $$

损失函数

逻辑回归损失函数:
$loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), y^{(i)})$ is the cost for a single data point, which is:

$$ \begin{equation} loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), y^{(i)}) = \begin{cases} - \log\left(f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) & \text{if $y^{(i)}=1$}\\ \log \left( 1 - f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) & \text{if $y^{(i)}=0$} \end{cases} \end{equation} $$

其中,$f_{\mathbf{w},b}(\mathbf{x}^{(i)})$ 是模型预测值, $y^{(i)}$ 是目标值(标签target), $f_{\mathbf{w},b}(\mathbf{x}^{(i)}) = g(\mathbf{w} \cdot\mathbf{x}^{(i)}+b)$ 其中 $g$ 是sigmoid函数。 损失函数可以重写为下面的形式,更易代码实现:

$$ loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), y^{(i)}) = (-y^{(i)} \log\left(f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) - \left( 1 - y^{(i)}\right) \log \left( 1 - f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) $$

这个公式看上去很吓人,不过考虑到$y^{(i)}$仅可以取0或1两个值,方程即可进一步改写为:

当 $y^{(i)} = 0$时

$$ \begin{align} loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), 0) &= (-(0) \log\left(f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) - \left( 1 - 0\right) \log \left( 1 - f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) \\ &= -\log \left( 1 - f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) \end{align} $$

当 $y^{(i)} = 1$时:

$$ \begin{align} loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), 1) &= (-1) \log\left(f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) - \left( 1 - 1\right) \log \left( 1 - f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right)\\ &= -\log\left(f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) \end{align} $$

参阅下图,易于理解:

Logistic loss functionLogistic loss function
Logistic loss functionLogistic loss function
故有代价函数:

$$ J(\mathbf{w},b) = \frac{1}{m} \sum_{i=0}^{m-1} \left[ loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), y^{(i)}) \right] $$

其中m是样本的总数量,$loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), y^{(i)})$ 是单个样本的损失函数,展开为:

$$loss(f_{\mathbf{w},b}(\mathbf{x}^{(i)}), y^{(i)}) = -y^{(i)} \log\left(f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) - \left( 1 - y^{(i)}\right) \log \left( 1 - f_{\mathbf{w},b}\left( \mathbf{x}^{(i)} \right) \right) $$

函数没有已知的闭式方程。
示例逻辑回归损失的计算过程:

逻辑回归损失的计算图示逻辑回归损失的计算图示

梯度下降

逻辑回归的梯度下降 Gradient Descent for Logistic Regression,也有人称之为梯度上升,无所谓,都是同一个意思。

$$\begin{align*} &\text{repeat until convergence:} \; \lbrace \\ & \; \; \;w_j = w_j - \alpha \frac{\partial J(\mathbf{w},b)}{\partial w_j} \tag{6} \; & \text{for j := 0..n-1} \\ & \; \; \; \; \;b = b - \alpha \frac{\partial J(\mathbf{w},b)}{\partial b} \\ &\rbrace \end{align*}$$

Where each iteration performs simultaneous updates on $w_j$ for all $j$, where

$$\begin{align*} \frac{\partial J(\mathbf{w},b)}{\partial w_j} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)})x_{j}^{(i)} \tag{7} \\ \frac{\partial J(\mathbf{w},b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)}) \tag{8} \end{align*}$$

与线性回归十分相似,对于每个实例,它都会计算预测误差并将其乘以第j个特征值,然后计算所有训练实例的平均值。一旦你有了包含所有偏导数的梯度向量就可以使用梯度下降算法了。就是这样,现在你知道如何训练逻辑模型了。对于随机梯度下降,一次使用一个实例;对于小批量梯度下降,一次使用一个小批量。

逻辑回归的正则化

与线性回归几乎一样,参阅前面的博客

逻辑回归正则化逻辑回归正则化
图片引自吴恩达,致敬。

多分类:softmax函数回归

softmax 回归(softmax regression)其实是 logistic 回归的一般形式,logistic 回归用于二分类,而 softmax 回归用于多分类。 给定一个实例x,Softmax回归模型首先计算出每个类k的分数,然后对这些分数应用softmax函数(也叫归一化指数),估算出每个类的概率。 Softmax回归分类器一次只能预测一个类(即它是多类,而不是多输出),因此它只能与互斥的类(例如不同类型的植物)一起使用。你无法使用它在一张照片中识别多个人。

softmax图softmax图
台大李宏毅《一天搞懂深度学习》:
softmax解释图softmax解释图
交叉熵成本函数:

$$ J(\Theta) = -\frac{1}{m} \sum_{i=1}^{m}\sum_{k=1}^{K} y_k^{(i)} log(\widehat{p}_k^{(i)}) $$

其中 $y_k^{(i)}$ 是属于类k的第i个实例的目标概率。一般而言等于1或0,具体取决于实例是否属于该类。当只有两个类(K=2)时,此成本函数等效于逻辑回归的成本函数。
softmax的正则化也一样,在代价函数的末尾加上正则项即可。
梯度下降,求导,得:

$$ \nabla_{\theta(k)} J(\Theta) = \frac{1}{m} \sum_{i=1}^{m}(\widehat{p}_k^{(i)} - y_k^{(i)}) \mathbf{x}^{(i)} $$

至于求导过程嘛,交给学数学的家伙们吧(狗头🐶)

拓展:分类模型的评估方法

在很多分类场景当中我们不一定只关注预测的准确率!比如检测癌症的问题,我们并不关注预测的准确率,而是关注在所有的样本当中,癌症患者有没有被全部预测(检测)出来

混淆矩阵

在分类任务下,预测结果(Predicted Condition)与正确标记(True Condition)之间存在四种不同的组合,构成混淆矩阵(适用于多分类)

Confusion Matrix 预测值
反例 正例
真实值 反例 TN(真反例) FP(假正例)
正例 FN(假反例) TP(真正例)

精确率(Precision)与召回率(Recall)

准确率 Accuracy :准确率是指,对于给定的测试数据集,分类器正确分类的样本书与总样本数之比,也就是预测正确的概率。 精确率 Precision :预测结果为正例样本中真实为正例的比例
召回率 Recall :真实为正例的样本中预测结果为正例的比例

$$ Accuracy = \frac{TP+TN}{TP+FP+TN+FN} $$

Accuracy是衡量分类模型的最直白的指标,但缺陷也是明显的。假设有100个样本,其中有99个都是正样本,则分类器只需要一直预测为正例,就可以得到99%的准确率,实际上这个分类器性能是很低下的。也就是说,当不同类别的样本所占的比例严重不平衡时,占比大的类别会是影响准确率的最主要的因素。所以,只有当数据集各个类别的样本比例比较均衡时,Accuracy这个指标才是一个比较好的衡量标准。因此,必须参考其他指标才能完整评估模型的性能。

$$ Precision = \frac{TP}{TP+FP} $$

精确率(Precision)又叫查准率,表示预测结果为正例的样本中实际为正样本的比例。
使用场景:当反例被错误预测成正例(FP)的代价很高时,适合用精确率。根据公式可知,精确率越高,FP越小。比如在垃圾在垃圾邮件检测中,假正例意味着非垃圾邮件(实际为负)被错误的预测为垃圾邮件(预测为正)。如果一个垃圾邮件监测系统的查准率不高导致很多非垃圾邮件被归到垃圾邮箱里去,那么邮箱用户可能会丢失或者漏看一些很重要的邮件。

$$ Recall = \frac{TP}{TP+FN} $$

召回率 Recall :又被称为查全率,表示 预测结果为正样本中实际正样本数量 全样本中正样本 的比例。
使用场景:当正例被错误的预测为反例(FN)产生的代价很高时,适合用召回率。根据公式可知,召回率越高,FN越小。比如说在银行的欺诈检测或医院的病患者检测中,如果将欺诈性交易(实际为正)预测为非欺诈性交易(预测为负),则可能会给银行带来非常严重的损失。

F1-score

$$ F_1 = 2*\frac{Precision*Recall}{Precision+Recall} $$

F1 score是精确率和召回率的一个加权平均。Precision体现了模型对负样本的区分能力,Precision越高,模型对负样本的区分能力越强;Recall体现了模型对正样本的识别能力,Recall越高,模型对正样本的识别能力越强。F1 score是两者的综合,F1 score越高,说明模型越稳健。

ROC曲线与AUC指标

将真阳性率(True Positive Rate,TPR)和假阳性率(False Positive Rate,FPR)作为横纵坐标来描绘分类器在不同阈值下的性能。当二者相等时,表示的意义则是:对于不论真实类别是1还是0的样本,分类器预测为1的概率是相等的,此时AUC为0.5
FPR的值等于1-特异性。特异性(Specificity)是指在所有实际为负例的样本中,模型正确地预测为负例的样本比例,其衡量的是模型对负例样本的判断能力。假如一个模型的特异性很高,则该模型在预测负例时的准确率很高,也就是说,该模型较少将负例预测为正例,从而使得假阳性率较低。因此,假阳性率和特异性都是用来衡量模型在负例样本上的性能,它们之间是负相关的,即假阳性率越低,特异性越高,反之亦然。

AUC(ROC曲线下面积)是ROC曲线下的面积,用于衡量分类器性能。AUC值越接近1,表示分类器性能越好;反之,AUC值越接近0,表示分类器性能越差。在实际应用中,我们常常通过计算AUC值来评估分类器的性能。
AUC的概率意义是随机取一对正负样本,正样本得分大于负样本的概率。AUC的最小值为0.5,最大值为1,取值越高越好。AUC=1,完美分类器,采用这个预测模型时,不管设定什么阈值都能得出完美预测。绝大多数预测的场合,不存在完美分类器。0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。

最终AUC的范围在[0.5, 1]之间,并且越接近1越好。AUC只能用来评价二分类

参阅: