sklearn实战系列
(1) sklearn实战之决策树
(2) sklearn实战之随机森林
(3) sklearn实战之数据预处理与特征工程
(4) sklearn实战之降维算法PCA与SVD
(5) sklearn实战之逻辑回归与制作评分卡
(6) sklearn实战之聚类算法

二、随机森林

0、概述

0.1集成算法概述

集成学习是时下非常流行的机器学习算法,它本身不是一个单独的机器学习模型,而是在以往的数据上构建多个模型,集成所有模型的建模结果,基本上所有机器学习领域都可以看到集成学习的身影,在显示中集成学习也有相当大的作用,它可以用来做市场营销模拟的建模,统计客户俩元,保留和损失,也可以用来预测疾病的风险和患病者的易感性。在现在的各种算法竞赛中,随机森林,梯度提升树,XGBoost等集成算法的身影也随处可见,可见其效果之好,应用之广。

集成算法的目标:集成算法会考虑多个评估器的建模效果,汇总之后得到一个综合的结果,以此来获取比单个模型更好的回归或分类表现。

多个模型集成成为的模型叫做集成评估其(ensemble estimator),组成的集成评估器的每个模型都叫做基评估器(base estimator)。通常来说,有三类集成算法:袋装(bagging),提升法(Boosting),和Stacking。

集成算法的分类

装袋法的核心思想是构建多个相互独立的评估器,然后对其预测进行平均或者多数表决原则来决定集成评估其的结果,装袋法的代表模型是随机森林。

提升法中,基评估其是相关的, 是按照顺序一一构建的,其核心思想是结合弱评估其的力量一次次对难以评估的样本进行预测,从而构成一个强评估器。其典型模型是
AdaBoost和梯度提升树。

1、Sklearn中的集成算法

  • sklearn中的集成算法模块ensemble
类的功能
ensemble.AdaBoostClassiferAdaBoost分类
ensemble.AdaBoost.RegressorAdaBoost回归
ensemble.BaggingClassifier袋装分类器
ensemble.BaggingRegressor袋装回归器
ensemble.ExtraTreesClassifierExtra-trees分类(超树,极端随机树)
ensemble.ExtraTreesRegressorExtra-trees回归
ensemble.GradientBoostingClassifier梯度提升分类
ensemble.GradientBoostingRegressor梯度提升回归
ensemble.IsolationForest隔离森林
ensemble.RandomForestClassifier随机森林分类
ensemble.RandomForestRegressor随机森林回归
ensemble.RandomTreesEmbedding完全随机数集合
ensemble.VotingClassifier用于不合适估算器的软投票/多数规则分类器

2、RandomForestClassifier

class sklearn.ensemble.RandomForestClassifier (n_estimators=10, criterion=’gini’, max_depth=None,
min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=’auto’,
max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False,
n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None)

随机森林是非常具有代表性的Bagging集成算法,它所有的基评估器都是决策树,分类树组成的森林就叫做随机森林分类器,回归树组成的森林就叫做随机森林回归器。这一讲主要是讲解RandomForestClassifier,随机森林分类器。

2.1 重要参数
2.2.1 控制基评估器的参数
参数含义
criterion不纯度的衡量指标,有基尼系数和信息熵两种选择
max_depth树的最大深度,超过最大深度的树枝都会被剪掉
min_samples_leaf一个节点在分支后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分支就不会发生
min_samples_split一个节点必须要包含至少min_samples_split个训练样本,这个节点才被允许分支,否则分支不会发生。
max_featuresmax_features限制分支时考虑的特振奋个数,超过限制个数的特征都会被舍弃,默认值为总特征个数开平方取整。
min_impurity_decrease限制信息增益的大小,信息增益小于设定数值的分支不会发生
2.1.2 n_estimator

