梯度提升回归树

梯度提升回归和Scikit-Learn简介

梯度提升回归树

梯度提升回归树(GBRT, 或者叫较短梯度提升)是一种可用于分类和回归的灵活非参数型统计学习方法。

这里我们展示一下如何在scikit-learn(一种简单易用, python实现机器学习的一种通用型工具箱)中使用GBRT。我将介绍一点关于scikit-learn和GBRT界面。这个教程将向你展示在实战中如何使用GBRT以及讨论了正则化,调参,模型解释的一些重要问题。

Scikit-learn

Scikit-learn是一个第三方库,它提供了各种各样包括监督和非监督学习的方法,也实现了通用任务,比如模型选择,特征提取和特征选择。

Scikit-learn通过以估计器为中心提供了一个对象引导的界面。根据Scikit-learn手册:”一个估计器是从数据中学习得到的任何对象,它可能是一个分类, 回归或者聚类算法活着,也可以是一个转换器—从原始数据中提取/过滤有效特征。“ 估计器的API粗略看起来如下:

1
2
3
4
5
6
7
8
9
10
class Estimator(object):
def fit(self, X, y=None):
"""Fits estimator to data"""
# set state of 'self'
return self
def predict(self, X):
"""Predict response of ''X''."""
# compute predictions ''pred''
return pred

Estimator.fit 方法设置了基于训练数据的估计器状态。通常, 数据由二维numpy数列组成(n_sample, n_predictors) , 它包含了所谓的特征矩,而一个一维的numpy数列y则包含了相应(要么是类标签或者回归值)。

估计器提供了Estimator.predict可以生产预测值。对于回归,estimator.predict返回预测的回归值;对于分类它则返回对应的类标签。分类器通过一个方法Estimator.predict_proba可以预测类成员的概率, 返回一个二维数组(n_samples, n_classes), 其中类安装字典顺序排列。

Scikit-learn为梯度提升提供了两个估计器: GradientBoostingClassifier 和 GradientBoostingRegressor. 两个方法都在sklearn.ensemble包中。

from sklearn.ensemble import GradientBoostClassifier, GradientBoostingRegressor

估计器提供参数来控制拟合行为—-这些参数通常叫做超参数。其中对GBRT比较重要的:

​- 回归树的数量(n_estimators)

​- 每个回归树的深度 (max_depth)

​- 损失函数 (loss)

​- 学习速率 (learning_rate)

举个例子,如果你要拟合一个带有100个树,每个树的深度为3回归模型, 并且使用最小二乘法:

1
est = GradientBoostingREgressor(n_estimators=100, max_depth=3, loss='ls')

你可以在scikit-learn官网或者估计器的文档找到更深入的文字描述—在Ipython中简单使用 GradientBoostingRegressor?去看文档吧。

在拟合过程中, 估计器的状态存在实例属性中(拖尾的下划线)。比如, 回归树的序列(sklearn.tree.DecisionTreeRegressor 对象)存在 est.estimators_中。

这里包含了一个实例,展示如何拟合梯度提升分类来合成数据集(取自 ESL,第二版).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sklearn.datasets import make_hastie_10_2
from sklearn.cross_validation import train_test_split
# generate synthetic data from ESLII - Example 10.2
X, y = make_hastie_10_2
X_train, X_test, y_train, y_test = train_test_split(X, y)
# fit estimator
est = GradientBoostingClassifier(n_estimators=200, max_depth=3)
est.fit(X_train, y_train)
# predict class labels
pred = est.predict(X_test)
# score on test data (accuracy)
acc = est.score(X_test, y_test)
print "Accuracy: %.4f" %acc
# predict class probabilities
est.predict_proba(x_test)[0]
# output:
>>> Accuracy: 0.9240
>>> array([0.26442503, 0.73557497])

梯度提升实战

