Sklearn--集成学习(2)

将多个决策树,通过Bagging 的方法进行集成,便是常用的随机森林了。

三、随机森林

BaggingRandomForest

from sklearn.ensemble import RandmForestClassifier


## 比较 BaggingClassifier + Decision 和 RandomForestClassifier
if __name__ == '__main__' :
    x, y, x_t, y_t = get_moons_data()
    ## 随机森林
    rnd_clf = RandomForestClassifier(n_estimators=500, # 500个树
                                     max_leaf_nodes=16,max_features = 1.0,
                                     n_jobs=-1, random_state = 42
    )
    rnd_clf.fit(x, y)
    y_prd_rf = rnd_clf.predict(x_t)

    ## Bagging + 决策树
    dec_clf = DecisionTreeClassifier(splitter='random', max_leaf_nodes=16, random_state = 42)
    bag_rf_clf = BaggingClassifier(dec_clf, n_estimators=500,max_features = 1.0,
                                   bootstrap=True, n_jobs=-1,random_state = 42
    )
    bag_rf_clf.fit(x, y)
    y_prd_bg = bag_rf_clf.predict(x_t)
    print(sum((y_t-y_prd_rf)==0)/len(y_t))
    print(sum((y_t-y_prd_bg)==0)/len(y_t))


""" 结果 0.88 0.89 可见两者拟合效果基本是相同的 """

四、极端树

在随机森林生长树时,每个节点分裂时只考虑随机特征集上的特征。相比于找到更好的特征,可以通过使用对特征使用随机阈值使树更加随机。
这种极端随机的树被称为Extremely Randomized Trees ,或者更简单的称为Extra-Tree, 再一次用高偏差换低方差。它还使得Extra-Tree比规则的随机森林更快地训练,因为在每个节点上,每个特征的最佳阈值 使生长树最耗时的任务之一

from sklearn.datasets import load_iris
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.metrics import accuracy_score

def get_importance(data, model):
    for name, score in zip(data.feature_names, model.feature_importances_):
        print(name, score)

if __name__ == '__main__' :
    iris = load_iris()
    rnd_clf = RandomForestClassifier(n_estimators=500, n_jobs=-1)
    rnd_clf.fit(iris['data'], iris['target'])
    get_importance(iris, rnd_clf)
    print(accuracy_score(iris['target'],rnd_clf.predict(iris['data'])))

    et_clf = ExtraTreesClassifier(n_estimators = 500, n_jobs = -1)
    et_clf.fit(iris['data'], iris['target'])
    get_importance(iris, et_clf)
    print(accuracy_score(iris['target'],et_clf.predict(iris['data'])))
""" sepal length (cm) 0.09194217773494667 sepal width (cm) 0.022488796221977456 petal length (cm) 0.4367589103747967 petal width (cm) 0.44881011566827916 1.0 sepal length (cm) 0.09575810061808965 sepal width (cm) 0.05854316642710482 petal length (cm) 0.41837709271746937 petal width (cm) 0.4273216402373357 1.0 """

极端树的拟合速度更加快

五、Boosting 提升树

一般常用的adaboost
简单的做法就是:e 为错误率,alpha 为基分类器的权重
加大错误分类器的权重, np.exp(-1 * alpha * y[i] * h(x[i]))
加大优分类器的权重, alpha = 1/2 * np.log((1- e) / e)

sklearn 通常使用adaboost 的多分类版本 SAMME 这表明分段加建模使用多分类指数损失函数。如果是二分类, 那么SAMME 是与 Adaboost 相同。要预测概率(即有predict_prob()),需要用SAMME.R,
依赖于概率的分类器通常比仅仅的分类更加好

from sklearn import AdaBoostClassifier

if __name__ == '__main__' :
    x, y, x_t, y_t = get_moons_data()
    dec_clf = DecisionTreeClassifier(max_depth=1)
    ada_clf = AdaBoostClassifier(dec_clf, n_estimators=200, algorithm='SAMME.R', learning_rate=0.5)
    ada_clf.fit(x, y)
    accuracy_score(y_t, ada_clf.predict(x_t))

""" 0.86 """

六、梯度提升

它不像Adaboost 那样每次迭代都更改实例的权重,这个方法是去使用新的分类器去拟合前面分类器预测的残差


from sklearn.tree import DecisionTreeRegressor
import numpy as np

def Get_poly_data():
    np.random.seed(1212)
    x = np.linspace(-2, 2, 60)
    y_2 = 3 * x ** 2 + 2 * np.random.rand(len(x)) + 0.3
    x_in_t1 = x.reshape((-1, 1))
    return np.c_[np.ones((len(x), 1)), x_in_t1], y_2.reshape((-1, 1))

if __name__ == "__main__" :
    x, y =  Get_poly_data()
    tree_reg = DecisionTreeRegressor(max_depth=2)
    tree_reg.fit(x, y)
    ## 在一个分类器的残差上训练第二个分类器
    y2 = y - tree_reg.predict(x)
    tree_reg2 = DecisionTreeRegressor(max_depth=2)
    tree_reg2.fit(x, y2)
    ## 在第二个分类器的残差上训练第三个分类器
    y3 = y2 - tree_reg2.predict(x)
    tree_reg3 = DecisionTreeRegressor(max_depth=2)
    tree_reg3.fit(x, y3)
    ## 通过集成所有树的预测来在一个新的实例上进行预测
    y_prd = sum(reg.predict(x) for reg in (tree_reg, tree_reg2, tree_reg3))
    print(np.var(y-y_prd))

""" 1.5815921095948544 """

GBRT来训练。与RandomForestClassifier 相似,它也有超参数去决定决策树的生长,也有超参数去控制集成训练

from sklearn.ensemble import GradientBoostingRegressor

if __name__ == '__main__' :
    x, y = Get_poly_data()
    gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=.1)
    gbrt.fit(x, y)
    print(np.var(y-gbrt.predict(x)))
""" 15.005525005176338 """

为了找到树的最优数量,可以使用早停技术。最简单使用这个技术的方法就是使用 staged_predict():
它训练的每个阶段(用一棵树,两棵树等)返回一个迭代器。

用120棵树去训练一个GBRT集成,然后在训练的每个阶段验证错误以找到树的最佳数量,最后
使用GBRT树的最优数量训练另一个集成

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

if __name__ == '__main__' :
    x, y = Get_poly_data()
    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)
    print('梯度提升:',np.var(y_val - gbrt.predict(x_val)))
    ## 早停方法
    errors = [mean_squared_error(y_val, y_prd) for y_prd 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)
    print('梯度提升-早停:',np.var(y_val - gbrt_best.predict(x_val)))

""" 梯度提升: 18.56750851278303 梯度提升-早停: 14.492333262607817 """

可以看出梯度提升早停的方法拟合的更加好

点赞