前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >机器学习6:集成学习--boosting(AdaBoost)与GBDT

机器学习6:集成学习--boosting(AdaBoost)与GBDT

作者头像
用户5473628
发布2019-08-08 15:38:23
2.2K0
发布2019-08-08 15:38:23
举报
文章被收录于专栏:MiningAlgorithms

算法框架彼此融合产生新的算法:

1)Bagging + 决策树 = 随机森林;

2)AdaBoost + 决策树 = 提升树;

3)Gradient Boosting+ 决策树 =GBDT。

目录:

第一部分:Boosting与AdaBoost;

1,Boosting(AdaBoost)

2,Adboosting优缺点

3,Adaboost的应用场景

第二部分:GBDT;

1,GBDT算法原理

2,Shrinkage(缩减)

3,一个实例

4,GBDT应用场景

第三部分:代码实现;

1,adboosting实现

2,GBDT实现

GBDT相关知识模块:前向分布算法,负梯度拟合,损失函数,回归,二分类,多分类,正则化。

第一部分:Boosting与AdaBoost:

1,Boosting

Boosting是一簇可将弱学习器提升为强学习器的方法。这簇算法的工作机制相似:先从初始训练集训练出一个基学习器,再根据及学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下一个基学习器。如此重复进行,直至基学习器的数目达到事先指定的值T,最终将这T个基学习器进行加权结合。

从图中可以看出,Boosting算法的工作机制是首先从训练集用初始权重训练出一个弱学习器1,根据弱学习的学习误差率表现来更新训练样本的权重,使得之前弱学习器1学习误差率高的训练样本点的权重变高,使得这些误差率高的点在后面的弱学习器2中得到更多的重视。然后基于调整权重后的训练集来训练弱学习器2.,如此重复进行,直到弱学习器数达到事先指定的数目T,最终将这T个弱学习器通过集合策略进行整合,得到最终的强学习器。

Boosting系列算法里最著名算法主要有AdaBoost算法和提升树(boosting tree)系列算法。提升树系列算法里面应用最广泛的是梯度提升树(GradientBoosting Tree)。

下图是Adboosting的算法流程图:

下面我们举一个简单的例子来看看adaboost的实现过程:

图中,“+”和“-”分别表示两种类别,在这个过程中,我们使用水平或者垂直的直线作为分类器,来进行分类。

第一步:

根据分类的正确率,得到一个新的样本分布D2­,一个子分类器h1 ;

其中划圈的样本表示被分错的。在右边的途中,比较大的“+”表示对该样本做了加权。

第二步:

根据分类的正确率,得到一个新的样本分布D3,一个子分类器h2

第三步:

得到一个子分类器h3。

第四步:

  整合所有子分类器:

因此可以得到整合的结果,从结果中看,即使简单的分类器,组合起来也能获得很好的分类效果,在例子中所有的。

2,Adboosting优缺点:

Adaboost的主要优点有:

1)Adaboost作为分类器时,分类精度很高

2)在Adaboost的框架下,可以使用各种回归分类模型来构建弱学习器,非常灵活。

3)作为简单的二元分类器时,构造简单,结果可理解。

4)不容易发生过拟合

Adaboost的主要缺点有:

1)对异常样本敏感,异常样本在迭代中可能会获得较高的权重,影响最终的强学习器的预测准确性。

3,Adaboost的应用场景:

1)用于二分类或多分类的应用场景

2)用于做分类任务的baseline:

  无脑化,简单,不会overfitting,不用调分类器

3)用于特征选择(feature selection)

4)Boosting框架用于对badcase的修正:

只需要增加新的分类器,不需要变动原有分类器

  由于adaboost算法是一种实现简单,应用也很简单的算法。Adaboost算法通过组合弱分类器而得到强分类器,同时具有分类错误率上界随着训练增加而稳定下降,不会过拟合等的性质,应该说是一种很适合于在各种分类场景下应用的算法。

第二部分:GBDT:

1,GBDT算法原理:

