书籍信息
Hands-On Machine Learning with Scikit-Learn and Tensorflow
出版社: O’Reilly Media, Inc, USA
平装: 566页
语种: 英语
ISBN: 1491962291
条形码: 9781491962299
商品尺寸: 18 x 2.9 x 23.3 cm
ASIN: 1491962291
系列博文为书籍中文翻译
代码以及数据下载:https://github.com/ageron/handson-ml
另一种流行的Boosting方法是Gradient Boosting。和AdaBoost类似,Gradient Boosting逐个训练模型,当前的模型尝试纠正前面的模型的错误。但是,AdaBoost纠正错误的方法是更加关注前面模型的错误,Gradient Boosting纠正错误的方法是拟合前面模型的残差。
以下是简单的回归实例。首先使用DecisionTreeRegressor训练第1棵树。
import numpy.random as rnd
from sklearn.tree import DecisionTreeRegressor
rnd.seed(42)
X = rnd.rand(100, 1) - 0.5
y = 3*X[:, 0]**2 + 0.05 * rnd.randn(100)
tree_reg1 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg1.fit(X, y)
然后基于第1棵树的残差训练第2棵树。
y2 = y - tree_reg1.predict(X)
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg2.fit(X, y2)
然后基于第2棵树的残差训练第3棵树。
y3 = y2 - tree_reg2.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg3.fit(X, y3)
进行预测时,将所有决策树的结果相加得到最终的结果。
X_new = np.array([[0.8]])
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
print(y_pred)
# output
# [ 0.75026781]
下图展示模型的结果。第1行,模型只有1棵树,模型的预测结果和第1棵树的预测结果相同。第2行,第2棵树根据第1棵树的残差得到,模型的预测结果是第1棵树和第2棵树的和。第3行,第3棵树根据第2棵树的残差得到,模型的预测结果是3棵树的和。可以发现,模型的效果逐渐提升。
def plot_predictions(regressors, X, y, axes, label=None, style="r-", data_style="b.", data_label=None):
x1 = np.linspace(axes[0], axes[1], 500)
y_pred = sum(regressor.predict(x1.reshape(-1, 1)) for regressor in regressors)
plt.plot(X[:, 0], y, data_style, label=data_label)
plt.plot(x1, y_pred, style, linewidth=2, label=label)
if label or data_label:
plt.legend(loc="upper center", fontsize=16)
plt.axis(axes)
plt.figure(figsize=(11,11))
plt.subplot(321)
plot_predictions([tree_reg1], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h_1(x_1)$", style="g-", data_label="Training set")
plt.ylabel("$y$", fontsize=16, rotation=0)
plt.title("Residuals and tree predictions", fontsize=16)
plt.subplot(322)
plot_predictions([tree_reg1], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h(x_1) = h_1(x_1)$", data_label="Training set")
plt.ylabel("$y$", fontsize=16, rotation=0)
plt.title("Ensemble predictions", fontsize=16)
plt.subplot(323)
plot_predictions([tree_reg2], X, y2, axes=[-0.5, 0.5, -0.5, 0.5], label="$h_2(x_1)$", style="g-", data_style="k+", data_label="Residuals")
plt.ylabel("$y - h_1(x_1)$", fontsize=16)
plt.subplot(324)
plot_predictions([tree_reg1, tree_reg2], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h(x_1) = h_1(x_1) + h_2(x_1)$")
plt.ylabel("$y$", fontsize=16, rotation=0)
plt.subplot(325)
plot_predictions([tree_reg3], X, y3, axes=[-0.5, 0.5, -0.5, 0.5], label="$h_3(x_1)$", style="g-", data_style="k+", data_label="Residuals")
plt.ylabel("$y - h_1(x_1) - h_2(x_1)$", fontsize=16)
plt.xlabel("$x_1$", fontsize=16)
plt.subplot(326)
plot_predictions([tree_reg1, tree_reg2, tree_reg3], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h(x_1) = h_1(x_1) + h_2(x_1) + h_3(x_1)$")
plt.xlabel("$x_1$", fontsize=16)
plt.ylabel("$y$", fontsize=16, rotation=0)
plt.show()
我们能够通过scikit-learn提供的GradientBoostingClassifier/GradientBoostingRegressor实现Gradient Boosting,这些函数拥有控制决策树训练的参数(例如max_depth)和控制模型集成的参数(例如n_estimators)。下面的GradientBoostingClassifier和上面的模型类似。
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1)
gbrt.fit(X, y)
learning_rate参数指定基础模型的贡献,如果我们将其设为较低的值,我们需要较多的基础模型拟合训练数据,但是通常能够获得更好的效果,这是shrinkage的基本思想。下图展示较低学习速率的训练结果,左边的模型基础模型过少,存在欠拟合的问题,右边的模型基础模型过多,存在过拟合的问题。
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=0.1, random_state=42)
gbrt.fit(X, y)
gbrt_slow = GradientBoostingRegressor(max_depth=2, n_estimators=200, learning_rate=0.1, random_state=42)
gbrt_slow.fit(X, y)
plt.figure(figsize=(11,4))
plt.subplot(121)
plot_predictions([gbrt], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="Ensemble predictions")
plt.title("learning_rate={}, n_estimators={}".format(gbrt.learning_rate, gbrt.n_estimators), fontsize=14)
plt.subplot(122)
plot_predictions([gbrt_slow], X, y, axes=[-0.5, 0.5, -0.1, 0.8])
plt.title("learning_rate={}, n_estimators={}".format(gbrt_slow.learning_rate, gbrt_slow.n_estimators), fontsize=14)
plt.show()
为了寻找基础模型的最佳数量,我们可以使用early stopping(在验证数据效果最优时停止训练)。实现以上思想的简单方法是使用staged_predict方法,其在训练的所有阶段分别进行预测。下面的实例通过staged_predict方法确定基础模型的最佳数量。左边是验证数据的错误率,右边是最终模型的效果。
from sklearn.cross_validation import train_test_split
from sklearn.metrics import mean_squared_error
# train the models
X_train, X_val, y_train, y_val = train_test_split(X, y)
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120, learning_rate=0.1, random_state=42)
gbrt.fit(X_train, y_train)
errors = [mean_squared_error(y_val, y_pred) for y_pred in gbrt.staged_predict(X_val)]
# find the best model
best_n_estimators = np.argmin(errors)
min_error = errors[best_n_estimators]
gbrt_best = GradientBoostingRegressor(max_depth=2, n_estimators=best_n_estimators, learning_rate=0.1, random_state=42)
gbrt_best.fit(X_train, y_train)
# visualize the result
plt.figure(figsize=(11, 4))
plt.subplot(121)
plt.plot(errors, "b.-")
plt.plot([best_n_estimators, best_n_estimators], [0, min_error], "k--")
plt.plot([0, 120], [min_error, min_error], "k--")
plt.plot(best_n_estimators, min_error, "ko")
plt.text(best_n_estimators, min_error*1.2, "Minimum", ha="center", fontsize=14)
plt.axis([0, 120, 0, 0.01])
plt.xlabel("Number of trees")
plt.title("Validation error", fontsize=14)
plt.subplot(122)
plot_predictions([gbrt_best], X, y, axes=[-0.5, 0.5, -0.1, 0.8])
plt.title("Best model", fontsize=14)
plt.show()
译者注:
事实上,最优的模型应该是n_estimators=best_n_estimators+1(48),因为python数组索引从0开始,根据下面程序的输出结果可以验证。
我们同样可以通过在模型的效果没有显著提升时停止模型的训练,而不是完成训练后寻找最优的基础模型数量。通过设置warm_start=True,我们可以使用前面模型的结果,在此基础上添加更多基础模型。下面的实例展现这样的思想,训练在验证数据效果5次没有提升时停止。
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=1, learning_rate=0.1, random_state=42, 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_val)
val_error = mean_squared_error(y_val, 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 # early stopping
print gbrt.n_estimators
# output
# 53
GradientBoostingClassifier/GradientBoostingRegressor的参数subsample让我们能够使用部分训练数据训练基础模型。这样的措施能够加速训练,会增加偏差,减少方差。这样的思想是Stochastic Gradient Boosting。
注释:
我们可以通过loss参数指定模型使用的损失函数。