这是森林中树木的数量,即基评估器的数量。这个参数对随机森林模型的精确性影响是单调的,n_estimator越大,模型的效果越好。但相应的,任何模型都有决策边界,n_estimator达到一定程度后,随机森林的精确性往往不再上升或开始波动,并且,n_estimator越大,需要的计算量和内存也就越大,训练的时间也会越来越长,对于这个参数,我们是渴望在训练难度和模型效果之间取得 平衡。

  • 实践:来建一片森林吧

树模型的有点是简单易懂,可视化后的树人人都能看懂,可惜随机森林是无法可视化的。所以为了更加直观地让大家体会到随机森林的效果,我们来进行一个随机森林和当个决策树的效益的对比,我们依然使用红酒数据集。

1、导入我们需要的包

%matplotlib inline
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine

2、导入需要的数据集

wine = load_wine()
wine.data
wine.target

3、复习sklearn建模的基本流程

from sklearn.model_selection import train_test_split
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
clf = DecisionTreeClassifier(random_state=0)
rfc = RandomForestClassifier(random_state=0)
clf = clf.fit(Xtrain,Ytrain)
rfc = rfc.fit(Xtrain,Ytrain)
score_c = clf.score(Xtest,Ytest)
score_r = rfc.score(Xtest,Ytest)
print("Single Tree:{}".format(score_c)
     ,"Random Forest:{}".format(score_r)
     )

4、画出随机森林和决策树在一组交叉验证下的效果对比

#目的是带大家复习一下交叉验证
#交叉验证:是数据集划分为n分,依次取每一份做测试集,每n-1份做训练集,多次训练模型以观测模型稳定性的方法
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
rfc = RandomForestClassifier(n_estimators=25)
rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10)
clf = DecisionTreeClassifier()
clf_s = cross_val_score(clf,wine.data,wine.target,cv=10)
plt.plot(range(1,11),rfc_s,label = "RandomForest")
plt.plot(range(1,11),clf_s,label = "Decision Tree")
plt.legend()
plt.show()
#====================一种更加有趣也更简单的写法===================#
"""
label = "RandomForest"
for model in [RandomForestClassifier(n_estimators=25),DecisionTreeClassifier()]:
   score = cross_val_score(model,wine.data,wine.target,cv=10)
   print("{}:".format(label)),print(score.mean())
   plt.plot(range(1,11),score,label = label)
   plt.legend()
   label = "DecisionTree"
"""

一组交叉验证

5、画出随机森林和决策树在10组交叉验证下的效果比较

rfc_l = []
clf_l = []
for i in range(10):
    rfc = RandomForestClassifier(n_estimators=25)
    rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10).mean()
    rfc_l.append(rfc_s)
    clf = DecisionTreeClassifier()
    clf_s = cross_val_score(clf,wine.data,wine.target,cv=10).mean()
    clf_l.append(clf_s)
    
plt.plot(range(1,11),rfc_l,label = "Random Forest")
plt.plot(range(1,11),clf_l,label = "Decision Tree")
plt.legend()
plt.show()
#是否有注意到,单个决策树的波动轨迹和随机森林一致?
#再次验证了我们之前提到的,单个决策树的准确率越高,随机森林的准确率也会越高

10组交叉验证

6、n_estimators学习曲线

#####【TIME WARNING: 2mins 30 seconds】#####
superpa = []
for i in range(200):
    rfc = RandomForestClassifier(n_estimators=i+1,n_jobs=-1)
    rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10).mean()
    superpa.append(rfc_s)
print(max(superpa),superpa.index(max(superpa)))
plt.figure(figsize=[20,5])
plt.plot(range(1,201),superpa)
plt.show()

思考:随机森林用了什么方法,来保证集成的效果一定好于单个分类器?

2.1.3 random_state

随机森林的本质是一种袋装集成算法(Bagging),袋装集成算法是对基评估器的预测结果进行平均或用多数表决原则来决定集成评估器的结果。在刚才的红酒例子中,我们建了25棵树,对于任何一个样本而言,平均或多数表决原则下,当且仅当有13课树以上的树判断错误的时候,随机森林才会判断错误。单独一颗决策树对红酒数据集的分类准确率在0.85上下浮动,假设一颗树判断错误的可能性为0.2(ε),那20棵树以上都判断错误的可能性是:

