人生苦短

记录大学,记录生活,天道殷勤,请多关注!

人生苦短,我用Python!
大数据、机器学习、深度学习
  menu

提升树AdaBoost算法【自适应增强算法(集成学习算法)】

回忆多元线性回归算法:多元线性回归的构造实质上是将输入特征X进行加权运算,即y=a0+a1x1+a2x2+a3x3+.......+apxp = a0+sum(ai*xi) (其中、i=1~p)

AdaBoost算法【自适应增强算法】

提升树算法与线性回归模型的思想类似,所不同的是该算法实现了多颗基础决策树f(x)的加权运算,最经典的是AdaBoost算法(是一个迭代算法),即:F(x)=sum(a[m]*f[m] (x))=f[m-1] (x)+a[m] *fm (其中,m为1~M)

  • 自适应在于:前一个基本分类器分错的样本会得到加强,加权后的全体样本再次被用来训练下一个基本分类器,同时在每一轮中加入一个新的弱分类器,直到达到某个预定的足够小的错误率或达到预先指定的最大迭代次数。

其中、F(x)是由M颗基础决策树构成的最终提升树,Fm-1表示经过m-1轮迭代后的提升树,a[m]为第m颗基础决策树所对应的权重,fm为第m颗基础决策树。此外,每一颗决策树的生成并不像随机森林那样,而是基于前一颗基础决策树的分类结果对样本设置不同的权重。如果在前一颗基础决策树中将样本点预测错误,就会增大该样本点的权重,否则会相应降低样本点的权重,进而再构建下一颗基础决策树,更加关注权重大的样本点。

所以,AdaBoost算法需要解决三大难题:【需要从损失函数入手】

  • 样本点的权重W[mi]如何确定
  • 基础决策树f(x)如何选择
  • 每一颗基础决策树所对应的权重am如何计算

损失函数(loss function)或代价函数(cost function)是将随机事件或其有关随机变量的取值映射为非负实数以表示该随机事件的“风险”或“损失”的函数。在应用中,损失函数通常作为学习准则与优化问题相联系,即通过最小化损失函数求解和评估模型。例如在统计学和机器学习中被用于模型的参数估计(parameteric estimation) ,在宏观经济学中被用于风险管理(risk mangement)和决策 ,在控制理论中被应用于最优控制理论(optimal control theory) 。

AdaBoost算法的损失函数:

对于分类问题而言、通常提升树的损失函数会选择使用指数损失函数;对于预测问题、通常选择平方损失函数。

  • 说白了,损失函数为预测值与真实值之间的差距或者差距程度。

AdaBoost在解决分类问题时,核心就是不断地改变样本点的权重、并将每一轮的基础决策树通过权重的方式进行线性组合。

在迭代过程需要经过一下四个步骤:

  • 在第一轮基础决策树的构建中,会设置每一个样本点的权重W1i均为1/N
  • 计算基础决策树fm(x)在训练数据集上的误判率em = sum(Wmi *I(yi != fm(xi)))
  • 计算基础决策树fm(x)所对应的权重am* = 1/2*log((1-em)/em)
  • 根据基础决策树fm(x)的预测结果,计算下一轮用于构建基础决策树的样本点权重W((m+1)i)*,

