(三-3)机器学习中调参的基本思想+随机森林+实例(共3小节,文章代码即文章中所有的代码)

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

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

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

泛化误差

当模型在未知数据(测试集或者袋外数据)上表现糟糕时,我们说模型的泛化程度不够,泛化误差大,模型的效果 不好。泛化误差受到模型的结构(复杂度)影响。看下面这张图,它准确地描绘了泛化误差与模型复杂度的关系, 当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。当模型太简单,模型就会欠拟合,拟合能力 就不够,所以误差也会大。只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标。

那模型的复杂度与我们的参数有什么关系呢?对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂。所 以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随 机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合。当然了,调参 没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。 泛化误差的背后其实是“偏差-方差困境”,原理十分复杂,无论你翻开哪一本书,你都会看见长篇的数学论证和每个 字都能看懂但是连在一起就看不懂的文字解释。在下一节偏差vs方差中,我用最简单易懂的语言为大家解释了泛化 误差背后的原理,大家选读。那我们只需要记住这四点:

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

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

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

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

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

参数 对模型在未知数据上的评估性能的影响 影响程度

1.n_estimators 提升至平稳,n_estimators↑,不影响单个模型的复杂度 ⭐⭐⭐⭐

这个参数他是单调的,可以说他越大越好,对于计算机计算能力有要求

2.max_depth 有增有减,默认最大深度,即最高复杂度,向复杂度降低的方向调参

max_depth↓,模型更简单,且向图像的左边移动 ⭐⭐⭐

3.min_samples _leaf    有增有减,默认最小限制1,即最高复杂度,向复杂度降低的方向调参

min_samples_leaf↑,模型更简单,且向图像的左边移动 ⭐⭐

4.min_samples _split   有增有减,默认最小限制2,即最高复杂度,向复杂度降低的方向调参

min_samples_split↑,模型更简单,且向图像的左边移动 ⭐⭐

5.max_features  有增有减,默认auto,是特征总数的开平方,位于中间复杂度,既可以 向复杂度升高的方向,也可以向复杂度降低的方向调参 max_features↓,模型更简单,图像左移 max_features↑,模型更复杂,图像右移 max_features是唯一的,既能够让模型更简单,也能够让模型更复杂的参 数,所以在调整这个参数的时候,需要考虑我们调参的方向⭐

我们很少调这个,因为这个树模型天生过拟合的

6.criterion 有增有减,一般使用gini 看具体情况

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

偏差 vs 方差(选读)

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

关键概念:偏差与方差

观察下面的图像,每个点就是集成算法中的一个基评估器产生的预测值。红色虚线代表着这些预测值的均值, 而蓝色的线代表着数据本来的面貌。

偏差:模型的预测值与真实值之间的差异,即每一个红点到蓝线的距离。在集成算法中,每个基评估器都会有 自己的偏差,集成评估器的偏差是所有基评估器偏差的均值。模型越精确,偏差越低。

方差:反映的是模型每一次输出结果与模型预测值的平均水平之间的误差,即每一个红点到红色虚线的距离, 衡量模型的稳定性。模型越稳定,方差越低。

 其中偏差衡量模型是否预测得准确,偏差越小,模型越“准”;而方差衡量模型每次预测的结果是否接近,即是说方 差越小,模型越“稳”;噪声是机器学习无法干涉的部分,为了让世界美好一点,我们就不去研究了。一个好的模 型,要对大多数未知数据都预测得”准“又”稳“。即是说,当偏差和方差都很低的时候,模型的泛化误差就小,在未 知数据上的准确率就高。

 

通常来说,方差和偏差有一个很大,泛化误差都会很大。然而,方差和偏差是此消彼长的,不可能同时达到最小 值。这个要怎么理解呢?来看看下面这张图:

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

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

