sklearn机器学习库(二)sklearn中的随机森林

sklearn机器学习库(二)sklearn中的随机森林

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

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

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

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

sklearn中的集成算法模块ensemble

类的功能
ensemble.AdaBoostClassifier AdaBoost分类
ensemble.AdaBoostRegressor Adaboost回归
ensemble.BaggingClassifier 装袋分类器
ensemble.BaggingRegressor 装袋回归器
ensemble.ExtraTreesClassifier Extra-trees分类(超树,极端随机树)
ensemble.ExtraTreesRegressor Extra-trees回归
ensemble.GradientBoostingClassifier 梯度提升分类
ensemble.GradientBoostingRegressor 梯度提升回归
ensemble.IsolationForest 隔离森林
ensemble.RandomForestClassifier 随机森林分类
ensemble.RandomForestRegressor 随机森林回归
ensemble.RandomTreesEmbedding 完全随机树的集成
ensemble.VotingClassifier 用于不合适估算器的软投票/多数规则分类器

集成算法中,有一半以上都是树的集成模型。

决策树的核心问题有两个,一个是如何找出正确的特征来进行分枝,二是树生长到什么时候应该停下。

  • 对于第一个问题,我们定义了用来衡量分枝质量的指标不纯度,分类树的不纯度用基尼系数或信息熵来衡量,回归
    树的不纯度用MSE均方误差来衡量。每次分枝时,决策树对所有的特征进行不纯度计算,选取不纯度最低的特征进
    行分枝,分枝后,又再对被分枝的不同取值下,计算每个特征的不纯度,继续选取不纯度最低的特征进行分枝。
  • 决策树非常容易过拟合,为了防止决策树的过拟合,我们要对决策树进行剪枝,sklearn中提供了大量的剪枝参数 。

1 RandomForestClassifier

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

sklearn.ensemble.RandomForestClassifier(
    n_estimators=100, 
    *, 
    criterion='gini', 
    max_depth=None, 
    min_samples_split=2, 
    min_samples_leaf=1, 
    min_weight_fraction_leaf=0.0, 
    max_features='sqrt', 
    max_leaf_nodes=None, 
    min_impurity_decrease=0.0, 
    bootstrap=True, 
    oob_score=False, 
    n_jobs=None, 
    random_state=None, 
    verbose=0, 
    warm_start=False, 
    class_weight=None, 
    ccp_alpha=0.0, 
    max_samples=None
)

1.1 重要参数

1.1.1 基评估器的控制参数

参数 含义
criterion 不纯度的衡量指标,{“gini”, “entropy”, “log_loss”},默认是”gini”
max_depth 树的最大深度,超过最大深度的树枝都会被剪掉
min_samples_leaf 一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生,
min_samples_split 一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则分枝就不会发生
max_features max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃, 默认值为总特征个数开平方取整(sqrt),可选值有**{“sqrt”, “log2”, None}**
min_impurity_decrease 信息增益小于设定数值的分枝不会发生

详细解释可参考官网。https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier

1.1.2 n_estimators

  • 这是森林中树木的数量,即基评估器的数量。这个参数对随机森林模型的精确性影响是单调的,n_estimators越大,模型的效果往往越好

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

  • n_estimators的默认值在0.22版本中,默认值从10改为100,这个修正显示出了使用者的调参倾向:要更大的n_estimators。

from matplotlib import pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split,cross_val_score
%matplotlib inline


# 导入红酒数据集
wine = load_wine()

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)


tree_score = clf.score(Xtest,Ytest)
forest_score = rfc.score(Xtest,Ytest)

print('单棵树:{}'.format(tree_score),'随机森林:{}'.format(forest_score))
单棵树:0.9444444444444444 随机森林:1.0
# 交叉验证
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()

在这里插入图片描述

# n_estimators的学习曲线
forest_scores = []

for i in range(100):
    rfc = RandomForestClassifier(n_estimators=i+1)
    s = cross_val_score(rfc,wine.data,wine.target,cv=10).mean()
    forest_scores.append(s)


# 打印最大值及最大值的索引
print(max(forest_scores),forest_scores.index(max(forest_scores)))
plt.figure(figsize=(10,8))
plt.plot(range(1,101),forest_scores)
plt.legend()
plt.show()