GBDT也是集成学习Boosting家族的成员,但是却和传统的Adaboost有很大的不同。

GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论累加起来做最终答案。它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法。

GBDT中的树都是回归树,不是分类树,这点对理解GBDT相当重要(尽管GBDT调整后也可用于分类但不代表GBDT的树是分类树)。

DT-Decision Tree决策树,GB是Gradient Boosting,是一种学习策略,GBDT的含义就是用Gradient Boosting的策略训练出来的DT模型。模型的结果是一组回归分类树组合(CART Tree Ensemble):T1……Tk。其中Tj学习的是之前j-1棵树预测结果的残差,这种思想就像准备考试前的复习,先做一遍习题册,然后把做错的题目挑出来,在做一次,然后把做错的题目挑出来在做一次,经过反复多轮训练,取得最好的成绩。

GBDT还内含了第三个概念:Shrinkage (算法的一个重要演进分枝,目前大部分源码都按该版本实现)。搞定这三个概念后就能明白GBDT是如何工作的,要继续理解它如何用于搜索排序则需要额外理解RankNet概念。

2,Shrinkage(缩减):

Shrinkage(缩减)的思想认为,每次走一小步逐渐逼近结果的效果,要比每次迈一大步很快逼近结果的方式更容易避免过拟合。即它不完全信任每一个棵残差树,它认为每棵树只学到了真理的一小部分,累加的时候只累加一小部分,通过多学几棵树弥补不足。

Shrinkage仍然以残差作为学习目标,但对于残差学习出来的结果,只累加一小部分(step*残差)逐步逼近目标,step一般都比较小,如0.01~0.001(注意该step非gradient的step),导致各个树的残差是渐变的而不是陡变的。直觉上这也很好理解,不像直接用残差一步修复误差,而是只修复一点点,其实就是把大步切成了很多小步。本质上,Shrinkage为每棵树设置了一个weight,累加时要乘以这个weight,但和Gradient并没有关系。这个weight就是step。就像Adaboost一样,Shrinkage能减少过拟合发生也是经验证明的,目前还没有看到从理论的证明。

因此,模型最后的输出,是一个样本在各个树中输出的结果的和:

3,一个实例:

下面以一个例子来直观GBDT算法:

上图很直观地体现了GradientBoosting的策略,而每一步的预测模型是回归树:Decision Tree决策树。

GBDT的核心就在于,每一棵树学的是之前所有树结论和的残差,这个残差就是一个加预测值后能得真实值的累加量。比如A的真实年龄是18岁,但第一棵树的预测年龄是12岁,差了6岁,即残差为6岁。那么在第二棵树里我们把A的年龄设为6岁去学习,如果第二棵树真的能把A分到6岁的叶子节点,那累加两棵树的结论就是A的真实年龄;如果第二棵树的结论是5岁,则A仍然存在1岁的残差,第三棵树里A的年龄就变成1岁,继续学。

4,应用场景:

GBDT几乎可用于所有回归问题(线性/非线性),相对logistic regression仅能用于线性回归,GBDT的适用面非常广。亦可用于二分类问题(设定阈值,大于阈值为正例,反之为负例)。

第三部分:代码实现;

1,Adboosting实现:

共分成4步:1,构造数据;2,构建adaboost模型,并训练模型;3,预测;4,可视化。

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

from sklearn.ensemble import AdaBoostClassifier#adaboost引入方法
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_gaussian_quantiles#造数据

# 1,构造数据:
X1, y1 = make_gaussian_quantiles(cov=2.,
                                 n_samples=200, n_features=2,
                                 n_classes=2, random_state=1)#创建符合高斯分布的数据集
X2, y2 = make_gaussian_quantiles(mean=(3, 3), cov=1.5,
                                 n_samples=300, n_features=2,
                                 n_classes=2, random_state=1)

X = np.concatenate((X1, X2))
y = np.concatenate((y1, - y2 + 1))

# 2,构建adaboost模型,并训练模型:
bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2),
                         algorithm="SAMME.R",#可以不写
                         n_estimators=200)