随机森林2

其中,i是判断错误的次数,也是判错的树的数量,ε是一棵树判断错误的概率,(1-ε)是判断正确的概率,共判对 25-i次。采用组合,是因为25棵树中,有任意i棵都判断错误。

可见,判断错误的几率非常小,这让随机森林在红酒数据集上的表现远远好于单颗决策树。

那现在就有一个问题了:我们说袋装法服从少数服从多数原则或对基分类器结果取平均,这即是说,我们默认森林中的每棵树应该是不同的,并且会返回不同的结果,设想以下,如果随机森林里所有的树的判断结果都一致(全判对或全判错),那随机森林无论应用何种集成原则来求结果,都应该无法比单颗决策树取得更好的效果才对,但我们使用了一样的类,一样的参数,一样的训练集和测试集,为什么随机森林里的众多树会有不同的判断结果呢?

问到这个问题,我们或许就可以想到了:sklearn中的分类树DecisionTreeClassifier自带随机性,所以随机森林中的树天生就是不一样的,我们在讲解分类树的时曾提到,决策树从最重要的特征中随机选择出一个特征来进行分支,因此每次生成的决策树都是不一样的,这个功能便是由random_state控制。

随机森林中也有random_state,用法和分类树相似。只不过在分类树中,一个random_state只能控制生成一棵树,而随机森林的random_state控制的是生成森林的模式,并非是让森林中只有一棵树。

rfc = RandomForestClassifier(n_estimators=20,random_state=2)
rfc = rfc.fit(Xtrain, Ytrain)
#随机森林的重要属性之一:estimators,查看森林中树的状况
rfc.estimators_[0].random_state
for i in range(len(rfc.estimators_)):
    print(rfc.estimators_[i].random_state)

输出:

1872583848
794921487
111352301
1853453896
213298710
1922988331
1869695442
2081981515
1805465960
......

我们可以观察到,当random_state固定时,随机森林中生成是一组固定的树,但每棵树依然是不一致的,这是 用”随机挑选特征进行分枝“的方法得到的随机性。并且我们可以证明,当这种随机性越大的时候,袋装法的效果一 般会越来越好。用袋装法集成时,基分类器应当是相互独立的,是不相同的。 但这种做法的局限性是很强的,当我们需要成千上万棵树的时候,数据不一定能够提供成千上万的特征来让我们构 筑尽量多尽量不同的树。因此,除了random_state。我们还需要其他的随机性。

2.1.4 boostrap & oob_score

要让基分类器尽量都不一样,一种很容易理解的方法是使用不同的训练集来进行训练,而袋装法正是通过有放回的随机抽样技术来形成不同的训练数据,boostrap就是用来控制抽样技术的参数。

在一个含有n个样本的原始训练集中,我们进行随机采样,每采一个样本,并在抽取下一个样本之前讲将该样本放回原始训练集,也就是说,在下次采样时,这个样本依然有可能被采集到,这样采集n次,最终得到一个和原始训练集一样大,n个样本组成的自助集。由于随机采样,使得每次的自助集和原始数据集不同,和其他的采样集也不同。这样我们就可以自由地创造取之不尽用之不竭,并且互不相同的自助集,用这些自助集来训练我们的基分类器,我们的基分类器也就各不相同了。

boostrap参数默认True,代表采用又放回的随机抽样技术。通常,这个参数不会被我们设置为False。

boostrap