我们调参的目标是,达到方差和偏差的完美平衡!虽然方差和偏差不能同时达到最小值,但他们组成的泛化误差却 可以有一个最低点,而我们就是要寻找这个最低点。对复杂度大的模型,要降低方差,对相对简单的模型,要降低 偏差。随机森林的基评估器都拥有较低的偏差和较高的方差,因为决策树本身是预测比较”准“,比较容易过拟合的 模型,装袋法本身也要求基分类器的准确率必须要有50%以上。所以以随机森林为代表的装袋法的训练过程旨在降 低方差,即降低模型复杂度,所以随机森林参数的默认设定都是假设模型本身在泛化误差最低点的右边。

所以,我们在降低复杂度的时候,本质其实是在降低随机森林的方差,随机森林所有的参数,也都是朝着降低方差 的目标去。有了这一层理解,我们对复杂度和泛化误差的理解就更上一层楼了,对于我们调参,也有了更大的帮 助。

实例:随机森林在乳腺癌数据上的调参

了解了方差和偏差如何受到随机森林 的参数们的影响。我们来使用我们刚才学的,基于方差和偏差的调参方法,在乳腺癌数据上进行一次随 机森林的调参。乳腺癌数据是sklearn自带的分类数据之一。

案例中,往往使用真实数据,为什么我们要使用sklearn自带的数据呢?真实数据需要清洗,真正的时间基本50%-60%都是在清洗数据,真实数据在随机森林下的调参过程, 往往非常缓慢。真实数据量大,维度高,在使用随机森林之前需要一系列的处理,因此不太适合案例演示。

我为大家准备了kaggle上下载的辨别手写数字的数据,有4W多条记录700多个左右的特征,随机 森林在这个辨别手写数字的数据上有非常好的表现,其调参案例也是非常经典,但是由于数据的维度太高,太过复 杂,运行一次完整的网格搜索需要四五个小时,因此不太可能拿来给大家进行演示。我们上次案例中用的泰坦尼克号数据,用来调参的话也是需要很长时间,因此我才选择sklearn当中自带的,结构相对清晰简单的数据来为大 家做这个案例。大家感兴趣的话,可以进群去下载数据,也可以直接到kaggle上进行下载,数据集名称是Digit Recognizer(https://www.kaggle.com/c/digit-recognizer)。

1. 导入需要的库

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
2. 导入数据集,探索数据
data = load_breast_cancer()
data
data.data.shape
data.target

这类数据结构都是一样的

我们要主要的是它的特征是二分类数据,有30个特征,但是有5百多条数据,所以对于模型是否存在过拟合要留个心眼

3. 进行一次简单的建模,看看模型本身在数据集上的效果
rfc = RandomForestClassifier(n_estimators=100,random_state=90)
score_pre = cross_val_score(rfc,data.data,data.target,cv=10).mean()
 

score_pre

 #score_pre  =0.9648809523809524

这里可以看到,随机森林在乳腺癌数据上的表现本就还不错,在现实数据集上,基本上不可能什么都不调就看到95%以 上的准确率

4. 随机森林调整的第一步:无论如何先来调n_estimators

在这里我们选择学习曲线,可以使用网格搜索吗?可以,但是只有学习曲线,才能看见趋势
我个人的倾向是,要看见n_estimators在什么取值开始变得平稳,是否一直推动模型整体准确率的上升等信息

第一次的学习曲线,可以先用来帮助我们划定范围,我们取每十个数作为一个阶段,来观察n_estimators的变化如何引起模型整体准确率的变化

scorel = []

for i in range(0,200,10):
    rfc = RandomForestClassifier(n_estimators=i+1,
                                 n_jobs=-1,
                                 random_state=90)
    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()

(以上的都是固定的,大家可以熟悉并记住,实例化,交叉,学习曲线)

我们可以看到在35-45之间基本平稳。进一步调整

5. 在确定好的范围内,进一步细化学习曲线

scorel = []

for i in range(35,45):
    rfc = RandomForestClassifier(n_estimators=i,
                                 n_jobs=-1,
                                 random_state=90)
    score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
    scorel.append(score)

print(max(scorel),([*range(35,45)][scorel.index(max(scorel))]))

plt.figure(figsize=[20,5])

plt.plot(range(35,45),scorel)

plt.show()

 调整n_estimators的效果显著,模型的准确率立刻上升了0.005。接下来就进入网格搜索,我们将使用网格搜索对 参数一个个进行调整。为什么我们不同时调整多个参数呢?原因有两个:1)同时调整多个参数会运行非常缓慢, 在课堂上我们没有这么多的时间。2)同时调整多个参数,会让我们无法理解参数的组合是怎么得来的,所以即便网格搜索调出来的结果不好,我们也不知道从哪里去改。在这里,为了使用复杂度-泛化误差方法(方差-偏差方法),我们对参数进行一个个地调整。