#数据量大的时候,可以增加内部分类器的树深度,也可以不限制树深
#max_depth树深,数据量大的时候,一般范围在10——100之间
#数据量小的时候,一般可以设置树深度较小,或者n_estimators较小
#n_estimators 迭代次数或者最大弱分类器数:200次
#base_estimator:DecisionTreeClassifier 选择弱分类器,默认为CART树
#algorithm:SAMME 和SAMME.R 。运算规则,后者是优化算法,以概率调整权重,迭代速度快,
#需要能计算概率的分类器支持
#learning_rate:0<v<=1,默认为1,正则项 衰减指数
#loss:linear、‘square’exponential’。误差计算公式:一般用linear足够
bdt.fit(X, y)

plot_step = 0.02
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
                     np.arange(y_min, y_max, plot_step))

# 3,预测:
Z = bdt.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)  #设置维度

# 4,可视化:
plot_colors = "br"
class_names = "AB"

plt.figure(figsize=(10, 5), facecolor='w')

plt.subplot(121)  # 局部子图
plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired) 
for i, n, c in zip(range(2), class_names, plot_colors):
    idx = np.where(y == i)
    plt.scatter(X[idx, 0], X[idx, 1],
                c=c, cmap=plt.cm.Paired,
                label=u"类别%s" % n)
plt.xlim(x_min, x_max)
plt.ylim(y_min, y_max)
plt.legend(loc='upper right')
plt.xlabel('x')
plt.ylabel('y')
plt.title(u'AdaBoost分类结果,正确率为:%.2f%%' % (bdt.score(X, y) * 100))

twoclass_output = bdt.decision_function(X)  # 获取决策函数的数值

plot_range = (twoclass_output.min(), twoclass_output.max())   # 获取范围
plt.subplot(122)
for i, n, c in zip(range(2), class_names, plot_colors):   #直方图

    plt.hist(twoclass_output[y == i],
             bins=20,
             range=plot_range,
             facecolor=c,
             label=u'类别 %s' % n,
             alpha=.5)
x1, x2, y1, y2 = plt.axis()
plt.axis((x1, x2, y1, y2 * 1.2))
plt.legend(loc='upper right')
plt.ylabel(u'样本数')
plt.xlabel(u'决策函数值')
plt.title(u'AdaBoost的决策值')

plt.tight_layout()
plt.subplots_adjust(wspace=0.35)
plt.show()

2,GBDT实现

共分成6步:1,加载数据;2,特征工程;3,构建一个调用模型的函数;4,训练模型;5,使用模型进行测试集样本预测; 6,保存预测结果。

代码语言:javascript
复制
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn import cross_validation, metrics
from sklearn.model_selection import GridSearchCV

import matplotlib.pylab as plt
%matplotlib inline

# 1,加载数据:
train = pd.read_csv("DataSets/Titanic/train.csv")
test = pd.read_csv("DataSets/Titanic/test.csv")
target='Survived' # Disbursed的值就是二元分类的输出
IDcol = 'PassengerId'

# 2,特征工程:
train = train.drop(['PassengerId','Name','Ticket','Cabin'],axis=1)   # 剔除无关数据
test = test.drop(['Name','Ticket','Cabin'], axis=1)
train["Embarked"] = train["Embarked"].fillna("S")

train['Fare'] = train['Fare'].astype(int)   #float 2 int
test.Fare.fillna(test.Fare.median(), inplace=True)   #插补缺失数据
test['Fare'] = test['Fare'].astype(int)

embark_dummies_train  = pd.get_dummies(train['Embarked'])     # pd.get_dummies()方法对离散数据重新编码,生成0-1矩阵
embark_dummies_test  = pd.get_dummies(test['Embarked'])
train = train.join(embark_dummies_train)   #合并矩阵
test = test.join(embark_dummies_test)
train.drop(['Embarked'], axis=1,inplace=True)   #删除原来的Embark列,inplace=Ture
test.drop(['Embarked'], axis=1,inplace=True)