然而Boostrap也会有自己的问题。由于有放回,一些样本可能在同一个自助集中出现多次,而其他一些却可能被忽略。一般来说,自助集大约平均会包含63%的原始数据。因为每一个样本被抽到某一个自助集的概率为:
1 − ( 1 − 1 n ) n 1-(1-\frac{1}{n})^n 1(1n1)n
当n足够大时,这个概率收敛于1-(1/e),约等于0.632。因此,会有约37%的训练数据被浪费掉,没有参与建模,这个数据被称为袋外数据(out og bagging,简称为oob)。除了我们最开始就划分好的测试集之外,这些数据也可以被用来作为集成算法的测试集。也就是说,在使用随机森林时,我们可以不划分测试集和训练集,只需要用袋外数据来测试我们的模型即可。 当然,这也不是绝对的,当n_estimators和n都不够大的时候,很可能就没有树掉落在袋外,自然也就无法使用oob数据来册数模型了。

如果希望用袋外数据来测试,则在实例化时需要将oob_score这个参数调整为True,训练完毕时,我们可以用随机森林的另外一个重要属性哦:oob_score_来查看我们的模型在袋外数据上测试的结果。

#无需划分训练集和测试集
rfc = RandomForestClassifier(n_estimators=25,oob_score=True)
rfc = rfc.fit(wine.data,wine.target)
#重要属性oob_score_
rfc.oob_score_

# 0.9550561797752809
2.2 重要属性和接口

至此,我们已经讲完了所有随机森林中的重要参数,并通过n_estimators, random_state,boostrap和oob_score这四个参数帮助大家了解了袋装法的基本流程和重要概念。同时,我们还 介绍了.estimators_ 和 .oob_score_ 这两个重要属性。除了这两个属性之外,作为树模型的集成算法,随机森林 自然也有.feature_importances_这个属性。

随机森林的接口和决策树完全一致,因此依然有四个常用接口:apply(apply返回每个测试样本所在的叶子节点的索引),fit,predict,score。除此之外,还需要注意随机森林内的predict_proba接口,这个接口返回每个测试样本对应被分到每一类标签的概率,标签有几个分类就返回几个概率,如果是二分类问题,则predict_proba返回的数值大于0.5的,被分为1,小于的被分为0。传统的随机森林是利用袋装法中的规则,平均或少数服从多数来决定集成的结果,而sklearn中的随机森林是平均每个样本对应的predict_praba返回的概率,得到一个平均概率,从而决定测试样本的分类。

掌握了上面的知识,基本上要实现随机森林分类已经是没问题了。从红酒数据集的表现上来看,随机森林的效用比 单纯的决策树要强上不少。

Bonus:Bagging的另一个必要条件

之前我们说过,在使用袋装法时应要求基评估器要尽量独立,其实,袋装法还有另外一个必要条件:基分类器的判断准确率至少要超过随机分类器。即说:基分类器的判断准确率至少要超过50%,之前我们展示过的随机森林的准确率公式,基于这个公式,我们画出了基分类器的准确率率ε和随机森林的误差率之间的图像。大家可以自己运行一 下这段代码,看看图像呈什么样的分布。

import numpy as np
x = np.linspace(0,1,20)
y = []
for epsilon in np.linspace(0,1,20):
    E = np.array([comb(25,i)*(epsilon**i)*((1-epsilon)**(25-i)) 
                  for i in range(13,26)]).sum()
    y.append(E)
plt.plot(x,y,"o-",label="when estimators are different")
plt.plot(x,x,"--",color="red",label="if all estimators are same")
plt.xlabel("individual estimator's error")
plt.ylabel("RandomForest's error")
plt.legend()
plt.show()

可以从图像上看出,当基分类器的误差率小于0.5,即准确率大于0.5时,集成的效果是比基分类器要好的。相反, 当基分类器的误差率大于0.5,袋装的集成算法就失效了。所以在使用随机森林之前,一定要检查,用来组成随机 森林的分类树们是否都有至少50%的预测正确率。

3、RandomForestRegressor

class sklearn.ensemble.RandomForestRegressor (n_estimators=’warn’, criterion=’mse’, max_depth=None,
min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=’auto’,
max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False,
n_jobs=None, random_state=None, verbose=0, warm_start=False)

