前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Python机器学习实战】决策树与集成学习(五)——集成学习(3)GBDT应用实例

【Python机器学习实战】决策树与集成学习(五)——集成学习(3)GBDT应用实例

作者头像
冬夜先生
修改2021-09-09 10:33:07
5390
修改2021-09-09 10:33:07
举报
文章被收录于专栏:csico

前面对GBDT的算法原理进行了描述,通过前文了解到GBDT是以回归树为基分类器的集成学习模型,既可以做分类,也可以做回归,由于GBDT设计很多CART决策树相关内容,就暂不对其算法流程进行实现,本节就根据具体数据,直接利用Python自带的Sklearn工具包对GBDT进行实现。


  数据集采用之前决策树中的红酒数据集,之前的数据集我们做了类别的处理(将连续的数据删除了,且小批量数据进行了合并),这里做同样的处理,将其看为一个多分类问题。

  首先依旧是读取数据,并对数据进行检查和预处理,这里就不再赘述,所得数据情况如下:

代码语言:javascript
复制
wine_df = pd.read_csv('./winequality-red.csv', delimiter=';', encoding='utf-8')
columns_name = list(wine_df.columns)
for name in columns_name:
    q1, q2, q3 = wine_df[name].quantile([0.25, 0.5, 0.75])
    IQR = q3 - q1
    lower_cap = q1 - 1.5 * IQR
    upper_cap = q3 + 1.5 * IQR
    wine_df[name] = wine_df[name].apply(lambda x: upper_cap if x > upper_cap else (lower_cap if (x < lower_cap) else x))


sns.countplot(wine_df['quality'])
wine_df.describe()

  接下来就是先导入使用GBDT所需要用到的工具包:

代码语言:javascript
复制
# 这里采用的是分类,因此是GradientBoostingClassifier,如果是回归则使用GradientBoostingRegressor
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import mean_squared_error
from sklearn import metrics
from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt

  然后依旧是对数据进行切分,将数据分为训练集和测试集:

代码语言:javascript
复制
trainX, testX, trainY, testY = train_test_split(wine_df.drop(['quality']), wine_df['quality'], test_size=0.3, random_state=22)

  然后就是建立模型:

代码语言:javascript
复制
model = GradientBoostingClssifier()

  这里模型就有很多可选参数,用于调整模型,下面进行具体介绍:

  首先是Boosting的框架的参数,这里在使用GradientBoostingRegressor和GradientBoostingClassifier是一样的,具体包括:

  • n_estimators:弱分类器的最大迭代次数,也就是多少个弱分类器组成,默认值为100。当该值越小时容易欠拟合,太大又会过拟合;
  • learning_rate:学习率,这在前面原理部分有进行介绍,默认值为1,较小的learning_rate意味着步长较小,需要更多的分类器才能够达到效果,通常需要与上面n_eatimators结合共同调参;
  • subsample:这也是一种正则化的方法,在正则化中有提到过,取值为(0,1],默认值为1,即不进行子采样;
  • init:初始化的弱分类器,即f0(x),默认为采用训练样本初始化的分类回归预测,若赋予值需要根据一定的先验知识或者预拟合;
  • loss:即损失函数,在原理篇介绍过相关损失函数,对于分类和回归中损失函数是不相同的:
    • 在分类模型中,有对数似然损失函数“deviance”和指数损失函数“exponential”,默认为对数似然损失函数,一般选择默认,因为选择指数损失函数“exponential”又退回到AdaBoost了;
    • 在回归模型中,有方差损失“ls”、绝对损失“lad”、Huber损失“huber”和分位数损失“quantile”,默认为均方差损失“ls”,一般来说,数据的噪音不多,采用均方差损失即可,噪音点较多,则推荐使用抗噪能力较强的Huber损失,如果需要对训练集进行分段预测时则采用分位数损失“quantile”;
  • alpha:这个参数只存在于GradientBoostingRegressor中,当使用Huber损失和分位数损失时,需要指定分位数的值,默认为0.9,如果噪音数据较多,可以适当降低这个值。

  然后就是弱分类器有关的参数值,弱分类器采用的CART回归树,决策树中的相关参数在决策树实现部分已经进行介绍,这里主要对其中一些重要的参数再进行解释:

  • max_features:划分树时所用到的最大特征数,默认为None,即使用全部的特征,当取值为“log2”时,划分时使用log2N个特征,如果是“sqrt”和“auto”则分别对应着最多考虑√N¯个特征,如果是整数,则代表考虑特征的绝对数,如果是浮点数,则代表考虑总特征数的百分比。一般来说样本总特征数小于50,直接采用50即可,当样本特征数量较大时,再考虑其他特征数;
  • max_depth:每个弱分类器的最大深度,默认为不输入,树的深度为3,一般对于数据较少或者特征较少,该值不需要输入,当样本数量和特征数量过于庞大,推荐使用最大深度限制,一般选择10~100;
  • min_samples_split:内部节点再划分所需最小的样本数,它限制了子树进一步划分的条件,如果节点的样本数小于min_samples_split则不再进行分裂。默认值为2,若样本数量较大,则推荐增大该值;
  • min_samples_leaf:叶子节点最小样本数,该值限定了叶子节点的最小样本数,默认值为1,如果叶子节点样本数量小于该值,则会和兄弟节点一起被剪枝,如果样本量巨大,则推荐增加该值;
  • min_weight_fraction_leaf:叶子节点最小样本权重和,该值限制了叶子节点所有样本权重和的最小值,若小于该值,则会和兄弟节点被剪枝,默认值为0,即不考虑权重。当样本分类问题中类别分布偏差较大,则会引入样本权重,需要调整该值;
  • max_leaf_nodes:最大叶子节点数,通过限制最大叶子结点数防止过拟合,默认为None。如果加入了限制,则算法会建立在最大叶子节点数内最优的决策树,当样本特征数量过多的话,可以限制该值;
  • min_impurity_split:节点划分最小不纯度,这个值限定了决策树的生长,若节点的不纯度(即基尼系数)小于这个值,则该节点不再生长,即为叶子结点,默认值为1e-7,一般不推荐修改。

  上述即为模型的主要参数,这里首先全部使用默认值,对样本进行训练:

