机器学习工具学习 -python

工具学习 -python -04 Sklearn

2019-06-18  本文已影响21人  ZL玲啊玲

借着别人的文章(王圣元 [王的机器] ),复盘一下python的基础知识点。感谢原作者的分享!

Sklearn (全称 Scikit-Learn) 是基于 Python 语言的机器学习工具。它建立在 NumPy, SciPy, Pandas 和 Matplotlib 之上,里面的 API 的设计非常好,所有对象的接口简单,很适合新手上路。

在 Sklearn 里面有六大任务模块:分别是分类、回归、聚类、降维、模型选择和预处理,如下图从其官网的截屏。

image.png

要使用上述六大模块的方法,可以用以下的伪代码,注意 import 后面我用的都是一些通用名称,如 SomeClassifier, SomeRegressor, SomeModel,具体化的名称由具体问题而定,比如

# 分类 (Classification)
from sklearn import SomeClassifier
from sklearn.linear_model import SomeClassifier
from sklearn.ensemble import SomeClassifier

# 回归 (Regression)
from sklearn import SomeRegressor
from sklearn.linear_model import SomeRegressor
from sklearn.ensemble import SomeRegressor

# 聚类 (Clustering)
from sklearn.cluster import SomeModel

# 降维 (Dimensionality Reduction)
from sklearn.decomposition import SomeModel

# 模型选择 (Model Selection)
from sklearn.model_selection import SomeModel

# 预处理 (Preprocessing)
from sklearn.preprocessing import SomeModel

SomeClassifier, SomeRegressor, SomeModel 其实都叫做估计器 (estimator),就像 Python 里「万物皆对象」那样,Sklearn 里「万物皆估计器」。

此外,Sklearn 里面还有很多自带数据集供,引入它们的伪代码如下。

数据集 (Dataset)

from sklearn.datasets import SomeData

本文我们用以下思路来讲解:


1 机器学习简介

1.1 定义和组成元素

什么是机器学习?字面上来讲就是 (人用) 计算机来学习。谈起机器学习就一定要提起汤姆米切尔 (Tom M.Mitchell),就像谈起音乐就会提起贝多芬,谈起篮球就会提起迈克尔乔丹,谈起电影就会提起莱昂纳多迪卡普里奥。米切尔对机器学习定义的原话是:

A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P if its performance at tasks in T, as measured by P, improves with experience E.
假设用性能度量 P 来评估机器在某类任务 T 的性能,若该机器通利用经验 E 在任务 T 中改善其性能 P,那么可以说机器对经验 E 进行了学习。

在该定义中,除了核心词机器和学习,还有关键词经验 E,性能度量 P 和任务 T。在计算机系统中,通常经验 E 是以数据 D 的形式存在,而机器学习就是给定不同的任务 T 从数据中产生模型 M,模型 M 的好坏就用性能度量 P 来评估。

由上述机器学习的定义可知机器学习包含四个元素:
数据 (Data)任务 (Task)性能度量 (Quality Metric) 模型 (Model)

image.png

1.2 数据

数据 (data) 是经验的另一种说法,也是信息的载体。数据可分为

  1. 结构化数据和非结构化数据 (按数据具体类型划分)
  2. 原始数据和加工数据 (按数据表达形式划分)
  3. 样本内数据和样本外数据 (按数据统计性质划分)

在统计中,把研究对象的全体称为总体 (population),而把组成总体的各个元素称为个体,把从总体中抽取的若干个体称为样本 (sample)。举个调查中国男性平均身高的例子:普查所有男性金钱花费和时间成本太高,通常会抽取若干男性作为样本,计算样本里的男性平均身高作为总体里的所有男性平均身高的推理 (inference)。

统计学中做的事情就是用样本数据的统计 (statistics) 来推出总体数据的参数 (parameter)。样本数据也叫做样本内数据,除样本内数据之外的总体数据叫做样本外数据。

在机器学习中,样本内和样本外数据的定义稍微有些不同
样本内数据是用来训练模型的数据,也叫训练数据。它们是已知的,可计算统计的。样本外数据是未来的没见过的新数据。它们是未知的,不可计算统计的。

1.3 任务

