提升

提升(Boosting,最初称为假设增强)指的是可以将几个弱学习者组合成强学习者的集成方法。对于大多数的提升方法的思想就是按顺序去训练分类器,每一个都要尝试修正前面的分类。现如今已经有很多的提升方法了,但最著名的就是 Adaboost(适应性提升,是 Adaptive Boosting 的简称) 和 Gradient Boosting(梯度提升)。让我们先从 Adaboost 说起。

Adaboost

使一个新的分类器去修正之前分类结果的方法就是对之前分类结果不对的训练实例多加关注。这导致新的预测因子越来越多地聚焦于这种情况。这是 Adaboost 使用的技术。

举个例子,去构建一个 Adaboost 分类器,第一个基分类器(例如一个决策树)被训练然后在训练集上做预测,在误分类训练实例上的权重就增加了。第二个分类机使用更新过的权重然后再一次训练,权重更新,以此类推(详见图 7-7)

图7-7

图 7-8 显示连续五次预测的 moons 数据集的决策边界(在本例中,每一个分类器都是高度正则化带有 RBF 核的 SVM)。第一个分类器误分类了很多实例,所以它们的权重被提升了。第二个分类器因此对这些误分类的实例分类效果更好,以此类推。右边的图代表了除了学习率减半外(误分类实例权重每次迭代上升一半)相同的预测序列。你可以看出,序列学习技术与梯度下降很相似,除了调整单个预测因子的参数以最小化代价函数之外,AdaBoost 增加了集合的预测器,逐渐使其更好。

图7-8

一旦所有的分类器都被训练后,除了分类器根据整个训练集上的准确率被赋予的权重外,集成预测就非常像Bagging和Pasting了。

序列学习技术的一个重要的缺点就是:它不能被并行化(只能按步骤),因为每个分类器只能在之前的分类器已经被训练和评价后再进行训练。因此,它不像Bagging和Pasting一样。

让我们详细看一下 Adaboost 算法。每一个实例的权重wi初始都被设为1/m第一个分类器被训练,然后他的权重误差率r1在训练集上算出,详见公式 7-1。

公式7-1:第j个分类器的权重误差率

公式7-1

其中 \widetilde{y_{j}}^{i} 是第j个分类器对于第i实例的预测。

分类器的权重 α_{j} 随后用公式 7-2 计算出来。其中η是超参数学习率(默认为 1)。分类器准确率越高,它的权重就越高。如果它只是瞎猜,那么它的权重会趋近于 0。然而,如果它总是出错(比瞎猜的几率都低),它的权重会使负数。

公式 7-2:分类器权重

公式7-2

接下来实例的权重会按照公式 7-3 更新:误分类的实例权重会被提升。

公式7-3 权重更新规则

对于i=1, 2, ..., m

公式7-3

随后所有实例的权重都被归一化(例如被 \sum_{i=1}^{m}w^{i} 整除)

最后,一个新的分类器通过更新过的权重训练,整个过程被重复(新的分类器权重被计算,实例的权重被更新,随后另一个分类器被训练,以此类推)。当规定的分类器数量达到或者最好的分类器被找到后算法就会停止。

为了进行预测,Adaboost 通过分类器权重 α_{j} 简单的计算了所有的分类器和权重。预测类别会是权重投票中主要的类别。(详见公式 7-4)

公式7-4: Adaboost 分类器

公式7-4

其中N是分类器的数量。

sklearn 通常使用 Adaboost 的多分类版本 SAMME(这就代表了 分段加建模使用多类指数损失函数)。如果只有两类别,那么 SAMME 是与 Adaboost 相同的。如果分类器可以预测类别概率(例如如果它们有predict_proba()),如果 sklearn 可以使用 SAMME 叫做SAMME.R的变量(R 代表“REAL”),这种依赖于类别概率的通常比依赖于分类器的更好。

接下来的代码训练了使用 sklearn 的AdaBoostClassifier基于 200 个决策树桩 Adaboost 分类器(正如你说期待的,对于回归也有AdaBoostRegressor)。一个决策树桩是max_depth=1的决策树-换句话说,是一个单一的决策节点加上两个叶子结点。这就是AdaBoostClassifier的默认基分类器:

  1. >>>from sklearn.ensemble import AdaBoostClassifier
  2. >>>ada_clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1), n_estimators=200,algorithm="SAMME.R", learning_rate=0.5)
  3. >>>ada_clf.fit(X_train, y_train)

如果你的 Adaboost 集成过拟合了训练集,你可以尝试减少基分类器的数量或者对基分类器使用更强的正则化。

梯度提升

另一个非常著名的提升算法是梯度提升。与 Adaboost 一样,梯度提升也是通过向集成中逐步增加分类器运行的,每一个分类器都修正之前的分类结果。然而,它并不像 Adaboost 那样每一次迭代都更改实例的权重,这个方法是去使用新的分类器去拟合前面分类器预测的残差

