sklearn机器学习——逻辑回归
Posted 咸鱼2K
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sklearn机器学习——逻辑回归相关的知识,希望对你有一定的参考价值。
1 概述
1.1 名为“回归”的分类器
x为特征向量
通过函数,线性回归使用输入的特征矩阵X来输出一组连续型的标签值y_pred,以完成各种预测连续型变量的任务(比如预测产品销量,预测股价等等)。那如果我们的标签是离散型变量,尤其是,如果是满足0-1分布的离散型变量,我们要怎么办呢?我们可以通过引入联系函数(link function),将线性回归方程z变换为g(z)。
并且令g(z)的值分布在(0,1)之间,且当g(z)接近0时样本的标签为类别0,当g(z)接近1时样本的标签为类别1,这样就得到了一个分类模型
z越大,越靠近1;越小越靠近0。都是无限趋近
限此,其对应的模型被称为”对数几率回归“(logistic Regression),也就是我们的逻辑回归
长年以来人们都是以”返回概率“的方式来理解逻辑回归,并且这样使用它的性质。可以说,逻辑回归返回的数字,即便本质上不是概率,却也有着概率的各种性质,可以被当成是概率来看待和使用。
不是概率,但也有概率的性质,此处当作概念使用。
1.2 为什么需要逻辑回归
逻辑回归的原理其实并不简单。一个人要理解逻辑回归,必须要有一定的数学基础,必须理解损失函数,正则化,梯度下降,海森矩阵等等这些复杂的概念,才能够对逻辑回归进行调优。其涉及到的数学理念,不比支持向量机少多少。况且,要计算概率,朴素贝叶斯可以计算出真正意义上的概率,要进行分类,机器学习中能够完成二分类功能的模型简直多如牛毛。因此,在数据挖掘,人工智能所涉及到的医疗,教育,人脸识别,语音识别这些领域,逻辑回归没有太多的出场机会。(也就是是或者不是等分类,非线性的情况)
逻辑回归对线性关系的拟合效果好到丧心病狂(非线性则不好用)
逻辑回归计算快
逻辑回归返回的分类结果不是固定的0,1,而是以小数形式呈现的类概率数字
由此,我们已经了解了逻辑回归的本质,它是一个返回对数几率的,在线性数据上表现优异的分类器,它主要被应用在金融领域。其数学目的是求解能够让模型对数据拟合程度最高的参数的值,以此构建预测函数,然后将特征矩阵输入预测函数来计算出逻辑回归的结果y。注意,虽然我们熟悉的逻辑回归通常被用于处理二分类问题,但逻辑回归也可以做多分类
1.3 sklearn中的逻辑回归
2 linear_model.LogisticRegression
2.1 二元逻辑回归的损失函数
2.1.1 损失函数的概念与解惑
使用”损失函数“这个评估指标,来衡量参数为的模型拟合训练集时产生的信息损失的大小,并以此衡量参数的优劣。
如果用一组参数建模后,模型在训练集上表现良好,那我们就说模型拟合过程中的损失很小,损失函数的值很小,这一组参数就优秀;相反,如果模型在训练集上表现糟糕,损失函数就会很大,模型就训练不足,效果较差,这一组参数也就比较差。即是说,我们在求解参数时,追求损失函数最小,让模型在训练数据上的拟合效果最优,即预测准确率尽量靠近100%
由于我们追求损失函数的最小值,让模型在训练集上表现最优,可能会引发另一个问题:如果模型在训练集上表示优秀,却在测试集上表现糟糕,模型就会过拟合。虽然逻辑回归和线性回归是天生欠拟合的模型,但我们还是需要控制过拟合的技术来帮助我们调整模型,对逻辑回归中过拟合的控制,通过正则化来实现。——————用正则化来防止过拟合
2.2 重要参数penalty & C
2.2.1 正则化
正则化也就是加入惩罚的方法,以防止模型过拟合。
有L1和L2两种方式。
因为常数不能与参数的拟合运算,所以j要大于等于1
上下两个式子是同一个意思,但在sklearn中是前者,常数项C是在损失函数的前面,通过调控损失函数身的大小,来调节对模型的惩罚。
当正则化强度逐渐增大(即C逐渐变小),参数的取值会逐渐变小,但L1正则化会将参数压缩为0,L2正则化只会让参数尽量小,不会取到0。
在L1正则化在逐渐加强的过程中,携带信息量小的、对模型贡献不大的特征的参数,会比携带大量信息的、对模型有巨大贡献的特征的参数更快地变成0,所以L1正则化本质是一个特征选择的过程,掌管了参数的“稀疏性”。L1正则化越强,参数向量中就越多的参数为0,参数就越稀疏,选出来的特征就越少,以此来防止过拟合。因此,如果特征量很大,数据维度很高,我们会倾向于使用L1正则化。由于L1正则化的这个性质,逻辑回归的特征选择可以由Embedded嵌入法来完成。
维度很高是,用L1;维度不高时,用L2
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_breast_cancer
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
data = load_breast_cancer()
X = data.data
y = data.target
data.data.shape
lrl1 = LR(penalty="l1",solver="liblinear",C=0.5,max_iter=1000)
lrl2 = LR(penalty="l2",solver="liblinear",C=0.5,max_iter=1000)
#逻辑回归的重要属性coef_,查看每个特征所对应的参数
lrl1 = lrl1.fit(X,y)
lrl1.coef_
(lrl1.coef_ != 0).sum(axis=1)
lrl2 = lrl2.fit(X,y)
lrl2.coef_
l1= []
l2 = []
l1test = []
l2test = []
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
for i in np.linspace(0.05,1,19):#0.05到1之间,取19个数
lrl1 = LR(penalty="l1",solver="liblinear",C=i,max_iter=1000)
lrl2 = LR(penalty="l2",solver="liblinear",C=i,max_iter=1000)
lrl1 = lrl1.fit(Xtrain,Ytrain)
l1.append(accuracy_score(lrl1.predict(Xtrain),Ytrain))
l1test.append(accuracy_score(lrl1.predict(Xtest),Ytest))
lrl2 = lrl2.fit(Xtrain,Ytrain)
l2.append(accuracy_score(lrl2.predict(Xtrain),Ytrain))
l2test.append(accuracy_score(lrl2.predict(Xtest),Ytest))
graph = [l1,l2,l1test,l2test]
color = ["green","black","lightgreen","gray"]
label = ["L1","L2","L1test","L2test"]
plt.figure(figsize=(6,6))
for i in range(len(graph)):
plt.plot(np.linspace(0.05,1,19),graph[i],color[i],label=label[i])
plt.legend(loc=4) #图例的位置在哪里?4表示,右下角
plt.show()
2.2.2 逻辑回归中的特征工程
也就降维
(1)业务选择
(2)PCA和SVD一般不用
说到降维,我们首先想到的是之前提过的高效降维算法,PCA和SVD,遗憾的是,这两种方法大多数时候不适用于逻辑回归。
逻辑回归是由线性回归演变而来,线性回归的一个核心目的是通过求解参数来探究特征X与标签y之间的
关系,而逻辑回归也传承了这个性质,我们常常希望通过逻辑回归的结果,来判断什么样的特征与分类结果相关,因此我们希望保留特征的原貌。PCA和SVD的降维结果是不可解释的,因此一旦降维后,我们就无法解释特征和标签之间的关系了。当然,在不需要探究特征与标签之间关系的线性数据上,降维算法PCA和SVD也是可以使用的。
重点是SVD是没有解释 意义的
(3)统计方法可以使用,但不是非常必要
逻辑回归不用考虑数据总体分布和方差,也不用排除特征之间的共线性
(4)高效的嵌入法embedded
由于L1正则化会使得部分特征对应的参数为0,因此L1正则化可以用来做特征选择,结合嵌入法的模块SelectFromModel,我们可以很容易就筛选出让模型十分高效的特征。
注意,此时我们的目的是,尽量保留原数据上的信息,让模型在降维后的数据上的拟合效果保持优秀,因此我们不考虑训练集测试集的问题,把所有的数据都放入模型进行降维
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_breast_cancer
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectFromModel
data = load_breast_cancer()
data.data.shape
LR_ = LR(solver="liblinear",C=0.9,random_state=420)
cross_val_score(LR_,data.data,data.target,cv=10).mean()
X_embedded = SelectFromModel(LR_,norm_order=1).fit_transform(data.data,data.target)
#模型选择中,thredhold是一个非常重要的参数,用到超参数调参。
#此处norm是指范式的意思,norm_order=1是指用范式1进行过滤
#X_embedded就是过滤后的数据
X_embedded.shape
#缩减成了9个特征
cross_val_score(LR_,X_embedded,data.target,cv=10).mean()
#降成了93%,也不算差太多
#下一步为进行超参数调参,处理threhold
在这里,我们有两种调整方式:
1)调节SelectFromModel这个类中的参数threshold
这是嵌入法的阈值,表示删除所有参数的绝对值低于这个阈值的特征。也就是只有高过这个阈值的才行。
现在threshold默认为None,所以SelectFromModel只根据L1正则化的结果来选择了特征,即选择了所有L1正则化后参数不为0的特征。
我们此时,只要调整threshold的值(画出threshold的学习曲线),就可以观察不同的threshold下模型的效果如何变化。
一旦调整threshold,就不是在使用L1正则化选择特征,而是使用模型的属性.coef_中生成的各个特征的系数来选择。
coef_虽然返回的是特征的系数,但是系数的大小和决策树中的feature_ importances_以及降维算法中的可解释性方差explained_vairance_概念相似,其实都是衡量特征的重要程度和贡献度的,因此SelectFromModel中的参数threshold可以设置为coef_的阈值,即可以剔除系数小于
threshold中输入的数字的所有特征。
fullx = []
fsx = []
threshold = np.linspace(0,abs((LR_.fit(data.data,data.target).coef_)).max(),20)
#threshold从0到系数的最大值之间,取20个数
k=0
for i in threshold:#i是threshold里面的具体数
X_embedded = SelectFromModel(LR_,threshold=i).fit_transform(data.data,data.target)
fullx.append(cross_val_score(LR_,data.data,data.target,cv=5).mean())
fsx.append(cross_val_score(LR_,X_embedded,data.target,cv=5).mean())
print((threshold[k],X_embedded.shape[1]))
#k在这里是整数,记录的是索引
k+=1
plt.figure(figsize=(20,5))
plt.plot(threshold,fullx,label="full")
plt.plot(threshold,fsx,label="feature selection")
plt.xticks(threshold)
plt.legend()
plt.show()
也就是这样调节threshold没有什么意义,一直在下降
这种方法其实是比较无效的,大家可以用学习曲线来跑一跑:当threshold越来越大,被删除的特征越来越多,模型的效果也越来越差,模型效果最好的情况下需要保证有17个以上的特征。
2)第二种调整方法,是调逻辑回归的类LR_,通过画C的学习曲线来实现:
fullx = []
fsx = []
C=np.arange(0.01,10.01,0.5)
for i in C:
LR_ = LR(solver="liblinear",C=i,random_state=420)#根据不同的i,训练模型
fullx.append(cross_val_score(LR_,data.data,data.target,cv=10).mean())#不筛选,用全部的特征向量
X_embedded = SelectFromModel(LR_,norm_order=1).fit_transform(data.data,data.target)#筛选出剩余的特征向量
fsx.append(cross_val_score(LR_,X_embedded,data.target,cv=10).mean())#根据筛选出来的特征向量来评分
print(max(fsx),C[fsx.index(max(fsx))])#打印最高的分数,以及最高分数对应索引对应的C的数值
plt.figure(figsize=(20,5))
plt.plot(C,fullx,label="full")
plt.plot(C,fsx,label="feature selection")
plt.xticks(C)
plt.legend()
plt.show()
继续细化学习曲线:
选择5.01附近
#继续细化学习曲线:选择5.01附近
fullx = []
fsx = []
C=np.arange(6.05,7.05,0.005)
for i in C:
LR_ = LR(solver="liblinear",C=i,random_state=420)
fullx.append(cross_val_score(LR_,data.data,data.target,cv=10).mean())
X_embedded = SelectFromModel(LR_,norm_order=1).fit_transform(data.data,data.target)
fsx.append(cross_val_score(LR_,X_embedded,data.target,cv=10).mean())
print(max(fsx),C[fsx.index(max(fsx))])#打印最高的分数,以及最高分数对应索引对应的C的数值
plt.figure(figsize=(20,5))
plt.plot(C,fullx,label="full")
plt.plot(C,fsx,label="feature selection")
plt.xticks(C)
plt.legend()
plt.show()
验证:
发现筛选之后,比没筛选的分数更高
2.3 梯度下降:重要参数max_iter
逻辑回归的数学目的是求解能够让模型最优化,拟合程度最好的参数的值,即求解能够让损失函数最小化的值。
对于二元逻辑回归来说,有多种方法可以用来求解参数,最常见的有梯度下降法(Gradient Descent),坐标下降法(Coordinate Descent),牛顿法(Newton-Raphson method)等,其中又以梯度下降法最为著名。每种方法都涉及复杂的数学原理,但这些计算在执行的任务其实是类似的。
2.3.1 梯度下降求解逻辑回归
梯度法,损失函数最小点,就是图像的最低点,蓝色区域
在这个图像上随机放一个小球,当我松手,这个小球就会顺着这个华丽的平面滚落,直到滚到深蓝色的区域——损失函数的最低点。为了严格监控这个小球的行为,我让小球每次滚动的距离有限,不让他一次
性滚到最低点,并且最多只允许它滚动100步,还要记下它每次滚动的方向,直到它滚到图像上的最低点。
可以看见,小球从高处滑落,在深蓝色的区域中来回震荡,最终停留在了图像凹陷处的某个点上。非常明显,我们可以观察到几个现象:
首先,小球并不是一开始就直向着最低点去的,它先一口气冲到了蓝色区域边缘,后来又折回来,我们已经规定了小球是多次滚动,所以可见,小球每次滚动的方向都是不同的。
另外,小球在进入深蓝色区域后,并没有直接找到某个点,而是在深蓝色区域中来回震荡了数次才停下。这有两种可能:1) 小球已经滚到了图像的最低点,所以停下了,2) 由于我设定的步数限制,小球还没有找到最低点,但也只好在100步的时候停下了。也就是说,小球不一定滚到了图像的最低处。
但无论如何,小球停下的就是我们在现有状况下可以获得的唯一点了。如果我们够幸运,这个点就是图像的最低点,那我们只要找到这个点的对应坐标( ),就可以获取能够让损失函数最小的参数取值了。如
此,梯度下降的过程就已经完成。
小球多次滚动的可能原因
在这个过程中,小球其实就是一组组的坐标点;小球每次滚动的方向就是那一个坐标点的梯度向量的方
向,因为每滚动一步,小球所在的位置都发生变化,坐标点和坐标点对应的梯度向量都发生了变化,所以每次滚动的方向也都不一样;人为设置的100次滚动限制,就是sklearn中逻辑回归的参数max_iter,代表着能走的最大步数,即最大迭代次数。
参数max_iter的意义~即梯度下降法中的最大迭代次数
所以梯度下降,其实就是在众多可能的值中遍历,一次次求解坐标点的梯度向量,不断让损失函数的取值
逐渐逼近最小值,再返回这个最小值对应的参数取值的过程。
2.3.2 梯度下降的概念与解惑
核心误区:到底在哪个函数上,求什么的偏导数?
在多元函数(损失函数)上对自变量(逻辑回归的预测函数y(x)的参数求偏导,求解梯度的方式,和逻辑回归本身的预测函数y(x)没有一丝联系。
所以梯度下降,其实就是在众多可能的值中遍历,一次次求解坐标点的梯度向量,不断让损失函数的取值
逐渐逼近最小值,再返回这个最小值对应的参数取值的过程。
那梯度有什么含义呢?梯度是一个向量,因此它有大小也有方向。它的大小,就是偏导数组成的向量的大小,又叫做向量的模,记作d。
它的方向,几何上来说,就是损失函数的值增加最快的方向,就是小球每次滚动的方向的反方向。只要沿着梯度向量的反方向移动坐标,损失函数的取值就会减少得最快,也就最容易找到损失函数的最小值。
2.3.3 步长的概念与解惑
所以,步长不是任何物理距离,它甚至不是梯度下降过程中任何距离的直接变化,它是梯度向量的大小上的一个比例,影响着参数向量每次迭代后改变的部分。
步长相当于一个缩放器,影响每一次迭代后走的距离。
max_iter为迭代次数,迭代次数越多,步长越小
l2 = []
l2test = []
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
for i in np.arange(1,201,10):
lrl2 = LR(penalty="l2",solver="liblinear",C=0.9,max_iter=i)
lrl2 = lrl2.fit(Xtrain,Ytrain)
l2.append(accuracy_score(lrl2.predict(Xtrain),Ytrain))
l2test.append(accuracy_score(lrl2.predict(Xtest),Ytest))
graph = [l2,l2test]#二维数组
color = ["black","gray"]
label = ["L2","L2test"]
plt.figure(figsize=(20,5))
for i in range(len(graph)):
plt.plot(np.arange(1,201,10),graph[i],color[i],label=label[i])#分别对二维数组做图
plt.legend(loc=4)
plt.xticks(np.arange(1,201,10))
plt.show()
#我们可以使用属性.n_iter_来调用本次求解中真正实现的迭代次数
lr = LR(penalty="l2",solver="liblinear",C=0.9,max_iter=300).fit(Xtrain,Ytrain)
lr.n_iter_
尽管设置了最大迭代次数,但实际只有25次
2.4 二元回归与多元回归:重要参数solver & multi_class
之前我们对逻辑回归的讨论,都是针对二分类的逻辑回归展开,其实sklearn提供了多种可以使用逻辑回归处理多分类问题的选项。比如说,我们可以把某种分类类型都看作1,其余的分类类型都为0值,和”数据预处理“中的二值化的思维类似,这种方法被称为"一对多"(One-vs-rest),简称OvR,在sklearn中表示为“ovr"。
又或者,我们可以把好几个分类类型划为1,剩下的几个分类类型划为0值,这是一种”多对多“(Many-vs-Many)的方法,简称MvM,在sklearn中表示为"Multinominal"。每种方式都配合L1或L2正则项来使用。
也就是二分类的问题,也可以处理多分类的类型。但实际上也有多分类的的参数来处理多分类的问题。
sklearn为我们提供了多种选择,让我们可以使用不同的求解器来计算逻辑回归。求解器的选择,由参数"solver"控制,共有五种选择。其中“liblinear”是二分类专用,也是现在的默认求解器。
有灰色底的是不利的特点
使用时,针对不同的数据情况找这张表使用
对于未标准化的数据集,可以进行标准化处理后,再使用求解器中可以使用的方法
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
iris = load_iris()
for multi_class in ('multinomial', 'ovr'):
clf = LogisticRegression(solver='sag', max_iter=100, random_state=42,
multi_class=multi_class).fit(iris.data, iris.target)
#打印两种multi_class模式下的训练分数
#%的用法,用%来代替打印的字符串中,想由变量替换的部分。%.3f表示,保留三位小数的浮点数。%s表示,字符串。
#字符串后的%后使用元祖来容纳变量,字符串中有几个%,元祖中就需要有几个变量
print("training score : %.3f (%s)" % (clf.score(iris.data, iris.target),multi_class))
#打印的格式,用%表示变量。后面跟元组,元组里面的元素,分别按顺序表示变量
结论:对于鸢尾花数据,是多分类类型,用多分类的multi_class比较好。multinomial
2.5样本不均衡与参数class_weight
重视小样本的其中一个例子:
在银行要判断“一个新客户是否会违约”,通常不违约的人vs违约的人会是99:1的比例,真正违约的人其实是非常少的。这种分类状况下,即便模型什么也不做,全把所有人都当成不会违约的人,正确率也能有99%,这使得模型评估指标变得毫无意义,根本无法达到我们的“要识别出会违约的人”的建模目的。
因此我们要使用参数class_weight对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少数类,向捕获少数类的方向建模。该参数默认None,此模式表示自动给与数据集中的所有标签相同的权重,即自动1:1。当误分类的代价很高的时候,我们使用”balanced“模式,我们只是希望对标签进行均衡的时候,什么都不填就可以解决样本不均衡问题。
即默认是1:1的权重
以上是关于sklearn机器学习——逻辑回归的主要内容,如果未能解决你的问题,请参考以下文章