前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >盘一盘 Python 系列特别篇 - Sklearn (0.22)

盘一盘 Python 系列特别篇 - Sklearn (0.22)

作者头像
用户5753894
发布2019-12-25 14:49:08
1.1K0
发布2019-12-25 14:49:08
举报
文章被收录于专栏:王的机器

在〖机器学习之 Sklearn〗一贴中,我们已经介绍过 Sklearn,它全称是 Scikit-learn,是基于 Python 语言的机器学习工具。

在 2019 年 12 月 3 日,Sklearn 已经更新到版本 0.22,里面添加了若干功能,这也是本帖的内容。

首先在 Anaconda 的提示窗中输入以下代码,来安装更新当前的 Sklearn,如果代码运行时报错就以管理员身份打开提示窗。

代码语言:javascript
复制
pip install --upgrade scikit-learn

检查一下 Sklearn 的版本确定已经更新到 0.22。

代码语言:javascript
复制
import sklearn
print( sklearn.__version__ )
代码语言:javascript
复制
0.22

在添加的众多功能中,我觉得以下几个算是比较有用的。

  • 一行画出 ROC-AUC 图
  • 实现堆积法 (stacking)
  • 为任何模型估计特征重要性
  • 用 k-近邻法来填充缺失值

首先加载下面例子共用的包。

1

ROC-AUC

首先介绍一下接受者操作特征 (ROC)。

接受者操作特征

ROC 是 receiver operating characteristic 的简称,直译为「接受者操作特征」。「ROC 曲线」非常类似「PR 曲线」,但图的横轴纵轴并不是查准率和查全率。「ROC 曲线」反映在不同分类阈值上,真正类率 (true positive rate, TPR) 和假正类率 (false positive rate, FPR) 的关系。

  • TPR 是「真正类」和所有正类 (真正类+假负类) 的比率,真正类率 = 查全率
  • FPR 是「假正类」和所有负类 (假正类+真负类) 的比率,假正类率 = 1- 真负类率 = 1 - 特异率 (specificity)

一般来说,阈值越高

  • 越不容易预测出正类,TPR 下降 ( TPR 和阈值成递减关系)
  • 越容易预测出负类,(1- FPR) 上升 ( FPR 和阈值成递减关系)

阈值越低

  • 越容易预测出正类,TPR 上升 ( TPR 和阈值成递减关系)
  • 越不容易预测出负类,(1- FPR) 下降 ( FPR 和阈值成递减关系)

因此 TPR 和 FPR 是单调递增关系。

「PR 曲线」和「ROC 曲线」对比图见下,后者和横轴之间的面积叫AUC,是 area under the curve 的简称。

AUC 将所有可能分类阈值的评估标准浓缩成一个数值,根据 AUC 大小,我们得出

如何计算 AUC 和计算 PR 曲线下的面积一样的,把横坐标和纵坐标代表的变量弄对就可以了,如下图。

如何确定这些 TPRi 和 FPRi (i = 0,1,...,5) 不是一件容易讲清的事,我试试,先看一个二分类预测类别以及预测正类概率的表 (按照预测概率降序排序,其中正类 P 和负类 N 都有 10 个)。

第一个点:当阈值 = 0.9,那么第 1 个样本预测为 P,后 19 个样本预测为 N,这时

  • TPR = 真正类/全部正类 = 1/10 =0.1
  • FPR = 1 - 真负类/全部负类 = 1 - 10/10 =0
  • 阈值 0.9 → (0, 0.1)

第二个点:当阈值 = 0.8,那么第 1, 2 个样本预测为 P,后 18 个样本预测为 N,这时

  • TPR = 真正类/全部正类 = 2/10 =0.2
  • FPR = 1 - 真负类/全部负类 = 1 - 10/10 =0
  • 阈值 0.8 → (0, 0.2)

...

第四个点:当阈值 = 0.6,那么前 4 个样本预测为 P,后 16 个样本预测为 N,这时

  • TPR = 真正类/全部正类 = 3/10 =0.3
  • FPR = 1 - 真负类/全部负类 = 1 - 9/10 =0.1
  • 阈值 0.8 → (0.1, 0.3)

...

最后一个点:当阈值 = 0.1,那么全部样本预测为 P,零样本预测为 N,这时

  • TPR = 真正类/全部正类 = 10/10 =1
  • FPR = 1 - 真负类/全部负类 = 1 - 0/10 =1
  • 阈值 0.8 → (1, 1)

因此可画出下图右半部分,即 ROC 曲线,再根据横坐标纵坐标上的 FPR 和 TPR 计算 AUC。

AUC 越大,分类器的质量越好。