在这里插入图片描述

1.1.3 random_state

  • 随机森林的本质是一种装袋集成算法(bagging),装袋集成算法是对基评估器的预测结果进行平均或用多数表决原则来决定集成评估器的结果。

  • 在刚才的红酒例子中,我们建立了25棵树,对任何一个样本而言,平均或多数表决原则下,当且仅当有13棵以上的树判断错误的时候,随机森林才会判断错误。单独一棵决策树对红酒数据集的分类准确率在0.85上下浮动,假设一棵树判断错误的可能性为0.2(ε),那20棵树以上都判断错误的可能性是0.000369。判断错误的几率非常小,这让随机森林在红酒数据集上的表现远远好于单棵决策树

在这里插入图片描述

  • 随机森林中其实也有random_state,用法和分类树中相似。
    • 只不过在分类树中,一个random_state只控制生成一棵树
    • 而随机森林中的random_state控制的是生成森林的模式,而非让一个森林中只有一棵树
rfc = RandomForestClassifier(n_estimators=20,random_state=2)
rfc = rfc.fit(Xtrain, Ytrain)

#随机森林的重要属性之一:estimators,查看森林中树的状况
for i in range(len(rfc.estimators_)):
    print(rfc.estimators_[i].random_state)
1872583848
794921487
111352301
1853453896
213298710
1922988331
......
  • 当random_state固定时,随机森林中生成是一组固定的树,但每棵树依然是不一致的,这是用随机挑选特征进行分枝的方法得到的随机性。并且我们可以证明,当这种随机性越大的时候,袋装法的效果一般会越来越好。用袋装法集成时,基分类器应当是相互独立的,是不相同的

  • 但这种做法的局限性是很强的,当我们需要成千上万棵树的时候,数据不一定能够提供成千上万的特征来让我们构筑尽量多尽量不同的树。因此,除了random_state。我们还需要其他的随机性

1.1.4 bootstrap & oob_score

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

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

  • bootstrap参数默认True,代表采用这种有放回的随机抽样技术。

  • 一般来说,自助集大约平均会包含63%的原始数据 。因此,会有约37%的训练数据被浪费掉,没有参与建模,这些数据被称为袋外数据(out of bag data,简写为oob)

1 − ( 1 − 1 / n ) n 当 n 足够大时,这个概率收敛于 1 − ( 1 / e ) ,约等于 0.632 。 1-(1-1/n)^n \\ 当n足够大时,这个概率收敛于1-(1/e),约等于0.632。 1(11/n)nn足够大时,这个概率收敛于1(1/e),约等于0.632

  • 在使用随机森林时,我们可以不划分测试集和训练集,只需要用袋外数据来测试我们的模型即可。
    • 当然,这也不是绝对的,当n和n_estimators都不够大的时候,很可能就没有数据掉落在袋外,自然也就无法使用oob数据来测试模型了。
    • 如果希望用袋外数据来测试,则需要在实例化时就将oob_score这个参数调整为True
    • 训练完毕之后,可以用oob_score_来查看在袋外数据上测试的结果
# 无需划分训练集和测试集
rfc = RandomForestClassifier(n_estimators=25,oob_score=True)
rfc = rfc.fit(wine.data, wine.target)

# 重要属性oob_score_
print(rfc.oob_score_) 

1.2 重要接口

  • 随机森林的接口与决策树完全一致,因此依然有四个常用接口:apply, fit, predict和score

  • 除此之外,还需要注意随机森林的predict_proba接口,这个接口返回每个测试样本对应的被分到每一类标签的概率,标签有几个分类就返回几个概率。如果是二分类问题,则predict_proba返回的数值大于0.5的,被分为1,小于0.5的,被分为0。

  • 传统的随机森林是利用袋装法中的规则,平均或少数服从多数来决定集成的结果,而sklearn中的随机森林是平均每个样本对应的predict_proba返回的概率,得到一个平均概率,从而决定测试样本的分类

在这里插入图片描述

在这里插入图片描述

注意:

当基分类器的误差率小于0.5,即准确率大于0.5时,集成的效果是比基分类器要好的。