根据学习的任务模式 (训练数据是否有标签),机器学习可分为四大类:

  1. 有监督学习 (有标签)
  2. 无监督学习 (无标签)
  3. 半监督学习 (有部分标签)
  4. 增强学习 (有评级标签)

深度学习只是一种方法,而不是任务模式,因此与上面四类不属于同一个维度,但是深度学习与它们可以叠加成:深度有监督学习、深度非监督学习、深度半监督学习和深度增强学习。迁移学习也是一种方法,也可以分类为有监督迁移学习、非监督迁移学习、半监督迁移学习和增强迁移学习。

1.4 性能度量

回归和分类任务中最常见的误差函数以及一些有用的性能度量如下。

image.png

除上述损失函数之外,分类任务还有很多其他有用的性能度量。

错误率:分类错误的样本数占样本总数的比例称为错误率 (error rate),相应的分类正确的样本数占样本总数的比例称为精度 (accuracy)。在 10 个样本中有 2 个样本分类错误,则错误率为 20%,而精度为 80%。

查准率和查全率:错误率和精度虽然常用,但是不能满足所有任务需求。假定用训练好的模型预测骑士赢球,显然,错误率衡量了多少比赛实际是赢球但预测成输球。但是若我们关心的是“预测出的比赛中有多少是赢球”,或“赢球的比赛中有多少被预测出了”,那么错误率这个单一指标显然就不够用了,这时需要引进更为细分的性能度量,即查准率 (precision) 和查全率 (recall)。

其他概念比如混淆矩阵、ROC、AUC 我们再下帖的实例用到时再细讲。

1.5 模型

有监督模型如下图所示:

image.png

无监督模型包括各种聚类分析 (KMeans, DBSCAN)、主成分分析 (PCA)、独立成分分析 (ICA)、隐含狄利克雷分配 (LDA) 等等。


2 Sklearn 数据

Sklearn 和之前讨论的 NumPy, SciPy, Pandas, Matplotlib 相似,就是一个处理特殊任务的包,Sklearn 就是处理机器学习 (有监督学习和无监督学习) 的包,更精确的说,它里面有六个任务模块和一个数据引入模块

2.1 数据格式

在 Sklean 里,模型能即用的数据有两种形式:

  1. Numpy 二维数组 (ndarray) 的稠密数据 (dense data),通常都是这种格式。
  2. SciPy 矩阵 (scipy.sparse.matrix) 的稀疏数据 (sparse data),比如文本分析每个单词 (字典有 100000 个词) 做独热编码得到矩阵有很多 0,这时用 ndarray 就不合适了,太耗内存。

上述数据在机器学习中通常用符号 X 表示,是模型自变量。它的大小 = [样本数, 特征数]。有监督学习除了需要特征 X 还需要标签 y,而 y 通常就是 Numpy 一维数组,无监督学习没有 y。

2.2 自带数据集

Sklearn 里面有很多自带数据集供用户使用。来看看 Sklearn 三种引入数据形式。

打包好的数据:对于小数据集,用 sklearn.datasets.load_*
分流下载数据:对于大数据集,用 sklearn.datasets.fetch_*
随机创建数据:为了快速展示,用 sklearn.datasets.make_*


3 核心 API

Sklearn 里万物皆估计器。估计器是个非常抽象的叫法,可把它不严谨的当成一个模型 (用来回归、分类、聚类、降维),或当成一套流程 (预处理、网格最终)。

本节三大 API 其实都是估计器:

  1. 估计器 (estimator) 当然是估计器
  2. 预测器 (predictor) 是具有预测功能的估计器
  3. 转换器 (transformer) 是具有转换功能的估计器

这三句看似废话,其实蕴藏了很多内容。其实我对第 1 点这个估计器的起名不太满意,我觉得应该叫拟合器 (fitter) - 具有拟合功能的估计器。看完这一节你就会明白「拟合器」这种叫法更合理。

3.1 估计器

定义:任何可以基于数据集对一些参数进行估计的对象都被称为估计器。

两个核心点:1. 需要输入数据,2. 可以估计参数。估计器首先被创建,然后被拟合。

创建估计器:需要设置一组超参数,比如