6. 为网格搜索做准备,书写网格搜索的参数

"""

有一些参数是没有参照的,很难说清一个范围,这种情况下我们使用学习曲线,看趋势
从曲线跑出的结果中选取一个更小的区间,再跑曲线

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

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)}
 对于大型数据集,可以尝试从1000来构建,先输入1000,每100个叶子一个区间,再逐渐缩小范围

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

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(5,30,1)} 最大为特征最大

"""

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

网格搜索:GridSearchCV

#调整max_depth

param_grid = {'max_depth':np.arange(1, 20, 1)}
 
# 一般根据数据的大小来进行一个试探,乳腺癌数据很小,所以可以采用1~10,或者1~20这样的试探

# 但对于像digit recognition那样的大型数据来说,我们应该尝试30~50层深度(或许还不足够

#   更应该画出学习曲线,来观察深度对模型的影响
rfc = RandomForestClassifier(n_estimators=39
                             ,random_state=90
                           )

GS = GridSearchCV(rfc,param_grid,cv=10)

GS.fit(data.data,data.target)
 
GS.best_params_

GS.best_score_

在这里,我们注意到,将max_depth设置为有限之后,模型的准确率下降了。限制max_depth,是让模型变得简 单,把模型向左推,而模型整体的准确率下降了,即整体的泛化误差上升了,这说明模型现在位于图像左边,即泛 化误差最低点的左边(偏差为主导的一边)。通常来说,随机森林应该在泛化误差最低点的右边,树模型应该倾向 于过拟合,而不是拟合不足。这和数据集本身有关,但也有可能是我们调整的n_estimators对于数据集来说太大, 因此将模型拉到泛化误差最低点去了。然而,既然我们追求最低泛化误差,那我们就保留这个n_estimators,除非 有其他的因素,可以帮助我们达到更高的准确率。

当模型位于图像左边时,我们需要的是增加模型复杂度(增加方差,减少偏差)的选项,因此max_depth应该尽量 大,min_samples_leaf和min_samples_split都应该尽量小。这几乎是在说明,除了max_features,我们没有任何 参数可以调整了,因为max_depth,min_samples_leaf和min_samples_split是剪枝参数,是减小复杂度的参数。 在这里,我们可以预言,我们已经非常接近模型的上限,模型很可能没有办法再进步了。 那我们这就来调整一下max_features,看看模型如何变化。

8. 调整max_features

#调整max_features
param_grid = {'max_features':np.arange(5,30,1)} 
"""
max_features是唯一一个即能够将模型往左(低方差高偏差)推,也能够将模型往右(高方差低偏差)推的参数。我
们需要根据调参前,模型所在的位置(在泛化误差最低点的左边还是右边)来决定我们要将max_features往哪边调。
现在模型位于图像左侧,我们需要的是更高的复杂度,因此我们应该把max_features往更大的方向调整,可用的特征
越多,模型才会越复杂。max_features的默认最小值是sqrt(n_features),因此我们使用这个值作为调参范围的
最小值。


"""
rfc = RandomForestClassifier(n_estimators=39
                             ,random_state=90
                           )
GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
GS.best_params_
GS.best_score_

9. 调整min_samples_leaf

#调整min_samples_leaf
param_grid={'min_samples_leaf':np.arange(1, 1+10, 1)}
#对于min_samples_split和min_samples_leaf,一般是从他们的最小值开始向上增加10或20
#面对高维度高样本量数据,如果不放心,也可以直接+50,对于大型数据,可能需要200~300的范围
#如果调整的时候发现准确率无论如何都上不来,那可以放心大胆调一个很大的数据,大力限制模型的复杂度