在 Scikit-learn 里,还记得有三种方式引入数据吗?

  1. 用 load_dataname 来加载小数据
  2. 用 fetch_dataname 来下载大数据
  3. 用 make_dataname 来构造随机数据

这里我们用第三种:

用支持向量机分类器 svc 和随机森林分类器 rfc 来训练一下。

一行画 ROC 图需要引入 plot_roc_curve 包。

代码语言:javascript
复制
from sklearn.metrics import plot_roc_curve

再运行下面一行代码,需要传进三个参数:估计器 svc,特征 x_test,标签 y_test。

代码语言:javascript
复制
plot_roc_curve( svc, X_test, y_test );

在版本 v0.22 之前,要画出上面这图需要写好多行代码:

所以现在这个 plot_roc_curve 包真的方便,不过其实在 Scikit-plot 里,早就有类似的函数了,见〖机学可视化之 Scikit-Plot〗一贴。那个函数叫做 plot_roc 用到的参数有 3 个:

  • y_test:测试集真实标签
  • y_prob:测试集预测标签的概率
  • figsize:图片大小

我猜想 v0.22 是借鉴了 Scikit-plot 里 plot_roc 函数的写法得到了 plot_roc_curve。

此外,plot_roc_curve 函数还可以画出不同估计器得到的 ROC 曲线。只需要将 svc 模型下的 ROC 图中的坐标系传到 rfc 模型下的 ROC 图中的 ax 参数中。代码如下:

2

Stacking

首先介绍一下堆积法 (stacking)。

堆积法

堆积法实际上借用交叉验证思想来训练一级分类器,解释如下图:

训练一级分类器 – 首先将训练数据分为 3 份:D1, D2, D3,h1 在 D1 和 D2 上训练,h2 在 D1 和 D3 上训练,h3 在 D2 和 D3 上训练。

新训练数据 – 包含:h1 在 D3 上的产出,h2 在 D2 上的产出,h3 在 D1 上的产出。

训练二级分类器 – 在新训练数据和对应的标签上训练出第二级分类器 H。

接着我们拿手写数字 (MNIST) 数据举例。

代码语言:javascript
复制
from sklearn.datasets import fetch_openml

下面也是 v0.22 的一个特功能 (但我觉得没什么太大用):可以从 openML 返回数据帧的值,需要将 as_frame 参数设置为 True。再用操作符 . 来获取 X 和 y。,代码如下:

代码语言:javascript
复制
X_y = fetch_openml( 'mnist_784', 
                    version=1,
                    as_frame=True )
X, y = X_y.data, X_y.target

其实我觉得原来将 return_X_y 参数设置为 True,一次性的把 X 和 y 都返回出来更简便,代码如下:

代码语言:javascript
复制
X, y = fetch_openml( 'mnist_784',
                      version=1, 
                      return_X_y=True )

按 80:20 划分训练集和测试集,在标准化照片像素使得值在 0-1 之间。

接下来重头戏来了,用 StackingClassifier 作为元估计器(meta-estimators),来集成两个子估计器(base-estimator),我们用了随机森林分类器 rfc 和梯度提升分类器 gbc 作为一级分类器,之后用对率回归分类器作为二级分类器。

代码非常简单,如下:

代码通用格式为 (其中 FE 代表一级估计器,BE 代表,SE 代表)

FE = [('BE1', BE1),

('BE2', BE2),

...,

('BEn', BEn)]

clf = StackingClassifier(

estimators = FE

final_estimators = SE)

在 Scikit-learn 里没有实现 StackingClassifier,我们只能用 mlxtend 里面的模型。现在在版本 0.22 里不需要这么做了。

代码语言:javascript
复制
from mlxtend.classifier import StackingClassifier

比较子估计器元估计器的在测试集上的表现。

代码语言:javascript
复制
rfc.fit(X_train, y_train)
gbc.fit(X_train, y_train)
clf.fit(X_train, y_train)
rfc.score(X_test, y_test)
gbc.score(X_test, y_test)
clc.score(X_test, y_test)
代码语言:javascript
复制
0.9482142857142857
0.8391428571428572
1.0

集成分类器的得分比随机森林分类器和梯度提升分类器都高,几乎完全分类正确测试集了。堆积法的效果还真不错。

3

Feature Importance

首先介绍一下如何用置换检验 (permutation test) 来计算特征重要性 (feature importance)。

置换检验计算特征重要性

核心思想是“如果某个特征是重要特征,那么加入一些随机噪声模型性能会下降”。

做法是把所有数据在特征上的值重新随机排列,此做法被称为置换检验。这样可以保证随机打乱的数据分布和原数据接近一致。下图展示了在特征“性格”上随机排列后的数据样貌,随机排列将“好坏坏好坏坏好好”排成“坏坏好坏好坏坏好”。