所有的参数,属性与接口,全部和随机森林分类器一致。仅有的不同就是回归树与分类树的不同,不纯度的指标, 参数Criterion不一致。

3.1 重要参数,属性和接口
3.1.1 重要参数
  • criterion

回归树衡量分支质量的指标,支持的指标有以下三种:

(1)输入"mse"均方误差(mean squared error)。父节点和叶节点之间的均方误差的差额将被用来作为特征选择的标准,这种方法通过对叶子节点的均值来最小化L2损失。

(2)输入”fredman_mse“使用费尔德曼均方误差,这种指标使用福利费尔德曼针对潜在分支中的问题改进后的均方误差

(3)输入”mae“使用绝对平均误差MAE(mean absolute error),这指标使用叶节点的中值来最小化 L1损失。

在这里插入图片描述
其中N是样本数量,i是每一个数据样本,fi是模型回归出的数值,yi是样本点i实际的数值标签。所以MSE的本质, 其实是样本真实数据与回归结果的差异。在回归树中,MSE不只是我们的分支质量衡量指标,也只我们最常用的衡量回归树回归质量的指标。当我们使用交叉验证,或者其他方式获取回归树你的结果时,我们往往选取MSE作为我们的评估。MSE越小,说明我们的回归效果越好。然而,回归树score接口返回的和分类树一样,是R平方,并不是MSE。

R方定义如下:

R方
其中u是残差平方和(MSE * N),v是总平方和,N是样本数量,i是每一个数据样本,fi是模型回归出的数值,yi 是样本点i实际的数值标签。y帽是真实数值标签的平均数。R平方可以为正为负(如果模型的残差平方和远远大于 模型的总平方和,模型非常糟糕,R平方就会为负),而均方误差永远为正。

值得一提的是,虽然均方误差永远为正,但是sklearn当中使用均方误差作为评判标准时,却是计算”负均方误 差“(neg_mean_squared_error)。这是因为sklearn在计算模型评估指标的时候,会考虑指标本身的性质,均 方误差本身是一种误差,所以被sklearn划分为模型的一种损失(loss),因此在sklearn当中,都以负数表示。真正的 均方误差MSE的数值,其实就是neg_mean_squared_error去掉负号的数字。

3.1.2 重要属性和接口

最重要的属性和接口,都与随机森林的分类器相一致,还是apply, fit, predict和score最为核心。值得一提的是,随机森林回归并没有predict_proba这个接口,因为对于回归来说,并不存在一个样本要被分到某个类别的概率问题,因此没有predict_proba这个接口。

  • 随机森林回归用法

和决策树完全相同,多了参数n_estimators

from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestRegressor
boston = load_boston()
regressor = RandomForestRegressor(n_estimators=100,random_state=0)
cross_val_score(regressor, boston.data, boston.target, cv=10
               ,scoring = "neg_mean_squared_error")
sorted(sklearn.metrics.SCORERS.keys())

返回十次交叉验证的结果,注意在这里,如果不填写scoring = “neg_mean_squared_error”,交叉验证默认的模型 衡量指标是R平方,因此交叉验证的结果可能有正也可能有负。而如果写上scoring,则衡量标准是负MSE,交叉验 证的结果只可能为负。

3.2 实例:用随机森林回归填补缺失值

我们从现实中收集的数据,几乎不可能是完美无缺的,往往都会有一些缺失值。面对缺失值,很多人选择的方式是 直接将含有缺失值的样本删除,这是一种有效的方法,但是有时候填补缺失值会比直接丢弃样本效果更好,即便我 们其实并不知道缺失值的真实样貌。在sklearn中,我们可以使用sklearn.impute.SimpleImputer来轻松地将均 值,中值,或者其他最常用的数值填补到数据中,在这个案例中,我们将使用均值,0,和随机森林回归来填补缺 失值,并验证四种状况下的拟合状况,找出对使用的数据集来说最佳的缺失值填补方法。

