机器学习实战——基于Scikit-Learn和TensorFlow 阅读笔记 之 第七章:集成学习和随机森林

版权声明:访问者可将本博客提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。除此以外,将本网站任何内容或服务用于其他用途时,须及时征得本网站及相关权利人的明确许可。 https://blog.csdn.net/qq_38262728/article/details/88081372

《机器学习实战——基于Scikit-Learn和TensorFlow》
这是一本非常好的机器学习和深度学习入门书,既有基本理论讲解,也有实战代码示例。
我将认真阅读此书,并为每一章内容做一个知识笔记。
我会摘录一些原书中的关键语句和代码,若有错误请为我指出。
在这里插入图片描述

第七章 集成学习和随机森林

利用 群体智慧 ,聚合一组预测器叫做 集成学习

训练一组决策树分类器,每一棵树都基于训练集不同的随机子集进行预测。
做出预测时,获得所有树各自的预测,然后给出得票最多的类别作为预测结果。叫做 随机森林
迄今为止可用的最强大的机器学习算法之一。

1 投票分类器

聚合各个分类器的预测,然后将得票最多的结果作为预测分类。叫做 硬投票分类器

即使每个分类器都是弱学习器,通过集成依然可以实现一个强学习器,只要有足够大数量并且足够多种类的弱学习器。

基于的前提是,所有的分类器都是完全独立的,彼此的错误毫不相关。
当预测器尽可能互相独立时,集成方法的效果最优。

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

log_clf = LogisticRegression(solver="liblinear", random_state=42)
rnd_clf = RandomForestClassifier(n_estimators=10, random_state=42)
svm_clf = SVC(gamma="auto", random_state=42)

voting_clf = VotingClassifier(
    estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
    voting='hard')

voting_clf.fit(X_train, y_train)

from sklearn.metrics import accuracy_score

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))

如果所有分类器都能够估算出类别的概率,那么你可以将概率在所有单个分类器上平均,然后让scikit-learn给出平均概率最高的类别作为预测。这叫做 软投票法

2 bagging和pasting

另一种方法是每个预测器使用的算法相同,但是在不同的训练集随机子集上进行训练。
采样时,如果将样本放回叫做 bagging ,不放回叫做 pasting

一旦预测器训练完成,集成就可以通过简单地聚合所有预测器的预测来对新实例做出预测。
聚合函数通常是 统计法 用于分类,或是平均法用于回归。

每个预测器单独的偏差都高于在原始训练集上训练的偏差,但是通过聚合,同时降低了偏差和方差。
总体来说,最终结果是,与直接在原始训练集上训练的单个预测器相比,集成的偏差相近,但是方差更低。

2.1 scikit-learn的bagging和pasting

scikit-learn提供了一个简单的API,可用BaggingClassifier类进行bagging和pasting。

from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier

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

bagging比pasting的偏差略高,通常baggin生成的模型更好。

2.2 包外评估

使用bagging,有些实例可能会被采样多次,而有些实例则可能根本不被采样。

正好可以利用这些37%左右不被采样的实例进行评估,从而不需要单独的验证集或是交叉验证。
将每个预测器在其包外实例上的评估结果进行平均,就可以得到对集成的评估。

创建BaggingClassifier时,设置oob_score=True,就可以请求在训练结束后自动进行包外评估。

3 Random Patches和随机子空间

BaggingClassifier也支持对特征进行抽样,通过控制两个超参数:max_features和bootstrap_features。
工作方式跟max_samples和bootstrap相同,只是抽象对象不是实例而是特征。

这对处理高维(例如图像)特别有用。
对训练实例和特征都进行抽样,称为 Ramdom Patches 方法。
而保留所有训练实例但是对特征进行抽样被称为 随机子空间法

对特征抽样给预测器带来了更大的多样性,所以略高的偏差换来了更低的方差。

4 随机森林

随机森林是决策树的集成,通常用bagging方法训练,训练集大小通过max_samples来设置。

使用RandomForesetClassifier和RandomForestRegressor类。

from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1, random_state=42)
rnd_clf.fit(X_train, y_train)

y_pred_rf = rnd_clf.predict(X_test)

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

4.1 极端随机树

对每个特征使用随机阈值,而不是搜索得出的最佳阈值,则可能让决策树生长得更随机。称为 极端随机树

它也是以更高的偏差换取了更低的方差,训练快得多。

