一、泛化误差
当模型在未知数据(测试集或者袋外数据)上表现糟糕时,我们说模型的泛化程度不够,泛化误差大,模型的效果不好。泛化误差受到模型的结构(复杂度)影响。看下面这张图,它准确地描绘了泛化误差与模型复杂度的关系,当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。当模型太简单,模型就会欠拟合,拟合能力就不够,所以误差也会大。只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标。
那模型的复杂度与我们的参数有什么关系呢?对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂。所以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合。当然了,调参没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。
泛化误差的背后其实是“偏差-方差困境”
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
看具体情况
二、方差和偏差
一个集成模型(f)在未知数据集(D)上的泛化误差E(f;D),由方差(var),偏差(bais)和噪声(ε)共同决定。
![](https://img-blog.csdnimg.cn/6358283b98e34a15b40914a21abc34cb.png)
观察下面的图像,每个点就是集成算法中的一个基评估器产生的预测值。红色虚线代表着这些预测值的均值,而蓝色的线代表着数据本来的面貌。
偏差:模型的预测值与真实值之间的差异,即每一个红点到蓝线的距离。在集成算法中,每个基评估器都会有自己的偏差,集成评估器的偏差是所有基评估器偏差的均值。模型越精确,偏差越低。
方差:反映的是模型每一次输出结果与模型预测值的平均水平之间的误差,即每一个红点到红色虚线的距离,衡量模型的稳定性。模型越稳定,方差越低。
其中偏差衡量模型是否预测得准确,偏差越小,模型越“准”;而方差衡量模型每次预测的结果是否接近,即是说方差越小,模型越“稳”;噪声是机器学习无法干涉的部分,为了让世界美好一点,我们就不去研究了。一个好的模型,要对大多数未知数据都预测得”准“又”稳“。即是说,当偏差和方差都很低的时候,模型的泛化误差就小,在未知数据上的准确率就高。
偏差大
偏差小
方差大
模型不适合这个数据换模型
过拟合
模型很复杂
对某些数据集预测很准确
对某些数据集预测很糟糕
方差小
欠拟合
模型相对简单预测很稳定
但对所有的数据预测都不太准确
泛化误差小,我们的目标
通常来说,方差和偏差有一个很大,泛化误差都会很大。然而,方差和偏差是此消彼长的,不可能同时达到最小值。这个要怎么理解呢?来看看下面这张图:
从图上可以看出,模型复杂度大的时候,方差高,偏差低。偏差低,就是要求模型要预测得“准”。模型就会更努力去学习更多信息,会具体于训练数据,这会导致,模型在一部分数据上表现很好,在另一部分数据上表现却很糟糕。模型泛化性差,在不同数据上表现不稳定,所以方差就大。而要尽量学习训练集,模型的建立必然更多细节,复杂程度必然上升。所以,复杂度高,方差高,总泛化误差高。
相对的,复杂度低的时候,方差低,偏差高。方差低,要求模型预测得“稳”,泛化性更强,那对于模型来说,它就不需要对数据进行一个太深的学习,只需要建立一个比较简单,判定比较宽泛的模型就可以了。结果就是,模型无法在某一类或者某一组数据上达成很高的准确度,所以偏差就会大。所以,复杂度低,偏差高,总泛化误差高。
我们调参的目标是,达到方差和偏差的完美平衡!虽然方差和偏差不能同时达到最小值,但他们组成的泛化误差却可以有一个最低点,而我们就是要寻找这个最低点。对复杂度大的模型,要降低方差,对相对简单的模型,要降低偏差。随机森林的基评估器都拥有较低的偏差和较高的方差,因为决策树本身是预测比较”准“,比较容易过拟合的模型,装袋法本身也要求基分类器的准确率必须要有50%以上。所以以随机森林为代表的装袋法的训练过程旨在降低方差,即降低模型复杂度,所以随机森林参数的默认设定都是假设模型本身在泛化误差最低点的右边。
所以,我们在降低复杂度的时候,本质其实是在降低随机森林的方差,随机森林所有的参数,也都是朝着降低方差的目标去。有了这一层理解,我们对复杂度和泛化误差的理解就更上一层楼了,对于我们调参,也有了更大的帮助。
三、随机森林调参
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
#导入画图用的matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
data = load_breast_cancer()
2.查看数据集
data
#查看特征矩阵的结构
#569个样本,30个特征
#说明这个数据集非常容易过拟合
data.data.shape
data.target
3.初次简单建模,查看效果
#进行一次简单的建模,看看模型本身在数据集上的效果
#实例化随机森林的分类器
rfc = RandomForestClassifier(n_estimators=100,random_state=70)
#交叉验证
#第一个参数,我们的模型,第二个参数我们的完整的特征矩阵,第三个参数我们完整的特征标签,第四个参数,我们交叉验证的次数
#指定scoring指定到底用什么打分来指定我们交叉验证的参数(默认是按照导入的模型来决定)
#然后将这十个不同的分数求平均值
score_pre = cross_val_score(rfc,data.data,data.target,cv=10,scoring="accuracy").mean()
score_pre
#这里可以看到,随机森林在乳腺癌数据上的表现本就还不错,在现实数据集上,基本上不可能什么都不调就看到95%以上的准确率
四、调参
1.调n_estimators
我们n_estimators对于模型准确率的影响是十分大的,所以需要多次调整
①初步确定n_estimators
#1.随机森林调参第一步:无论如何先来调n_estimators
#这一步我们是在绘制n_estimators的学习曲线。
#这里当然也可以使用网格搜索,但是只有学习曲线才能看见趋势
#第一次使用的学习曲线可以更好地帮助我们划定范围。
#我们取每十个数作为一个阶段,来观察n_estimators的变化如何引起我们模型的变化
scorel = []
for i in range(0,200,10):
#实例化随机森林分类器
#让n_estimator,也就是树的数量不断增长
#然后动用全部的计算资源去计算
#随机的模式为70
rfc = RandomForestClassifier(n_estimators=i+1,
n_jobs=-1,
random_state=70)
#使用交叉验证为我们的模型打分,然后求平均值
score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
#将我们的模型的分数追加到我们的结果列表中
scorel.append(score)
#将列表中的最大值,也就是最好的打分打印出来
#并且打印其运行时n_estimators所设置的参数,也就是树的数量
print(max(scorel),(scorel.index(max(scorel))*10)+1)
plt.figure(figsize=[20,5])
plt.plot(range(1,201,10),scorel)
plt.show()
#list.index([object])
#返回这个object在列表list中的索引
#我们第一步的学习曲线只是给我们确定一个大致的范围
#现在我们知道我们的n_estimators在大概91的范围左右将会有一个峰值
②进一步确定范围
#2.确定好了范围之后,进一步绘制学习曲线
scorel = []
#由于我们上面得知的最好的n_estimators大概在81左右,所以我们可以探索75-85之间有没有更好的结果
for i in range(85,95):
rfc = RandomForestClassifier(n_estimators=i,
n_jobs=-1,
random_state=70)
score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
scorel.append(score)
#打印出最大的打分和在85到95中,分数最高的时候所对应的索引
print(max(scorel),([*range(85,95)][scorel.index(max(scorel))]))
plt.figure(figsize=[20,5])
plt.plot(range(85,95),scorel)
plt.show()
#从下面的图中可以知道n_estimators调整为91的时候,我们的随机森林将会有更好的表现
2.网格搜索
接下来就进入网格搜索,我们将使用网格搜索对参数一个个进行调整。
为什么我们不同时调整多个参数呢?原因有两个:
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)}
对于大型数据集,可以尝试从1000来构建,先输入1000,每100个叶子一个区间,再逐渐缩小范围
有一些参数是可以找到一个范围的,或者说我们知道他们的取值和随着他们的取值,模型的整体准确率会如何变化,这样的参数我们就可以直接跑网格搜索
param_grid = {'criterion':['gini', 'entropy']}
#在最大值上加个20先调调看,不好再进行修改
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)}
①使用网格搜索调整max_depth
#3.使用网格搜索帮助我们改进参数
#1.开始按照参数对模型整体准确率的影响程度进行调参,首先调整max_depth
#调整max_depth
param_grid = {'max_depth':np.arange(1, 20, 1)}
# 一般根据数据的大小来进行一个试探,乳腺癌数据很小,所以可以采用1~10,或者1~20这样的试探
# 但对于像digit recognition那样的大型数据来说,我们应该尝试30~50层深度(或许还不足够
# 更应该画出学习曲线,来观察深度对模型的影响
#n_estimators是我们刚刚调制出来的结果91
rfc = RandomForestClassifier(n_estimators=91
,random_state=70
)
#网格搜索,第一个参数是我们已经实例化好的模型,第二个是我们希望它进行搜索的参数,第三个是网格搜索中交叉验证的次数
GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
#显示调整出来的最佳参数
GS.best_params_
#返回调整好的最佳参数对应的准确率
GS.best_score_
②微调max_feature
#这里我们观察到我们的准确率并没有发生变化
#其实我们已经非常接近模型的上限了
#我们可以再微调一下max_features
#调整max_features
#max_feature默认是最大的特征数开平方
#当前我们的特征数是30,开平方大概是5
#我们这里的准确率已经不发生变化了,但是我么可以将max_feature往大和小的方向都试试看
param_grid = {'max_features':np.arange(1,30,1)}
"""
max_features是唯一一个即能够将模型往左(低方差高偏差)推,也能够将模型往右(高方差低偏差)推的参数。我
们需要根据调参前,模型所在的位置(在泛化误差最低点的左边还是右边)来决定我们要将max_features往哪边调。
max_features的默认最小值是sqrt(n_features),因此我们使用这个值的左右进行调整。
max_feature越大,也就是越复杂,就往右推
max_feature越小,也就是越简单,就往左推
"""
rfc = RandomForestClassifier(n_estimators=91
,random_state=70
)
#使用网格搜索,哪一个max_feature会有更好的效果
GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
GS.best_params_
GS.best_score_
#从上面的结果看,我们当前的max_feature也就是默认的5就是泛化误差的最低点
#同时我们的best_score也没有发生变化
#这说明模型本身已经处于泛化误差最低点,已经达到了模型的预测上限,没有参数可以左右的部分了。剩下的那些
#误差,是噪声决定的,已经没有方差和偏差的舞台了。
#也就是说这个分数已经达到极限了
#也就是我们已经达到了随机森林决策的上限了
#如果我们希望模型更进一步,我们会选择更换算法,或者更换做数据预处理的方式。
#我们这里再调整看看别的参数
③微调min_sample_leaf
#调整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=91
,random_state=70
)
GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
#这里的min_samples_leaf取了最小值,就跟没调一样
GS.best_params_
GS.best_score_
#我们发现我们的准确率依旧没有发生变化
④微调min_sample_split
#继续尝试min_samples_split
#调整min_samples_split
param_grid={'min_samples_split':np.arange(2, 2+20, 1)}
rfc = RandomForestClassifier(n_estimators=91
,random_state=70
)
GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
#min_samples_split=2,就跟没调一样
GS.best_params_
GS.best_score_
#我们的打分并没有变化,说明我们的模型已经到极限了
⑤微调criterion
#最后尝试一下criterion
#调整Criterion
#criterion是不知道会对模型造成什么样的变化的
#但是我们现在既不是过拟合也不是欠拟合,也可以试试看
param_grid = {'criterion':['gini', 'entropy']}
rfc = RandomForestClassifier(n_estimators=91
,random_state=70
)
GS = GridSearchCV(rfc,param_grid,cv=10)
GS.fit(data.data,data.target)
#默认就是gini系数,也就是不调整
GS.best_params_
#最好的打分也没有发生变化
GS.best_score_
⑥总结
#调整完毕,总结出模型的最佳参数
rfc = RandomForestClassifier(n_estimators=91,random_state=70)
score = cross_val_score(rfc,data.data,data.target,cv=10).mean()
score
提升效果
#调参后的参数减去调参前的参数
score - score_pre
在整个调参过程之中,我们首先调整了n_estimators(无论如何都请先走这一步),然后调整max_depth,通过max_depth产生的结果,来判断模型位于复杂度-泛化误差图像的哪一边,从而选择我们应该调整的参数和调参的方向。如果感到困惑,也可以画很多学习曲线来观察参数会如何影响我们的准确率,选取学习曲线中单调的部分来放大研究(如同我们对n_estimators做的)。学习曲线的拐点也许就是我们一直在追求的,最佳复杂度对应的泛化误差最低点(也是方差和偏差的平衡点)。
网格搜索也可以一起调整多个参数,(网格参数是一定会调整参数的,并不存在不调这种选项)可以看看网格搜索会给我们怎样的结果,有时候,它的结果比我们的好,有时候,我们手动调整的结果会比较好。
五、袋装法和提升法对比
**装袋法 **Bagging
**提升法 **Boosting
评估器
相互独立,同时运行
相互关联,按顺序依次构建,后建的模型会在先建模型预测失败的样本上有更多的权重
抽样数集
有放回抽样
有放回抽样,但会确认数据的权重,每次抽样都会给容易预测失败的样本更多的权重
决定集成的结果
平均或少数服从多数原则
加权平均,在训练集上表现更好的模型会有更大的权重
目标
降低方差,提高模型整体的稳定性
降低偏差,提高模型整体的精确度
单个评估器存在过拟合问题的时候
能够一定程度上解决过拟合问题
可能会加剧过拟合问题
单个评估器的效力比较弱的时候
不是非常有帮助
很可能会提升模型表现
代表算法
随机森林
梯度提升树,Adaboost
版权归原作者 桜キャンドル淵 所有, 如有侵权,请联系我们删除。