在创建好的估计器 model 可以直接访问这些超参数,用 . 符号。

但 model 中有很多超参数,你不可能一开始都知道要设置什么值,没设置的用 Sklearn 会给个合理的默认值,因此新手不用担心。

拟合估计器:需要训练集。

在有监督学习中的代码范式为:model.fit( X_train, y_train )
在无监督学习中的代码范式为:model.fit( X_train )

拟合之后可以访问 model 里学到的参数,比如线性回归里的特征前的系数 coef_,或 K 均值里聚类标签 labels_。

说了这么多抽象的东西,现在展示有监督学习的「线性回归」的具体例子。

线性回归

# 首先从 sklearn 下的 linear_model 中引入 LinearRegression,再创建估计器起名 model,设置超参数 normalize 为 **True**,指的在每个特征值上做标准化,这样会加速数值运算。

from sklearn.linear_model import LinearRegression

model = LinearRegression(normalize=True)

# 自己创建一个简单数据集 (没有噪声完全线性) 只为了讲解估计器里面的特征。
x = np.arange(10)
y = 2 * x + 1
plt.plot( x, y, 'o' )

# Sklearn 里模型要求特征 X 是个两维变量么 (样本数×特征数)?但在本例中 X 是一维,因为我们用 np.newaxis 加一个维度,它做的事情就是把 [1, 2, 3] 转成 [[1],[2],[3]]。再把 X 和 y 丢进 fit() 函数来拟合线性模型的参数。
X = x[:, np.newaxis]
model.fit( X, y )

#拟合完后的估计器和创建完的样子看起来一样,但是已经用「model.param_」可以访问到学好的参数了,展示如下。
print( model.coef_ )
print( model.intercept_ )

3.2 预测器

定义:预测器在估计器上做了一个延展,延展出预测的功能。

两个核心点:1. 基于学到的参数预测,2. 预测有很多指标。最常见的就是 predict() 函数:

举个例子:

# 加载数据集
from sklearn.datasets import load_iris
iris = load_iris()

# 切分数据集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split( iris['data'],  iris['target'], test_size=0.2 )

# 创建估计器
from sklearn.linear_model import LinearRegression
model = LinearRegression(solver = 'lbfgs')
# 拟合估计器
model.fit(X_train, y_train)

# 预测器
y_pred = model.predict( X_test )      #预测的类别
p_pred = model.predict_proba( X_test )      # 预测该类别的信心

预测器里还有额外的两个函数可以使用。在分类问题中

score() 返回的是分类准确率
decision_function() 返回的是每个样例在每个类下的分数值

print( model.score( X_test, y_test ) )
decision_score = model.decision_function( X_test )
print( decision_score )

小节: 估计器都有 fit() 方法,预测器都有 predict() 和 score() 方法,言外之意不是每个预测器都有 predict_proba() 和 decision_function() 方法

3.3 转换器

定义:转换器也是一种估计器,两者都带拟合功能,但估计器做完拟合来预测,而转换器做完拟合来转换。

核心点:估计器里 fit + predict,转换器里 fit + transform。

本节介绍两大类转换器

  1. 将分类型变量 (categorical) 编码成数值型变量 (numerical)
  2. 规范化 (normalize) 或标准化 (standardize) 数值型变量
3.3.1分类型变量编码

LabelEncoder & OrdinalEncoder

LabelEncoder和 OrdinalEncoder 都可以将字符转成数字,但是

# 首先给出要编码的列表 enc 和要解码的列表 dec。
enc = ['win','draw','lose','win']
dec = ['draw','draw','win']

# LabelEncoder编码
# 从 sklearn 下的 preprocessing 中引入 LabelEncoder,再创建转换器起名 LE。 
from sklearn.preprocessing import LabelEncoder
LE = LabelEncoder()

print( LE.fit(enc) )           --LabelEncoder()
print( LE.classes_ )           --['draw' 'lose' 'win']
print( LE.transform(dec) )     --[0 0 2]

# OrdinalEncoder编码
# 从 sklearn 下的 preprocessing 中引入 OrdinalEncoder,再创建转换器起名 OE
from sklearn.preprocessing import OrdinalEncoder
OE = OrdinalEncoder()