在置换检验后,特征的重要性可看成是模型“在原数据的性能”和“在特征数据置换后的性能”的差距,有

接着我们拿鸢尾花 (iris) 数据举例。

首先按 80:20 划分训练集和测试集。

用随机森林训练,并在每个特征维度上做 10 次置换操作,注意 n_repeats 设置为 10。

打印出 result,得到重要性的均值标准差10 组具体值

这种数据形式最适合用箱形图 (box plot) 展示,均值是用来决定哪个特征最重要的,在箱形图中用一条线表示 (通常这条线指的中位数)。

根据上图,我们得出花瓣长度 (petal length) 特征最重要,而花萼长度 (sepal length) 特征最不重要。当特征多时可用该方法来选择特征。

这个 permutation_importance 函数可以用在任何估计器上,再用一个支持向量机 svc。

根据上图,我们得出同样结论,花瓣长度特征最重要,花萼长度特征最不重要,虽然具体特征重要性均值和标准差不同,但在判断特征重要性的大方向还是一致的。

4

KNN Imputation

缺失数据的处理方式通常有三种:删除 (delete)、推算 (impute) 和归类(categorize)。下面举例用的数据如下:

删除法

删除数据最简单,有两种方式:

  • 删除行 (数据点)
  • 删除列 (特征)

删除法的优点

  • 操作简单
  • 可以用在任何模型比如决策树、线性回归等等

删除法的缺点

  • 删除的数据可能包含重要信息
  • 不知道删除行好还是删除列好
  • 对缺失数据的测试集没用

推算法

根据特征值是分类型或数值变量,两种方式:

  1. 用众数来推算分类型
  2. 用平均数来推算数值

特征“性格”的特征值是个分类型变量,因此计数未缺失数据得到 2 个好和 7 个坏,根据众数原则应该将缺失数据用“坏”来填充。特征“收入”的特征值是个数值型变量,根据平均数原则算出未缺失数据的均值 20.4 万来填充。

推算法的优点

  • 操作简单
  • 可以用在任何模型比如决策树和线性回归等等
  • 对缺失数据的测试集有用,运用同样的规则 (众数分类型变量,平均数数值型变量)

推算法的缺点是可能会造成系统型误差。

系统性误差有真实案例。在华盛顿的银行里申请贷款,根据当地法律,申请人是不允许填年龄的。如果整合所有美国申请人资料,发现所有来自华盛顿的数据缺失年龄那一栏。假如按照“平均化数值型变量”规则算出均值 41岁,那么把所有华盛顿申请者的年龄填为 41 岁是不合理的。

归类法

归类的核心思想是把缺失 (unknown) 也当作是一种特征值。下图举例用决策树将“收入缺失”和“收入低”归纳成同一类。

这时缺失值是实实在在的一个类别了。

用 KNN 填充缺失值

这里介绍的填充缺失值的方法是用 k-近邻 (k-nearest neighbor, KNN) 来估算缺失值的,即在每个特征下,缺失值都是使用在训练集中找到 k 个最近邻居的平均值估算的。

代码如下 (引入 sklearn.impute 里面的 KNNImputer):

代码语言:javascript
复制
[[1. 2. 4.]
 [3. 4. 3.]
 [5. 6. 5.]
 [7. 8. 9.]]

结果是合理的。X 的每一列代表一个特征,原始的 X 为

代码语言:javascript
复制
[[1.  2. nan]
 [3.  4. 3. ]
 [nan 6. 5. ]
 [7.  8. 9. ]]

在第一列中,离 nan 最近的 2 个邻居是 3 和 7,它们平均数是 5。在第四列中,离 nan 最近的 2 个邻居是 3 和 5,它们平均数是 4。总结图如下:

5

总结

回顾上面介绍的四个新填功能:

I. 一行画出 ROC-AUC 图,代码用

代码语言:javascript
复制
from sklearn.metrics import plot_roc_curve

II. 实现堆积法,代码用

代码语言:javascript
复制
from sklearn.ensemble import StackingClassifier

III. 为任何模型估计特征重要性,代码用

代码语言:javascript
复制
from sklearn.inspection import permutation_importance

IV. 用 k-近邻法来填充缺失值,代码用

代码语言:javascript
复制
from sklearn.impute import KNNImputer

Stay Tuned!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 王的机器 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
TI-ONE 训练平台
TI-ONE 训练平台(以下简称TI-ONE)是为 AI 工程师打造的一站式机器学习平台,为用户提供从数据接入、模型训练、模型管理到模型服务的全流程开发支持。TI-ONE 支持多种训练方式和算法框架,满足不同 AI 应用场景的需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档