1、导入需要的库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score

2、以波士顿数据集为例,导入完整的数据集并探索

dataset = load_boston()
dataset.data.shape
#总共506*13=6578个数据
X_full, y_full = dataset.data, dataset.target
n_samples = X_full.shape[0]
n_features = X_full.shape[1]

3、**为完整数据集放入缺失值 **

#首先确定我们希望放入的缺失数据的比例,在这里我们假设是50%,那总共就要有3289个数据缺失
rng = np.random.RandomState(0)
missing_rate = 0.5
n_missing_samples = int(np.floor(n_samples * n_features * missing_rate))
#np.floor向下取整,返回.0格式的浮点数
#所有数据要随机遍布在数据集的各行各列当中,#而一个缺失的数据会需要一个行索引和一个列索#引
#如果能够创造一个数组,包含3289个分布在#0~506中间的行索引,和3289个分布在0~13之间的列索引,那我们就#可以利用索引来为数据中的任意3289个位置赋空值
#然后我们用0,均值和随机森林来填写这些缺失值,然后查看回归的结果如何
missing_features = rng.randint(0,n_features,n_missing_samples)
missing_samples = rng.randint(0,n_samples,n_missing_samples)
#missing_samples = rng.choice(dataset.data.shape[0],n_missing_samples,replace=False)
#我们现在采样了3289个数据,远远超过我们的样本量506,所以我们使用随机抽取的函数randint。但如果我们需要的数据量小于我们的样本量506,那我们可以采用np.random.choice来抽样,choice会随机抽取不重复的随机数,
#因此可以帮助我们让数据更加分散,确保数据不会集中在一些行中
X_missing = X_full.copy()
y_missing = y_full.copy()
X_missing[missing_samples,missing_features] = np.nan
X_missing = pd.DataFrame(X_missing)
#转换成DataFrame是为了后续方便各种操作,numpy对矩阵的运算速度快到拯救人生,但是在索引等功能上却不如
#pandas来得好用

4、使用0和均值填补缺失值

#使用均值进行填补
from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')
X_missing_mean = imp_mean.fit_transform(X_missing)
#使用0进行填补
imp_0 = SimpleImputer(missing_values=np.nan, strategy="constant",fill_value=0)
X_missing_0 = imp_0.fit_transform(X_missing)

5、使用随机森林填补缺失值

任何回归都是从特征矩阵中学习,然后求解连续型标签y的过程,之所以能够实现这个过程,是因为回归算法认为,特征 矩阵和标签之前存在着某种联系。实际上,标签和特征是可以相互转换的,比如说,在一个“用地区,环境,附近学校数 量”预测“房价”的问题中,我们既可以用“地区”,“环境”,“附近学校数量”的数据来预测“房价”,也可以反过来, 用“环境”,“附近学校数量”和“房价”来预测“地区”。

而回归填补缺失值,正是利用了这种思想。 对于一个有n个特征的数据来说,其中特征T有缺失值,我们就把特征T当作标签,其他的n-1个特征和原本的标签组成新 的特征矩阵。

那对于T来说,它没有缺失的部分,就是我们的Y_test,这部分数据既有标签也有特征,而它缺失的部 分,只有特征没有标签,就是我们需要预测的部分。

特征T不缺失的值对应的其他n-1个特征 + 本来的标签:X_train

特征T不缺失的值:Y_train 特征T缺失的值对应的其他n-1个特征 + 本来的标签:X_test

特征T缺失的值:未知,我们需要预测的Y_test 这种做法,对于某一个特征大量缺失,其他特征却很完整的情况,非常适用。

