目录
什么是逻辑回归?
Sigmoid 函数
似然函数
梯度下降
判定边界
损失函数
逻辑回归优点
逻辑回归缺点
代码实现
Logistic Regression参数详解
正则化选择参数:penalty
优化算法选择参数:solver
其他参数
代码案例
结果显示
每文一语
逻辑回归算法是用来解决分类问题的。回归与分类的区别在于:回归所预测的目标量的取值是连续的(例如房屋的价格);而分类所预测的目标变量的取值是离散的(例如判断肿瘤大小是否为恶性)。
如图所示,X为数据点-肿瘤的大小,Y为观测值-是否为恶性肿瘤(0良性肿瘤,1恶性肿瘤)。通过构建线性回归模型,即可根据肿瘤大小,预测是否为恶性肿瘤,hθ(x)≥.05为恶性,hθ(x)<0.5为良性。
那有的人就会提出疑问,逻辑回归可以做分类,那么线性回归可不可以做分类呢?答案是可以,但是线性回归的鲁棒性(稳定和可靠)很差,如下图所示,因最右边噪点的存在,使回归模型在训练上表现很差,这主要是由于线性回归在整个实数域内敏感度一致,而分类范围,需要在[0,1]。
逻辑回归就是一种减小预测范围,将预测值限定为[0,1]间的一种回归模型,此时引入了一个sigmoid函数,这个函数的性质,非常好的满足了,函数的定义域x的输入是全体实数,而值域输出y总是[0,1],以一种概率的形式表示. 并且当x=0的时,y=0.5, 这是决策边界。
Sigmoid 函数在有个很漂亮的“S”形,如下图所示:
对于线性回归的情况,方程如下:
构造预测函数为:取值在[0,1]
LogisticRegression回归模型在Sklearn.linear_model子类下,调用sklearn逻辑回归算法步骤比较简单,即:
(1) 导入模型。调用逻辑回归LogisticRegression()函数。
(2) fit()训练。调用fit(x,y)的方法来训练模型,其中x为数据的属性,y为所属类型。
(3) predict()预测。利用训练得到的模型对数据集进行预测,返回预测结果。
最大似然估计就是通过已知结果去反推最大概率导致该结果的参数。极大似然估计是概率论在统计学中的应用,它提供了一种给定观察数据来评估模型参数的方法,即 “模型已定,参数未知”,通过若干次试验,观察其结果,利用实验结果得到某个参数值能够使样本出现的概率为最大,则称为极大似然估计。逻辑回归是一种监督式学习,是有训练标签的,就是有已知结果的,从这个已知结果入手,去推导能获得最大概率的结果参数,只要我们得出了这个参数,那我们的模型就自然可以很准确的预测未知的数据了。
当我们确定了目标之后,我们就需要一个算法来解决问题,现在最常用也比较好用的就是梯度下降。
我们先来看看官网是怎么介绍梯度下降的:梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
大白话理解:就是你现在蒙眼在山顶要下山。你肯定要顺着山的一侧一步一步的往下挪是把,山的一侧就是梯度的,你每往下挪一点,就是在下降,当你达到山底的时候就成功了。
稍微专业点的:我们对一个多元函数求偏导,会得到多个偏导函数.这些导函数组成的向量,就是梯度.我们用梯度下降是用来求解一个损失函数的最小值,所谓下降实际上是这个损失函数的值在下降。
在梯度下降的算法中,还有两个比较重要的点叫做学习率和步数。
学习率是指你在下山的过程中每走的一步大小,你要是厉害能一步跨到山底也算是你厉害,当然你也有可能跨到对面山上,所以学习率我们在设置的时候就要设置的稍微小点,当然也别太小了,这样学习的会很慢。学习率是自己设定的,比如0.01。
迭代次数就是指你在下山过程中你要走多少步,小了的话你可能没有走到山底,多了的话可能你就会在山底来回的走。
学习率和迭代次数都是自定义的,需要通过经验和实际操作去验证和设置。最好在运行代码的时候打印出来,这个就会看到收敛的情况,当很长一段时间内,收敛的效果已经不明显了,基本就到头了。
梯度下降有三种方法:
批量梯度下降BGD(Batch Gradient Descent):优点:会获得全局最优解,易于并行实现。缺点:更新每个参数时需要遍历所有的数据,计算量会很大并且有很多的冗余计算,导致当数据量大的时候每个参数的更新都会很慢。
随机梯度下降SGD:优点:训练速度快;缺点:准确率下降,并不是全局最优,不易于并行实现。它的具体思路是更新没一个参数时都是用一个样本来更新。(以高方差频繁更新,优点是使得sgd会跳到新的和潜在更好的局部最优解,缺点是使得收敛到局部最优解的过程更加的复杂)
small batch梯度下降:结合了上述两点的优点,每次更新参数时仅使用一部分样本,减少了参数更新的次数,可以达到更加稳定的结果,一般在深度学习中采用这种方法。
通过上述的边界,应该也可以了解到逻辑回归的原理了,很容易想到我们的支持向量机采用的超平面,而逻辑回归则是使用的线性可分,那么必然会有损失函数
损失函数用来评价模型的预测值和真实值不一样的程度,损失函数越好,通常模型的性能越好。不同的模型用的损失函数一般也不一样。
损失函数分为经验风险损失函数和结构风险损失函数。经验风险损失函数指预测结果和实际结果的差别,结构风险损失函数是指经验风险损失函数加上正则项。
通俗点讲,损失函数 = 代价函数 = 目标函数,它们三个是一个意思。
但是它们之间还是有细微的差别的。
损失函数(Loss Function )是定义在单个样本上的,算的是一个样本的误差。
代价函数(Cost Function )是定义在整个训练集上的,是所有样本误差的平均,也就是损失函数的平均。
目标函数:目标函数跟它俩有联系,但不是一个意思。目标函数是一个最优化函数,它是由经验风险+结构风险(也就是Cost Function + 正则化项)构成。
比如说:我们需要优化模型,我们得有个目标把,比如说我们误差不能大于0.1 ,那这个0.1就是我们的目标函数,但是技术不行,最后只能把误差维持在0.2,那么0.1就是目标函数,0.2就是损失函数,当技术好的情况下,将误差降低在0.1 那么损失=目标。
下面介绍下 逻辑回归中常用的损失函数,逻辑回归主要是是分类模型,所以用的也都是分类方面的损失函数:
1)交叉熵损失:上面基本也都介绍了就不多说了。
2)Hinge Loss/多分类 SVM 损失:简言之,在一定的安全间隔内(通常是 1),正确类别的分数应高于所有错误类别的分数之和。因此 hinge loss 常用于最大间隔分类(maximum-margin classification),最常用的是支持向量机。
尽管不可微,但它是一个凸函数,因此可以轻而易举地使用机器学习领域中常用的凸优化器。
一是逻辑回归的算法已经比较成熟,预测较为准确;
二是模型求出的系数易于理解,便于解释,尤其在银行业,80%的预测是使用逻辑回归;
三是结果是概率值;
四是训练快。
分类较多的y都不是很适用;(多分类不适用)
对于自变量的多重共线性比较敏感,所以需要利用因子分析或聚类分析来选择代表性的自变量;(要做特征筛选)
另外预测结果呈现S型,两端概率变化小,中间概率变化大比较敏感,导致很多区间的变量的变化对目标概率的影响没有区分度,无法确定阈值。
总结一下:回归假设数据服从伯努利分布,通过极大似然函数的方法,运用梯度下降来求解参数,最终达到数据二分类的目的。
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
其中,参数 penalty 表示惩罚项( L1 、 L2 值可选
L1 向量中各元素绝对值的和,作用是产生少量的特征,而其他特征都是0,常用于特征选择;
L2向量中各个元素平方之和再开根号,作用是选择较多的特征,使他们都趋近于0。);
C值的目标函数约束条件:s.t.||w||1
在调参时如果我们主要的目的只是为了解决过拟合,一般penalty选择L2正则化就够了。但是如果选择L2正则化发现还是过拟合,即预测效果差的时候,就可以考虑L1正则化。另外,如果模型的特征非常多,我们希望一些不重要的特征系数归零,从而让模型系数稀疏化的话,也可以使用L1正则化。
penalty参数的选择会影响我们损失函数优化算法的选择。即参数solver的选择,如果是L2正则化,那么4种可选的算法{‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’}都可以选择。
但是如果penalty是L1正则化的话,就只能选择‘liblinear’了。这是因为L1正则化的损失函数不是连续可导的,而{‘newton-cg’, ‘lbfgs’,‘sag’}这三种优化算法时都需要损失函数的一阶或者二阶连续导数。而‘liblinear’并没有这个依赖。
solver参数决定了我们对逻辑回归损失函数的优化方法,有4种算法可以选择,分别是:
a) liblinear:使用了开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数。
b) lbfgs:拟牛顿法的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
c) newton-cg:也是牛顿法家族的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
d) sag:即随机平均梯度下降,是梯度下降法的变种,和普通梯度下降法的区别是每次迭代仅仅用一部分的样本来计算梯度,适合于样本数据多的时候,SAG是一种线性收敛算法,这个速度远比SGD快。关于SAG的理解,参考博文线性收敛的随机优化算法之 SAG、SVRG(随机梯度下降)
从上面的描述可以看出,newton-cg, lbfgs和sag这三种优化算法时都需要损失函数的一阶或者二阶连续导数,因此不能用于没有连续导数的L1正则化,只能用于L2正则化。而liblinear通吃L1正则化和L2正则化。
同时,sag每次仅仅使用了部分样本进行梯度迭代,所以当样本量少的时候不要选择它,而如果样本量非常大,比如大于10万,sag是第一选择。但是sag不能用于L1正则化,所以当你有大量的样本,又需要L1正则化的话就要自己做取舍了。要么通过对样本采样来降低样本量,要么回到L2正则化。
newton-cg, lbfgs和sag这三种优化算法时都需要损失函数的一阶或者二阶连续导数,因此不能用于没有连续导数的L1正则化,只能用于L2正则化。而liblinear通吃L1正则化和L2正则化。
这里的参数只做介绍,一般也不需要调试
sag每次仅仅使用了部分样本进行梯度迭代,所以当样本量少的时候不要选择它,而如果样本量非常大,比如大于10万,sag是第一选择。
但是sag不能用于L1正则化,所以当你有大量的样本,又需要L1正则化的话就要自己做取舍了。要么通过对样本采样来降低样本量,要么回到L2正则化。
对于多分类问题,只有 'newton-cg'、'sag'、'saga' 和 'lbfgs' 能够处理多项损失,
而 'liblinear' 面对多分类问题,得先把一种类别作为一个类别,剩余的所有类别作为另外一个类别。依次类推,遍历所有类别,进行分类。
max_iter: int, default: 100 ——最大迭代次数。当选择newton - cg, sag and lbfgs这三种算法时才能用。multi_class: str, {‘ovr’, ‘multinomial’}, default: ‘ovr’ ——指定对于多分类问题的策略,可以为如下的值。
‘ovr’ :采用one - vs - rest策略。OvR的思想很简单,无论你是多少元逻辑回归,我们都可以看做二元逻辑回归。
具体做法是,对于第K类的分类决策,我们把所有第K类的样本作为正例,除了第K类样本以外的所有样本都作为负例,
然后在上面做二元逻辑回归,得到第K类的分类模型。其他类的分类模型获得以此类推。T-1
‘multinomial’:many-vs-many(MvM) 直接采用多分类逻辑回归策略。如果模型有T类,我们每次在所有的T类样本里面选择两类样本出来,
不妨记为T1类和T2类,把所有的输出为T1和T2的样本放在一起,把T1作为正例,T2作为负例,进行二元逻辑回归,得到模型参数。我们一共需要T(T-1)/2次分类。
从上面的描述可以看出OvR相对简单,但分类效果相对略差(这里指大多数样本分布情况,某些样本分布下OvR可能更好)。而MvM分类相对精确,但是分类速度没有OvR快。
'auto' 顾名思义根据实际情况,自动选择,它主要依据 solver 参数来决定,逻辑为如果 solver 为 liblinear,multi_class 为 ovr,如果分类类别大于 2,multi_class 为 multinomial,否则为 ovr
warm_start: bool, default: False ——如果为True,那么使用前一次训练结果继续训练,否则从头开始训练。n_jobs: int, default: 1 ——指定任务并行时的CPU数量。如果为 - 1则使用所有了用的CPU。
一般来说,我们知道这些参数所使用的的场景,那么就会少走一些坑,比如我们知道L1,L2正则项的意义是什么,L2主要解决过拟合,L1特征选取,也可以解决过拟合,默认是L2
具体的代码步骤,我这里就不一一的做详细的解释了,因为我喜欢在jupyter notebook中机器学习,每一步都可以知道如何优化,下面的案例我首先通过逻辑回归进行调参,并使用相关系数进行特征选取,然后得出的效果分数可以达到91%,这比不做特征选取要好很多,随后我又使用逻辑回归自带的特征评估权重做了一次选取,最后效果有所提升。
feature_weight = model.coef_.tolist()[0]
feature_name = X_name
feature_sort = pd.Series(data = feature_weight ,index = feature_name)
feature_sort = feature_sort.sort_values(ascending = False)
print(feature_sort.index)
plt.figure(figsize=(10,8))
sns.barplot(feature_sort.values,feature_sort.index, orient='h')
选取了8个重要的特征
完整代码如下,由于数据集是科研的数据,这里就不做导入说明了
# 导入数据 分割数据
df=**************不做展示***********************
# # 特征筛选(经过一番测试,不需要进行特征筛选)
# X_name=df.corr()[["n23"]].sort_values(by="n23",ascending=False).iloc[1:9].index.values.astype("U")
# X_name=feature_sort.index[:1]
# 测试出来的最佳特征!!!!!!!!
X_name=['n22', 'n8', 'n19f1', 'n18', 'n13', 'n10new','n9','leibie']
print(X_name)
X=df.loc[:,X_name]
# X=df.iloc[:,:-1]
y=df.iloc[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,stratify=y,random_state=1)
# X_train
score = []
C_range = np.linspace(0.01, 30, 50)
for i in C_range:
clf = LogisticRegression(C=i,penalty="l2").fit(X_train, y_train)
score.append(clf.score(X_test, y_test))
print(max(score), C_range[score.index(max(score))])
best_C=C_range[score.index(max(score))]
#设置标题
plt. title(f' LogisticRegression {max(score)}')
#设置x轴标签
plt. xlabel(' C')
#设置y轴标签
plt. ylabel(' Score')
#添加图例
# plt. legend()
plt.plot(C_range, score)
plt.show()
solvers = ["newton-cg", "lbfgs", "liblinear","sag"]
for solver in solvers:
clf = LogisticRegression(solver=solver,
C=best_C
).fit(X_train, y_train)
print("The accuracy under solver %s is %f" % (solver, clf.score(X_test, y_test)))
best_sover='liblinear'
model=LogisticRegression(C=11.02673469387755,penalty='l2',solver=best_sover)
# 训练模型
model.fit(X_train,y_train)
# 预测值
y_pred = model.predict(X_test)
'''
评估指标
'''
# 求出预测和真实一样的数目
true = np.sum(y_pred == y_test )
print('预测对的结果数目为:', true)
print('预测错的的结果数目为:', y_test.shape[0]-true)
# 评估指标
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score
print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100))
print('预测数据的精确率为:{:.4}%'.format(
precision_score(y_test,y_pred)*100))
print('预测数据的召回率为:{:.4}%'.format(
recall_score(y_test,y_pred)*100))
# print("训练数据的F1值为:", f1score_train)
print('预测数据的F1值为:',
f1_score(y_test,y_pred))
print('预测数据的Cohen’s Kappa系数为:',
cohen_kappa_score(y_test,y_pred))
# 打印分类报告
print('预测数据的分类报告为:','\n',
classification_report(y_test,y_pred))
from sklearn.metrics import precision_recall_curve
from sklearn import metrics
# 预测正例的概率
y_pred_prob=model.predict_proba(X_test)[:,1]
# y_pred_prob ,返回两列,第一列代表类别0,第二列代表类别1的概率
#https://blog.csdn.net/dream6104/article/details/89218239
fpr, tpr, thresholds = metrics.roc_curve(y_test,y_pred_prob, pos_label=2)
#pos_label,代表真阳性标签,就是说是分类里面的好的标签,这个要看你的特征目标标签是0,1,还是1,2
roc_auc = metrics.auc(fpr, tpr) #auc为Roc曲线下的面积
# print(roc_auc)
plt.figure(figsize=(8,6))
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.plot(fpr, tpr, 'r',label='AUC = %0.2f'% roc_auc)
plt.legend(loc='lower right')
# plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0, 1.1])
plt.ylim([0, 1.1])
plt.xlabel('False Positive Rate') #横坐标是fpr
plt.ylabel('True Positive Rate') #纵坐标是tpr
plt.title('Receiver operating characteristic example')
plt.show()
效果还是有所提升,通过反复的调试,我们的逻辑回归已经大功告成了!
快乐是自己给的,而不是索取的!