调参步骤及思想
选择较高的学习速率(learning rate)。一般情况下,学习速率的值为0.1。但是,对于不同的问题,理想的学习速率有时候会在0.05到0.3之间波动。选择对应于此学习速率的理想决策树数量。XGBoost有一个很有用的函数“cv”,这个函数可以在每一次迭代中使用交叉验证,并返回理想的决策树数量。
对于给定的学习速率和决策树数量,进行决策树特定参数调优(max_depth, min_child_weight, gamma, subsample, colsample_bytree)。在确定一棵树的过程中,我们可以选择不同的参数,待会儿我会举例说明。
xgboost的正则化参数的调优。(lambda, alpha)。这些参数可以降低模型的复杂度,从而提高模型的表现。
降低学习速率,确定理想参数。
XGBoost代码案例
XGBoost效果确实不错,但是前提是要将参数不断地优化和调整,这样最终才能得到一个较好的模型,下面依然以一个实际的案例进行XGBoost代码演示
#导入所需要的包 from sklearn.metrics import precision_score from sklearn.model_selection import train_test_split import xgboost as xgb import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from sklearn.preprocessing import LabelEncoder from sklearn.metrics import classification_report from sklearn.model_selection import GridSearchCV #网格搜索 import matplotlib.pyplot as plt#可视化 import seaborn as sns#绘图包 # 忽略警告 import warnings warnings.filterwarnings("ignore")
相关性分析
plt.figure(figsize=(20,20)) plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus']=False sns.heatmap(df.corr(),cmap="YlGnBu",annot=True) plt.title("相关性分析图")
XGBoost无参数模型
model=xgb.XGBClassifier() # 训练模型 model.fit(X_train,y_train) # 预测值 y_pred = model.predict(X_test) ''' 评估指标 ''' # 求出预测和真实一样的数目 true = np.sum(y_pred == y_test ) print('预测对的结果数目为:', true) print('预测错的的结果数目为:', y_test.shape[0]-true) # 评估指标 from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100)) print('预测数据的精确率为:{:.4}%'.format( precision_score(y_test,y_pred)*100)) print('预测数据的召回率为:{:.4}%'.format( recall_score(y_test,y_pred)*100)) # print("训练数据的F1值为:", f1score_train) print('预测数据的F1值为:', f1_score(y_test,y_pred)) print('预测数据的Cohen’s Kappa系数为:', cohen_kappa_score(y_test,y_pred)) # 打印分类报告 print('预测数据的分类报告为:',' ', classification_report(y_test,y_pred))
效果还是不错,比之前用其他的模型反复的调优的效果还好,但是这不是XGBoost的最终效果
n_estimators(学习曲线)
scorel = [] for i in range(0,300,10): model = xgb.XGBClassifier(n_estimators=i+1, n_jobs=--4, random_state=90).fit(X_train,y_train) score = model.score(X_test,y_test) scorel.append(score) print(max(scorel),(scorel.index(max(scorel))*10)+1) #作图反映出准确度随着估计器数量的变化,51的附近最好 plt.figure(figsize=[20,5]) plt.plot(range(1,300,10),scorel) plt.show()
继续迭代优化,缩小范围
scorel = [] for i in range(0,20,1): model = xgb.XGBClassifier(n_estimators=i+1, n_jobs=--4, random_state=90).fit(X_train,y_train) score = model.score(X_test,y_test) scorel.append(score) print(max(scorel),(scorel.index(max(scorel))*1)+1) #作图反映出准确度随着估计器数量的变化,51的附近最好 plt.figure(figsize=[20,5]) plt.plot(range(0,20,1),scorel) plt.show()
max_depth(学习曲线)
scorel = [] for i in range(0,20,1): model = xgb.XGBClassifier(max_depth =i, n_estimators=8, n_jobs=--4, random_state=90).fit(X_train,y_train) score = model.score(X_test,y_test) scorel.append(score) print(max(scorel),(scorel.index(max(scorel))*1)+1) #作图反映出准确度随着估计器数量的变化,51的附近最好 plt.figure(figsize=[20,5]) plt.plot(range(0,20,1),scorel) plt.show()
效果有所提升,继续调优,但是我们知道XGBoost的参数过于的多,如果按照传统的学习曲线迭代调优,效果可能局限在局部最优 ,下面试试XGBoost的交叉验证
调整max_depth 和min_child_weight
param_test1 = { # 'n_estimators':list(range(3,15,1)), 'max_depth':list(range(3,10,2)), 'min_child_weight':list(range(1,6,2)) } gsearch1 = GridSearchCV(estimator = xgb.XGBClassifier( learning_rate =0.1, n_estimators=8, max_depth=8, min_child_weight=1, gamma=0, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1, seed=27), param_grid = param_test1, scoring='roc_auc',iid=False, cv=5) gsearch1=gsearch1.fit(X,y) gsearch1.best_params_, gsearch1.best_score_ ({'max_depth': 3, 'min_child_weight': 5}, 0.9200952807471878)
进一步调整
param_test1 = { 'max_depth':list(range(8,11,1)), 'min_child_weight':list(range(1,4,1)) } gsearch1 = GridSearchCV(estimator = xgb.XGBClassifier( learning_rate =0.1, n_estimators=7, max_depth=3, min_child_weight=5, gamma=0, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1, seed=27), param_grid = param_test1, scoring='roc_auc',iid=False, cv=5) gsearch1.fit(X,y) gsearch1.best_params_, gsearch1.best_score_ ({'max_depth': 8, 'min_child_weight': 3}, 0.9158850465045336)
调整gamma
param_test3 = { 'gamma':[i/10.0 for i in range(0,5)] } gsearch3 = GridSearchCV(estimator = xgb.XGBClassifier( learning_rate =0.1, n_estimators=8, max_depth=8, min_child_weight=3, gamma=0, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test3, scoring='roc_auc',iid=False, cv=5) gsearch3.fit(X,y) gsearch3.best_params_, gsearch3.best_score_
({'gamma': 0.4}, 0.9103697321364825)
调整subsample 和colsample_bytree
param_test5 = { 'subsample':[i/100.0 for i in range(75,90,5)], 'colsample_bytree':[i/100.0 for i in range(75,90,5)] } gsearch5 = GridSearchCV(estimator = xgb.XGBClassifier( learning_rate =0.1, n_estimators=8, max_depth=8, min_child_weight=3, gamma=0.4, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test5, scoring='roc_auc',iid=False, cv=5) gsearch5.fit(X_train,y_train) gsearch5.best_params_, gsearch5.best_score_ ({'colsample_bytree': 0.85, 'subsample': 0.85}, 0.9420290874342445)
效果一下就有了显著的提升,继续调参,因为这个参数还是比较的重要有放回随机抽样
调整正则化参数
param_test6 = { 'reg_alpha':[1e-5, 1e-2, 0.1, 1, 100] } gsearch6 = GridSearchCV(estimator = xgb.XGBClassifier( learning_rate =0.1, n_estimators=8, max_depth=8, min_child_weight=3, gamma=0.2, subsample=0.85, colsample_bytree=0.85, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test6, scoring='roc_auc',iid=False, cv=5) gsearch6.fit(X_train,y_train) gsearch6.best_params_, gsearch6.best_score_ ({'reg_alpha': 1e-05}, 0.9426155352636133) param_test6 = { 'learning_rate':[0.01, 0.02, 0.1, 0.2] } gsearch6 = GridSearchCV(estimator = xgb.XGBClassifier( learning_rate =0.1, n_estimators=8, max_depth=8, min_child_weight=1, gamma=0.2, subsample=0.8, colsample_bytree=0.85, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test6, scoring='roc_auc',iid=False, cv=5) gsearch6.fit(X_train,y_train) gsearch6.best_params_, gsearch6.best_score_ ({'learning_rate': 0.1}, 0.9506471151048836)
网格搜索
根据前面所出来的参数,可以进一步的缩放数值,最后采用暴力搜索网格调参,但是由于XGBoost本身就是一个运行速度慢,占据CPU的一个模型,加上网格搜索速度反而变的更慢了,所以这里需要谨慎设置参数,不然电脑会跑的嗡嗡响
# learning_rate =0.1, n_estimators=8, max_depth=8, # min_child_weight=1, gamma=0.2, subsample=0.8, colsample_bytree=0.85, # objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27 import numpy as np from sklearn.model_selection import GridSearchCV parameters = {'n_estimators':np.arange(6,10,1) ,'max_depth':np.arange(6,10,1) ,"colsample_bytree":np.arange(0.8,0.9,0.05) ,"subsample":np.arange(0.8,0.9,0.05) ,'gamma':np.arange(0.1,0.5,0.1) ,'min_child_weight':np.arange(1,3,1) } clf = xgb.XGBClassifier(random_state=25) GS = GridSearchCV(clf, parameters, cv=5) # cv交叉验证 GS.fit(X_train,y_train) print(GS.best_params_) print(GS.best_score_) {'colsample_bytree': 0.8, 'gamma': 0.4, 'max_depth': 7, 'min_child_weight': 1, 'n_estimators': 8, 'subsample': 0.8} 0.9048780487804878
额,这么说呢,网格搜索的效果还不如自己之前的参数靠谱,事实上说明,我们在优化参数的时候,按照固定思维得出的数值只是一个参考值,我们自己还可以做一些调整(细微的)
最终模型代码
model=xgb.XGBClassifier(eta =0.1, n_estimators=8, max_depth=8, min_child_weight=2, gamma=0.8, subsample=0.85, colsample_bytree=0.8) # 训练模型 model.fit(X_train,y_train) # 预测值 y_pred = model.predict(X_test) ''' 评估指标 ''' # 求出预测和真实一样的数目 true = np.sum(y_pred == y_test ) print('预测对的结果数目为:', true) print('预测错的的结果数目为:', y_test.shape[0]-true) # 评估指标 from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100)) print('预测数据的精确率为:{:.4}%'.format( precision_score(y_test,y_pred)*100)) print('预测数据的召回率为:{:.4}%'.format( recall_score(y_test,y_pred)*100)) # print("训练数据的F1值为:", f1score_train) print('预测数据的F1值为:', f1_score(y_test,y_pred)) print('预测数据的Cohen’s Kappa系数为:', cohen_kappa_score(y_test,y_pred)) # 打印分类报告 print('预测数据的分类报告为:',' ', classification_report(y_test,y_pred))
效果还是不错的,和随机森林有点相近,从这里也可以看出集成学习的优势所在
绘制特征重要性图
from xgboost import plot_importance # plt.figure(figsize=(15,15)) plt.rcParams["figure.figsize"] = (14, 8) plot_importance(model)
XGBoost可视化
xgboosts=xgb.to_graphviz(model) xgboosts.format = 'png' xgboosts.view('./xgboost')
这里绘制来一个树的过程,其中leaf代表预测分数
ROC曲线AUC面积
# ROC曲线、AUC from sklearn.metrics import precision_recall_curve from sklearn import metrics # 预测正例的概率 y_pred_prob=model.predict_proba(X_test)[:,1] # y_pred_prob ,返回两列,第一列代表类别0,第二列代表类别1的概率 #https://blog.csdn.net/dream6104/article/details/89218239 fpr, tpr, thresholds = metrics.roc_curve(y_test,y_pred_prob, pos_label=2) #pos_label,代表真阳性标签,就是说是分类里面的好的标签,这个要看你的特征目标标签是0,1,还是1,2 roc_auc = metrics.auc(fpr, tpr) #auc为Roc曲线下的面积 # print(roc_auc) plt.figure(figsize=(8,6)) plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--') plt.plot(fpr, tpr, 'r',label='AUC = %0.2f'% roc_auc) plt.legend(loc='lower right') # plt.plot([0, 1], [0, 1], 'r--') plt.xlim([0, 1.1]) plt.ylim([0, 1.1]) plt.xlabel('False Positive Rate') #横坐标是fpr plt.ylabel('True Positive Rate') #纵坐标是tpr plt.title('Receiver operating characteristic example') plt.show()
在使用XGBoost的过程中,遇到了很多的参数调优问题,其次就是CPU不足,运行网格搜索的时候,最后有人推荐去使用lightGBM这种轻量级的,但是好像也有数据量的局限性,后期可以测试一下效果。
数据的数量每天都在增加,对于传统的数据科学算法来说,很难快速的给出结果。LightGBM的前缀‘Light’表示速度很快。LightGBM可以处理大量的数据,运行时占用很少的内存。另外一个理由,LightGBM为什么这么受欢迎是因为它把重点放在结果的准确率上。LightGBM还支持GPU学习,因此,数据科学家广泛的使用LightGBM来进行数据科学应用的部署。
不建议在小数据集上使用LightGBM。LightGBM对过拟合很敏感,对于小数据集非常容易过拟合。对于多小属于小数据集,并没有什么阈值,但是从我的经验,我建议对于10000+以上的数据的时候,再使用LightGBM。