那如果数据中除了特征T之外,其他特征也有缺失值怎么办? 答案是遍历所有的特征,从缺失最少的开始进行填补(因为填补缺失最少的特征所需要的准确信息最少)。 填补一个特征时,先将其他特征的缺失值用0代替,每完成一次回归预测,就将预测值放到原本的特征矩阵中,再继续填 补下一个特征。每一次填补完毕,有缺失值的特征会减少一个,所以每次循环后,需要用0来填补的特征就越来越少。当 进行到最后一个特征时(这个特征应该是所有特征中缺失值最多的),已经没有任何的其他特征需要用0来进行填补了, 而我们已经使用回归为其他特征填补了大量有效信息,可以用来填补缺失最多的特征。 遍历所有的特征后,数据就完整,不再有缺失值了。

X_missing_reg = X_missing.copy()
sortindex = np.argsort(X_missing_reg.isnull().sum(axis=0)).values
for i in sortindex:
    
    #构建我们的新特征矩阵和新标签
    df = X_missing_reg
    fillc = df.iloc[:,i]
    df = pd.concat([df.iloc[:,df.columns != i],pd.DataFrame(y_full)],axis=1)
    
    #在新特征矩阵中,对含有缺失值的列,进行0的填补
    df_0 =SimpleImputer(missing_values=np.nan,
                        strategy='constant',fill_value=0).fit_transform(df)
    
    #找出我们的训练集和测试集
    Ytrain = fillc[fillc.notnull()]
    Ytest = fillc[fillc.isnull()]
    Xtrain = df_0[Ytrain.index,:]
    Xtest = df_0[Ytest.index,:]
    
    #用随机森林回归来填补缺失值
    rfc = RandomForestRegressor(n_estimators=100)
    rfc = rfc.fit(Xtrain, Ytrain)
    Ypredict = rfc.predict(Xtest)
    
    #将填补好的特征返回到我们的原始的特征矩阵中
    X_missing_reg.loc[X_missing_reg.iloc[:,i].isnull(),i] = Ypredict

6、对填补好的数据进行建模

#对所有数据进行建模,取得MSE结果
X = [X_full,X_missing_mean,X_missing_0,X_missing_reg]
mse = []
std = []
for x in X:
    estimator = RandomForestRegressor(random_state=0, n_estimators=100)
    scores = cross_val_score(estimator,x,y_full,scoring='neg_mean_squared_error', 
cv=5).mean()
    mse.append(scores * -1)

7、用所得结果画出条形图

x_labels = ['Full data',
            'Zero Imputation',
            'Mean Imputation',
            'Regressor Imputation']
colors = ['r', 'g', 'b', 'orange']
plt.figure(figsize=(12, 6))
ax = plt.subplot(111)
for i in np.arange(len(mse)):
    ax.barh(i, mse[i],color=colors[i], alpha=0.6, align='center')
ax.set_title('Imputation Techniques with Boston Data')
ax.set_xlim(left=np.min(mse) * 0.9,
             right=np.max(mse) * 1.1)
ax.set_yticks(np.arange(len(mse)))
ax.set_xlabel('MSE')
ax.set_yticklabels(x_labels)
plt.show()

结果:

在这里插入图片描述

4、机器学习中调参的基本思想

我发现大多数的机器学习相关的书都是遍历各种算法和案例,为大家讲解各种各样算法 的原理和用途,但却对调参探究甚少。

这中间有许多原因,其一是因为,调参的方式总是根据数据的状况而定,所 以没有办法一概而论;其二是因为,其实大家也都没有特别好的办法。

通过画学习曲线,或者网格搜索,我们能够探索到调参边缘(代价可能是训练一次模型要跑三天三夜),但是在现实中,高手调参恐怕还是多依赖于经验,而这些经验,来源于:1)非常正确的调参思路和方法,2)对模型评估指 标的理解,3)对数据的感觉和经验,4)用洪荒之力去不断地尝试。

我们也许无法学到高手们多年累积的经验,但我们可以学习他们对模型评估指标的理解和调参的思路。 那我们首先来讲讲正确的调参思路。

