集成学习 Ensemble

sklearn xgboot

Ensemble 好处 feature transform和regularization 单一模型通常只能倾向于feature transform和regularization之一,但是Ensemble却能将feature transform和regularization各自的优势结合起来

常见的 Ensemble 方法有这么几种:

Blending

用不相交的数据训练不同的 Base Model,将它们的输出取(加权)平均。实现简单,但对训练数据利用少了。 vote 分类 avg 回归

加权Linear blending

训练过程

validation 训练不同的base model, 得到的值代入 Linear blending 获取对应的权重a。 使用完整的data set 训练不同的base model得到更好的model + 之前的权重 => 目标函数

获取base model

  • select different base model
  • different params

投票分类器

1
2
3
4
5
6
7
>>> from sklearn.ensemble import RandomForestClassifier
>>> from sklearn.ensemble import VotingClassifier
>>> from sklearn.linear_model import LogisticRegression
>>> from sklearn.svm import SVC
>>> log_clf = LogisticRegression()
>>> rnd_clf = RandomForestClassifier()
>>> svm_clf = SVC()

voting="soft"有更好的效果,使用交叉验证去预测类别概率,其降低了训练速度

If ‘hard’, uses predicted class labels for majority rule voting. Else if ‘soft’, predicts the class label based on the argmax of the sums of the predicted probabilities, which is recommended for an ensemble of well-calibrated classifiers.

1
2
>>> voting_clf = VotingClassifier(estimators=[('lr', log_clf), ('rf', rnd_clf), >>> ('svc', svm_clf)],voting='hard')
>>> voting_clf.fit(X_train, y_train)

测试集准确率

1
2
3
4
5
6
7
8
9
>>> 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))
LogisticRegression 0.864
RandomForestClassifier 0.872
SVC 0.888
VotingClassifier 0.896

Stacking

blending的基础上 => 多层blending Blending只有一层,而Stacking有多层 使用前一层的输出(预测结果)作为本层的训练集(有点像多层神经网络) ,不使用琐碎的函数(如硬投票)来聚合集合中所有分类器的预测,训练一个模型来执行这个聚合 模型复杂度过高,容易造成过拟合

例子:

诀窍是将训练集分成三个子集:第一个子集用来训练第一层,第二个子集用来创建训练第二层的训练集(使用第一层分类器的预测值), 第三个子集被用来创建训练第三层的训练集(使用第二层分类器的预测值)。以上步骤做完了,我们可以通过逐个遍历每个层来预测一个新的实例。 你也可以使用开源的项目例如 brew (网址为 https://github.com/viisar/brew)

Bagging

使用训练数据的不同随机子集来,有放回随机抽取m次每次n个样本作为子集训练相同Base Model,由于不同data-set子集得到不同的model 最后进行每个 Base Model 权重相同的 Vote。也即 Random Forest 的原理。Pasting 是无放回

Blending & Bagging 都是可并行训练base model 再aggregation

以上算法不能解决下图 阴影是三个model都出错部分

BaggingClassifier类或者对于回归可以是BaggingRegressor。尝试Pasting,就设置bootstrap=False 如果基分类器可以预测类别概率(例如它拥有predict_proba()方法),那么BaggingClassifier会自动的运行软投票,这是决策树分类器的情况。

1
2
3
4
5
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
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)

OOB

out-of-bag bagging 没有被选上的数据 用来验证G- 直接验证G 加快效率 BaggingClassifier来自动评估时设置oob_score=True来自动评估

1
2
3
4
>>> bag_clf = BaggingClassifier(DecisionTreeClassifier(), n_estimators=500,bootstrap=True, n_jobs=-1, oob_score=True)
>>> bag_clf.fit(X_train, y_train)
>>> bag_clf.oob_score_
0.93066666666666664

测试集

1
2
3
4
>>> from sklearn.metrics import accuracy_score
>>> y_pred = bag_clf.predict(X_test)
>>> accuracy_score(y_test, y_pred)
0.93600000000000005

Boosting

迭代地训练 Base Model,每次根据上一个迭代中预测错误的情况修改训练样本的权重。也即 Gradient Boosting,Adaboost 的原理。比 Bagging 效果好,但更容易 Overfit。

前一个base model 预测错误的的样本会增权,正确的会减少权重,权值更新过后的样本训练下一个新的base model 直到目标错误率或最大迭代次数 aggregation时 表现好的model 权重就大

AdaBoost

错误因子犯错放大,正确缩小 通常学习比乱猜要好所以犯错率ϵt <= 0.5 u 表示重要性初始化 aggregation时 表现好的 权重就大 $\alpha\;=\;\frac12\ln\left(\frac{1-\varepsilon_t}{\varepsilon_t}\right)$

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

Gradient Boosting

也是通过向集成中逐步增加分类器运行的,每一个分类器都修正之前的分类结果,不像Adaboost那样每一次迭代都更改实例的权重, 这个方法是去使用新的分类器去拟合前面分类器预测的残差

GBRT Gradient Boosted Regression Trees

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn.tree import DecisionTreeRegressor
tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg1.fit(X, y)

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

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

y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
1
2
3
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
gbrt.fit(X, y)

找到树的最佳数量 staged_predict

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

X_train, X_val, y_train, y_val = train_test_split(X, y)
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120)
gbrt.fit(X_train, y_train)
errors = [mean_squared_error(y_val, y_pred)
  for y_pred in gbrt.staged_predict(X_val)]
bst_n_estimators = np.argmin(errors)
gbrt_best = GradientBoostingRegressor(max_depth=2,n_estimators=bst_n_estimators)
gbrt_best.fit(X_train, y_train)

早停法 设置warm_start=True来实现 ,这使得当fit()方法被调用时 sklearn 保留现有树,并允许增量训练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>>gbrt = GradientBoostingRegressor(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_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