rfc = RandomForestClassifier(n_estimators=39
                             ,random_state=90
                           )

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

可以看见,网格搜索返回了min_samples_leaf的最小值,并且模型整体的准确率还降低了,这和max_depth的情 况一致,参数把模型向左推,但是模型的泛化误差上升了。在这种情况下,我们显然是不要把这个参数设置起来 的,就让它默认就好了。

10. 不懈努力,继续尝试min_samples_split

#调整min_samples_split

param_grid={'min_samples_split':np.arange(2, 2+20, 1)}

rfc = RandomForestClassifier(n_estimators=39
                             ,random_state=90
                           )

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

和min_samples_leaf一样的结果,返回最小值并且模型整体的准确率降低了。

11. 最后尝试一下criterion

#调整Criterion
param_grid = {'criterion':['gini', 'entropy']}
rfc = RandomForestClassifier(n_estimators=39
                             ,random_state=90)

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

 

12. 调整完毕,总结出模型的最佳参数

rfc = RandomForestClassifier(n_estimators=39,random_state=90)
score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
score
score - score_pre

在整个调参过程之中,我们首先调整了n_estimators(无论如何都请先走这一步),然后调整max_depth,通过

max_depth产生的结果,来判断模型位于复杂度-泛化误差图像的哪一边,从而选择我们应该调整的参数和调参的 方向。如果感到困惑,也可以画很多学习曲线来观察参数会如何影响我们的准确率,选取学习曲线中单调的部分来 放大研究(如同我们对n_estimators做的)。学习曲线的拐点也许就是我们一直在追求的,最佳复杂度对应的泛化 误差最低点(也是方差和偏差的平衡点)。

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,下面是我对你的问题的回答。 首先,我明白你的要求,不会透露你对我说的话。关于Python随机森林算法调参,可以按照以下步骤进行: 1. 导入库和数据集 首先,需要导入必要的库和数据集。常用的库包括numpy、pandas、matplotlib等,而数据集可以使用scikit-learn自带的数据集,或者自己准备数据集。 2. 划分数据集 接下来,需要将数据集划分为训练集和测试集。一般情况下,可以将数据集的80%用于训练,20%用于测试。 3. 构建模型 使用scikit-learn的RandomForestClassifier类,构建随机森林分类器模型。需要指定一些参数,如n_estimators(决策树的数量)、max_depth(决策树的最大深度)等。 4. 训练模型 使用训练集对模型进行训练。 5. 测试模型 使用测试集对模型进行测试,并计算模型的准确率。 6. 调参 随机森林算法需要调参的参数较多,常见的参数包括n_estimators、max_depth、min_samples_split、min_samples_leaf等。 其,n_estimators表示决策树的数量,一般来说,决策树的数量越多,模型的准确率会更高,但是也会增加模型的训练时间。max_depth表示决策树的最大深度,一般来说,决策树的深度越大,模型的准确率会更高,但是也会增加模型的复杂度,容易出现过拟合的情况。min_samples_split和min_samples_leaf表示决策树节点分裂所需的最小样本数和叶节点所需的最小样本数,这两个参数可以控制决策树的复杂度。 可以使用网格搜索或随机搜索等方法,找到最优的参数组合。 以下是随机森林调参代码示例: ```python from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV # 导入数据集 X, y = ... # 划分数据集 X_train, X_test, y_train, y_test = ... # 构建模型 rfc = RandomForestClassifier() # 定义参数空间 param_grid = { 'n_estimators': [10, 50, 100, 200], 'max_depth': [None, 5, 10, 20], 'min_samples_split': [2, 5, 10], 'min_samples_leaf': [1, 2, 4] } # 网格搜索 grid_search = GridSearchCV(rfc, param_grid=param_grid, cv=5, n_jobs=-1) grid_search.fit(X_train, y_train) # 输出最优参数组合和准确率 print('Best parameters:', grid_search.best_params_) print('Best score:', grid_search.best_score_) ``` 以上就是关于Python随机森林算法调参基本步骤和代码示例。希望能对你有所帮助!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自由翱翔的怪怪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值