相反,当基分类器的误差率大于0.5,袋装的集成算法就失效了。

所以在使用随机森林之前,一定要检查,用来组成随机森林的分类树们是否都有至少50%的预测正确率。

2 RandomForestRegressor

DecisionTreeRegressor(*,
                      criterion='squared_error', 
                      splitter='best', 
                      max_depth=None, 
                      min_samples_split=2, 
                      min_samples_leaf=1, 
                      min_weight_fraction_leaf=0.0, 
                      max_features=None, 
                      random_state=None, 
                      max_leaf_nodes=None, 
                      min_impurity_decrease=0.0, 
                      ccp_alpha=0.0
)

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

2.1 Criterion

Criterion是衡量回归树分枝质量的指标 :

  • 均方误差的squared_error,父节点和叶子节点之间的均方误差的差额将被用来作为特征选择的标准,这种方法通过使用叶子节点的均值来最小化L2损失

  • friedman_mse,这种指标使用弗里德曼针对潜在分枝中的问题改进后的均方误差

  • 平均绝对误差absolute_error,这种指标使用叶节点的中值来最小化L1损失

  • 以及poisson,它使用泊松偏差的减少来寻找分裂

虽然均方误差永远为正,但是sklearn当中使用均方误差作为评判标准时,却是计算负均方误差

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

# 读取波士顿数据集,注意:新版本的sklearn中自带的已经删除
data = pd.read_csv("boston_housing.data", sep='\s+', header=None)

x = data.iloc[:, :-1]
y = data.iloc[:, -1]


regressor = RandomForestRegressor(n_estimators=100,random_state=0)
cross_val_score(regressor, x, y, cv=10,scoring = "neg_mean_squared_error")

在这里插入图片描述

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

而如果写上scoring,则衡量标准是负MSE,交叉验证的结果只可能为负。

2.2 使用随机森林填补缺失值

我们从现实中收集的数据,往往都会有一些缺失值。面对缺失值,很多人选择的方式是直接将含有缺失值的样本删除,这是一种有效的方法。

有时候填补缺失值会比直接丢弃样本效果更好。在sklearn中,我们用sklearn.impute.SimpleImputer来轻松地将均值,中值,或者其他最常用的数值填补到数据中。

在这个案例中,我们将使用均值,0,和随机森林回归来填补缺失值,并验证四种状况下的拟合状况,找出对使用的数据集来说最佳的缺失值填补方法。

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

# 读取波士顿数据集,注意:新版本的sklearn中自带的已经删除
data = pd.read_csv("boston_housing.data", sep='\s+', header=None)

x = data.iloc[:, :-1]
y = data.iloc[:, -1]

print(x.shape)
x.info()  # 均非空

在这里插入图片描述

# 人为填充缺失值
#所有数据要随机遍布在数据集的各行各列当中,而一个缺失的数据会需要一个行索引和一个列索引
#如果能够创造一个数组,包含3289个分布在0~506中间的行索引,和3289个分布在0~13之间的列索引,那我们就可以利用索引来为数据中的任意3289个位置赋空值

n_samples = x.shape[0]
n_features = x.shape[1]

rng = np.random.RandomState(0)
miss_rate = 0.5 # 缺失50%,即3289数据

n_missing_samples = int(np.floor(n_samples * n_features * miss_rate))

missing_features = rng.randint(0,n_features,n_missing_samples)
missing_samples = rng.randint(0,n_samples,n_missing_samples)
X_missing = x.copy().to_numpy()
y_missing = y.copy()
X_missing[missing_samples,missing_features] = np.nan
X_missing = pd.DataFrame(X_missing)


X_missing.head(10)

在这里插入图片描述

# 使用0和均值填补缺失值

# 1、使用均值进行填充
s_mean = SimpleImputer(missing_values=np.nan, strategy='mean')

x_miss_mean = s_mean.fit_transform(X_missing)


# 2、使用常数0进行填充
zero_mean = SimpleImputer(missing_values=np.nan, strategy='constant',fill_value=0)

x_miss_zero = zero_mean.fit_transform(X_missing)