通常很难知道极端随机树和常规随机树哪一种更好,唯一的方法是都试一次并进行比较。

4.2 特征重要性

重要的特征更可能出现在靠近根节点的位置,而不重要的特征通常出现在叶节点的位置。
通过计算一个特征在森林中所有树上的平均深度可以估算一个特征的重要程度。
通过访问feature_importances_可以访问到这个结果。

想快速了解哪个是重要的特征,随机森林是一个非常便利的方法。

5 提升法

提升法(Boosting)是指可以将几个弱学习器结合成一个强学习器的任意集成方法。

大多数提升法的总体思路是循环训练预测器,每一次都对其前序做出一些改正。

目前最流行的是 自适应提升法 ,AdaBoost。

5.1 Adaboost

新预测器对其前序进行纠正的办法之一,就是更多地关注前序拟合不足的训练实例,从而使新的预测器不断的越来越关注于难缠的问题,这就是Adaboost使用的技术。

首先训练一个基础分类器对训练集进行预测,然后对错误分类的训练实例增加其权重,使用最新的权重对第二个分类器进行训练并再次预测,不断循环向前。

一旦全部训练器训练完成,集成整体做出预测时跟bagging或pasting方法一样了,除非预测器有不同的权重,因为它们总的准确率是基于加权后的训练集。

依序学习的缺陷是无法并行导致影响了拓展,此方面表现不如bagging和pasting方法。

scikit-learn使用的其实是Adab\Boost的一个多分类版本,SAMME。

from sklearn.ensemble import AdaBoostClassifier

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

m = len(X_train)

plt.figure(figsize=(11, 4))
for subplot, learning_rate in ((121, 1), (122, 0.5)):
    sample_weights = np.ones(m)
    plt.subplot(subplot)
    for i in range(5):
        svm_clf = SVC(kernel="rbf", C=0.05, gamma="auto", random_state=42)
        svm_clf.fit(X_train, y_train, sample_weight=sample_weights)
        y_pred = svm_clf.predict(X_train)
        sample_weights[y_pred != y_train] *= (1 + learning_rate)
        plot_decision_boundary(svm_clf, X, y, alpha=0.2)
        plt.title("learning_rate = {}".format(learning_rate), fontsize=16)
    if subplot == 121:
        plt.text(-0.7, -0.65, "1", fontsize=14)
        plt.text(-0.6, -0.10, "2", fontsize=14)
        plt.text(-0.5,  0.10, "3", fontsize=14)
        plt.text(-0.4,  0.55, "4", fontsize=14)
        plt.text(-0.3,  0.90, "5", fontsize=14)

save_fig("boosting_plot")
plt.show()

5.2 梯度提升

梯度提升也是逐步在集成中添加预测器,每一个对其前序做出改正。
不同之处在于它不是像AdaBoost那样在每个迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合。

梯度树提升或梯度提升回归树(GBRT)。

训练GBRT使用scikit-learn的GradientBoostingRegressor类。

np.random.seed(42)
X = np.random.rand(100, 1) - 0.5
y = 3*X[:, 0]**2 + 0.05 * np.random.randn(100)

from sklearn.tree import DecisionTreeRegressor

tree_reg1 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg1.fit(X, y)

y2 = y - tree_reg1.predict(X)
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg2.fit(X, y2)

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))

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+")
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)

save_fig("gradient_boosting_plot")
plt.show()

可使用早期停止法调整树的数量。

from sklearn.ensemble import GradientBoostingRegressor

gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0, 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)

save_fig("gbrt_learning_rate_plot")
plt.show()

可使subsample=0.25,每棵树用25%的随机选择的实例进行训练。为 随机梯度提升

6 堆叠法

堆叠法(stacking)或叠泛化法。

基于的简单想法:训练一个模型来执行模型的聚合。

训练混合器的常用方法是使用留存集。

首先,将训练集分成两个子集,第一个子集用来训练第一层的预测器。

然后,用第一层的预测器在第二个子集上进行预测。

现在每个实例都有了三个预测值,可以使用这些预测值作为输入特征,创建一个新的训练集,并保留目标值。
在新的训练集上训练混合器,让它学习根据第一层的预测来预测目标值。

一旦训练完成,可以按照顺序遍历每层来对新实例进行预测。

scikit-learn不直接支持堆叠,可以自己实现。

猜你喜欢

转载自blog.csdn.net/qq_38262728/article/details/88081372