知识背景
关于逻辑回归的几个问题
逻辑回归相比线性回归,有何异同?
逻辑回归和线性回归最大的不同点是逻辑回归解决的是分类问题,而线性回归解决的是回归问题。
逻辑回归又可以认为是广义线性回归的一种特殊形式,其特殊之处在于目标(label/target)的取值服从二元分布。
所谓逻辑回归是一种特殊的广义线性回归,我们可以通过狭义线性回归到逻辑回归的转化过程来理解。
狭义线性回归的表达式可表示为:
如果我们希望这个模型可以对二分类任务做出预测,即满足0,1分布,那么希望预测出来的值经过某种转换后可以分布在0,1两个值附近。
sigmoid函数可以做这样的转换,sigmoid函数的表达式为:
令:
则:
可以看出,狭义线性回归采用y作为预测结果,逻辑回归则采用作为预测结果。
逻辑回归还可以表示为:
通过以上步骤推演,我们知道逻辑回归就是在线性回归的基础上,再做一个sigmoid计算。所以它们都可以用相同的方法,比如梯度下降来求解参数。
回归问题常用的性能度量指标
- 点对点误差
- MSE(Mean Square Error)均方误差 -- 预测数据和原始数据对应误差的平方和的均值
- RMSE(Root Mean Square Error)-- 观测值与真实偏差的平方和与观测次数n比值的平方根,用来衡量观测值同真值之间的偏差
- MAE(Mean Absolute Error)-- 计算模型输出与真实值之间的平均绝对误差
- 带归一化的误差求解方法
- MAPE(Mean Absolute Percentage Error)-- 不仅考虑预测值与真实值误差,还考虑误差与真实值之间的比例
- MASE(Mean Absolute Scaled Error)-- 平均平方百分比误差
- 点对面误差
- R-Square决定系数(coefficient of determination)
分类问题常用的性能度量指标
-
准确率:
所有预测正确的样本与所有样本的比例
-
精确率:
预测为正的样本中有多少是真正的正样本
-
召回率:
样本中的正例有多少被预测正确了
-
F1值:
精确率和召回率的调和值
-
AUC(Area Under Curve):
ROC曲线下的面积,ROC曲线是通过取不同的阈值来分别计算在每个阈值下,FPR(False Positive Rate)和TPR(True Positive Rate)的值来绘制的。
逻辑回归处理多标签分类问题时,一般怎么做?
如果y不是在[0,1]中取值,而是在k个类别中取值,这时问题就变成一个多分类问题。有两种方式可以处理该问题:
- 当K个类别不是互斥的时候,即存在样本可能属于多个标签的情况,我们可以训练k个二分类的逻辑回归分类器。第i个分类器用以区分样本是否可以归为第i类,训练分类器时需要把标签重新整理为“第i类标签”和“非第i类标签”两类。通过这样的方法,我们就可以解决每个样本可能拥有多个标签的情况。
- 当K个类别互斥的时候,即每个样本只属于一个标签,可以假设每个样本属于不同标签的概率服从于几何分布,使用多项式逻辑回归(Softmax Regression)来进行分类。
通过几个问题了解了逻辑回归后,接着就上代码来体验一番吧
本文将使用sklearn和著名的鸢尾花数据集来用逻辑回归做分类预测
学习目标
- 了解逻辑回归理论
- 掌握sklearn中LogisticsRegression的基本用法
Step 1:读取数据
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
#load data
from sklearn.datasets import load_iris
data = load_iris() # 数据特征
iris_labels = data.target # 数据标签
iris_names = data.target_names #iris分类对应的名字
iris_features = pd.DataFrame(data=data.data, columns=data.feature_names)
查看数据基本信息,大致了解一下特征和标签
# 查看数据基本信息
iris_features.info()
print(iris_features.head())
print(iris_names)
结果如下:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 sepal length (cm) 150 non-null float64
1 sepal width (cm) 150 non-null float64
2 petal length (cm) 150 non-null float64
3 petal width (cm) 150 non-null float64
dtypes: float64(4)
memory usage: 4.8 KB
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
['setosa' 'versicolor' 'virginica']
如上基本信息我们了解到:
- 一共有150个样本
- 每个样本有4个特征,都是float型。
sepal length
,sepal width
,petal length
和petal width
分别代表花萼和花瓣的长宽。通过外观的长宽大小来判断植物的种类,也很符合人的学习习惯。 - 一共有3个标签:
setosa
,versicolor
,virginica
,分别代表3种花,在数据中表示为0, 1, 2.
再看看数据集中3类样本分别占的比例:
pd.Series(iris_labels).value_counts()
结果发现:
2 50
1 50
0 50
一共150个样本,3种花每种分别50个,分布还是很平均的嘛。
Step 2:数据可视化
一图胜千言,通过可视化的方法能更快更直观地了解数据,找到数据的特征,能让数据的学习和训练达到事半功倍的效果。
# 准备数据,为了不影响原数据,我们用原数据集的拷贝来画图
iris_all = iris_features.copy()
iris_all['label'] = iris_labels
# 查看数据分布
sns.pairplot(data=iris_all, diag_kind='hist', hue='label')
plt.show()
从图中可以看出:
- petal width和petal length的区分能力比较好
- 蓝色的这个类别主要集中在左下,集中在petal width和petal length的值比较小的部分,说明这个种类的花瓣又窄又短
通过箱线图来查看一下各个特征的数据分布:
# 从箱线图查看各个特征数据分布
for col in iris_features.columns:
sns.boxplot(x='label', y = col, saturation=0.5, palette = 'pastel', data=iris_all)
plt.title(col)
plt.show()
通过数据分布也能明显看出来,petal length 和 petal width 的数据在各类别内集中,且在各类别间有较明显的区分度。
选取前三个特征绘制三维散点图:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
iris_all_class0 = iris_all[iris_all['label']==0].values
iris_all_class1 = iris_all[iris_all['label']==1].values
iris_all_class2 = iris_all[iris_all['label']==2].values
ax.scatter(iris_all_class0[:,0], iris_all_class0[:,1], iris_all_class0[:,2], label='setosa')
ax.scatter(iris_all_class1[:,0], iris_all_class1[:,1], iris_all_class1[:,2], label='versicolor')
ax.scatter(iris_all_class2[:,0], iris_all_class2[:,1], iris_all_class2[:,2], label='virginica')
ax.set_xlabel('sepal length')
ax.set_ylabel('sepal width')
ax.set_zlabel('petal length')
plt.legend()
plt.show()
三维散点图更清晰地看到了类别的区分。
Step 3:逻辑回归二分类训练
先用逻辑回归进行二分类训练,训练出一个区分0、1标签的分类器。
# 分割数据集和训练集
from sklearn.model_selection import train_test_split
# 先选取类别为0和1的数据,进行二分类训练
iris_features_part = iris_all[iris_all['label']<2]
iris_labels_part = iris_features_part.pop('label').tolist()
x_train, x_test, y_train, y_test = train_test_split(iris_features_part, iris_labels_part, test_size=0.3, random_state=2020)
参数解释:
- test_size: test数据占的比例,例如0.3表示test数据占30%,train数据占70%
- random_state: 随机数种子,代表该组随机数的编号,在重复实验中能保证每次得到的随机数是相同的。当为None时,每次得到的结果就会是随机的。
分好数据集,就可以用逻辑回归模型来训练了
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(random_state=0, solver='lbfgs')
clf.fit(x_train, y_train)
参数solver
代表逻辑回归损失函数的优化方法,分别有4种方法,官方文档建议如下:
- ‘liblinear’:Small dataset or L1 penalty
- ‘lbfgs’、‘newton-cg’ or ‘sag’:Multinomial loss or large dataset
- ‘sag’:Very Large dataset
根据数据集的情况,我们选择了‘lbfgs’,这是拟牛顿法的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
# 在测试集和训练集上预测
train_pred = clf.predict(x_train)
test_pred = clf.predict(x_test)
# 预测结果检验
from sklearn import metrics
print('The accuracy in train data: ', metrics.accuracy_score(y_train, train_pred))
print('The accuracy in test data: ', metrics.accuracy_score(y_test, test_pred))
结果如下:
The accuracy in train data: 1.0
The accuracy in test data: 1.0
都达到了100%的准确率,就是这么秀~~
查看在测试集上的混淆矩阵:
confusion_matrix_result = metrics.confusion_matrix(test_pred, y_test)
plt.figure(figsize=(10, 8))
sns.heatmap(confusion_matrix_result, annot=True, cmap='Wistia_r')
plt.xlabel('Predict labels')
plt.ylabel('Actual labels')
plt.show()
Step 4:三种分类上的逻辑回归模型训练
和二分类任务下的方法相似,这次来进行多分类训练。从文章开头的几个问题可知,这个多分类问题属于互斥的多分类问题,即每个样本只能属于一个分类,我们使用多项式逻辑回归模型就可以了。
# 分割测试集和训练集(20%/80%)
x_train, x_test, y_train, y_test = train_test_split(iris_features, iris_labels, test_size=0.2, random_state=2020)
clf = LogisticRegression(random_state=0, solver='lbfgs')
clf.fit(x_train, y_train)
预测:
# 在训练集和测试集上预测
train_pred = clf.predict(x_train)
test_pred = clf.predict(x_test)
print('The accuracy in train data: ', metrics.accuracy_score(y_train, train_pred))
print('The accuracy in test data: ', metrics.accuracy_score(y_test, test_pred))
confusion_matrix_result = metrics.confusion_matrix(test_pred, y_test)
plt.figure(figsize=(10, 8))
sns.heatmap(confusion_matrix_result, annot=True, cmap='Wistia_r')
plt.xlabel('Predict labels')
plt.ylabel('Actual labels')
plt.show()
在三分类问题上的预测结果稍微有点偏差:
The accuracy in train data: 0.9833333333333333
The accuracy in test data: 0.8666666666666667
从图中能看出来,在1和2这两个类别下分别有两个预测错误。我们在之前的数据可视化中也可以看出来,橙色和绿色两个类别的分解有点模糊,这和我们之前的认识是相符合的。
总结
逻辑回归虽然叫“回归”,但实际上是一种分类算法。由线性回归经过sigmoid函数变换而来,所以预测值在0~1之间,通常用来解决二分类问题。
刚才用逻辑回归解决了二分类问题和多分类问题,这里的多分类问题是通过多项式损失函数优化来完成的,还可以将多个二分类逻辑回归模型组合来实现多分类。
END