逻辑回归
-
逻辑回归,是一种名为"回归"的线性分类器,其本质是由线型回归变化而来,一种广泛使用分类问题中的广义回归算法。
-
线性回归是机器学习中最简单的的回归算法,回归函数用z来表示,𝛉0被称为截距(intercept), 𝛉1~𝛉n 被称为系数(coefficient),使用矩阵表示方程:
1 Sigmod
函数
-
Sigmoid函数是一个S型的函数,当自变量z趋近正无穷时,因变量g(z)趋近于1,而当z趋近负无穷时,g(z)趋近于0,它能够将任何实数(非0和1的标签数据)映射到(0,1)区间,使其可用于将任意值函数转换为更适合二分类的函数。 因为这个性质,Sigmoid函数也被当作是归一化的一种方法,与
MinMaxSclaer
同理,是属于数据预处理中的“缩放”功能,可以将数据压缩到[0,1]之内。区别在于,MinMaxScaler
归一化之后,是可以取到0和1的(最大值归一化后就是1,最小值归一化后就是0),但Sigmoid函数只是无限趋近于0和1。 -
将线性回归方程z变换为g(z),并且将g(z)的值分布在(0,1)之间,且当g(z)接近0时样本的标签为类别0,当g(z)接近1时样本的标签为类别1,这样就得到了一个分类模型。
2.逻辑回归和线性回归之间的关联
- 线性回归中 z=𝛉Tx,于是我们将z带入,就得到了二元逻辑回归模型的一般形式:
- y(x)就是我们逻辑回归返回的标签值。此时y(x)的取值都在[0,1]之间,即y(x)+(1-y(x))=1。如果我们将y(x)除以1-y(x)可以得到形似几率的y(x)/1-y(x),在此基础上取对数,可以很容易就得到:
-
即y(x)逻辑回归的形似几率取对数的本质其实就是我们的线性回归z,我们实际上是在对线性回归模型的预测结果取对数几率来让其的结果无限逼近0和1。因此,其对应的模型被称为”对数几率回归“(logistic Regression),也就 是我们的逻辑回归,这个名为“回归”却是用来做分类工作的分类器。
- 逻辑回归的形似几率取对数就是线性回归
- 线性回归解的对数几率就是逻辑回归
-
线性回归的核心任务是通过求解θ构建z这个预测函数,并希望预测函数z能够尽量拟合数据,因此逻辑回归的核心任务也是类似的:求解θ来构建一个能够尽量拟合数据的预测函数z,并通过向预测函数中输入特征矩阵来获取相应的标签值y。
3.逻辑回归的优点
- 逻辑回归是一个受工业商业热爱,使用广泛的模型。
- 1.逻辑回归对线性关系(特征与标签之间的线性关系极强的数据)的拟合效果非常好。相对,逻辑回归在非线性数据中的效果非常差,事先知道数据之间的联系是非线性的,一定不要使用逻辑回归。
- 其实最简单判别一个模型是否为线性的,只需要判别决策边界是否是直线,也就是是否能用一条直线来划分
- 2.逻辑回归计算快:对于线性数据,逻辑回归的拟合和计算都非常快,计算效率优于SVM和随机森林,在大型数据上尤其能看出区别。
- 3.逻辑回归返回的分类结果不是固定的0,1,而是以小数形式呈现的类概率数字:我们因此可以把逻辑回归返回的结果当成连续型数据来利用。比如在评分卡制作时,我们不仅需要判断客户是否会违约,还需要给出确定的”信用分“,而这个信用分的计算就需要使用类概率计算出的对数几率(概率)。
- 1.逻辑回归对线性关系(特征与标签之间的线性关系极强的数据)的拟合效果非常好。相对,逻辑回归在非线性数据中的效果非常差,事先知道数据之间的联系是非线性的,一定不要使用逻辑回归。
- 逻辑回归的本质是一个返回对数几率的在线性数据上表现优异的分类器,它主要被应用在金融领域。注意,虽然逻辑回归通常被用于处理二分类问题,但逻辑回归也可以做多分类。
4.逻辑回归的损失函数
- 在逻辑回归分类的时候,不管原始样本中的类别使用怎样的值或者文字表示,逻辑回归统一将其视为0类别和1类别。
- 对数似然损失函数:
- 使用-log函数为损失函数的原因
- 损失函数的本质就是,如果我们预测对了,则没有损失,反之则损失需要变的很大,而-log函数在【0,1】之间正好符合。
- log(h)表示分类到正例1的损失
- log(1-h)表示分类到反例0的损失
- 损失函数表征预测值与真实值之间的差异程度,如果预测值与真实值越接近则损失函数应该越小
- 使用-log函数为损失函数的原因
-
损失函数解释
yilog(h)
表示分类到真实标签的正例的损失,根据-log函数得知如果分类正确则损失值小,反之损失大-(1-yi)log(1-h)
表示分类到真实标签反例的损失,根据-log函数得知如果分类正确则损失小,反之损失大- 两者相加就获得了逻辑回归模型总分类结果的损失,例如:
5.梯度下降
- 逻辑回归的数学目的是求解能够让模型最优化,拟合程度最好的参数𝛉的值,即求解能够让损失函数J(𝛉)最小化的𝛉值。
- 梯度下降,其实就是在众多[𝛉1,𝛉2]可能的值中遍历,一次次求解坐标点的梯度向量,不断让损失函数的取值J逐渐逼近于最小值,再返回这个最小值对应的参数取值[𝛉1,𝛉2]的过程。
5.1正则化
-
由于我们追求损失函数的最小值,让模型在训练集上表现最优,可能会引发另一个问题:如果模型在训练集上表示优秀,却在测试集上表现糟糕,模型就会过拟合。所以我们还需要使用控制过拟合来帮助我们调整模型,对过拟合的控制,通过正则化来实现。
-
正则化是用来防止模型过拟合的过程,常用的有L1正则化和L2正则化两种选项,分别通过在损失函数后加上参数向量𝛉的L1范式和L2范式的倍数来实现。这个增加的范式,被称为“正则项”,也被称为"惩罚项"。
-
L1范式【L1范式表现为参数向量𝛉中的每个参数的绝对值之和】
- L2范式【L2范数表现为参数向量𝛉中的每个参数的平方和的开方值】
-
J(𝛉)
是损失函数,C是用来控制正则化程度的超参数,n是方程中特征的总数,也是方程中参数的总数,j代表每个𝛉参数(w系数)。j要大于等于1,是因为我们的参数向量中,第一个参数是𝛉0,是我们的截距它通常是不参与正则化的。 -
通过正则化的L1和L2范式可以加入惩罚项C来矫正模型的拟合度。因为C越小则损失函数会越大表示正则化的效力越强,参数𝛉会被逐渐压缩得越来越小
-
L1正则化会将参数w压缩为0,L2正则化只会让参数尽量小,不会取到0
-
L1和L2范式的区别
- 在L1正则化在逐渐加强的过程中,携带信息量小的,对模型贡献不大的特征的参数w,会比携带大量信息的,对模型有巨大贡献的特征的参数更快地变成0,所以L1正则化的本质是一个特征选择的过程。L1正则化越强,参数向量中就越多的参数0,选出来的特征就越少,以此来防止过拟合。因此,如果特征量很大,数据维度很高,会倾向于使用L1正则化
- L2正则化在加强的过程中,会尽量让每个特征对模型都有一些小的贡献,但携带信息少,对模型贡献不大的特征的参数w会非常接近于0,如果我们的主要目的只是为了防止过拟合,选择L2就可以了,但是如果选择L2正则化后还是过拟合,模型在未知数据集上的效果表现很差,就可以考虑L1正则化
from sklearn.linear_model import LogisticRegression as LR from sklearn.datasets import load_breast_cancer import numpy as np from sklearn.model_selection import train_test_split #加载样本数据 data = load_breast_cancer() X = data.data y = data.target #建立两种模型 lrl1 = LR(penalty="l1",C=0.1,solver='liblinear') lrl2 = LR(penalty="l2",C=0.1, solver='liblinear') #逻辑回归的重要属性coef_,查看每个特征所对应的参数 lrl1.fit(X,y) print('L1范式:',lrl1.coef_) # L1范式: [[ 0.701396 0. 0.23855475 -0.00449913 0. 0. # 0. 0. 0. 0. 0. 0. # 0. -0.04874308 0. 0. 0. 0. # 0. 0. 0.42809827 -0.13096795 -0.13658048 -0.02003529 # 0. 0. 0. 0. 0. 0. ]] lrl2 = lrl2.fit(X,y) print('L2范式:',lrl2.coef_) # L2范式: [[ 0.6696085 0.08701521 0.31941682 -0.011727 -0.02583367 -0.11308548 # -0.1616179 -0.07059674 -0.03609775 -0.00644473 0.02286071 0.28965163 # 0.05778744 -0.07476973 -0.00254651 -0.02051678 -0.03054608 -0.00904962 # -0.00835848 -0.00165526 0.66693562 -0.23022737 -0.21636058 -0.01551804 # -0.04769801 -0.34625492 -0.43524623 -0.13641774 -0.11482434 -0.03257099]]
5.逻辑回归API
-
from sklearn.linear_model import LogisticRegression
-
超参数介绍
-
penalty
:可以输入l1或者l2来指定使用哪一种正则化方式,默认为"l2",如果选择"l1"正则化,参数solver
仅能够使用求解方式"liblinear
"和saga
,若使用"l2"正则化,参数solver
中所有的求解方式都可以使用 -
C
:惩罚项,必须是一个大于0的浮点数,默认为1.0,即默认正则化与损失函数的比值是1:1,C越小,损失函数越大,模型对损失函数的惩罚越重,正则化的效力越强,参数会逐渐被压缩得越来越小 -
max_iter
:梯度下降能走的最大步数,默认为100,步数的不同取值可以帮助我们获得不同的损失函数的损失值。目前没有好的方法可以计算出最优的max_iter
的值,一般是通过绘制学习曲线对其进行取值 -
solver
:使用不同的求解器来计算逻辑回归,即求解器的选择,五种选择liblinear
:是二分类专用(梯度下降),默认的求解器lbfgs,newton-cg,sag,saga
:是多分类专用,几乎不用
-
-
multi_class
:输入ovr
,multinomial
,auto
来告知模型我们要处理的分类问题的类型,默认是ovr
ovr
:表示分类问题是二分类,或让模型使用"一对多"的形式来处理多分类问题multinomial
:表示处理多分类问题,参数solver=libinear
时这个参数不可用auto
:表示会根据数据的分类情况和其他参数来确定模型要处理的分类问题的类型,比如,如果数据是二分类,或者solver=liblinear
,auto
会默认选择ovr
,反之,则会选择multinomial
-
class_weight
:表示样本不平衡处理的参数,样本不平衡指的是在一组数据中,某一类标签天生占有很大的比例,或误分类的代价很高,即我们想要捕捉出某种特定的分类时候的情况。None
:使用参数class_weight
对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少数类,向捕获少数类的方向建模,该参数默认为None
,此模型表示自动给与数据集中得所有标签相同的权重,即自动1:1- 什么情况下无误分类的代价很高?
- 在银行要判断“一个新客户是否会违约”,通常不违约的人vs违约的人=99:1,真正违约的人其实是非常少的。这种分类状况下,即便模型什么也不做,全把所有人都当成不会违约的人,正确率也能有99%, 这使得模型评估指标变得毫无意义,根本无法达到我们的“要识别出会违约的人”的建模目的。
- 我们要对潜在犯罪者和普通人进行分类,如果没有能够识别出潜在犯罪者,那么这些人就可能去危害社会,识别失败的代价会非常高,但如果,我们将普通人错误地识别成了潜在犯罪者,代价却相对较小。所以我们宁愿将普通人分类为潜在犯罪者后再人工甄别,但是却不愿将潜在犯罪者 分类为普通人。
-
balanced
:当误差的代价很高的时候,我们使用balanced
模式,可以解决样本不均衡问题 -
乳腺癌数据集下,
max_iter
的学习曲线from sklearn.metrics import accuracy_score import matplotlib.pyplot as plt from sklearn.linear_model import LogisticRegression as LR from sklearn.datasets import load_breast_cancer import numpy as np from sklearn.model_selection import train_test_split data=load_breast_cancer() x=data.data y=data.target l2=[] l2_test=[] x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3,random_state=420) for i in range(1,201,10): lrl2=LR(penalty='l2',solver='liblinear',C=0.9,max_iter=i) lrl2.fit(x_train,y_train) l2.append(accuracy_score(lrl2.predict(x_train),y_train)) l2_test.append(accuracy_score(lrl2.predict(x_test),y_test)) graph=[l2,l2_test] color=['black','red'] label = ["L2train","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()