实战中成功应用GBRT的大部分挑战可以在简单的曲线拟合例子中阐明。下面你会看到带有一个特征x和对应响应y的回归问题。通过非均匀地随机选择x坐标,我们得到100训练数据点来估计真值((sinoid函数, 亮蓝线),并加入一些随机高斯噪声。此外,我们还得到100个测试集(红),并用其来评估我们的预测值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import numpy as np
def groud_truth(x):
"""Ground truth -- function to approximate"""
return x * np.sin(x) + np.sin(2 * x)
def gen_data(n_samples=200):
"""generate training and """
np.random.seed(13)
x = np.random.uniform(0, 10, size=n_samples)
x.sort()
y = ground_truth(x) + 0.75 * np.random.normal(size=n_samples)
train_mask = np.random.ranint(0, 2, size=n_samples).astype(np.bool)
x_train, y_train = x[train_mask, np.newaxis], y[train_mask]
x_test, y_test = x[~train_mask, np.newaxis], y[~train_mask]
return x_train, x_test, y_train, y_test
x_train, x_test, y_train, y_test = gen_data(200)
# plot ground truth
x_plot = np.linspace(0, 10, 500)
def plot_data(figsize=(8, 5)):
fig = plt.figure(figsize=figsize)
gt = plt.plot(x_plot, ground_truth(x_plot), alpha=0.4, label='ground truth')
# plot training and testing data
plt.scatter(x_train, y_train, s=10, alpha=0.4)
plt.scatter(x_test, y_test, s=10, alpha=0.4, color='red')
plt.xlim((0, 10))
plt.ylabel('y')
plt.xlabel('x')
plot_data(figsize=(8, 5))

如果你对上面的数据拟合一个独立的回归树,你会得到一个分段常数近似。树的深度越深,分段常数的片断更多,因此得到更多的偏差。

1
2
3
4
5
6
7
from sklearn.tree import DecisionTreeRegressor
plot_data()
est = DecisionTreeRegresssor(max_depth=1).fit(x_train, y_train)
plt.plot(x_plot, est.predict(x_plot[:, np.newaxis]), label="RT max_depth=1", color='g', alpha=0.9, linewidth=2)
est = DecisionTreeRegresssor(max_depth=3).fit(x_train, y_train)
plt.plot(x_plot, est.predict(x_plot[:, np.newaxis]), label="RT max_depth=3", color='g', alpha=0.7, linewidth=1)
plt.legend(loc='upper left')

现在, 用梯度提升模型来拟合训练数据,看看当树的数量越来越多时,模型越来越精确的过程。 Scikit-learn 梯度提升估计器允许你对模型的预测评估: 通过方法 (predict|predict_proba) 提供一个树的函数的函数, 返回一个生成器,可以迭代预测, 这样你既可以尽管添加树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from itertool import islice
plot_data()
est = GradientBoostingRegressor(n_estimators=1000, max_depth=1, learning_rate=1.0)
est.fit(x_train, y_train)
ax = plt.gca()
first = True
# step over prediction as we add 20 more trees.
for pred in islice(est.staged_predict(x_plot[:, np.newaxis]), 0, 1000, 10):
plt.plot(x_plot, pred, color='r', alpha=0.2)
if first:
ax.annotate("High bias - low variance", xy=(x_plot[x_plot.shape[0]//2],
pred[x_plot.shape[0]//2]),
xycoords='data',
xytext=(3,4), textcoords='data',
arrowprops=dict(arrowstyle="-",
connectionstyle="arc"))
first = False
pred = est.predict(x_plot[:, np.newaxis])
plt.plot(x_plot, pred, color='r', label='GBRT max_depth=1')
ax.annotate('Low bias - high variance', xy=(x_plot[x_plot.shape[0] // 2],
pred[x_plot.shape[0] // 2]),
xycoords='data', xytext=(6.25, -6),
textcoords='data',
arrowprops=dict(arrowstyle="-",
connectionstyle="arc"))
plt.legend(loc='upper left')
人艰不拆,生活不易