代码语言:javascript
复制
model.fit(trainX, trainY)
print("模型在训练集上分数为%s"%model.score(trainX, trainY))
pred_prob = model.predict_proba(trainX)
print('AUC:', metrics.roc_auc_score(np.array(trainY), pred_prob, multi_class='ovo'))

  模型在训练集上分数为0.8817106460418562   AUC: 0.9757763363472337

  可以看到在训练集上AUC表现还不错,模型的分数但并不高,尝试调整训练参数,首先对于迭代次数和学习率共同进行调整:

代码语言:javascript
复制
param_test1 = {'n_estimators': range(10, 501, 10), 'learning_rate': np.linspace(0.1, 1, 10)}
gsearch = GridSearchCV(estimator=GradientBoostingClassifier(learning_rate=1,
                                                            min_samples_split=2,
                                                            min_samples_leaf=1,
                                                            max_depth=3,
                                                            max_features=None,
                                                            subsample=0.8,
                                                            ), param_grid=param_test1, cv=5)
gsearch.fit(trainX, trainY)

means = gsearch.cv_results_['mean_test_score']
params = gsearch.cv_results_['params']
for i in range(len(means)):
    print(params[i], means[i])

print(gsearch.best_params_)
print(gsearch.best_score_)

# {'learning_rate': 0.2, 'n_estimators': 100}

找出最好的n_estimators=100和learning_rate=0.2,将其定下来,带回模型,再次验证:

代码语言:javascript
复制
model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.2,subsample=0.8)
model.fit(trainX, trainY)
print("模型在训练集上分数为%s"%model.score(trainX, trainY))
pred_prob = model.predict_proba(trainX)
print('AUC:', metrics.roc_auc_score(np.array(trainY), pred_prob, multi_class='ovo'))

  模型在训练集上分数为0.9663330300272975   AUC: 0.9977791940084874

  可以看到拟合效果已经很好了,再次调整参数,接下来调整弱分类器中的参数,max_depth和min_samples_split:

代码语言:javascript
复制
param_test1 = {'max_depth': range(1, 6, 1), 'min_samples_split': range(1, 101, 10)}
gsearch2 = GridSearchCV(estimator=GradientBoostingClassifier(n_estimators=100,
                                                             learning_rate=0.2,
                                                             max_features=None,
                                                             min_samples_leaf=1,
                                                             subsample=0.8,
                                                             ), param_grid=param_test1, cv=5)
gsearch2.fit(trainX, trainY)
means = gsearch2.cv_results_['mean_test_score']
params = gsearch2.cv_results_['params']
for i in range(len(means)):
    print(params[i], means[i])

print(gsearch2.best_params_)
print(gsearch2.best_score_)

  找出了树的最大深度为5,由于最小样本划分数量同叶子节点最小样本数量有一定关系,暂时不能定下min_samples_split,将其同min_samples_leaf共同调整:

代码语言:javascript
复制
param_test1 = {'min_samples_leaf': range(1, 101, 10), 'min_samples_split': range(1, 101, 10)}
gsearch3 = GridSearchCV(estimator=GradientBoostingClassifier(n_estimators=100,
                                                             learning_rate=0.2,
                                                             max_features=None,
                                                             max_depth=5,
                                                             subsample=0.8,
                                                             ), param_grid=param_test1, cv=5)
gsearch3.fit(trainX, trainY)
means = gsearch3.cv_results_['mean_test_score']
params = gsearch3.cv_results_['params']
for i in range(len(means)):
    print(params[i], means[i])

print(gsearch3.best_params_)
print(gsearch3.best_score_)

# {'min_samples_leaf': 21, 'min_samples_split': 41}

  可以找出最小样本划分数量21和叶子节点最小数量,我们将这些参数再带回模型:

