bagging集成和stacking集成(代码篇)机器学习你会遇到的“坑”
我们在昨天的《bagging集成和stacking集成(理论篇)》中说,没有任何一个模型可以胜任全部的机器学习任务,并且从理论上来讲,集成学习会扩大假设空间,使得最终的模型可以达到更好的性能。那么事实是否如此呢?在这篇文章中,我们主要把随机森林的实践与理论做对应。
在之前的《非参数模型(代码篇)》中,我们观察过未加任何限制条件的决策树,虽然其存在着过拟合,但也并没有对其进行对比。拿决策树与其他模型对比时,要获得它的最佳泛化误差,这样的对比才是一个有效的对比,所以我们以叶节点包含的最小样本数作为超参数,来观察泛化误差的变化:
import matplotlib.pyplot as plt
import seaborn as sns
fromsklearn.model_selection import cross_validate
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifieras DTC
iris = datasets.load_iris()
X = iris.data
y = iris.target
test_mse=[]
train_mse=[]
depths=range(1,20)
for d in depths:
clf =DTC(criterion='entropy',min_samples_leaf=d)
clf_dict=cross_validate(clf,X,y,cv=10,scoring='accuracy')
test_mse.append(clf_dict['test_score'].mean())
train_mse.append(clf_dict['train_score'].mean())
sns.set(style='darkgrid')
plt.plot(depths,train_mse,'b-',label='Train Accuracy')
plt.plot(depths,test_mse,'r-',label='Test Accuracy')
plt.xlabel(' minimum number ofsamples required to be at a leaf node')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
可以发现,当叶节点包含的最小样本个数为3时,单棵决策树的性能达到了最优,我们就可以说决策树在IRIS数据上的准确率最优可以达到0.967,请记住这个结果,接下来我们为iris数据构建随机森林:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifieras RFC
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target
def make_meshgrid(x, y, h=.02):
x_min, x_max = x.min() -1, x.max() +1
y_min, y_max = y.min() -1, y.max() +1
xx, yy =np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min,y_max, h))
return(xx, yy)
xx,yy=make_meshgrid(X[:,0],X[:,1])
clf =RFC(random_state=42)
clf.fit(X, y)
Z =clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure()
plt.contourf(xx,yy,Z,cmap=plt.cm.RdBu,alpha=0.6)
for c,i,names inzip("rgb",[0,1,2],iris.target_names):
plt.scatter(X[y==i,0],X[y==i,1],c=c,label=names,edgecolor='k')
plt.title("RandomForest")
plt.legend()
plt.show()
这个随机森林可能过拟合了,注意最左边的蓝点,显然我们的随机森林单独为一个孤立的样本创立了规则。
因为随机森林有着随机的特征扰动,所以我们需要设置随机数种子,确保每次结果的稳定性,在这里将随机数种子设置为42。随机森林是由决策树所构成,所以决策树的防止过拟合手段在随机森林中仍然使用,理论上我们可以进行剪枝,实践中可以通过限制叶节点的最小样本数。树的最大深度,以及叶节点的个数来对随机森林进行调节,但是比起这些,集成学习组合了大量的决策树,决策树的数量会成为一个很有意义的超参数。
有些人会想到,我们选取的决策树数量越多越好,因为bagging集成就要结合大量学习器。但这种看法是错误的,因为随着学习器数量的增加,我们很难保证模型的差异化足够大,集成学习反而会变得很糟糕,模型差异化是集成学习最重要的问题,根据主要的三种获得差异化的手段,基学习器的数量要随着样本量,特征数和学习器本身的参数分布来决定。
比如,以基学习器的数量作为超参数,将叶节点包含的最小样本数设置为3(与单棵决策树一样),来观察决策边界的变化:
.....
numbers=[1,5,10,50]
sns.set(style='darkgrid')
for k,j in enumerate(numbers):
clf =RFC(n_estimators=j,min_samples_leaf=3)
clf.fit(X,y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.subplot(len(numbers)/2,len(numbers)/2,k+1)
plt.contourf(xx,yy,Z,cmap=plt.cm.RdBu,alpha=0.6)
for i,v,l in [[0,'r','setosa'],[1,'g','vericolor'],[2,'b','virginica']]:
plt.scatter(X[y==i][:,0],X[y==i][:,1],c=v,label=l,edgecolor='k')
plt.title('$n=$%s'%j)
plt.legend()
......
如图,我们可以看到随着基学习器的增加,决策边界的变化率也会缓慢。当基学习器从1变到5时,决策边界的变化非常明显,但基学习器从10变到50时,决策边界几乎不再变化。
其中的原因与我们的随机森林的构建方式有关,有两点至关重要:
- IRIS数据只有四个,而我们为了可视化,也只选取了两个特征,只有两个特征的情况下,所谓的特征扰动法几乎不会对决策树的差异化做出贡献,所以少量的决策树就可以保证很好的差异化,继续增加决策树,只是在增加重复的决策树,并不会对最后的结果做出很大的优化。
- 在随机森林的框架下,只包含一个决策树时,因为添加了特征和数据扰动,所以性能比不上单棵决策树的效果,但随着基学习器数目的增加,随机森林的性能会很快超过单棵决策树。
同样根据理论,随机森林的性能会随着基学习器的增加而增加,但是会增加的越来越缓慢,而且集成学习的特性也决定了我们在训练集上和测试集上都会出现相同的趋势,因为我们继续来验证这一点:
......
iris = datasets.load_iris()
X = iris.data
y = iris.target
test_mse=[]
train_mse=[]
numbers=range(1,50)
for d in numbers:
clf =RFC(n_estimators=d,min_samples_leaf=3)
clf_dict=cross_validate(clf,X,y,cv=5,scoring='accuracy')
test_mse.append(clf_dict['test_score'].mean())
train_mse.append(clf_dict['train_score'].mean())
sns.set(style='darkgrid')
plt.plot(numbers,train_mse,'b-.',label='Train Accuracy')
plt.plot(numbers,test_mse,'r-.',label='Test Accuracy')
plt.xlabel(' n estimators')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
......
如图,随着基学习器的增加,随机森林的训练和测试误差都趋于稳定,在基学习器为1时,我们可以注意到随机森林的性能很低,随着基学习器数量的增加,性能提升的非常快,但增加到某个值后,随机森林的性能几乎不再提升。
盲目的增加基学习器虽然不会明显使得性能降低,但却会带来运算效率的问题,所以我们一般根据选取使性能稳定下来的最小基学习器的数目,随之而来的一个问题则是,随机森林在性能达到稳定时,准确率也低于0.97。
此处提供两个方向参考:一方面,对于随机森林的每个基学习器,能够限制的叶节点的最小样本数并不一定和单棵决策树相同,因为数据扰动和特征扰动都会影响这一结果;另外,可以使用一种被普遍证明良好的技巧,在构建随机森林的时候,在当前节点的特征集挑选出一个特征子集,我们挑选的最佳特征不会在整个特征集中搜索,而是在这个子集中,这个特征子集的规模普遍推荐大小是特征数的对数,我们尝试使用这一想法:
......
clf=RFC(n_estimators=d,max_features='log2',min_samples_leaf=3)
......
如图,对叶节点不加限制的话,训练集的泛化误差就会降到零,体现为每个样本都会预测对,准确率为1,而测试集整体的准确率却下降了,间隔变大,再一次证明了限制叶节点会降低过拟合的风险;另一方面,我们限制了节点的特征子集的规模。
可以发现,此时测试准确率会达0.974,比起单棵决策树虽有提升,但这样的提升还是太小,一方面我们不得不承认单棵决策树可能就已经可以非常好的适应数据,另一方面我们试着继续提升集成性能。在此基础上,对随机森林增大随机性,也就是继续想办法增大模型的差异性,使用一种叫做Extremely Randomized Trees的方法,它本质也是随机森林,但是在每次节点划分的时候,普通的随机森林会在属性上选择最佳值,而Extremely Randomized Trees在选择划分上是完全随机的,这样而来的基学习器性能差异是非常大的,理论上性能也会更好。
from sklearn.ensemble import ExtraTreesClassifier as ETC
......
test_mse=[]
train_mse=[]
numbers=range(1,50)
for d in numbers:
clf=ETC(n_estimators=d,min_samples_leaf=3,bootstra=True)
clf_dict=cross_validate(clf,X,y,cv=5,scoring='accuracy')
test_mse.append(clf_dict['test_score'].mean())
train_mse.append(clf_dict['train_score'].mean())
......
我们可以看到测试误差和普通的随机森林相差不多,但是测试集性能整体有提升,即对于一定数量的基学习器,性能均有所提高。
读芯君开扒
课堂TIPS
• 集成学习与一般的机器学习算法的不同点还体现在分集测试上,如果我们采用bootstrap来采样,会有一部分数据未进入训练集,所以可以考虑包外估计,我们需要设置模型的oob_score=True,这样模型会对使用过的数据做记录。本文未采用包外估计,读者可自己实践。
• 在实践随机森林的时候,我未设置随机数种子,因为随机性越大的随机森林,模型的差异也会越明显,在实践过程中,可以多设置几遍随机数种子,会发现每次的结果都有差异,这也是我们需要考虑的因素。
• 一般地,我们会认为随着模型复杂度的升高,数据不变的情况下,就一定会出现过拟合。但可以发现随着基学习器数量的增长,并没有出现所谓的过拟合,而是训练集和测试集都会趋于平稳,因为基学习器的数量并不会增加整体的复杂度,也并没有改变模型的偏差,而是降低了模型的方差,我们会在下一章节讲解其中的道理并且引出另一种非常强大的集成框架Boosting。
作者:唐僧不用海飞丝
如需转载,请后台留言,遵守转载规范