作者:xiaoyu
知乎:https://zhuanlan.zhihu.com/pypcfx
介绍:一个半路转行的数据挖掘工程师
▍前言
前几篇介绍了逻辑回归在机器学习中的重要性:5个原因告诉你:为什么在成为数据科学家之前,“逻辑回归”是第一个需要学习的
以及逻辑回归的理论和公式推导:【机器学习笔记】:从零开始学会逻辑回归(一)
继上一篇,本篇将引出一个逻辑回归的实战练习,利用逻辑回归进行二分类,通过练习你将学会:
理解逻辑回归模型参数的含义
使用sklearn构建逻辑回归模型
可视化逻辑回归分类效果
评估逻辑回归模型
▍两个变量的简单数据集
上一篇,我们已经推导出了逻辑回归参数求解的迭代公式,自己通过numpy和scipy的使用就可以很容易地实现一个逻辑回归模型。当然,sklearn库已经有了封装好了的逻辑回归类LogisticRegression,下面我们将在一个简单的数据集上使用逻辑回归实现二分类。
import numpy as npimport matplotlib.pyplot as pltimport pandas as pdfrom sklearn.linear_model import LogisticRegression# 导入数据集dataset = pd.read_csv('Social_Network_Ads.csv')X = dataset.iloc[:, [2, 3]].valuesY = dataset.iloc[:,4].values# 将数据集分成训练集和测试集from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, Y, test_size = 0.2, random_state = 0)y_test = pd.Series(y_test)# 简单的预处理,特征缩放from sklearn.preprocessing import StandardScalersc = StandardScaler()X_train = sc.fit_transform(X_train)X_test = sc.transform(X_test)# 构建模型,预测测试集结果classifier = LogisticRegression()classifier.fit(X_train, y_train)y_pred = classifier.predict(X_test)
通过上面简单的调用sklearn库的类方法我们就训练出了一个简单的逻辑回归模型,使用的都是默认参数。下面让我们看看简单的模型分类效果究竟如何。
from matplotlib.colors import ListedColormapX_set,y_set=X_train,y_trainX1,X2=np.meshgrid(np.arange(start=X_set[:,0].min()-1, stop=X_set[:, 0].max()+1, step=0.01), np.arange(start=X_set[:,1].min()-1, stop=X_set[:,1].max()+1, step=0.01))plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(),X2.ravel()]).T).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('#C65749', '#338DFF')))plt.xlim(X1.min(),X1.max())plt.ylim(X2.min(),X2.max())for i,j in enumerate(np. unique(y_set)): plt.scatter(X_set[y_set==j,0],X_set[y_set==j,1], c = ListedColormap(('red', 'blue'))(i), label=j)plt. title(' LOGISTIC(Training set)')plt. xlabel(' Age')plt. ylabel(' Estimated Salary')plt. legend()plt. show()X_set,y_set=X_test,y_testX1,X2=np. meshgrid(np. arange(start=X_set[:,0].min()-1, stop=X_set[:, 0].max()+1, step=0.01), np. arange(start=X_set[:,1].min()-1, stop=X_set[:,1].max()+1, step=0.01))plt.contourf(X1, X2, classifier.predict(np.array([X1.ravel(),X2.ravel()]).T).reshape(X1.shape), alpha = 0.75, cmap = ListedColormap(('#C65749', '#338DFF')))plt.xlim(X1.min(),X1.max())plt.ylim(X2.min(),X2.max())for i,j in enumerate(np. unique(y_set)): plt.scatter(X_set[y_set==j,0],X_set[y_set==j,1], c = ListedColormap(('red', 'blue'))(i), label=j)plt.title(' LOGISTIC(Test set)')plt.xlabel(' Age')plt.ylabel(' Estimated Salary')plt.legend()plt.show()
上图是训练集分类效果,下图是测试集的分类效果,简单总结一下上面内容:
Python语言方面:使用了meshgrid方法将二维坐标划分成了间隔非常小的矩阵细小点,然后使用了contourf方法与模型预测结果对比判断分类结果。此外,使用了scatter散点图绘制了原数据分布点作为分类前后的比对。
逻辑回归理论方面:可以明显看到,在二维坐标中,逻辑回归是以一条直线进行分类的,很好的说明了逻辑回归的本质是线性分类的。同时我们也看到,目前情况下训练集中混在蓝色点中的一些红色点是无论如何也无法正确的分类出来的,这也正是它的缺点,所以说逻辑回归的分类准确度还是相对稍低的。
▍更多变量的数据集
下面我们将要在一个数据量更大,变量更多的的数据集上进行测试。由于我们需要一个二分类的数据集,所以这里使用了“泰坦尼号生还者预测”的数据集。
https://www.kaggle.com/c/titanic/data
当然,直接使用原始数据是不行的。为了方便,数据已被我提前处理,经过了清洗、预处理、变换、筛选和衍生,可直接供模型训练。因此,我们可以直接通过sklearn构建模型,看一下sklearn中逻辑回归的参数都有哪些。
from sklearn.linear_model import LogisticRegressionclassifier_default = LogisticRegression()classifier_default
结果如下:
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)
上面是是逻辑回归的所有参数配置,都是默认的。乍一看是有很多参数,但其实只有几个是比较关键的。下面将对逻辑回归参数进行总结分类和解释。
▍Logistic模型参数解释
正则化参数:属于该分类的参数有 C 和 penalty。
C:C参数与惩罚系数成反比,C值越小,则正则化效果越强,即对参数的惩罚程度越大。
penalty:提供我们正则化的类型,L1范数正则化和L2范数正则化(在线性回归中相当于lasso回归和岭回归),默认情况下使用L2正则化,但此参数也需要与solver类型配合使用,因为一些solver有一些限制。关于L1和L2正则化的区别和理解后续进行介绍。
优化算法参数选择 solver:优化算法有四种实现方式,分别是:liblinear,lbfgs,newton-cg,sag,下面是四种算法的介绍。
这四种算法各有一些特点,如果是L2正则化,可选的优化算法有newton-cg,lbfgs,liblinear,sag,四个均可以选择。但是如果是L1正则化,就只能选择liblinear。这是因为L1正则化的损失函数不是连续可导的,而newton-cg,lbfgs,sag这三种优化算法都需要损失函数的一阶或者二阶连续导数,liblinear并没有这个依赖。简单来说,liblinear对于L1和L2都适用,而其他三种只适用L2。
另外,sag是我们上一篇使用的梯度下降的变种随机平均梯度下降,它每次仅使用了部分样本进行梯度迭代,所以当数据量较少时不宜选用,而当数据量很大时,sag是第一选择,计算速度会加快。但是sag不能用于L1正则化,所以当你有大量的样本,又需要L1正则化的话就要自己做取舍了。要么通过对样本采样来降低样本量,要么回到L2正则化。
迭代参数:相关参数有max_iter和tol。
max_iter:参数求解的迭代次数,默认100。迭代次数过小会影响准确率,迭代次数过高会影响速度,一般会折中考虑。
tol:残差收敛条件,即迭代的连续两次之间残差小于tol就停止,默认是0.0001。
权重参数 class_weight:这个参数可以调节样本比例。一个很常见的例子是网贷违约预测中用户的好坏比,通常好用户占绝大部分,所以样本是不均衡的。除了采样方法处理外,也可以使用该参数进行调节。参数选择 balanced 则可以自动计算样本比例来达到平衡,或者也可以通过自定义比例来达到同样效果。
多分类参数 multi_class:我们上面举的例子都是默认二分类的,但逻辑回归也可以用于多分类,有 ovr 和 multinomial 两个值可以选择,默认是 ovr。ovr是one-vs-rest(OvR),而multinomial是many-vs-many(MvM)。如果是二元逻辑回归,ovr和multinomial并没有任何区别,区别主要在多元逻辑回归上。
OvR相对简单,但分类效果相对略差(这里指大多数样本分布情况,某些样本分布下OvR可能更好)。而MvM分类相对精确,但是分类速度没有OvR快。如果选择了ovr,则4种损失函数的优化方法liblinear,newton-cg, lbfgs和sag都可以选择。但是如果选择了multinomial,则只能选择newton-cg, lbfgs和sag了。
了解参数的意义后,我们可以开始尝试调节一下这些参数,通过参数调节,可以有效避免过拟合等现象,以此实现一个效果更优,更健壮的模型。
▍Logistic模型参数调试
下面我们手动调节几个参数,来感受一下这些参数是如何影响最终结果的。
# 配置调节参数C_list = [0.0001, 1, 1000]max_iter_list = [1, 50, 100]classifiers = ClassifierGenerator('C', C_list)Auc_plot(classifiers, X_test, 'C')
当然,对于不同参数的选择,我们可以通过查看ROC和AUC,以此来衡量结果。首先,我们调节C参数,分别是0.0001,1,和1000,查看这三个不同值下的ROC曲线和AUC值。如果对ROC/AUC不清楚,可以参考:【机器学习笔记】:一文让你彻底记住什么是ROC/AUC(看不懂你来找我)
上面是三个C取值在测试集数据上的评估结果,可以看到:C=0.0001的时候,AUC值为0.91,这说明模型可能出现了过拟合的现象。因为,C越小,惩罚程度越大,参数可能过度学习而无法在测试集上泛化,因此测试集上AUC值相对低。
而C的其它两个取值,1和1000,虽然二者ROC曲线稍有不同,但是AUC值都是0.92,所以就单独C参数来讲,二者皆可。
同样的,可以对比不同max_iter参数值的不同结果。
# 配置调节参数C_list = [0.0001, 1, 1000]max_iter_list = [1, 100]classifiers = ClassifierGenerator('max_iter', max_iter_list)Auc_plot(classifiers, X_test, 'max_iter')
迭代次数为1情况下AUC值0.91,偏低,而为100的时候AUC值为0.92。这说明迭代次数(max_iter=1)太低导致出现了欠拟合。
▍自动化查找最优参数
上面我们手动地调节了一些参数,但是这些参数是一起其作用的,单独调节的情况下不一定能够保证最好,最好的情况是一个完美的参数搭配或者组合。那么如何找到这样的搭配呢?除了凭借经验手动调参以外,还可以使用sklearn的一个工具。
sklearn中提供了一种自动搜索最优参数的方法:GridSearchCV,它是基于提供的参数选项,组合出各种可能,然后结合交叉验证对所有可能组合进行筛选。因此,通过使用该方法,我们就可以简单地找到相对上面单独参数调节更好的参数组合了。
还是通过上面的例子继续说明,自动调参代码如下:
from sklearn.model_selection import GridSearchCVimport warningswarnings.filterwarnings("ignore")# 参数设置params = {'C':[0.0001, 1, 100, 1000], 'max_iter':[1, 10, 100, 500], 'class_weight':['balanced', None], 'solver':['liblinear','sag','lbfgs','newton-cg'] }lr = LogisticRegression()clf = GridSearchCV(lr, param_grid=params, cv=10)clf.fit(X_train,y_train)
将所有参数设置考虑进去,训练数据,结果我们可以得到:
>>clf.best_params_out:{'C': 1, 'class_weight': None, 'max_iter': 10, 'solver': 'sag'}
上面结果就是我们基于自己提供的参数选择要找的最佳参数组合了。
▍构建最优模型
有了最优的参数后,我们将这些参数作为输入重新建立一个逻辑回归模型。代码如下:
# 根据以上绘图结果选择一个合理参数值classifier = LogisticRegression(**clf.best_params_)# 训练模型classifier.fit(X_train, y_train)# 预测测试集结果y_pred = classifier.predict(X_test)y_score = classifier.predict_proba(X_test)[:,1]
成功构建模型后,我们再次进行分类,并看一下最终的评估指标ROC曲线,AUC值,以及KS值:
# roc/auc计算y_score = classifier.predict_proba(X_test)[:,1]fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)roc_auc = metrics.auc(fpr,tpr)# 绘制roc曲线f,ax = plt.subplots(figsize=(8,6))plt.stackplot(fpr, tpr, color='#338DFF', alpha = 0.5, edgecolor = 'black')plt.plot(fpr, tpr, color='black', lw = 1.5)plt.plot([0,1],[0,1], color = 'red', linestyle = '--', lw = 2)plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)plt.xlabel('1-Specificity')plt.ylabel('Sensitivity')plt.show()
from ks_visual import plot_ks# 观察ks指标,并绘制ks曲线plot_ks(y_test = y_test, y_score = y_score, pf = 1)
可以看到,AUC值为0.92,KS值为0.75,说明分类模型的区分能力和准确度都不错。但要提醒的是:这样的好结果并不全是逻辑回归模型的功劳,而更多的是前期特征工程的贡献。只有做出好的特征,提高数据质量,才能最大程度上的提高最终效果。
参考:
https://www.cnblogs.com/pinard/p/6035872.html
从零开始学习数据分析和挖掘,刘顺祥
https://github.com/MLEveryday/100-Days-Of-ML-Code