代码语言:javascript
复制
model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.2, max_depth=5, min_samples_leaf=21, min_samples_split=41, subsample=0.8)
model.fit(trainX, trainY)
print("模型在训练集上分数为%s"%model.score(trainX, trainY))
pred_prob = model.predict_proba(trainX)
print('AUC:', metrics.roc_auc_score(np.array(trainY), pred_prob, multi_class='ovo'))

  模型在训练集上分数为1.0   AUC: 1.0

  可以看到在训练集上已经完美拟合了,但为了验证模型,我们需要再分离出一部分用于验证模型的数据集:

代码语言:javascript
复制
validX, tX, validY, tY = train_test_split(testX, testY, test_size=0.2)

  然后使用验证集,验证模型:

代码语言:javascript
复制
print("模型在测试集上分数为%s"%metrics.accuracy_score(validY, model.predict(validX)))
pred_prob = model.predict_proba(validX)
print('AUC test:', metrics.roc_auc_score(np.array(validY), pred_prob, multi_class='ovo'))

  模型在测试集上分数为0.726790450928382   AUC test: 0.8413890948027345

  可以看到模型在验证集上表现并不是很好,上面模型存在一定的过拟合问题,继续调整参数,通过调整max_features来提高模型的泛华能力:

代码语言:javascript
复制
param_test1 = {'max_features': range(3, 12, 1)}
gsearch4 = GridSearchCV(estimator=GradientBoostingClassifier(n_estimators=100,
                                                             learning_rate=0.2,
                                                             min_samples_leaf=21,
                                                             min_samples_split=41,
                                                             max_depth=5,
                                                             subsample=0.8,
                                                             ), param_grid=param_test1, cv=5)
gsearch4.fit(trainX, trainY)
means = gsearch4.cv_results_['mean_test_score']
params = gsearch4.cv_results_['params']
for i in range(len(means)):
    print(params[i], means[i])

print(gsearch4.best_params_)
print(gsearch4.best_score_)

# {'max_features': 5}

  进一步调整subsamples:

代码语言:javascript
复制
param_test1 = {'subsample': np.linspace(0.1, 1, 10)}
gsearch5 = GridSearchCV(estimator=GradientBoostingClassifier(n_estimators=100,
                                                             learning_rate=0.2,
                                                             min_samples_leaf=21,
                                                             min_samples_split=41,
                                                             max_depth=5,
                                                             max_features=5
                                                             ), param_grid=param_test1, cv=5)
gsearch5.fit(trainX, trainY)
means = gsearch5.cv_results_['mean_test_score']
params = gsearch5.cv_results_['params']
for i in range(len(means)):
    print(params[i], means[i])

print(gsearch5.best_params_)
print(gsearch5.best_score_)

# {'subsample': 0.7}

  到这里基本主要参数都进行了调整,带回到模型中:

代码语言:javascript
复制
model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.2, max_depth=5, min_samples_leaf=21, min_samples_split=41, max_features=5, subsample=0.7)
model.fit(trainX, trainY)
print("模型在训练集上分数为%s"%model.score(trainX, trainY))
pred_prob = model.predict_proba(trainX)
print('AUC:', metrics.roc_auc_score(np.array(trainY), pred_prob, multi_class='ovo'))

  模型在训练集上分数为0.9990900818926297   AUC: 0.9999992641648271

  有略微下降,因为通过提高模型的泛华能力,会增大模型的偏差,然后利用验证集验证模型:

代码语言:javascript
复制
print("模型在测试集上分数为%s"%metrics.accuracy_score(validY, model.predict(validX)))
pred_prob = model.predict_proba(validX)
print('AUC test:', metrics.roc_auc_score(np.array(validY), pred_prob, multi_class='ovo'))

模型在测试集上分数为0.7161803713527851
AUC test: 0.8429467644071055

  进一步将模型的迭代次数增加一倍,学习率减半:

代码语言:javascript
复制
model = GradientBoostingClassifier(n_estimators=200, learning_rate=0.1, max_depth=5, min_samples_leaf=21, min_samples_split=41, max_features=5, subsample=0.7)
model.fit(trainX, trainY)
print("模型在训练集上分数为%s"%model.score(trainX, trainY))
pred_prob = model.predict_proba(trainX)
print('AUC:', metrics.roc_auc_score(np.array(trainY), pred_prob, multi_class='ovo'))
# validX, tX, validY, tY = train_test_split(testX, testY, test_size=0.2)
print("模型在测试集上分数为%s"%metrics.accuracy_score(validY, model.predict(validX)))
pred_prob = model.predict_proba(validX)
print('AUC test:', metrics.roc_auc_score(np.array(validY), pred_prob, multi_class='ovo'))

  模型在训练集上分数为0.9990900818926297   AUC: 1.0

  模型在测试集上分数为0.7427055702917772   AUC test: 0.851199242237048

本文系转载,前往查看

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

本文系转载前往查看

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

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