# 3、使用随机森林填充缺失值
'''
特征T不缺失的值对应的其他n-1个特征 + 本来的标签:X_train
特征T不缺失的值:Y_train

特征T缺失的值对应的其他n-1个特征 + 本来的标签:X_test
特征T缺失的值:未知,我们需要预测的Y_test

这种做法,对于某一个特征大量缺失,其他特征却很完整的情况,非常适用

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

'''
X_missing_reg = X_missing.copy()
sortindex = np.argsort(X_missing_reg.isnull().sum(axis=0))

for i in sortindex:
    #构建我们的新特征矩阵和新标签
    df = X_missing_reg
    fillc = df.iloc[:,i]
    df = pd.concat([df.iloc[:,df.columns != i],y],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
# 对填补好的数据进行建模
X = [x,x_miss_mean,x_miss_zero,X_missing_reg]


mse = []
std = []
for x in X:
    estimator = RandomForestRegressor(random_state=0, n_estimators=100)
    scores = cross_val_score(estimator,x,y,scoring='neg_mean_squared_error',cv=5).mean()
    mse.append(scores * -1)
# 画图
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()

在这里插入图片描述

3 泛化误差详解

模型调参,第一步是要找准目标:我们要做什么?

一般来说,这个目标是提升某个模型评估指标,比如对于随机森林来说,我们想要提升的是模型在未知数据上的准确率(由score或oob_score_来衡量)。

找准了这个目标,我们就需要思考:模型在未知数据上的准确率受什么因素影响?

在机器学习中,我们用来衡量模型在未知数据上的准确率的指标,叫做泛化误差(Genelization error)

3.1 泛化误差与模型结构的关系

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

如下图:

  • 当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。

  • 当模型太简单,模型就会欠拟合,拟合能力就不够,所以误差也会大。

  • 只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标

在这里插入图片描述

模型的复杂度与参数的关系

  • 对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂

  • 所以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合

  • 当然了,调参没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。

3.2 随机森林与泛化误差

我们现在已经知道:

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

3.3 方差与偏差

集成模型在未知数据集上的泛化误差E,由方差(var),偏差(bais)和噪声(ε)共同决定。

偏差:模型的预测值与真实值之间的差异,模型越精确,偏差越低。

方差:反映的是模型每一次输出结果与模型预测值的平均水平之间的误差,模型越稳定,方差越低。

其中偏差衡量模型是否预测得准确,偏差越小,模型越“准”;而方差衡量模型每次预测的结果是否接近,即是说方
差越小,模型越“稳” 。

偏差大 偏差小
方差大 模型不适合这个数据 过拟合 模型很复杂 对某些数据集预测很准确 对某些数据集预测很糟糕
方差小 欠拟合、模型相对简单 预测很稳定 但对所有的数据预测都不太准确 泛化误差小,我们的目标

泛化误差和方差及偏差的关系

在这里插入图片描述

  • 模型复杂度大的时候,方差高,偏差低。

    • 偏差低,就是要求模型要预测得“准”。模型就会更努力去学习更多信息,会具体于训练数据,这会导致,模型在一部分数据上表现很好,在另一部分数据上表现却很糟糕。
    • 模型泛化性差,在不同数据上表现不稳定,所以方差就大。而要尽量学习训练集,模型的建立必然更多细节,复杂程度必然上升。所以,复杂度高,方差高,总泛化误差高。
  • 相对的,复杂度低的时候,方差低,偏差高。

    • 方差低,要求模型预测得“稳”,泛化性更强,那对于模型来说,它就不需要对数据进行一个太深的学习,只需要建立一个比较简单,判定比较宽泛的模型就可以了。
    • 结果就是,模型无法在某一类或者某一组数据上达成很高的准确度,所以偏差就会大。所以,复杂度低,偏差高,总泛化误差高。

随机森林中的方差与偏差

随机森林的基评估器都拥有较低的偏差和较高的方差,因为决策树本身是预测比较”准“,比较容易过拟合的模型,装袋法本身也要求基分类器的准确率必须要有50%以上。

所以以随机森林为代表的装袋法的训练过程旨在降低方差,即降低模型复杂度,所以随机森林参数的默认设定都是假设模型本身在泛化误差最低点的右边

4 随机森林在乳腺癌数据集上的调参

from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

data = load_breast_cancer()

# 乳腺癌数据集有569条记录,30个特征,单看维度虽然不算太高,但是样本量非常少。过拟合的情况可能存在
data.data.shape
# 进行一次简单的建模,看看模型本身在数据集上的效果
rfc = RandomForestClassifier(n_estimators=100,random_state=20)
score_pre = cross_val_score(rfc,data.data,data.target,cv=10).mean()

print(score_pre)  # 0.9648809523809524
# 随机森林调整的第一步:无论如何先来调n_estimators

scorel = []
for i in range(0,200,10):
    rfc = RandomForestClassifier(n_estimators=i+1,
                                 n_jobs=-1,
                                 random_state=20)
    score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
    scorel.append(score)

# 打印最大分数及所在的索引
print(max(scorel),(scorel.index(max(scorel))*10)+1)
plt.figure(figsize=[20,5])
plt.plot(range(1,201,10),scorel)
plt.show()

在这里插入图片描述

# 在确定好的范围内,进一步细化学习曲线
scorel = []
for i in range(45,80):
    rfc = RandomForestClassifier(n_estimators=i,
                                 n_jobs=-1,
                                 random_state=20)
    score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
    scorel.append(score)

print(max(scorel),([*range(45,80)][scorel.index(max(scorel))]))
plt.figure(figsize=[20,5])
plt.plot(range(45,80),scorel)
plt.show()

在这里插入图片描述

# 接下来就进入网格搜索,使用网格搜索对参数一个个进行调整。
# 为什么我们不同时调整多个参数呢?
# 1)同时调整多个参数会运行非常缓慢。
# 2)同时调整多个参数,会让我们无法理解参数的组合是怎么得来的,所以即便网格搜索调出来的结果不好,我们也不知道从哪里去改。
# 在这里,为了使用复杂度-泛化误差方法(方差-偏差方法),我们对参数进行一个个地调整。


# 为网格搜索做准备,书写网格搜索的参数
"""
有一些参数是没有参照的,很难说清一个范围,这种情况下我们使用学习曲线,看趋势从曲线跑出的结果中选取一个更小的区间,再跑曲线

param_grid = {'n_estimators':np.arange(0, 200, 10)}
param_grid = {'max_depth':np.arange(1, 20, 1)}
param_grid = {'max_leaf_nodes':np.arange(25,50,1)}


有一些参数是可以找到一个范围的,或者说我们知道他们的取值和随着他们的取值,模型的整体准确率会如何变化,这样的参数我们就可以直接跑网格搜索

param_grid = {'criterion':['gini', 'entropy']}
param_grid = {'min_samples_split':np.arange(2, 2+20, 1)}
param_grid = {'min_samples_leaf':np.arange(1, 1+10, 1)}
param_grid = {'max_features':np.arange(1,30,1)}
"""

# 按照参数对模型整体准确率的影响程度进行调参,首先调整max_depth

# 调整max_depth
param_grid = {
    
    'max_depth':np.arange(1, 20, 1)}

# 一般根据数据的大小来进行一个试探,乳腺癌数据很小,所以可以采用1~10,或者1~20这样的试探
# 但对于大型数据来说,我们应该尝试30~50层深度(或许还不足够更应该画出学习曲线,来观察深度对模型的影响)
rfc = RandomForestClassifier(n_estimators=59,random_state=20)

GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
print(GS.best_params_)
print(GS.best_score_) # 模型评分和之前一模一样,说明已经在最佳参数了,无需再调了

#{'max_depth': 9}
#0.968421052631579
# 调整max_features
param_grid = {
    
    'max_features':np.arange(1,30,1)}
"""
max_features是唯一一个即能够将模型往左(低方差高偏差)推,也能够将模型往右(高方差低偏差)推的参数。

max_features的默认最小值是sqrt(n_features)。
"""
rfc = RandomForestClassifier(n_estimators=59
                                ,random_state=20
                                )

GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)

print(GS.best_params_)
print(GS.best_score_)  # 最佳模型评分和之前一模一样,说明模型达到了上限
# {'max_features': 5}
# 0.968421052631579

猜你喜欢

转载自blog.csdn.net/qq_44665283/article/details/132326251