Xgboost总结-论文阅读和代码解析

  • xgboost是一个系统
    必考题:xgb和gbdt的区别

  • xgb重新定义了树构建时切割的标准,以及子节点具体的取值
    一、模型上: 1. 加了正则项(叶子结点的数量和score,score的计算方法就是G**2/(H+lambda)); 2. 二阶泰勒展开,收敛更快,精度更高;3.每一步学习完子树之后乘上一个学习率之后加到模型里,为后续的学习留出一定的空间
    二、算法上:1. sparse aware algorithm,能处理缺失值,而且处理方式比较tricky,计算左孩子之后,右孩子直接通过G和H减去左孩子的GL和H得到:GR= G - GL,HR=H - HL;2. weighted approximate quantile sketch,二阶导的系数作为权重,提出一种在线的带权重的分位数计算方法。
    三、系统上:1. 提前使用block结构(稀疏矩阵存储格式CSC)保存所有连续特征的排序值,用的时候直接调用;2. 特征的计算是多线程并行计算;
    其他:借鉴了随机森林的思想,支持行采样和列采样。

  • 为什么xgboost要用二阶泰勒展开?
    泰勒展开的本质是拟合一个函数,二阶泰勒展开可以近似大量的损失函数,比如回归的平方损失函数,分类的对数似然损失函数。通过二阶泰勒展开推导出来的最终式子(叶子结点的得分和分裂标准),具有通用性,也就是说,任何一个损失函数,只要它能够二阶求导,就能够使用这套分裂标准和叶子的score来构造xgboost模型,也就是自定义损失函数。

  • xgb算法整理
  • 模型训练流程

    for i in range(1, M): # M是迭代的次数
      每次增加一棵树f_(i)
      对于f(i),采用贪心算法,目标是新增的树可以使得在当前的F(i-1)的情况下增益(包含正则的惩罚项)最大
          注意:xgboost中的分裂标准是根据一阶导和二阶导计算出来的,并不是常规的ID3,C4.5或者gini系数
      F_(i) = F_(i-1) + learning_rate * f_(i)
    
    • loss function包括两项,误差项和正则项

    • loss function
    • 正则项主要是针对叶子结点,包括叶子结点的个数和得分。

      • 正则项
    • 重点是loss function的设计:从sample-wise loss到 tree-structure-wise loss,样本误差group by叶子,正则项也是叶子结点的参数,实现统一。

    • sample-wise loss到 tree-structure-wise loss
    • 选择某个特征和对应的分裂点后,增益的计算方法

    • 增益的计算方法
  • 求解过程,包括数学上和工程上,为了提高准确率和效率所采取的措施

    • 采用了二阶泰勒展开,推导过程如下(以平方损失函数为例)
    • 二阶泰勒展开推导
    • 最终得到(推导中注意l(y_i,yhat_i)已经被放到const里面了):
    • loss function
  • xgboost分裂标准的推导(所有推导从loss function出发)
    分裂标准: Gain=\frac{1}{2}[\frac{G_L^2}{H_{L}+\lambda}+\frac{G_R^2}{H_{R}+\lambda}-\frac{(G_L+G_R)^2}{H_{L}+H_{R}+\lambda}]-\gamma

  • 叶子结点score的计算:-\frac{G_j}{H_{j}+\lambda}
    G_j=\sum_{i\in I_j}g_i;H_j=\sum_{i\in I_j}h_i
    g_{i}=\partial_{\hat{y}^{(t-1)}}l(y_i,\hat{y}^{(t-1)});h_{i}=\partial^{2}_{\hat{y}^{(t-1)}}l(y_i,\hat{y}^{(t-1)})

  • 预测值的传播,boost的理解:第一颗树的\hat{y}^{(t-1)}=0,之后\hat{y}^{(t-1)}的值为这个样本在之前所有树所落到的叶子结点的score之和。score不断叠加,达到了boost的效果。

  • xgboost和gbdt的区别

    • XGB加了正则项,普通的GBDT没有,用于防止过拟合。正则项包括:叶子节点的数量T(gamma参数控制),叶子的权重W(lambda参数控制)。回想一下使用xgb时候的参数设置。
    • 增加了二阶泰勒展开,损失函数的定义更加精确,优化的方向更准确,提高拟合能力。
    • Shrinkage(缩减),增加了学习率,防止过拟合
    • 为了提升效率所做的处理
      • 在训练前先保存每个特征值的排序(用于确定最佳分割点)为block,训练时重复使用这个block,各个特征的增益计算开多线程进行
      • 基于权重的ε-approximate 分位点提议算法
        • xgboost提供了两种greedy来学习每一颗子树的方法。一种是精确的方法,就是每次都遍历所有特征的所有可能分裂点。另一种是近似的算法,根据特征值的百分位来提议分裂点。近似的算法里面又分为两种,一种是global,在最开始的时候对每个特征都提议一些分裂点,在训练过程中这些可能的分裂点不变;另一种是local的方法,在每次分裂的时候重新提议分裂点。
        • 这种基于分位数来提议分裂点的方法能加快训练速度,而且在性能上和精确的方法接近。当数据量特别大的情况下,把所有数据进行排序然后获得分位点的方法非常消耗内存和时间,需要使用ε-approximate 分位点算法,对于权重一致的情况,已经存在quantile sketch算法来进行排序。可以参考ε-approximate quantiles
        • 但是在xgb中,每个点的权重是不一致的,权重系数是二阶导h(具体可以看论文,是从obj function推出来的),关于权重怎么理解,可以参考XGBoost之分位点算法,里面给了手写的例子,有助于理解原文中的排序函数r(z)。xgb中提出了基于权重的quantile sketch算法,也就是weighted quantile sketch。
    • 采样,借鉴随机森林的思路,支持训练数据采样和特征采样。
      • 训练数据采样比例 subsample[default=1]。Subsample ratio of the training instances. Setting it to 0.5 means that XGBoost would randomly sample half of the training data prior to growing trees. and this will prevent overfitting. Subsampling will occur once in every boosting iteration.
      • 列采样比例 colsample_bytree[default=1],控制每棵随机采样的列数的占比(每一列是一个特征)
    • 能够处理缺失值
      • 为缺失值选择默认方向的方法:一个结点要对某一个特征选择分裂点时,每个分裂点的gain都计算两次,分别为让缺失的样本进入左子树和右子树。
      • image
      • 文中还说到这种处理缺失值的方法在数据稀疏的时候计算效率提升了很多,和naive version对比,不确定这种naive version是怎么处理缺失值的?
        naive version是把所有缺失值和左孩子或者右孩子合并,计算Gain。
        //todo
  • 看一下训练好的xgb模型保存的结构

  • image
    • M颗树
    • 对于每一颗树
      • 非叶子结点保存的特征和对应的分裂点(如结点0,表示特征f4, 对应的分裂点为0.585072041;yes=1,no=2表示如果满足条件f4<0.585072041,走到结点1,否则走到结点2;missing=1表示如果是缺失值走到结点1)
      • 叶子结点保存的是得分(如结点10,leaf=-0.666666687表示走到这个叶子结点,获得-0.666666687的得分)
  • xgb如何处理分类问题

    • 处理二分类问题:直接在所有树的得分求和之后套上一个sigmoid函数:yhat = sigmoid(F(x))
    • 处理多分类问题:利用one vs other的算法。假设有C类,则训练C*M颗树,每一类获得一个预测结果。
  • xgb的调参