该权重W(m+1)i* 写为:

  • fm(xi)* = yi时,W((m+1)i)* = ((W(mi)* ) exp(-a(m) ))/(sum((W(mi)* ) *exp(-a(m) * ))

其中:

  • 1.实际上误判率em就是错分样本点权重之和;
  • 2.只有当第m轮决策树的误判率小于或者等于0.5时该基础决策树才有意义,即误判率em越小于0.5对应的权重a(m)* 越大,进而说明误判率越小的基础决策树越重要;
  • 关于样本点权重的计算,在第m论决策树中样本点预测错误时对应的权重是预测正确样本点权重的exp(2a(m)* )倍,进而可以使下一轮的基础决策树更加关注错分类的样本点。

AdaBoost在解决回归预测问题时,核心就是利用第m轮基础树的残差值拟合第m+1论基础树。

算法在迭代过程中需要进行如下四个步骤:

  • 初始化一棵仅包含根节点的树,并寻找一个常数能够使损失函数达到极小值。
  • 计算第m轮基础树的残差值r(mi)=yi-fm(xi)
  • 将残差值r[mi]视为因变量,再利用自变量的值对其构造第m+1轮基础树f(m+1)(x)
  • 重复步骤2和3,得到多颗基础树,最终将这些基础树相加得到回归提升树 F(x)=sum(fm(x))

sklearn中的子模块ensemble提供了AdaBoostClassifier用于实现分类,AdaBoostRegressor用于回归任务的实现。

ensemble.AdaBoostClassifier(base_estimator=None,n_estimators=50,learning_rate=1.0,algorithm='SAMME.R',random_state=None)

ensemble.AdaBoostRegressor(base_estimator=None,n_estimators=50,learning_rate=1.0,loss='linear',random_state=None)

参数解释:

  • base_estimator 用于指定提升算法所应用的基础分类器,默认为分类决策树(CART),也可以为其它基础分类器,但分类器必须支持带样本权重的学习,如神经网络。
  • n_estimators 用于指定基础分类器的数量,默认为50个,当模型在训练集中得到完美的拟合之后可以提前结束算法,不一定非要构建完所有分类器。
  • learning_rate用于指定模型迭代的学习率或者步长,即对应的提升模型F(x)可以表示为F(x)=F(m-1)+vamfm(x),其中v是该参数的指定值默认为1;对于较小的学习率v而言,则需要迭代更多次的基础分类器,通常情况下需要利用交叉验证法确定合理的基础分类器个数和学习率。
  • algorithm 用于指定AdaBoostClassifier分类器的算法,默认为'SAMME.R',也可以为'SAMME';使用'SAMME.R'时基础模型能够计算类别的概率值;一般来说,'SAMME.R'比'SAMME'收敛更快、误差更小、迭代数量更少。
  • loss用于指定AdaBoostRegressor回归提升树的损失函数,可以是'linear'表示用线性损失函数;可以是'square'表示使用平方损失函数;可以是'exponential'表示使用指数损失函数;默认为'linear'。
  • random_state 这个不用多说了,哪里都会见到它、记忆深刻...(用于指定随机数生成器的种子)

对于回归提升树而言、在不同的损失函数中,第k个基础分类器样本点损失值得计算也相同。

  • 1.线性损失函数: eki = abs(yi-fk(xi))/Ek
  • 2.平方损失函数: eki = (yi-fk(xi))**2 / Ek
    1. 指数损失函数:eki = 1-exp((-yi+fk(xi))/Ek)

其中、Ek表示第k个基础分类器在训练样本点上的最大误差。她的计算表示式子可以写成Ek = max(abs(yi-fk(xi)))

AdaBoost算法提升分类器实战:

* 数据集:信用卡违约数据,包含30000条记录和25个变量,因变量表示用户在下个月的信用卡还款中是否存在违约情况(1表示违约、0表示不违约),
自变量包含性别、教育水平、年龄、婚姻状况、信用额度、6个月的历史还款状态、账单金额以及还款金额。

读取数据

import pandas as pd
import matplotlib.pyplot as plt

# 读入数据
default = pd.read_excel(r'default of credit card clients.xls')
default.head(5)

查看数据的类别比例:default.y.value_counts()
#绘制标签比例饼图:


# 数据集中是否违约的客户比例
# 为确保绘制的饼图为圆形,需执行如下代码
plt.axes(aspect = 'equal')

# 中文乱码和坐标轴负号的处理
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 统计客户是否违约的频数
counts = default.y.value_counts()

# 绘制饼图
plt.pie(x = counts, # 绘图数据
        labels=pd.Series(counts.index).map({0:'不违约',1:'违约'}), # 添加文字标签
        autopct='%.1f%%' # 设置百分比的格式,这里保留一位小数
       )

# 显示图形
plt.show()

建模训练预测:

# 将数据集拆分为训练集和测试集
# 导入第三方包
from sklearn import model_selection
from sklearn import ensemble
from sklearn import metrics

# 排除数据集中的ID变量和因变量,剩余的数据用作自变量X;因为ID只是一个达到索引的作用
X = default.drop(['ID','y'], axis = 1)
y = default.y

# 数据拆分
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.25, random_state = 1234)

# 构建AdaBoostt提升分类树算法的类
AdaBoost1 = ensemble.AdaBoostClassifier()

# 算法在训练数据集上的拟合
AdaBoost1.fit(X_train,y_train)

# 算法在测试数据集上的预测
pred1 = AdaBoost1.predict(X_test)

# 返回模型的预测效果
print('模型的准确率为:\n',metrics.accuracy_score(y_test, pred1))
print('模型的评估报告:\n',metrics.classification_report(y_test, pred1))

多类分类问题中,分类结果一般有4种情况:

  • 属于类C的样本被正确分类到类C,记这一类样本数为 TP
  • 不属于类C的样本被错误分类到类C,记这一类样本数为 FP
  • 属于类别C的样本被错误分类到类C的其他类,记这一类样本数为 TN
  • 不属于类别C的样本被正确分类到了类别C的其他类,记这一类样本数为 FN