让我们通过一个使用决策树当做基分类器的简单的回归例子(回归当然也可以使用梯度提升)。这被叫做梯度提升回归树(GBRT,Gradient Tree Boosting 或者 Gradient Boosted Regression Trees)。首先我们用DecisionTreeRegressor去拟合训练集(例如一个有噪二次训练集):

  1. >>>from sklearn.tree import DecisionTreeRegressor
  2. >>>tree_reg1 = DecisionTreeRegressor(max_depth=2)
  3. >>>tree_reg1.fit(X, y)

现在在第一个分类器的残差上训练第二个分类器:

  1. >>>y2 = y - tree_reg1.predict(X)
  2. >>>tree_reg2 = DecisionTreeRegressor(max_depth=2)
  3. >>>tree_reg2.fit(X, y2)

随后在第二个分类器的残差上训练第三个分类器:

  1. >>>y3 = y2 - tree_reg1.predict(X)
  2. >>>tree_reg3 = DecisionTreeRegressor(max_depth=2)
  3. >>>tree_reg3.fit(X, y3)

现在我们有了一个包含三个回归器的集成。它可以通过集成所有树的预测来在一个新的实例上进行预测。

  1. >>>y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))

图7-9在左栏展示了这三个树的预测,在右栏展示了集成的预测。在第一行,集成只有一个树,所以它与第一个树的预测相似。在第二行,一个新的树在第一个树的残差上进行训练。在右边栏可以看出集成的预测等于前两个树预测的和。相同的,在第三行另一个树在第二个数的残差上训练。你可以看到集成的预测会变的更好。

我们可以使用 sklean 中的GradientBoostingRegressor来训练 GBRT 集成。与RandomForestClassifier相似,它也有超参数去控制决策树的生长(例如max_depthmin_samples_leaf等等),也有超参数去控制集成训练,例如基分类器的数量(n_estimators)。接下来的代码创建了与之前相同的集成:

  1. >>>from sklearn.ensemble import GradientBoostingRegressor
  2. >>>gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
  3. >>>gbrt.fit(X, y)

图7-9

超参数learning_rate 确立了每个树的贡献。如果你把它设置为一个很小的树,例如 0.1,在集成中就需要更多的树去拟合训练集,但预测通常会更好。这个正则化技术叫做 shrinkage。图 7-10 展示了两个在低学习率上训练的 GBRT 集成:其中左面是一个没有足够树去拟合训练集的树,右面是有过多的树过拟合训练集的树。

图7-10

为了找到树的最优数量,你可以使用早停技术(第四章讨论)。最简单使用这个技术的方法就是使用staged_predict():它在训练的每个阶段(用一棵树,两棵树等)返回一个迭代器。加下来的代码用 120 个树训练了一个 GBRT 集成,然后在训练的每个阶段验证错误以找到树的最佳数量,最后使用 GBRT 树的最优数量训练另一个集成:

  1. >>>import numpy as np
  2. >>>from sklearn.model_selection import train_test_split
  3. >>>from sklearn.metrics import mean_squared_error
  4. >>>X_train, X_val, y_train, y_val = train_test_split(X, y)
  5. >>>gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120)
  6. >>>gbrt.fit(X_train, y_train)
  7. >>>errors = [mean_squared_error(y_val, y_pred)
  8. for y_pred in gbrt.staged_predict(X_val)]
  9. >>>bst_n_estimators = np.argmin(errors)
  10. >>>gbrt_best = GradientBoostingRegressor(max_depth=2,n_estimators=bst_n_estimators)
  11. >>>gbrt_best.fit(X_train, y_train)

验证错误在图 7-11 的左面展示,最优模型预测被展示在右面。

图7-11

你也可以早早的停止训练来实现早停(与先在一大堆树中训练,然后再回头去找最优数目相反)。你可以通过设置warm_start=True来实现 ,这使得当fit()方法被调用时 sklearn 保留现有树,并允许增量训练。接下来的代码在当一行中的五次迭代验证错误没有改善时会停止训练:

  1. >>>gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True)
  2. min_val_error = float("inf")
  3. error_going_up = 0
  4. for n_estimators in range(1, 120):
  5. gbrt.n_estimators = n_estimators
  6. gbrt.fit(X_train, y_train)
  7. y_pred = gbrt.predict(X_val)
  8. val_error = mean_squared_error(y_val, y_pred)
  9. if val_error < min_val_error:
  10. min_val_error = val_error
  11. error_going_up = 0
  12. else:
  13. error_going_up += 1
  14. if error_going_up == 5:
  15. break # early stopping

GradientBoostingRegressor也支持指定用于训练每棵树的训练实例比例的超参数subsample。例如如果subsample=0.25,那么每个树都会在 25% 随机选择的训练实例上训练。你现在也能猜出来,这也是个高偏差换低方差的作用。它同样也加速了训练。这个技术叫做随机梯度提升

也可能对其他损失函数使用梯度提升。这是由损失超参数控制(见 sklearn 文档)。