enc_DF = pd.DataFrame(enc)
dec_DF = pd.DataFrame(dec)

print( OE.fit(enc_DF) )           --OrdinalEncoder(categories='auto', dtype=<class 'numpy.float64'>)      
print( OE.categories_ )           --[array(['draw', 'lose', 'win'], dtype=object)]
print( OE.transform(dec_DF) )     --[[0.] [0.] [2.]]

OneHotEncoder

转换器 OneHotEncoder 可以接受两种类型的输入:

  1. 用 LabelEncoder 编码好的一维数组
  2. DataFrame
from sklearn.preprocessing import OneHotEncoder

# 1. 用 LabelEncoder 编码好的一维数组 (元素为整数),重塑 成二维数组作为 OneHotEncoder 输入。
OHE = OneHotEncoder()
num = LE.fit_transform( enc )
print( num )           --[2 0 1 2]
OHE_y = OHE.fit_transform( num.reshape(-1,1) )
OHE_y.toarray()     # 想看该矩阵里具体内容,用 toarray() 函数。
--array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

# 2. 用 DataFrame作为 OneHotEncoder 输入
OHE = OneHotEncoder()
OHE.fit_transform( enc_DF ).toarray()
3.3.2特征缩放

数据要做的最重要的转换之一是特征缩放 (feature scaling)。当输入的数值的量刚不同时,机器学习算法的性能都不会好。具体来说,对于某个特征,我们有两种方法:

MinMaxScalerStandardScaler

整套转换器「先创建再 fit 在 transform」的流程应该很清楚了。自己读下面代码看看是不是秒懂。唯一需要注意的就是输入 X 要求是两维。

# MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
X = np.array( [0, 0.5, 1, 1.5, 2, 100] )
X_scale = MinMaxScaler().fit_transform( X.reshape(-1,1) )

# StandardScaler
from sklearn.preprocessing import StandardScaler
X_scale = StandardScaler().fit_transform( X.reshape(-1,1) )

警示: fit() 函数只能作用在训练集上,千万不要作用在测试集上,要不然你就犯了数据窥探的错误了!拿标准化举例,用训练集 fit 出来的均值标准差参数,来对测试集做标准化。


4 高级 API

Sklearn 里核心 API 接口是估计器,那高级 API 接口就是元估计器 (meta-estimator),即由很多基估计器 (base estimator) 组合成的估计器。元估计器把估计器当成参数。代码范式大概如下:

meta_model( base_model )

本节讨论五大元估计器,分别带集成功能的 ensemble,多分类和多标签的 multiclass,多输出的 multioutput,选择模型的 model_selection,和流水线的 pipeline

1 ensemble.BaggingClassifier
2 ensemble.VotingClassifier
3 multiclass.OneVsOneClassifier
4 multiclass.OneVsRestClassifier
5 multioutput.MultiOutputClassifier
6 model_selection.GridSearchCV
7 model_selection.RandomizedSearchCV
8 pipeline.Pipeline

在下面五节,我们会用的鸢尾花数据 iris 和数字数据 digits,还有一些自己创建的数据。

4.1 Ensemble 估计器

Ensemble 估计器是用来做集成学习,该估计器里面有若干个分类器 (classifier) 或回归器 (regressor)。

最常用的 Ensemble 估计器排列如下:

我们用鸢尾花数据 iris,拿

同质估计器 RandomForestClassifier
异质估计器 VotingClassifier

来举例。首先将数据分成 80:20 的训练集和测试集,并引入 metrics 来计算各种性能指标。

from sklearn.datasets import load_iris
iris = load_iris()
from sklearn.model_selection import train_test_split
from sklearn import metrics

