机器学习(七)

集成学习和随机森林

Posted by Gavin on June 3, 2019

细雨湿衣看不见

闲花落地听无声

前言

  • 集成学习:聚合一组预测器(分类或回归)的预测
  • 随即森林:基于一组决策树,每棵树都是对训练集不同随机子集进行训练,预测结果为得票最多的类别
  • 集成方法:bagging、boosting、stacking…

集成学习

投票分类器


聚合所有分类器的预测,得票最多的结果作为预测类别,这种大多数投票分类器叫做硬投票分类器

PS:预测器尽可能相互独立时,集成方法的效果最优,避免分类器犯同一类型的错误

from sklearn.ensemble import RandomForestClassifier, VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC

log_clf = LogisticRegression()
rnd_clf = RandomForestClassifier()
svm_clf = SVC()
voting_clf = VotingClassifier(
	estimators=[('lr',log_clf), ('rf',rnd_clf), ('svc', svm_clf)],
	voting='hard'
	)
voting_clf.fit(X_train, y_train)

查看各分类器准确率:

for clf in (log_clf, rnd_clf, svm_clf, voting_clf):
	clf.fit(X_train, y_train)
	y_pred = clf.predict(X_test)
	print(clf.__class__.__name__, accuracy_score(y_test,y_pred))

如果每个分类器都可以估算类别概率,那么可以将每个类别概率平均,最后平均概率最高的类别作为结果,这是软投票法。只需设置超参voting=’soft’。

bagging和pasting

获取不同分类器可以使用不同算法,也可以使用相同算法,但是是在不同训练集随机子集上进行训练。采样时如果将样本放回,叫做bagging(自举汇聚发);不放回叫做pasting。

PS:bagging、pasting都允许训练实例在多个预测器中被多次采样,但只有bagging允许训练实例被同一预测器多次采样

预测器完成训练之后,集成通过聚合所有的预测,对新实例作出判断。聚合函数通常是统计法用于分类,平均法用于回归。

bag_clf = BaggingClassifier(
	DecisionTreeClassifier(), n_estimators=500,
	max_samples=100, bootstrap=True, n_jobs=-1
	)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)
accuracy_score(y_test, y_pred)


如图所示,单个决策树与集成方法的偏差相近,但集成的方差更小。

包外评估

使用bagggin,往往会造成很多实例从未被抽样,因此刚好可以使用这些包外实例进行评估,从而不需要单独的验证集或是交叉验证。

bag_clf = BaggingClassifier(
	DecisionTreeClassifier(), n_estimators=500,
	max_samples=100, bootstrap=True, n_jobs=-1, oob_score=True)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_

Random Patches和随机子空间

BaggingClassifier也支持对特征进行抽样,通过max_features和bootstrap_features控制,因此每个预测器也可以用输入特征的随机子集进行训练。
这对处理高维输入(图像)很有用,对训练实例和特征都进行抽样,被称为Random Patches方法,保留所有训练实例(bootstrap=False && max_samples=1.0),对特征进行抽样(bootstrap_features=True)是随机子空间法。


随机森林

基本概念

随机森林是决策树的集成,通常使用bagging方法进行训练,训练集大小通过max_samples控制。

rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1)
rnd_clf.fit(X_train, y_train)
y_pred_rf = rnd_clf.predict(X_test)

随机森林在树的生长上引入了更多的随机性:分裂节点时不再是搜索最好的特征,而是在随机生成的特征子集里搜索最好的特征。这导致决策树具有更大的多样性,用更高的偏差换取更低的方差,是一个整体更优的模型。

极端随机树

随机森林里单棵树的生长只考虑了一个随机子集所包含的特征,如果我们对每个特征使用随机阈值,而不是使用搜索得出的最佳阈值,则可能让随机数生长得更加随机。这种极端随机的决策树组成的森林,被称为极端随机树集成。使用ExtraTreesClassifier可以创建,其比常规的随机森林训练要省时间。

特征重要性

查看单个决策树,重要的特征靠近根节点,不重要的靠近叶结点,因此可以使用一个特征在森林中所有树上的平均深度,估算一个特征的重要程度。

for name, score in zip(iris['feature_names'], rnd_clf.feature_importances_):
	print(name, score)

结果如下:

sepal length (cm) 0.0953139245913
sepal width (cm) 0.0312892669975
petal length (cm) 0.426432633724
petal width (cm) 0.446964174687

PS:如果想要快速了解什么是重要特征,随机森林是一个好方法


提升法

基本概念

提升法(Boosting)是指将几个弱学习器结合成一个强学习器的任意集成方法。大多数提升法的思路是循环训练预测器,每一次都对其前序做出一些改正。目前可用的方法很多,最流行的是:AdaBoost(自适应提升)和梯度提升。

AdaBoost

新预测器对谦虚前序进行纠正的办法之一,就是更多关注前序拟合不足的训练实例,从而使预测器不断越来越专注于难缠的问题。
首先训练一个基础分类器,用它对训练集进行预测,然后对预测错误分类的训练实例增加其相对权重,接着,使用新的权重对第二个分类器进行训练,不断循环向前。

PS:这种方法的缺点是无法并行,新的预测器只能依赖前一个预测器

AdaBoost算法,每个实例的初始权重为1/m,第一个预测器训练后,计算其加权误差率

预测器权重

权重更新规则

预测的时候,AdaBoost计算所有预测器结构,并对它们进行加权,最后大多数加权投票的结果类别就是预测类别。
预测

ada_clf = AdaBoostClassifier(
	DecisionTreeClassifier(max_depth=1),n_estimators=200,
	algorithm='SAMME.R',learning_rate=0.5)
ada_clf.fit(X_train, y_train)

梯度提升

梯度提升并不是在迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合。

gbrt = GradientBoostingClassifier(max_depth=2,n_estimators=3,learning_rate=1.0)
gbrt.fit(X_train, y_train)

超参数learning_rate对每棵树的贡献进行缩放,如果较低,则需要更多的树,但泛化结果通常更好,这是一种收缩的正则化技术。要找到最佳的树的数量,简单的方法是使用早期停止法。

errors = [mean_squared_error(y_train, y_pred)
	for y_pred in gbrt.staged_predict(X_train)]
bst_n_estimators = np.argmin(errors)
gbrt_best = GradientBoostingClassifier(max_depth=2,n_estimators=bst_n_estimators)
gbrt_best.fit(X_train, y_train)

实现早期停止法不一定要先训练大量的树,如下:

gbrt = GradientBoostingClassifier(max_depth=2, warm_start=True)
min_val_error = float('inf')
error_going_up = 0
for n_estimators in range(1,120):
	gbrt.n_estimators = n_estimators
	gbrt.fit(X_train, y_train)
	y_pred = gbrt.predict(X_test)
	val_error = mean_squared_error(y_test, y_pred)
	if val_error < min_val_error:
		min_val_error = val_error
		error_going_up = 0
	else:
		error_going_up += 1
		if error_going_up == 5:
			break

堆叠法

堆叠法(stacking),又名层泛化法。与其使用简单的函数(投票)来聚合所有预测器的结果,为什么不训练一个模型来进行聚合呢?如图所示

步骤如下:

  1. 先将训练集拆分为两个子集,第一个子集用于训练第一层的预测器
  2. 用第一层的预测器在第二个子集(留存集)上进行预测,得到新特征
  3. 在第二个子集得到的新特征上创建新的预测器