模型调参,第一步是要找准目标:我们要做什么?一般来说,这个目标是提升 某个模型评估指标,比如对于随机森林来说,我们想要提升的是模型在未知数据上的准确率(由score或 oob_score_来衡量)。找准了这个目标,我们就需要思考:模型在未知数据上的准确率受什么因素影响?在机器学习中,我们用来衡量模型在未知数据上的准确率的指标,叫做泛化误差(Genelization error)

  • 泛化误差

当模型在未知数据(测试集或者袋外数据)上表现糟糕时,我们说模型的泛化程度不够,泛化误差大,模型的效果不好。泛化误差受到模型的结构(复杂度)影响。

看下面这张图,它准确地描绘了泛化误差与模型复杂度的关系, 当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。当模型太简单,模型就会欠拟合,拟合能力 就不够,所以误差也会大。只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标。

泛化误差

那模型的复杂度与我们的参数有什么关系呢?对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂。所 以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随 机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合。当然了,调参 没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。

泛化误差的背后其实是“偏差-方差困境”,原理十分复杂,无论你翻开哪一本书,你都会看见长篇的数学论证和每个 字都能看懂但是连在一起就看不懂的文字解释。在下一节偏差vs方差中,我用最简单易懂的语言为大家解释了泛化 误差背后的原理,大家选读。那我们只需要记住这四点:

1)模型太复杂或者太简单,都会让泛化误差高,我们追求的是位于中间的平衡点

2)模型太复杂就会过拟合,模型太简单就会欠拟合

3)对树模型和树的集成模型来说,树的深度越深,枝叶越多,模型越复杂

4)树模型和树的集成模型的目标,都是减少模型复杂度,把模型往图像的左边移动

那具体每个参数,都如何影响我们的复杂度和模型呢?我们一直以来调参,都是在学习曲线上轮流找最优值,盼望 能够将准确率修正到一个比较高的水平。然而我们现在了解了随机森林的调参方向:降低复杂度,我们就可以将那 些对复杂度影响巨大的参数挑选出来,研究他们的单调性,然后专注调整那些能最大限度让复杂度降低的参数。对 于那些不单调的参数,或者反而会让复杂度升高的参数,我们就视情况使用,大多时候甚至可以退避。基于经验, 我对各个参数对模型的影响程度做了一个排序。在我们调参的时候,大家可以参考这个顺序:

参数对模型在未知数据上的评估性能的影响影响程度
n_estimators提升至平稳,n_estimators↑,不影响单个模型的复杂度⭐⭐⭐⭐
max_depth有增有减,默认最大深度,即最高复杂度,向复杂度降低的方向调参 max_depth↓,模型更简单,且向图像的左边移动⭐⭐⭐
min_samples_leaf有增有减,默认最小限制1,即最高复杂度,向复杂度降低的方向调参 min_samples_leaf↑,模型更简单,且向图像的左边移动⭐⭐
min_samples_split有增有减,默认最小限制2,即最高复杂度,向复杂度降低的方向调参 min_samples_split↑,模型更简单,且向图像的左边移动⭐⭐
max_features有增有减,默认auto,是特征总数的开平方,位于中间复杂度,既可以 向复杂度升高的方向,也可以向复杂度降低的方向调参 max_features↓,模型更简单,图像左移 max_features↑,模型更复杂,图像右移 max_features是唯一的,既能够让模型更简单,也能够让模型更复杂的参 数,所以在调整这个参数的时候,需要考虑我们调参的方向
criterion有增有减,一般使用gini看具体情况

有了以上的知识储备,我们现在也能够通过参数的变化来了解,模型什么时候到达了极限,当复杂度已经不能再降 低的时候,我们就不必再调整了,因为调整大型数据的参数是一件非常费时费力的事。除了学习曲线和网格搜索, 我们现在有了基于对模型和正确的调参思路的“推测”能力,这能够让我们的调参能力更上一层楼。

5、方差 VS 偏差(选读)

待补充…

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