本文共 6874 字,大约阅读时间需要 22 分钟。
XGBoost算法是由GBDT算法演变出来的,GBDT算法在求解最优化问题的时候应用了一阶导技术,而XGBoost则使用损失函数的一阶导和二阶导,不但如此,
还可以自己定义损失函数,自己定义损失函数前提是损失函数可一阶导和二阶导。其实算法的原理就是在一颗决策树的基础上不断地加树,比如在n-1颗树地基础上加一棵树变成n颗树的同时算法的精确率不断提高、效果提升。
基础理解:
损失函数: l(yi,yi^) = (yi-yi^)**2 【这里损失函数先以方差损失作为示例、因为比较好算和符号表达毕竟这个也很不错;当然损失函数可以更改】
如何最优化的求解? : F*(x) = argmin E(x,y)(L(y,F(x)))
最终,集成算法的表示:yi^ = sum(fk(xi)) ; 其中,k=1~K;fk属于F
yi0^ = 0
yi1^ = f1(xi) = yi0^+f1(xi)
yi2^ = f1(xi)+f2(xi) = yi1^+f2(xi)
…
yin^ = sum(fk(xi)) = yi{n-1}^ + fn(xi) ;其中,k=1~n。
在样本中着手计算,样本的真实值yi,预测值yi^。
目标: Obj{t} = sum(l(yi,yi^{t-1}+ft(xi)))+U(ft)+c 其中、c为常数,i=1~n, U(ft)为L2正则化的惩罚函数
(说白了就是L2正则项:qT+lambad* 1/2* sum(wj**2);其中j=1~T;qT是某个常数,T为叶子节点个数)知识补充:
所以,目标函数变换得到:Obj{t} ~ sum(l(yi,yi{t-1}^)+gi* ft{xi}+1/2* hi* ft(xi)**2) ;其中,i=1~n。其实l(yi,yi{t-1}^)相当于一个常数值,因为它从未改变,收敛于某个具体的常数。
再次变换:得到Obj{t} ~ sum(gi* ft(xi)+1/2* hi* ft(xi)**2)+U(ft) ;其中,i=1~n。
将样本上的遍历计算转换为在叶子节点上的遍历计算: Obj{t} = sum((sum(gi,i属于Ij)* wj+1/2*(sum(hi,i属于Ij)+lambad)* wj**2))+qT,其中,j=1~T。
最终目标函数简化为: Obj{t} = sum(Gj* wj+1/2*(Hj+lambad)* wj**2) + qT;其中Gj=sum(gi,i属于Ij),Hj=sum(hi,i属于Ij)。
求解最终的目标函数:一贯操作:求偏导、令偏导为0、代入原函数;
加入新节点的时候的模型复杂度代价:Gain = 1/2*(G{L}**2/(H{L}+lambad)+G{R}**2/(H{R}+lambad)-(G{L}+G{R})**2/(H{L}+H{R}+lambad))-q
XGBoost的思路总结:1.根据数据集初始化一棵树;2.确定损失函数;3.拿出一棵树来作为推导“样本上的遍历计算”这个过程,然后通过函数变换得到“叶子节点上的遍历计算”这个过程,之后求解模型。4.还可以根据计算得到的模型复杂度设置复杂度阈值、毕竟计算资源的代价太大的话也不是很好。
使用wl包安装xgboost之后,使用import引入即可使用
其中、xgboost模块的XGBClassifier类是解决分类问题、XGBRegressor类是解决回归问题。
xgboost.XGBClassifier(max_depth=3,learning_rate=0.1,n_estimators=100,silent=True,objective=‘binary:logistic’,booster=‘gbtree’,n_jobs=1,
nthread=None,gamma=0,min_child_weight=1,max_delta_step=0,subsample=1,colsample_bytree=1,colsample_bylevel=1,reg_alpha=0, reg_lambda=1,scale_pos_weight=1,base_score=0.5,random_state=0,seed=None,missing=None)xgboost.XGBRegressor(max_depth=3,learning_rate=0.1,n_estimators=100,silent=True,objective=‘binary:linear’,booster=‘gbtree’,n_jobs=1, nthread=None,gamma=0,min_child_weight=1,max_delta_step=0,subsample=1,colsample_bytree=1,colsample_bylevel=1,reg_alpha=0,reg_lambda=1,
scale_pos_weight=1,base_score=0.5,random_state=0,seed=None,missing=None)读取数据:
import pandas as pdcreditcard = pd.read_csv(r'creditcard.csv')creditcard.head(5)
查看各类别的标签比例:
#探索查看各类别的比例差异import matplotlib.pyplot as pltplt.rcParams['font.sans-serif']=['SimHei']plt.rcParams['axes.unicode_minus'] = False# 为确保绘制的饼图为圆形,需执行如下代码plt.axes(aspect = 'equal')# 统计交易是否为欺诈的频数counts = creditcard.Class.value_counts()# 绘制饼图plt.pie(x = counts, # 绘图数据 labels=pd.Series(counts.index).map({0:'正常',1:'欺诈'}), # 添加文字标签 autopct='%.2f%%' # 设置百分比的格式,这里保留一位小数 )# 显示图形plt.show()
在28w条交易数据中、欺诈交易仅占0.17%,两个类别的比例存在严重的不平衡。
如果直接建模则模型的准确率会偏向多数类别的样本,而正确预测交易为欺诈的概率几乎为0.所以,需要使用SMOTE算法转换为相对平衡的数据:from sklearn import model_selection# 将数据拆分为训练集和测试集# 删除自变量中的Time变量X = creditcard.drop(['Time','Class'], axis = 1)y = creditcard.Class# 数据拆分X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.3, random_state = 1234)from imblearn.over_sampling import SMOTE# 运用SMOTE算法实现训练数据集的平衡over_samples = SMOTE(random_state=1234) over_samples_X,over_samples_y = over_samples.fit_sample(X_train, y_train)#over_samples_X, over_samples_y = over_samples.fit_sample(X_train.values,y_train.values.ravel())# 重抽样前的类别比例print(y_train.value_counts()/len(y_train))# 重抽样后的类别比例print('')print(pd.Series(over_samples_y).value_counts()/len(over_samples_y))
使用默认参数直接建模:(也可以进行交叉验证以及其它方式找出最优参数,但我的目的是想看一下处理非平衡数据以及不处理非平衡数据会是怎么样的一种对比)
from sklearn import metricsimport xgboostimport numpy as np# 构建XGBoost分类器xgboost = xgboost.XGBClassifier()# 使用重抽样后的数据,对其建模xgboost.fit(over_samples_X,over_samples_y)# 将模型运用到测试数据集中resample_pred = xgboost.predict(np.array(X_test)) #传入的是array# 返回模型的预测效果print('模型的准确率为:\n',metrics.accuracy_score(y_test, resample_pred))print('模型的评估报告:\n',metrics.classification_report(y_test, resample_pred))
计算欺诈交易的概率值,用于生成ROC曲线的数据:
y_score = xgboost.predict_proba(np.array(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()
利用不平衡数据建模进行对比一下
# 构建XGBoost分类器import xgboostxgboost2 = xgboost.XGBClassifier()# 使用非平衡的训练数据集拟合模型xgboost2.fit(X_train,y_train)# 基于拟合的模型对测试数据集进行预测pred2 = xgboost2.predict(X_test)# 混淆矩阵pd.crosstab(pred2,y_test)
# 返回模型的预测效果print('模型的准确率为:\n',metrics.accuracy_score(y_test, pred2))print('模型的评估报告:\n',metrics.classification_report(y_test, pred2))
计算欺诈交易的概率值,用于生成ROC曲线的数据:
y_score = xgboost2.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()
AUC值一个是0.98,一个是0.97,虽然处理非平衡数据过后只是提升了0.01,但是也算是得到了优化。
希望大家能多多给予意见和建议。谢谢。转载地址:http://xrvb.baihongyu.com/