average_age_titanic   = train["Age"].mean()   #Age 主要是对缺失的值进行处理填补
std_age_titanic       = train["Age"].std()
count_nan_age_titanic = train["Age"].isnull().sum()

average_age_test   = test["Age"].mean()
std_age_test       = test["Age"].std()
count_nan_age_test = test["Age"].isnull().sum()

rand_1 = np.random.randint(average_age_titanic - std_age_titanic, average_age_titanic + std_age_titanic, size = count_nan_age_titanic)
rand_2 = np.random.randint(average_age_test - std_age_test, average_age_test + std_age_test, size = count_nan_age_test)
train["Age"][np.isnan(train["Age"])] = rand_1    
test["Age"][np.isnan(test["Age"])] = rand_2
train['Age'] = train['Age'].astype(int)
test['Age']  = test['Age'].astype(int)

def get_person(passenger):  #Sex  考虑到小孩及妇女优先对待,分三类
    age,sex = passenger
    return "child" if age < 16 else sex

train['Person'] = train[['Age','Sex']].apply(get_person,axis=1)
test['Person']  = test[['Age','Sex']].apply(get_person,axis=1)

train.drop(['Sex'],axis=1,inplace=True)
test.drop(['Sex'],axis=1,inplace=True)

person_dummies_titanic  = pd.get_dummies(train['Person'])
person_dummies_titanic.columns = ['Child','Female','Male']

person_dummies_test  = pd.get_dummies(test['Person'])
person_dummies_test.columns = ['Child','Female','Male']

train = train.join(person_dummies_titanic)
test = test.join(person_dummies_test)
train.drop(['Person'],axis=1,inplace=True)
test.drop(['Person'],axis=1,inplace=True)

pclass_dummies_titanic  = pd.get_dummies(train['Pclass'])   #P class
pclass_dummies_titanic.columns = ['Class_1','Class_2','Class_3']

pclass_dummies_test  = pd.get_dummies(test['Pclass'])
pclass_dummies_test.columns = ['Class_1','Class_2','Class_3']

train.drop(['Pclass'],axis=1,inplace=True)
test.drop(['Pclass'],axis=1,inplace=True)

train = train.join(pclass_dummies_titanic)
test= test.join(pclass_dummies_test)

# 3,构建一个调用模型的函数:
def modelfit(alg, dtrain, predictors, performCV=True, printFeatureImportance=True, cv_folds=5):
    #Fit the algorithm on the data
    alg.fit(dtrain[predictors], dtrain['Survived'])

    #Predict training set:
    dtrain_predictions = alg.predict(dtrain[predictors])
    dtrain_predprob = alg.predict_proba(dtrain[predictors])[:,1]

    #Perform cross-validation:
    if performCV:
        cv_score = cross_validation.cross_val_score(alg, dtrain[predictors], dtrain['Survived'], cv=cv_folds, scoring='roc_auc')

    #Print model report:
    print ("\nModel Report")
    print ("Accuracy : %.4g" % metrics.accuracy_score(dtrain['Survived'].values, dtrain_predictions))
    print ("AUC Score (Train): %f" % metrics.roc_auc_score(dtrain['Survived'], dtrain_predprob))

    if performCV:
        print ("CV Score : Mean - %.7g | Std - %.7g | Min - %.7g | Max - %.7g" % (np.mean(cv_score),np.std(cv_score),np.min(cv_score),np.max(cv_score)))

    #Print Feature Importance:
    if printFeatureImportance:
        feat_imp = pd.Series(alg.feature_importances_, predictors).sort_values(ascending=False)
        feat_imp.plot(kind='bar', title='Feature Importances')
        plt.ylabel('Feature Importance Score')

# 4,训练模型:
# 4.1,创建一个baseline model,不使用任何参数优化
predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm0 = GradientBoostingClassifier(random_state=10)
modelfit(gbm0,train,predictors)