import xgboost as xgb
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

train_data = pd.read_csv('train.csv')   # 读取数据
y = train_data.pop('30').values   # 用pop方式将训练数据中的标签值y取出来,作为训练目标,这里的‘30’是标签的列名
col = train_data.columns   
x = train_data[col].values  # 剩下的列作为训练数据
train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0)   # 分训练集和验证集
# 这里不需要Dmatrix

parameters = {
              'max_depth': [5, 10, 15, 20, 25],
              'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15],
              'n_estimators': [500, 1000, 2000, 3000, 5000],
              'min_child_weight': [0, 2, 5, 10, 20],
              'max_delta_step': [0, 0.2, 0.6, 1, 2],
              'subsample': [0.6, 0.7, 0.8, 0.85, 0.95],
              'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9],
              'reg_alpha': [0, 0.25, 0.5, 0.75, 1],
              'reg_lambda': [0.2, 0.4, 0.6, 0.8, 1],
              'scale_pos_weight': [0.2, 0.4, 0.6, 0.8, 1]

}

xlf = xgb.XGBClassifier(max_depth=10,
            learning_rate=0.01,
            n_estimators=2000,
            silent=True,
            objective='binary:logistic',
            nthread=-1,
            gamma=0,
            min_child_weight=1,
            max_delta_step=0,
            subsample=0.85,
            colsample_bytree=0.7,
            colsample_bylevel=1,
            reg_alpha=0,
            reg_lambda=1,
            scale_pos_weight=1,
            seed=1440,
            missing=None)
            
# 有了gridsearch我们便不需要fit函数
gsearch = GridSearchCV(xlf, param_grid=parameters, scoring='accuracy', cv=3)
gsearch.fit(train_x, train_y)

print("Best score: %0.3f" % gsearch.best_score_)
print("Best parameters set:")
best_parameters = gsearch.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
    print("\t%s: %r" % (param_name, best_parameters[param_name]))

【注】以上代码引用自XGBoost和LightGBM的参数以及调参

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容