X_train, X_test, y_train, y_test = train_test_split( iris['data'],  iris['target'], test_size=0.2 )```

RandomForestClassifier

随机森林 (random forest) 是决策树 (decision tree) 的一种集成模型,每棵决策树处理的数据用装袋法 (bagging) 生成。随机森林可以减小预测的方差,并且可以评估特征重要性。

from sklearn.ensemble import RandomForestClassifier

# 4 棵决策树 (森林由树组成);此外每棵树的最大树深为 5
RF = RandomForestClassifier( n_estimators=4, max_depth=5 )
RF.fit( X_train, y_train )

# 拟合 RF 完再做预测
print ( "RF - Accuracy (Train):  %.4g" %  metrics.accuracy_score(y_train, RF.predict(X_train)) )
--RF - Accuracy (Train): 0.9833
print ( "RF - Accuracy (Test):  %.4g" % metrics.accuracy_score(y_test, RF.predict(X_test)) )
--RF - Accuracy (Test): 1

print( RF.n_estimators )      --4
RF.estimators_
image.png

VotingClassifier

和随机森林由同质分类器「决策树」不同,投票分类器由若干个异质分类器组成。下例用 VotingClassifier 建立个含有对率回归 (LR)、随机森林 (RF) 和高斯朴素贝叶斯 (GNB) 三个分类器的集成模型。

RandomForestClassifier 的基分类器只能是决策树,因此只用通过控制 n_estimators 超参数来决定树的个数,而 VotingClassifier 的基分类器要实实在在的输入其本身

from sklearn.linear_model import LinearRegression
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier

LR = LinearRegression(solver= 'lbfgs', multi_class='multinomial')
RF = RandomForestClassifier( n_estimators=5)
GNB = GaussianNB()

Ensemble = VotingClassifier(estimators = [('lr', LR), ('rf', RF), 
                           ('gnb', GNB)], voting='hard' )

Ensemble.fit( X_train, y_train )

print( len(Ensemble.estimators_) )
Ensemble.estimators_
image.png

比较元估计器和它三个组成元素的表现。还是集成后的 Ensemble 表现最好。

LR.fit( X_train, y_train )
RF.fit( X_train, y_train )
GNB.fit( X_train, y_train )
image.png
LR - Accuracy (Train): 0.975
RF - Accuracy (Train): 0.9833
GNB - Accuracy (Train): 0.95
Ensemble - Accuracy (Train): 0.9833

LR - Accuracy (Test): 1
RF - Accuracy (Test): 1
GNB - Accuracy (Test): 1
Ensemble - Accuracy (Test): 1

4.2 Multiclass 估计器

sklearn.multiclass 可以处理多类别 (multi-class) 的多标签 (multi-label) 的分类问题。

从小节 4.2 到 4.4,我们都会使用数字数据集 digits。首先将数据分成 80:20 的训练集和测试集。

from sklearn.datasets import load_digits

digits = load_digits()
digits.keys()

X_train, X_test, y_train, y_test 
= train_test_split( digits['data'],
                    digits['target'],
                    test_size=0.2 )

4.2.1 多类别分类

手写数字有 0-9 十类,但手头上只有两分类估计器 (比如像支撑向量机) 怎么用呢?我们可以采取下面三种常见策略:

OneVsOneClassifier

image.png
print( len(ovo_lr.estimators_) )
ovo_lr.estimators
image.png

OneVsRestClassifier

image.png
print( len(ova_lr.estimators_) )
ova_lr.estimators_

image.png
4.2.2 多标签分类

为了阐明「多标签分类」的原理。在手写数字的例子上,我们特意为每个数字设计了多标签:

再建立多标签 y_train_multilabel,代码如下 (OneVsRestClassifier 也可以用来做多标签分类):

from sklearn.multiclass import OneVsRestClassifier

y_train_multilabel = np.c_[ y_train%2==0, y_train<=4 ]
print(y_train_multilabel)      --[[ True True] [False False] [False False] ... [False False] [False False] [False False]]

训练模型,只不过这时用的是 y_train_multilabel。

image.png
print( len(ova_ml.estimators_) )
ova_ml.estimators_
image.png

4.3 Multioutput 估计器*

sklearn.multioutput 可以处理多输出 (multi-output) 的分类问题。

Multioutput 估计器有两个:

本节只关注多输出分类。

MultiOutputClassifier

首先引入 MultiOutputClassifier 和 RandomForestClassifier。你看,这两个都是元估计器,因此在 Sklearn 里面估计器可以随意组合。

from sklearn.multioutput import MultiOutputClassifier
from sklearn.ensemble import RandomForestClassifier

在手写数字的例子上,我们也为特意每个数字设计了多标签而且每个标签的类别都大于二。

代码如下:

from sklearn.multioutput import MultiOutputClassifier
from sklearn.ensemble import RandomForestClassifier
用含有 100 棵决策树的随机森林来解决这个多输入分类问题。

image.png
# 这个模型在测试集前五张照片上的预测。
MO.predict( X_test[:5,:] )
--array([[0, 2],
       [0, 2],
       [0, 0],
       [2, 9],
       [1, 5]])


# 真实标签
y_test_1st = y_test.copy()
y_test_1st[ y_test<=4 ] = 0
y_test_1st[ np.logical_and(y_test>4, y_test<7) ] = 1
y_test_1st[ y_test>=7 ] = 2

y_test_multioutput 
= np.c_[ y_test_1st, y_test ]

y_test_multioutput[:5]
--array([[0, 2],
       [0, 2],
       [0, 0],
       [2, 9],
       [1, 5]]

4.4 Model Selection 估计器

模型选择 (Model Selction) 在机器学习非常重要,它主要用于评估模型表现,常见的 Model Selection 估计器有以下几个:

本小节关注调节超参数的两个估计器,即上面列出的最后两个。它们都要用到交叉验证,我们来看这两个调参的估计器,网格追踪和随机追踪。

网格追踪参数 1 在 [1, 10, 100, 1000] 中取值,**参数 2 **在 [0.01, 0.1, 1 10] 中取值,注意并不是等间距取值。模型在所有 16 个组合跑,选取一对对应最小交叉验证误差的参数。

随机追踪:根据指定分布随机搜索,可以选择独立于参数个数,比如 log(参数 1) 服从 0 到 3 的均匀分布, log(参数 2) 服从 -2 到 1 的均匀分布。此外,会设定一个预算参数。

原理讲清楚了,看代码吧。


image.png

4.5 Pipeline 估计器

Pipeline 估计器又叫流水线,把各种估计器串联 (Pipeline) 或并联 (FeatureUnion) 的方式组成一条龙服务。用好了它真的能大大提高效率。

4.5.1 Pipeline

Pipeline 将若干个估计器按顺序连在一起,比如

特征提取 -> 降维 -> 拟合 -> 预测

在整个 Pipeline 中,它的属性永远和最后一个估计器属性一样

下面用一个简单例子来说明如果用 Pipeline 来做「先填补缺失值-再标准化」这两步的。先生成含缺失值 NaN 的数据 X。

image.png

首先引入 Pipeline,再引入

image.png

由于最后一个估计器是转换器,因此 pipe 也是个转换器。写好了就可以那它来做「先填补缺失值-再标准化」的重复工作了。

看看运行结果,值都被填满了,而且两列也被标准化了。

image.png
4.5.2 FeatureUnion

如果我们想在一个节点同时运行几个估计器,我们可用 FeatureUnion。下例首先建立一个 DataFrame,

image.png
image.png image.gif

我们现在按下列步骤来清洗数据。

上面两步是平行进行的。

首先我们自己定义一个从 DataFrame 里面获取每列的类,起名叫 DataFrameSelector

image.png

接下来建立一个流水线 full_pipe,它并联着两个流水线

下面代码非常漂亮。

image.png

将结果打印出来,齐活!

X_proc = full_pipe.fit_transform( X )
print( X_proc )

5 总结

结合本帖讲的总结一套机器学习的初级框架:

确定任务:是「有监督」的分类或回归?还是「无监督」的聚类或降维?确定好后基本就能知道用 Sklearn 里哪些模型了。

数据预处理:这步最繁琐,要处理缺失值、异常值;要编码分类型变量;要正规化或标准化数值型变量,等等。但是有了 Pipeline 神器一切变得简单高效。

训练和评估:这步最简单,训练用估计器 fit() 先拟合,评估用预测器 predict() 来评估。

选择模型:启动 Model Selection 估计器里的 GridSearchCV 和 RandomizedSearchCV,选择得分最高的那组超参数 (即模型)。

上一篇下一篇

猜你喜欢

热点阅读