# 4.2,调参
predictors = [x for x in train.columns if x not in [target, IDcol]]
# 4.2.1
param_test1 = {'n_estimators':range(20,101,10),'learning_rate':[0.1,0.12,0.14,0.16]}  #首先从长和迭代次数开始进行网格搜索
gsearch1 = GridSearchCV(estimator = GradientBoostingClassifier(min_samples_split=10,min_samples_leaf=50,max_depth=8,max_features='sqrt',subsample=0.8,random_state=10), 
param_grid = param_test1, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch1.fit(train[predictors],train[target])
gsearch1.grid_scores_, gsearch1.best_params_, gsearch1.best_score_
# 4.2.2
param_test2 = {'max_depth':range(2,16,2), 'min_samples_split':range(5,31,5)}   #上述步骤确定了最佳步长0.14,最佳迭代次数90;接下来对决策树进行调参,ma_depth和min_sampls_split
gsearch2 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.14, n_estimators=90, max_features='sqrt', subsample=0.8, random_state=10), 
param_grid = param_test2, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch2.fit(train[predictors],train[target])
gsearch2.grid_scores_, gsearch2.best_params_, gsearch2.best_score_
# 4.2.3
param_test3 = { 'min_samples_leaf':range(50,101,10)}   #上述步骤确定了最佳'max_depth': 4, 'min_samples_split': 25,深度小可能是由于数据量较小所致;接下来选择叶结点最小样本数
gsearch3 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.14, n_estimators=90,max_depth=4,min_samples_split=25,max_features='sqrt', subsample=0.8, random_state=10), 
param_grid = param_test3, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch3.fit(train[predictors],train[target])
gsearch3.grid_scores_, gsearch3.best_params_, gsearch3.best_score_
# 4.2.4
param_test4 = {'max_features':range(1,14,1)}   #上述步骤确定'min_samples_leaf': 70;接下来选择最大特征数
gsearch4 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.14, n_estimators=90,max_depth=4,min_samples_leaf=70, min_samples_split=25,  subsample=0.8, random_state=10),
param_grid = param_test4, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch4.fit(train[predictors],train[target])
gsearch4.grid_scores_, gsearch4.best_params_, gsearch4.best_score_
# 4.2.5
param_test5 = {'subsample':[0.6,0.7,0.75,0.8,0.85,0.9]}   # 上述步骤中最大特征数为3 感觉不符合实际,所以还是选择默认情况
gsearch5 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.14, n_estimators=90,max_depth=4,min_samples_split=25, min_samples_leaf=70, random_state=10,max_features="sqrt"),
param_grid = param_test5, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch5.fit(train[predictors],train[target])
gsearch5.grid_scores_, gsearch5.best_params_, gsearch5.best_score_


# 5,使用模型进行测试集样本预测:
Y_pred = gsearch5.predict(test.drop('PassengerId',axis=1))

predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm_tuned_1 = GradientBoostingClassifier(learning_rate=0.07, n_estimators=180,max_depth=4, min_samples_split=25,min_samples_leaf=70, subsample=0.8, random_state=10, max_features="sqrt")
modelfit(gbm_tuned_1, train, predictors)

predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm_tuned_1 = GradientBoostingClassifier(learning_rate=0.07, n_estimators=140,max_depth=3, min_samples_split=10,min_samples_leaf=30, subsample=0.85, random_state=10, max_features="sqrt")
modelfit(gbm_tuned_1, train, predictors)

# 6,保存预测结果
my_submission =pd.DataFrame({'PassengerId': test['PassengerId'].as_matrix(),'Survived': Y_pred.astype(np.int32)})
my_submission.to_csv("DataSets/Titanic/my_submission2.csv",index=False)

Y_pred = gbm0.predict(test.drop('PassengerId',axis=1))  # 看一下不做优化的预测情况,以作对比

my_submission =pd.DataFrame({'PassengerId': test['PassengerId'].as_matrix(),'Survived': Y_pred.astype(np.int32)})
my_submission.to_csv("DataSets/Titanic/my_submission2.csv",index=False)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-04-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MiningAlgorithms 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档