print之后参数说明:

  • precision 精度. precision = TP / (TP + FP)
  • recall 召回率. recall = TP / (TP + TN)
  • f1-score F1值.F- measure = (A + 1) * precision * recall / (A ^ 2 * precision + recall);
    实际上就是precison 和 recall的调和平均值2precisionrecall / (precision + recall)
  • support列为每个标签的出现次数.
  • accuracy :(分类正确的样本个数) / (分类的所有样本个数)
  • macro avg :宏平均 :所有类的F1值取一个算术平均就得到了Macro-average
  • weighted avg 加权平均
    由于我的博客没有使用Ngixn进行反向代理,所以图片是无法加载的,因此需要run in yourself。
    使用默认参数得到模型准确率为81.25%;预测客户违约(y取1)的精准率为68%,覆盖率为32%;预测客户不违约的精准率为83%,覆盖率为96%;
    借此绘制出ROC曲线直观的评价模型:
# 计算客户违约的概率值,用于生成ROC曲线的数据
y_score = AdaBoost1.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
 
# # 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()

ROC曲线下的面积为0.78,接近于0.8,该模型在该数据集上表现不是特别突出。
接下来将采取交叉验证寻找最合适参数以及特征筛选选出影响客户是否违约的因素,因为可想而知、并不是什么因素都是影响结果的主要因素。

# 自变量的重要性排序
importance = pd.Series(AdaBoost1.feature_importances_, index = X.columns)
importance.sort_values().plot(kind = 'barh')
plt.show()

可以直观的观察到下面的特征对客户是否违约的影响不是很大、几乎没用关联;所以应该把其去掉之后再使用交叉验证选参数再重新建模:

# 取出重要性比较高的自变量建模
predictors = list(importance[importance>0.02].index)
predictors

# 通过网格搜索法选择基础模型所对应的合理参数组合
# 导入第三方包
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier

#先选出合适的深度
max_depth = [3,4,5,6]
params1 = {'base_estimator__max_depth':max_depth}
base_model = GridSearchCV(estimator = ensemble.AdaBoostClassifier(base_estimator = DecisionTreeClassifier()),
                          param_grid= params1, scoring = 'roc_auc', cv = 5, n_jobs = 4, verbose = 1)

base_model.fit(X_train[predictors],y_train)
# 返回参数的最佳组合和对应AUC值
base_model.best_params_, base_model.best_score_

对AdaBoostClassifier模型做交叉验证时,一共有两层参数需要调优,一个是基础模型的参数即DecisionTreeClassifier;另一个是提升树模型AdaBoostClassifier的参数。在对基础模型调参时,参数字典的键必须以"base_estimator_"开头,因为该参数是嵌在AdaBoostClassifier类下的DecisionTreeClassifier()类中,这才是对基础模型CART树做的参数调优。

基于CART树参数调优之后的结果进行提升分类树的参数调优:

# 通过网格搜索法选择提升树的合理参数组合
# 导入第三方包
from sklearn.model_selection import GridSearchCV

n_estimators = [100,200,300]
learning_rate = [0.01,0.05,0.1,0.2]
params2 = {'n_estimators':n_estimators,'learning_rate':learning_rate}
adaboost = GridSearchCV(estimator = ensemble.AdaBoostClassifier(base_estimator = DecisionTreeClassifier(max_depth = 3)),
                        param_grid= params2, scoring = 'roc_auc', cv = 5, n_jobs = 4, verbose = 1)

adaboost.fit(X_train[predictors] ,y_train)
# 返回参数的最佳组合和对应AUC值
adaboost.best_params_, adaboost.best_score_

经过5重交叉验证,得知AdaBoostClassifier算法的最佳基础模型个数为300、学习率为0.1。
模型参数调优结束(仅仅涉及了基础模型CART树的深度、提升树中包含的基础模型个数和学习率)。
使用最佳的参数组合构建AdaBoost模型:

AdaBoost2 = ensemble.AdaBoostClassifier(base_estimator = DecisionTreeClassifier(max_depth = 3),
                                       n_estimators = 300, learning_rate = 0.01)
# 算法在训练数据集上的拟合
AdaBoost2.fit(X_train[predictors],y_train)
# 算法在测试数据集上的预测
pred2 = AdaBoost2.predict(X_test[predictors])

# 返回模型的预测效果
print('模型的准确率为:\n',metrics.accuracy_score(y_test, pred2))
print('模型的评估报告:\n',metrics.classification_report(y_test, pred2))

模型仅仅提高了0.36%,可以继续调优或者改选模型.


标题:提升树AdaBoost算法【自适应增强算法(集成学习算法)】
作者:chenruhai
地址:http://www.love520.ltd/articles/2019/08/17/1566025129770.html
CSDN博客地址:https://blog.csdn.net/qq_42658739
GitHub地址:https://github.com/chenruhai

评论