machine-learning-notes
  • 封面
  • 目录
  • 前言
  • 个人前言
  • 机器学习前言
    • 什么是机器学习和模式识别
    • 机器学习的应用
    • 机器学习的流程
    • 不同的机器学习算法对相同数据预测效果不同
    • 快速入门机器学习
    • 机器学习需要参考哪些书
    • 机器学习的学习路径
    • 深度学习的学习路径
    • 互联网机器学习特定岗位所需技能
  • 机器学习面试
  • 数学基础
  • 微积分
    • 泰勒展开
    • e的直观认识
    • 傅里叶变换
    • 希尔伯特空间
  • 线性代数
    • 范数
    • 矩阵求导
    • 特征值
    • 奇异值分解
  • 概率与信息论
    • 综述概率论基本定义
    • 概率论与贝叶斯先验
    • 正态分布
    • 贝叶斯概率
    • 概率符号说明
    • 共轭先验
    • 信息论
  • 数值计算与优化
    • 最小二乘法
    • 等式约束的拉格朗日乘子法
    • 凸优化
      • 凸集和凸函数
      • 凸优化问题
  • 梯度下降算法
    • 随机梯度下降SGD
    • 动量法Momentum
    • 牛顿动量Nesterov
    • AdaGrad
    • RMSprop
    • Adadelta
    • Adam
    • Nadam
    • AMSGrad
    • AdasMax
  • 概率图模型
    • 概率图模型概论
    • 概率图简介
  • 编程基础
  • linux
    • linux常用命令
    • shell
      • 输入输出重定向
  • python
    • python简介
    • python语法
      • 基础语法
      • 数据结构
      • 过程控制
      • 函数
      • 类和对象
      • 文件操作
      • 正则表达式
    • python库
      • numpy
      • pandas
      • scipy
      • matplotlib
      • scikit-learn
    • python应用
      • 排序算法
  • 数据结构与算法
    • 数据结构
    • 算法思想
      • 排序
        • 堆排序
        • 归并排序
        • 快速排序
      • 递归
    • 剑指offer
      • 链表
      • 二叉树
      • 数组
      • 字符串
      • 栈和队列
      • 递归
      • 动态规划
      • 其他
    • leetcode
    • 编程语言
      • c++
  • Hadoop
    • Hadoop简介
    • MapReduce
  • Hive
  • Spark
  • TensorFlow
    • TensorFlow1.0
      • TensorFlow基础
      • TensorFlow基础概念解析
      • TensorFlow机器学习基础
      • Tensorflow分布式架构
    • TensorFlow2.0
  • PyTorch
  • 机器学习
  • 机器学习概论
  • 特征工程
  • 感知机
  • k近邻
  • 朴素贝叶斯
  • 线性模型
    • 最大熵模型
    • 指数族分布与广义线性模型
    • 线性回归
      • Ridge回归(岭回归)
      • Lasso回归
    • Logistic回归-对数几率回归
  • 决策树
  • 支持向量机
    • 线性可分支持向量机与硬间隔最大化
    • 线性支持向量机与软间隔最大化
    • 非线性支持向量机与核函数
    • 序列最小最优化算法SMO
    • SVM总结
  • 集成学习
    • Bagging
      • 随机森林
    • Boosting
      • AdaBoost
      • GradientBoosting
        • GBDT
        • XGBoost
          • XGBoost理论
          • XGBoost实践
    • Stacking
  • 降维
    • PCA主成分分析
    • 流形学习
  • EM算法
  • HMM隐马尔科夫模型
  • CRF条件随机场
  • 聚类
    • k均值聚类
    • 高斯混合模型
  • 主题模型
    • LDA隐狄利克雷分布
  • 知识点
    • 损失函数
    • 负采样
  • 机器学习算法总结
  • 深度学习
  • 深度学习概论
  • ANN人工神经网络
  • 知识点
    • Batch Normalization
  • CNN卷积神经网络
  • 深度学习优化算法
  • RNN循环神经网络
  • LSTM长短期记忆网络
  • GRU门控循环单元
  • GNN图神经网络
    • GNN图神经网络综述
    • GCN图卷积网络
      • GCN图卷积网络初步理解
      • GCN图卷积网络的numpy简单实现
      • GCN图卷积网络本质理解
      • GCN图卷积网络全面理解
      • SEMI-SUPERVISED CLASSIFICATION WITH GRAPH CONVOLUTIONAL NETWORKS ICLR2017
  • 神经网络架构搜索
    • Weight-Agnostic-Neural-Networks Google2019
  • 强化学习
  • 强化学习概论
  • 马尔科夫决策过程
  • 动态规划
  • 无模型方法一:蒙特卡洛
  • 无模型方法二:时间差分
  • 无模型方法三:多步自举
  • 函数近似和深度网络
  • 策略梯度算法
  • 深度强化学习
  • 基于模型的强化学习
  • 强化学习前景
  • 自然语言处理
  • 自然语言处理概论
  • 自然语言
  • 语言模型和中文分词
  • word2vec
    • word2vec概述
    • word2vec算法原理
    • word2vec源码分析
    • word2vec实践
  • Seq2Seq模型和Attention机制
  • Self-Attention和Transformer
  • 知识图谱
  • 推荐系统
  • 推荐系统概论
  • 基础知识
  • 进阶知识
    • 机器学习
      • Factorization Machines ICDM2010
    • embedding
      • Network Embedding
        • LINE: Large-scale Information Network Embedding
    • 深度学习
      • DeepFM: A Factorization-Machine based Neural Network for CTR Prediction 2017
      • DSSM: Learning Deep Structured Semantic Models for Web Search using Clickthrough Data CIKM2013
    • 图卷积网络
      • Graph Convolutional Neural Networks for Web-Scale Recommender Systems KDD2018
    • 强化学习
      • DRN基于深度强化学习的新闻推荐模型
  • 业界应用
    • YouTube
      • Deep Neural Networks for YouTube Recommendations RecSys2016
    • Alibaba
      • Learning Tree-based Deep Model for Recommender Systems KDD2018
      • Deep Interest Network for Click-Through Rate Prediction KDD2018
      • DSIN:Deep Session Interest Network for Click-Through Rate Prediction IJCAI2019
Powered by GitBook
On this page
  • XGBoost理论
  • XGBoost概述
  • 模型
  • 损失函数
  • 正则项
  • 牛顿法
  • 损失函数的二阶泰勒展开
  • 损失函数求导得最优值
  • 优化算法
  • XGBoost的增益函数
  • 树结点分裂方法(split finding)
  • 稀疏感知分割(缺失值处理)
  • XGBoost的系统设计
  • 分块并行(Column Block for Parallel Learning)
  • 缓存优化(Cache Aware Access)
  • 核外块计算(Blocks for Out-of-core Computation)
  • XGBoost的其他特性
  • XGBoost总结
  • XGBoost优点
  • XGBoost缺点
  • XGBoost和GradientBoost的比较
  • Xgboost使用经验总结
  • 参考资料

Was this helpful?

  1. 集成学习
  2. Boosting
  3. GradientBoosting
  4. XGBoost

XGBoost理论

PreviousXGBoostNextXGBoost实践

Last updated 5 years ago

Was this helpful?

XGBoost理论

XGBoost是从决策树一步步发展而来的:

  • 决策树 ⟶ 对样本重抽样,然后多个树平均 ⟶ Tree bagging

  • Tree bagging ⟶ 再同时对特征进行随机挑选 ⟶ 随机森林

  • 随机森林 ⟶ 对随机森林中的树进行加权平均,而非简单平均⟶ Boosing (Adaboost, GradientBoost)

  • boosting ⟶ 对boosting中的树进行正则化 ⟶ XGBoosting

从这条线一路发展,就能看出XGBoost的优势了。

XGBoost本质只过不就是函数空间上的牛顿法(也可理解为自适应变步长的梯度下降法),使用了损失函数的二阶导数信息,所以收敛更快。

XGBoost概述

最近引起关注的一个Gradient Boosting算法:XGBoost,在计算速度和准确率上,较GBDT有明显的提升。XGBoost 的全称是eXtreme Gradient Boosting,它是Gradient Boosting Machine的一个c++实现,作者为正在华盛顿大学研究机器学习的大牛陈天奇 。XGBoost最大的特点在于,它能够自动利用CPU的多线程进行并行,同时在算法上加以改进提高了精度。它的处女秀是Kaggle的希格斯子信号识别竞赛,因为出众的效率与较高的预测准确度在比赛论坛中引起了参赛选手的广泛关注。值得我们在GBDT的基础上对其进一步探索学习。

陈天奇论文及官网:

模型

例子:预测一个人是否喜欢玩电脑游戏

回归树的预测输出是实数分数,可以用于回归、分类、排序等任务中。对于回归问题,可以直接作为目标值,对于分类问题,需要映射成概率

损失函数

XGBoost的损失函数(在函数空间中,即把函数当做自变量)

其中,

相比原始的GBDT,XGBoost的目标函数多了正则项,使得学习出来的模型更加不容易过拟合。

正则项

有哪些指标可以衡量树的复杂度?

树的深度,内部节点个数,叶子节点个数(T),叶节点分数(w)...

而XGBoost采用的:

对叶子节点个数和叶节点分数进行惩罚,相当于在训练过程中做了剪枝。

牛顿法

怎么理解上面的迭代公式呢?其实很简单,可以理解为自适应变步长的梯度下降法。

我们回顾一下梯度下降法:

损失函数的二阶泰勒展开

此时损失函数可写作:

其中,

将公式中的常数项去掉(不影响求极值),得到:

得到:

注意上式最后一行的左边是对样本的累加,右边是对叶节点的累加,这该怎么统一起来?

则损失函数可以写成按叶节点累加的形式:

损失函数求导得最优值

为了使目标函数最小,可以令上式导数为0:

解得每个叶节点的最优预测分数为:

补充个理解上很重要的点,之前的GBM模型(GBDT、GBRank、LambdaMART等)都把Loss加在的树间而未改动单棵CART内部逻辑(或者说无伤大雅懒得改),XGBoost因为正则化要考虑优化树复杂度的原因,把Loss带入到CART分裂目标和节点权重上去了(或者说把树内和树间的优化目标统一了),即节点权重已经给出来了:

优化算法

当回归树的结构确定时,我们前面已经推导出其最优的叶节点分数以及对应的最小损失值,问题是怎么确定树的结构?

  • 暴力枚举所有的树结构,选择损失值最小的—NP难问题

  • 贪心法,每次尝试分裂一个叶节点,计算分裂前后的增益,选择增益最大的

暴力枚举显然不现实,这里我们像决策树划分那样选择贪心法。

XGBoost的增益函数

有了评判树的结构好坏的标准,我们就可以先求最佳的树结构,这个定出来后,最佳的叶子结点的值实际上在上面已经求出来了。

分裂前后的增益怎么计算?

  • ID3算法采用信息增益

  • C4.5算法采用信息增益比

  • CART采用Gini系数

  • XGBoost呢?

其实前面我们已经XGBoost的最小损失为

上式中的

衡量了每个叶子结点对总体损失的贡献,我们希望损失越小越好,则标红部分的值越大越好。

因此,对一个叶子结点进行分裂,分裂前后的增益定义为(树间和树内的loss是统一的):

这个公式的计算结果,通常用于在实践中评估候选分裂节点是不是应该分裂的划分依据,我们尽量找到使之最大的特征值划分点。

扫描结束后,我们就可以确定是否切分,如果切分,对切分出来的两个节点,递归地调用这个切分过程,我们就能获得一个相对较好的树结构。

树结点分裂方法(split finding)

暴力枚举(Basic Exact Greedy Algorithm)

在树学习中,一个关键问题是如何找到每一个特征上的分裂点。为了找到最佳分裂节点,分裂算法枚举特征上所有可能的分裂点,然后计算得分,这种算法称为Exact Greedy Algorithm,单机版本的XGBoost支持这种Exact Greedy Algorithm,算法如下所示:

近似算法(Approximate Algo for Split Finding)

Exact Greedy Algorithm使用贪婪算法非常有效地找到分裂节点,但是当数据量很大时,数据不可能一次性的全部读入到内存中,或者在分布式计算中,这样不可能事先对所有值进行排序,且无法使用所有数据来计算分裂节点之后的树结构得分。为解决这个问题,近似算法被设计出来。近似算法首先按照特征取值中统计分布的一些百分位点确定一些候选分裂点,然后算法将连续的值映射到buckets中,接着汇总统计数据,并根据聚合统计数据在候选节点中找到最佳节点。

XGBoost采用的近似算法对于每个特征,只考察分位点,减少复杂度,主要有两个变体:

  • Global variant:学习每棵树前就提出候选切分点,并在每次分裂时都采用这种分割

  • Local variant:每次分裂前将重新提出候选切分点

全局划分建议比局部划分建议需要更少的步骤,但需要更多的划分候选点才能达到局部划分建议的准确率。如下图所示:

作者的系统实现了贪婪算法,也可以选择全局划分和局部划分来实现近似算法。

具体算法如上,这里按照三分位点举例:

找到其中最大的信息增量的划分方法:

然而,这种划分分位点的方法在实际中可能效果不是很好,所以XGBoost实际采用的是加权分位数的方法做近似划分算法。

加权分位点(Weighted Quantile Sketch)

带权重直方图算法

由于用暴力枚举来划分分位点的方法在实际中可能效果不是很好,为了优化该问题,XGBoost实际采用的是一种新颖的分布式加权分位点算法,该算法的优点是解决了带权重的直方图算法问题,以及有理论保证。主要用于近似算法中分位点的计算。

实际上,XGBoost不是简单地按照样本个数进行分类,而是以二阶导数值作为权重。

假设分位点为

,假设

二阶导数h为权重的解释:

因为损失函数还可以写成带权重的形式:

如果损失函数是square loss,即

当数据量非常大时,也需要使用quantile summary的方式来近似计算分位点。

在xgboost中,需要根据特征值以及样本的权重分布,近似计算特征值的分位点,实现近似分割算法。近似计算特征值分位点的算法称为:weighted quantile sketch,该算法满足quantile summary通常的两个操作:merge和prune。

结点分裂时多机并行

节点分裂的时候是按照哪个顺序来的,比如第一次分裂后有两个叶子节点,先裂哪一个? 答案:呃,同一层级的(多机)并行,确立如何分裂或者不分裂成为叶子节点。

稀疏感知分割(缺失值处理)

在很多现实业务数据中,训练数据x可能很稀疏。造成这个问题得原因可能是:

  1. 存在大量缺失值

  2. 太多0值

  3. one-hot encoding所致

算法能够处理稀疏模式数据非常重要,XGBoost在树节点中添加默认划分方向的方法来解决这个问题,如下图所示:

该算法的主要思想是:分别假设特征缺失的样本属于右子树和左子树,而且只在不缺失的样本上迭代,分别计算缺失样本属于右子树和左子树的增益,选择增益最大的方向为缺失数据的默认方向。

XGBoost的系统设计

XGBoost 的快还体现在良好的系统设计,体现在几个方面:

分块并行(Column Block for Parallel Learning)

在建树的过程中,最耗时是找最优的切分点,为了节省排序的时间,XGBoost将数据存在内存单元block中,同时在block采用CSC 格式存放(Compressed Column format),每一列(一个属性列)均升序存放,这样,一次读入数据并排好序后,后面节点分裂时直接根据索引得到梯度信息,大大减少计算量。在精确贪心算法中,将所有数据均导入内存,算法只要在数据中线性扫描已经预排序过的特征就可以。对于近似算法,可以用多个block(Multiple blocks)分别存储不同的样本集,多个block可以并行计算,

重要的是,由于这种块结构将数据按列存储,可以同时访问所有列,那么可以对所有属性同时执行split finding算法,从而并行化split finding.

  • 特征预排序,以column block的结构存于内存中

  • 每个特征会存储指向样本梯度统计值的索引,方便计算一阶导和二阶导数值(instance indices)

  • block 的每个块结构中都采用了稀疏矩阵存储格式(Compressed Sparse Columns Format,CSC)进行存储,一个block存储一个或多个特征值

  • 缺失特征值将不进行排序

  • 对于列的blocks,并行的split finding算法很容实现

这种块结构存储的特征之间相互独立,方便计算机进行并行计算。在对节点进行分裂时需要选择增益最大的特征作为分裂,这时各个特征的增益计算可以同时进行,这也是 XGBoost 能够实现分布式或者多线程计算的原因。

这个结构加速split finding的过程,只需要在建树前排序一次,后面节点分裂时直接根据索引得到梯度信息。

缓存优化(Cache Aware Access)

虽然Column Block的设计可以减少节点分裂时的计算量,但其按特征大小顺序存储,相应的样本的梯度信息(一阶和二阶梯度)是分散的,造成内存的不连续访问,降低CPU cache命中率。解决办法是:为每个线程分配一个连续的缓存区,将需要的梯度信息存放在缓冲区中,这样就是实现了非连续空间到连续空间的转换,提高了算法效率。

  • 缓存优化方法

    • 预取数据到buffer中(非连续->连续),再统计梯度信息

    • 适当调整块大小,也可以有助于缓存优化。

核外块计算(Blocks for Out-of-core Computation)

当数据量过大时无法将数据全部加载到内存中,只能先将无法加载到内存中的数据暂存到硬盘中,并分成多个block存在磁盘上,直到需要时再进行加载计算,而这种操作必然涉及到因内存与硬盘速度不同而造成的资源浪费和性能瓶颈。

为了解决这个问题,XGBoost 独立一个线程专门用于从硬盘读入数据,以实现处理数据和读入数据同时进行。但是由于磁盘IO速度太慢,通常跟不上计算的速度。所以,XGBoost还采用了下面两种方法优化速度和存储:

  • 块压缩(Block compression):对 Block 进行按列压缩,并在读取时进行解压。具体是将block按列压缩,对于行索引,只保存第一个索引值,然后只保存该数据与第一个索引值之差(offset),一共用16个bits来保存offset,因此,一个block一般有2^16个样本。

  • 块拆分(Block sharding):将每个块存储到不同的磁盘中,从多个磁盘读取可以增加吞吐量。

XGBoost的其他特性

  • 行抽样(row sample)

    子采样:每轮计算可以不使用全部样本,使算法更加保守

  • 列抽样(column sample)

    XGBoost 借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算。训练的时候只用一部分特征(不考虑剩余的block块即可)

  • Shrinkage(缩减),即学习速率

    XGBoost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间;将学习速率调小,迭代次数增多,有正则化作用

  • 支持自定义损失函数(需二阶可导)

XGBoost总结

XGBoost优点

  1. 利用了二阶梯度来对节点进行划分,相对其他GBM来说,精度更加高。

  2. 提供并行计算能力,主要是在树节点求不同的候选的分裂点的Gain Infomation(分裂后,损失函数的差值)。

  3. 可以找到精确的划分条件

  4. 精度更高:GBDT只用到一阶泰勒展开,而XGBoost对损失函数进行了二阶泰勒展开。XGBoost引入二阶导一方面是为了增加精度,另一方面也是为了能够自定义损失函数,二阶泰勒展开可以近似大量损失函数;

  5. Shrinkage(缩减):相当于学习速率。XGBoost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间;

  6. 列抽样:XGBoost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算;

  7. 缺失值处理:XGBoost采用的稀疏感知算法极大的加快了节点分裂的速度;

  8. 可以并行化操作:块结构可以很好的支持并行计算。

XGBoost缺点

  1. 每次迭代训练时需要读取整个数据集,耗时耗内存;每轮迭代时,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。

  2. 使用Basic Exact Greedy Algorithm计算最佳分裂节点时需要预先将特征的取值进行排序,排序之后为了保存排序的结果,费时又费内存;需要pre-sorted,这个会耗掉很多的内存空间(2 * data * features)。

  3. 计算分裂节点时需要遍历每一个候选节点,然后计算分裂之后的信息增益,费时;数据分割点上,由于XGBoost对不同的数据特征使用pre-sorted算法而不同特征其排序顺序是不同的,所以分裂时需要对每个特征单独做依次分割,遍历次数为 (data * features) 来将数据分裂到左右子节点上。

  4. 由于pre-sorted处理数据,在寻找特征分裂点时(level-wise),会产生大量的cache随机访问。生成决策树是level-wise级别的,也就是预先设置好树的深度之后,每一颗树都需要生长到设置的那个深度,这样有些树在某一次分裂之后效果甚至没有提升但仍然会继续划分树枝,然后再次划分….之后就是无用功了,耗时。

  5. 尽管使用了局部近似计算,但是处理粒度还是太细了。

  6. 计算量巨大

  7. 内存占用巨大

  8. 易产生过拟合

  9. 虽然利用预排序和近似算法可以降低寻找最佳分裂点的计算量,但在节点分裂过程中仍需要遍历数据集;

  10. 预排序过程的空间复杂度过高,不仅需要存储特征值,还需要存储特征对应样本的梯度统计值的索引,相当于消耗了两倍的内存。

XGBoost和GradientBoost的比较

首先XGBoost是Gradient Boosting的一种高效系统实现,只是陈天奇写的一个工具包,本身并不是一种单一算法。XGBoost可以看作是对GradientBoost的优化。其原理还是基于GradientBoost,它的创新之处是用了二阶导数和正则项。

GBDT将树f类比于参数,通过f对负梯度进行回归,通过负梯度逐渐最小化Object目标;XGBoost版本通过使得当前Object目标最小化,构造出回归树f,更直接。两者都是求得f对历史累积F进行修正。

下面具体地进行比较:

  • 模型

    • XGBoost支持线性分类器

  • 模型

    • 学习目标和方法不同

      GBDT直接拿一阶导数来作为下一棵决策树的预测值,进行学习(具体树怎么学习不负责);XGBoost则是拿一阶和二阶导数直接作为下一棵决策树的增益score指导树的学习。

      GBDT主要对loss L(y, F)关于F求梯度,利用回归树拟合该负梯度;XGBOOST主要对loss L(y, F)二阶泰勒展开,然后求解析解,以解析解obj作为标准,贪心搜索split树是否obj更优。

      之前的GBM模型(GBDT、GBRank、LambdaMART等)都把Loss加在的树间而未改动单棵CART内部逻辑(或者说无伤大雅懒得改),XGBoost因为正则化要考虑优化树复杂度的原因,把Loss带入到CART分裂目标和节点权重上去了(或者说把树内和树间的优化目标统一了)

  • 优化算法

    • 传统GBDT在优化时只用到一阶导数信息,XGBoost则对损失函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,XGBoost工具支持自定义代价函数,只要函数可一阶和二阶求导

    • XGBoost对树的结构进行了正则化约束(regularization),防止模型过度复杂,降低了过拟合的可能性。

    • 节点分裂的方式不同,GBDT是用的gini系数,XGBoost是经过优化推导后的

    • 类似于学习率,学习到一棵树后,对其权重进行缩减,从而降低该棵树的作用,提升可学习空间

  • 其他细节

    • XGBoost使用了二阶导数。这个类似于gradient descent和Newton method的区别。所以,XGBoost应该优化目标函数更快,也就是需要更少的树

    • XGBoost支持列抽样(colsumn subsampling)

      XGBoost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是XGBoost异于传统GBDT的一个特性

    • XGBoost对于特征值有缺失的样本,XGBoost可以自动学习出它的分裂方向

    • XGBoost工具支持并行

      我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),XGBoost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能。在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。

    • 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以XGBoost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点

    • NULL值的特殊处理,作为一个特殊值来处理(实践中很有用)

Xgboost使用经验总结

  1. 多类别分类时,类别需要从0开始编码

  2. Watchlist不会影响模型训练。

  3. 类别特征必须编码,因为xgboost把特征默认都当成数值型的

  4. 训练的时候,为了结果可复现,记得设置随机数种子。

  5. XGBoost的特征重要性是如何得到的?某个特征的重要性(feature score),等于它被选中为树节点分裂特征的次数的和,比如特征A在第一次迭代中(即第一棵树)被选中了1次去分裂树节点,在第二次迭代被选中2次…..那么最终特征A的feature score就是 1+2+….

参考资料

本文主要参考这篇文档。

本文参考了这篇文档。

“损失函数求导得最优值”参考了这篇文档。

"优化算法"参考了这篇文档。

"XGBoost和GradientBoost的比较"参考这篇文档。

“Xgboost使用经验总结”参考此博客。

这篇论文适合看懂原理后直接照着这个文章推公式。

“加权分位点(Weighted Quantile Sketch)”参考了此博客。

介绍了xgboost的单机多线程和分布式的代码架构。

===

给定数据集D={(xi,yi)}D = \{ (x_i, y_i) \}D={(xi​,yi​)},XGBoost进行additive training,学习KKK棵树,采用以下函数对样本进行预测:

y^i=ϕ(xi)=∑k=1Kfk(xi),fk∈F\hat{y}_i=\phi(x_i)=\sum_{k=1}^Kf_k(x_i),\quad f_k\in Fy^​i​=ϕ(xi​)=k=1∑K​fk​(xi​),fk​∈F

,这里FFF是假设空间,f(x)f(x)f(x)是回归树(CART):

F={f(x)=wq(x)} (q:Rm→T,w∈RT)F=\{ f(x)=w_{q(x)} \}\ (q: \mathbb{R}^m\rightarrow T, w\in\mathbb{R}^T)F={f(x)=wq(x)​} (q:Rm→T,w∈RT)

q(x)q(x)q(x)表示将样本xxx分到了某个叶子节点上,www是叶子节点的分数(leaf score),所以,wq(x)w_{q(x)}wq(x)​表示回归树对样本的预测值。

σ(z)=11+exp(−z)\sigma(z)=\frac{1}{1+\text{exp}(-z)}σ(z)=1+exp(−z)1​
L(ϕ)=∑il(y^i,yi)+∑kΩ(fk)L(\phi)=\sum_i l(\hat{y}_i,y_i)+\sum_k\Omega(f_k)L(ϕ)=i∑​l(y^​i​,yi​)+k∑​Ω(fk​)
Ω(f)=γT+12λ∣∣w∣∣2\Omega(f)=\gamma T+\frac{1}{2}\lambda||w||^2Ω(f)=γT+21​λ∣∣w∣∣2

上式中,Ω(f)\Omega(f)Ω(f)为正则项,对每棵回归树的复杂度进行了惩罚。

Ω(f)=γT+12λ∣∣w∣∣2\Omega(f)=\gamma T+\frac{1}{2}\lambda||w||^2Ω(f)=γT+21​λ∣∣w∣∣2

在看下一节前,有必要讲下。

将L(θt)L(\theta^t)L(θt)在θt−1\theta^{t-1}θt−1处进行二阶泰勒展开:

L(θt)≈L(θt−1)+L′(θt−1)△θ+L′′(θt−1)△θ22L(\theta^t)\approx L(\theta^{t-1})+L'(\theta^{t-1})\bigtriangleup\theta+L''(\theta^{t-1})\frac{\bigtriangleup\theta^2}{2}L(θt)≈L(θt−1)+L′(θt−1)△θ+L′′(θt−1)2△θ2​

为了简化分析,假设参数是标量(即θ\thetaθ只有一维),则可将一阶和二阶导数分别记为ggg和hhh:

L(θt)≈L(θt−1)+g△θ+h△θ22L(\theta^t)\approx L(\theta^{t-1})+g\bigtriangleup\theta+h\frac{\bigtriangleup\theta^2}{2}L(θt)≈L(θt−1)+g△θ+h2△θ2​

要使L(θt)L(\theta^t)L(θt)极小,即让g△θ+h△θ22g\bigtriangleup\theta+h\frac{\bigtriangleup\theta^2}{2}g△θ+h2△θ2​极小,可令:

∂(g△θ+h△θ22)∂△θ=0\frac{\partial \left( g\bigtriangleup \theta + h\frac{\bigtriangleup\theta^2}{2} \right)}{\partial \bigtriangleup \theta}=0∂△θ∂(g△θ+h2△θ2​)​=0

求得△θ=−gh\bigtriangleup\theta=-\frac{g}{h}△θ=−hg​,故

θt=θt−1+△θ=θt−1−gh\theta^t=\theta^{t-1}+\bigtriangleup\theta=\theta^{t-1}-\frac{g}{h}θt=θt−1+△θ=θt−1−hg​

将参数θ\thetaθ推广到向量形式,迭代公式:

θt=θt−1−H−1g\theta^t=\theta^{t-1}-H^{-1}gθt=θt−1−H−1g

这里HHH是海森矩阵。

θt=θt−1−αL′(θt−1)=θt−1−αg\theta^t=\theta^{t-1}-\alpha L'(\theta^{t-1})=\theta^{t-1}-\alpha gθt=θt−1−αL′(θt−1)=θt−1−αg

看出来了没?牛顿法的−(1/h)g-(1/h)g−(1/h)g就相当于梯度下降法的−αg-\alpha g−αg,也就是牛顿法中的梯度下降的学习率不再是固定的α\alphaα了,而是自适应的1/h1/h1/h了,这个1/h1/h1/h是怎么自适应的呢?hhh是二阶导,hhh较大的时候,说明函数变化剧烈,所以学习率1/h1/h1/h就会很小;而hhh较小的时候,说明函数变化不剧烈,几乎就是一条直线,那么学习率1/h1/h1/h就会变大。所以牛顿法要比梯度下降法收敛迅速,因为它还获知了函数的二阶导数这一信息。

第ttt次迭代后,模型的也测等于前t−1t-1t−1次的模型预测加上第ttt棵树的预测:

y^i(t)=y^i(t−1)+ft(xi)\hat{y}^{(t)}_i=\hat{y}_i^{(t-1)}+f_t(x_i)y^​i(t)​=y^​i(t−1)​+ft​(xi​)
L(t)=∑i=1nl(yi,y^i(t−1)+ft(xi))+Ω(ft)L^{(t)}=\sum_{i=1}^nl(y_i,\hat{y}_i^{(t-1)}+f_t(x_i))+\Omega(f_t)L(t)=i=1∑n​l(yi​,y^​i(t−1)​+ft​(xi​))+Ω(ft​)

公式中,yi,y^i(t−1)y_i,\hat{y}_i^{(t-1)}yi​,y^​i(t−1)​都已知,模型要学习的只有第ttt棵树ftf_tft​。

将损失函数在y^i(t−1)\hat{y}_i^{(t-1)}y^​i(t−1)​处进行二阶泰勒展开:

L(t)≈∑i=1n[l(yi,y^(t−1))+gift(xi)+12hift2(xi)]+Ω(ft)L^{(t)}\approx\sum_{i=1}^n\left[ l(y_i, \hat{y}^{(t-1)})+g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i) \right]+\Omega(f_t)L(t)≈i=1∑n​[l(yi​,y^​(t−1))+gi​ft​(xi​)+21​hi​ft2​(xi​)]+Ω(ft​)
gi=∂l(yi,y^(t−1))∂y^(t−1),hi=∂2l(yi,y^(t−1))∂2y^(t−1)g_i=\frac{\partial l(y_i, \hat{y}^{(t-1)})}{\partial \hat{y}^{(t-1)}},\quad h_i=\frac{\partial^2 l(y_i, \hat{y}^{(t-1)})}{\partial^2 \hat{y}^{(t-1)}}gi​=∂y^​(t−1)∂l(yi​,y^​(t−1))​,hi​=∂2y^​(t−1)∂2l(yi​,y^​(t−1))​

来,答一个小问题,在优化第ttt棵树时,有多少个gig_igi​和hih_ihi​要计算?嗯,没错就是各有NNN个,NNN是训练样本的数量。如果有10万样本,在优化第ttt棵树时,就需要计算出个10万个gig_igi​和hih_ihi​。感觉好像很麻烦是不是?但是你再想一想,这10万个gig_igi​之间是不是没有啥关系?是不是可以并行计算呢?聪明的你想必再一次感受到了,为什么XGBoost会辣么快!因为gig_igi​和hih_ihi​可以并行求出来。

而且,gig_igi​和hih_ihi​是不依赖于损失函数的形式的,只要这个损失函数二次可微就可以了。这有什么好处呢?好处就是XGBoost可以支持自定义损失函数,只需满足二次可微即可。强大了我的哥是不是?

L~(t)=∑i=1n[gift(xi)+12hift2(xi)]+Ω(ft)\tilde{L}^{(t)}=\sum_{i=1}^n\left[ g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i) \right]+\Omega(f_t)L~(t)=i=1∑n​[gi​ft​(xi​)+21​hi​ft2​(xi​)]+Ω(ft​)

把ft,Ω(ft)f_t, \Omega(f_t)ft​,Ω(ft​)写成树结构的形式,即把下式带入损失函数中

f(x)=wq(x),Ω(f)=γT+12λ∣∣w∣∣2f(x)=w_{q(x)},\quad \Omega(f)=\gamma T+\frac{1}{2}\lambda||w||^2f(x)=wq(x)​,Ω(f)=γT+21​λ∣∣w∣∣2

注意:这里出现了γ\gammaγ和λ\lambdaλ,这是XGBoost自己定义的,在使用XGBoost时,你可以设定它们的值,显然,γ\gammaγ越大,表示越希望获得结构简单的树,因为此时对较多叶子节点的树的惩罚越大。λ\lambdaλ越大也是越希望获得结构简单的树。为什么XGBoost要选择这样的正则化项?很简单,好使!效果好才是真的好。

L~(t)=∑i=1n[gift(xi)+12hift2(xi)]+Ω(ft)=∑i=1n[giwq(xi)+12hiwq(xi)2]+γT+λ12∑j=1Twj2\begin{aligned} \tilde{L}^{(t)}&=\sum_{i=1}^n\left[ g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i) \right]+\Omega(f_t)\\ &=\sum_{i=1}^n\left[ g_iw_{q(x_i)}+\frac{1}{2}h_iw_{q(x_i)}^2 \right]+\gamma T +\lambda\frac{1}{2}\sum_{j=1}^Tw_j^2\\ \end{aligned}L~(t)​=i=1∑n​[gi​ft​(xi​)+21​hi​ft2​(xi​)]+Ω(ft​)=i=1∑n​[gi​wq(xi​)​+21​hi​wq(xi​)2​]+γT+λ21​j=1∑T​wj2​​

定义每个叶节点jjj上的样本集合为(这里需要停一停,这里很重要,但是也不难理解,小学知识,认真体会下。IjI_jIj​代表什么?它代表一个集合,即被分到第jjj颗树的所有样本集合,集合中每个值代表一个训练样本的序号,整个集合就是被第ttt棵CART树分到了第jjj个叶子节点上的训练样本。)

Ij={i∣q(xi)=j}I_j=\{ i|q(x_i)=j \}Ij​={i∣q(xi​)=j}

需要解释下这个wq(x)w_{q(x)}wq(x)​的定义,首先,一棵树有TTT个叶子节点,这TTT个叶子节点的值组成了一个TTT维向量www,q(x)q(x)q(x)是一个映射,用来将样本映射成1到TTT的某个值,也就是把它分到某个叶子节点,q(x)q(x)q(x)其实就代表了CART树的结构。wq(x)w_{q(x)}wq(x)​自然就是这棵树对样本xxx的预测值了。

L~(t)=∑i=1n[giwq(xi)+12hiwq(xi)2]+γT+12λ∑j=1Twj2=∑j=1T[(∑i∈Ijgi)wj+12(∑i∈Ijhi+λ)wj2]+γT=∑j=1T[Gjwj+12(Hj+λ)wj2]+γT\begin{aligned} \tilde{L}^{(t)}&=\sum_{i=1}^n\left[ g_iw_{q(x_i)}+\frac{1}{2}h_iw_{q(x_i)}^2 \right]+\gamma T +\frac{1}{2}\lambda\sum_{j=1}^Tw_j^2\\ &=\sum_{j=1}^T\left[ \left(\sum_{i\in I_j}g_i\right)w_j+\frac{1}{2}\left(\sum_{i\in I_j}h_i+\lambda\right)w^2_j \right]+\gamma T\\ &=\sum_{j=1}^T\left[ G_jw_j+\frac{1}{2}\left(H_j+\lambda\right)w^2_j \right]+\gamma T\\ \end{aligned}L~(t)​=i=1∑n​[gi​wq(xi​)​+21​hi​wq(xi​)2​]+γT+21​λj=1∑T​wj2​=j=1∑T​​​i∈Ij​∑​gi​​wj​+21​​i∈Ij​∑​hi​+λ​wj2​​+γT=j=1∑T​[Gj​wj​+21​(Hj​+λ)wj2​]+γT​

这里是XGBoost最精髓的部分,它将基于样本的loss转化为了基于叶子节点的loss,即完成了参数的转变,这样才能将loss部分和正则部分都转为叶子节点TTT的目标方程。

如果确定了树的结构(即q(x)q(x)q(x)确定),上式中叶子节点权重wjw_jwj​有闭式解。

∂L~(t)∂wj=∂∑j=1T[Gjwj+12(Hj+λ)wj2]+γT∂wj=Gj+(Hj+λ)wj=0\begin{aligned} \frac{\partial \tilde{L}^{(t)}}{\partial w_j}&=\frac{\partial \sum_{j=1}^T\left[ G_jw_j+\frac{1}{2}\left(H_j+\lambda\right)w^2_j \right]+\gamma T}{\partial w_j}\\ &=G_j+(H_j+\lambda)w_j\\ &=0\\ \end{aligned}∂wj​∂L~(t)​​=∂wj​∂∑j=1T​[Gj​wj​+21​(Hj​+λ)wj2​]+γT​=Gj​+(Hj​+λ)wj​=0​
wj∗=−GjHj+λw^*_j=-\frac{G_j}{H_j+\lambda}wj∗​=−Hj​+λGj​​

然后将让损失函数最小的wj∗w^{*}_jwj∗​(即上式)带入损失函数,得到最小损失为:

L~∗=−12∑j=1TGj2Hj+λ+γT\tilde{L}^*=-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+\gamma TL~∗=−21​j=1∑T​Hj​+λGj2​​+γT

L~∗\tilde{L}^*L~∗代表了什么呢?它表示了这棵树的结构有多好,值越小,代表这样结构越好!也就是说,它是衡量第ttt棵CART树的结构好坏的标准。注意~注意~注意~,这个值仅仅是用来衡量结构的好坏的,与叶子节点的值可是无关的。为什么?请再仔细看一下L~∗\tilde{L}^*L~∗的推导过程。L~∗\tilde{L}^*L~∗只和GjG_jGj​、HjH_jHj​和TTT有关,而它们又只和树的结构q(x)q(x)q(x)有关,与叶子节点的值可是半毛关系没有。

上式能视作衡量函数来测量树结构qqq的质量,类似不纯度(基尼系数)的衡量指标,来衡量一棵树的优劣程度。下图展示了如何计算一棵树的分值:

这里,我们对wj∗w^{*}_jwj∗​给出一个直觉的解释,以便能获得感性的认识。我们假设分到jjj这个叶子节点上的样本只有一个。那么,wj∗w^{*}_jwj∗​就变成如下这个样子:

wj∗=(1hj+λ)⋅(−gj)w_j^*=\left(\frac{1}{h_j+\lambda}\right)\cdot(-g_j)wj∗​=(hj​+λ1​)⋅(−gj​)

这个式子告诉我们,wj∗w^{*}_jwj∗​的最佳值就是负的梯度乘以一个权重系数,该系数类似于随机梯度下降中的学习率。观察这个权重系数,我们发现,hjh_jhj​越大,这个系数越小,也就是学习率越小。hjh_jhj​越大代表什么意思呢?代表在该点附近梯度变化非常剧烈,可能只要一点点的改变,梯度就从10000变到了1,所以,此时,我们在使用反向梯度更新时步子就要小而又小,也就是权重系数要更小。

wj∗=−GjHj+λw^*_j=-\frac{G_j}{H_j+\lambda}wj∗​=−Hj​+λGj​​

并不是像GBDT那样为了特意去拟合−g/h-g/h−g/h,所以,XGBoost新的树的输入其实无所谓,但为了计算hih_ihi​和gig_igi​,则输入就成了

(yi,y^i(t−1))(y_i,\hat{y}_i^{(t-1)})(yi​,y^​i(t−1)​)

,而GBDT下一棵树的输入是(xi,−Gi)(x_i,-G_i)(xi​,−Gi​)。但是XGBoost已经把这种梯度带入到CART分裂目标和节点权重上去了,表现在其叶子节点的值是−Gj/(Hj+λ)-G_j/(H_j+\lambda)−Gj​/(Hj​+λ),而非对yiy_iyi​的拟合。

也就是说,XGBoost不刻意拟合任何数值,它在第ttt步只是寻找一种能使当前损失最小的树。因此它不像adaboost(拟合带权值样本集)和gbdt(拟合负梯度)一样以拟合为核心,而是以使损失函数最低为核心。它的方法就是通过分裂节点,使得新树的gain大于原来树的gain,从而降低损失函数,而不是数据拟合。

在目标函数是二分类log loss损失函数下,这里给出一阶导gig_igi​和二阶导hih_ihi​的推导:

l(yi,y^i(t−1))=−∑i=1N(yilogpi+(1−yi)log(1−pi))=−∑i=1N(yilog(11+exp(−y^i(t−1)))+(1−yi)log(exp(−y^i(t−1))1+exp(−y^i(t−1))))\begin{aligned} l(y_i,\hat{y}_i^{(t-1)})&=-\sum_{i=1}^N\left( y_i\text{log}p_i+(1-y_i)\text{log}(1-p_i) \right)\\ &=-\sum_{i=1}^N\left( y_i\text{log}\left( \frac{1}{1+\text{exp}(-\hat{y}_i^{(t-1)})} \right)+(1-y_i)\text{log}\left(\frac{\text{exp}(-\hat{y}_i^{(t-1)})}{1+\text{exp}(-\hat{y}_i^{(t-1)})}\right) \right)\\ \end{aligned}l(yi​,y^​i(t−1)​)​=−i=1∑N​(yi​logpi​+(1−yi​)log(1−pi​))=−i=1∑N​(yi​log(1+exp(−y^​i(t−1)​)1​)+(1−yi​)log(1+exp(−y^​i(t−1)​)exp(−y^​i(t−1)​)​))​

则gig_igi​为:

gi=∂l(yi,y^(t−1))∂y^(t−1)=−yi(1−11+exp(−y^i(t−1)))+(1−yi)(11+exp(−y^i(t−1)))=11+exp(−y^i(t−1))−yi=Pred−Label\begin{aligned} g_i&=\frac{\partial l(y_i, \hat{y}^{(t-1)})}{\partial \hat{y}^{(t-1)}}\\ &=-y_i\left( 1-\frac{1}{1+\text{exp}(-\hat{y}_i^{(t-1)})} \right)+ (1-y_i)\left( \frac{1}{1+\text{exp}(-\hat{y}_i^{(t-1)})} \right)\\ &=\frac{1}{1+\text{exp}(-\hat{y}_i^{(t-1)})}-y_i\\ &=\text{Pred}-\text{Label} \end{aligned}gi​​=∂y^​(t−1)∂l(yi​,y^​(t−1))​=−yi​(1−1+exp(−y^​i(t−1)​)1​)+(1−yi​)(1+exp(−y^​i(t−1)​)1​)=1+exp(−y^​i(t−1)​)1​−yi​=Pred−Label​

hih_ihi​为:

hi=∂2l(yi,y^(t−1))∂2y^(t−1)=exp(−y^i(t−1))(1+exp(−y^i(t−1)))2=Pred⋅(1−Pred)\begin{aligned} h_i&=\frac{\partial^2 l(y_i, \hat{y}^{(t-1)})}{\partial^2 \hat{y}^{(t-1)}}\\ &=\frac{\text{exp}(-\hat{y}_i^{(t-1)})}{\left( 1+\text{exp}(-\hat{y}_i^{(t-1)}) \right)^2}\\ &=\text{Pred}\cdot (1-\text{Pred})\\ \end{aligned}hi​​=∂2y^​(t−1)∂2l(yi​,y^​(t−1))​=(1+exp(−y^​i(t−1)​))2exp(−y^​i(t−1)​)​=Pred⋅(1−Pred)​
L~∗=−12∑j=1TGj2Hj+λ+γT\tilde{L}^*=-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+\gamma TL~∗=−21​j=1∑T​Hj​+λGj2​​+γT
Gj2Hj+λ\frac{G_j^2}{H_j+\lambda}Hj​+λGj2​​

一棵树在该衡量指标下分值越低,说明这个树的结构越好(表示的是损失)。训练数据可能有很多特征,构建一棵树可能有许多种不同的构建形式,我们不可能枚举所有可能的树结构qqq来一一计算它的分值。所以主要采用贪心算法来解决这个问题,贪心算法从一个单独树叶开始,迭代地增加分支,直到最后停止。(如何更快地生成树是关键)

Gainsplit=L~pre_split∗−L~aft_split∗=(−12∑j=1TGj2Hj+λ+γ)−(−12∑j=1TGj2Hj+λ−12∑j=1TGj2Hj+λ+2γ)=12[(∑i∈ILgi)2∑i∈ILhi+λ+(∑i∈IRgi)2∑i∈IRhi+λ−(∑i∈Igi)2∑i∈Ihi+λ]−γ\begin{aligned} &\text{Gain}_{\text{split}}\\ =&\tilde{L}^*_{pre\_split}-\tilde{L}^*_{aft\_split}\\ =&\left(-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+\gamma\right)-\left(-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+2\gamma\right)\\ =&\frac{1}{2}\left[ \frac{(\sum_{i\in I_L} g_i)^2}{\sum_{i\in I_L} h_i+\lambda} + \frac{(\sum_{i\in I_R} g_i)^2}{\sum_{i\in I_R} h_i+\lambda} - \frac{(\sum_{i\in I} g_i)^2}{\sum_{i\in I} h_i+\lambda} \right]-\gamma \end{aligned}===​Gainsplit​L~pre_split∗​−L~aft_split∗​(−21​j=1∑T​Hj​+λGj2​​+γ)−(−21​j=1∑T​Hj​+λGj2​​−21​j=1∑T​Hj​+λGj2​​+2γ)21​[∑i∈IL​​hi​+λ(∑i∈IL​​gi​)2​+∑i∈IR​​hi​+λ(∑i∈IR​​gi​)2​−∑i∈I​hi​+λ(∑i∈I​gi​)2​]−γ​

GainGainGain的值越大,分裂后损失函数减小越多。所以当对一个叶节点分割时,计算所有候选(feature, value)对应的GainGainGain,选取GainGainGain最大的进行分割。

这个GainGainGain实际上就是单节点的L~∗\tilde{L}^*L~∗减去切分后的两个节点的树L~∗\tilde{L}^*L~∗,GainGainGain如果是正的,并且值越大,表示切分后L~∗\tilde{L}^*L~∗越小于单节点的L~∗\tilde{L}^*L~∗,就越值得切分。同时,我们还可以观察到,GainGainGain的左半部分如果小于右侧的γ\gammaγ,则GainGainGain就是负的,表明切分后L~∗\tilde{L}^*L~∗反而变大了。γ\gammaγ在这里实际上是一个临界值,它的值越大,表示我们对切分后L~∗\tilde{L}^*L~∗下降幅度要求越严。这个值也是可以在xgboost中设定的。

注意:xgboost的切分操作和普通的决策树切分过程是不一样的。普通的决策树在切分的时候并不考虑树的复杂度,而依赖后续的剪枝操作来控制。xgboost在切分的时候就已经考虑了树的复杂度,就是那个γ\gammaγ参数。所以,它不需要进行单独的剪枝操作。

为了限制树的生长,我们可以加入阈值,当增益大于阈值时才让节点分裂,上式中的γ\gammaγ即阈值,它是正则项里叶子节点数TTT的系数,所以xgboost在优化目标函数的同时相当于做了预剪枝。另外,上式中还有一个系数λ\lambdaλ,是正则项里leaf score的L2模平方的系数,对leaf score做了平滑,也起到了防止过拟合的作用,这个是传统GBDT里不具备的特性。

最优的树结构找到后,确定最优的叶子节点就很容易了。我们成功地找出了第ttt棵树!

注意:xgboost的切分操作和普通的决策树切分过程是不一样的。普通的决策树在切分的时候并不考虑树的复杂度,而依赖后续的剪枝操作来控制。xgboost在切分的时候就已经考虑了树的复杂度,就是那个GainsplitGain_{split}Gainsplit​中的γ\gammaγ参数。所以,它不需要进行单独的剪枝操作。

遍历所有特征的所有可能的分割点,计算gaingaingain值,选取值最大的(feature, value)去分割

为了有效率的找到最佳分裂节点,算法必须先将该特征的所有取值进行排序,之后按顺序取分裂节点计算LsplitL_{s p l i t}Lsplit​。时间复杂度是O(Nu)O(N_u)O(Nu​),NuN_uNu​是这个特征不同取值的个数。

1/ϵ1 / \epsilon1/ϵ表示buckets数,在同等准确率的情况global比local需要更多的候选点。

Gain=max{Gain,G12H1+λ+G232H23+λ−G1232H123+λ−γ,G122H12+λ+G32H3+λ−G1232H123+λ−γ}\begin{aligned} \text{Gain}=\text{max}\{ &\text{Gain}, \\ &\frac{G_1^2}{H_1+\lambda}+\frac{G_{23}^2}{H_{23}+\lambda} - \frac{G_{123}^2}{H_{123}+\lambda}-\gamma,\\ &\frac{G_{12}^2}{H_{12}+\lambda}+\frac{G_3^2}{H_3+\lambda} - \frac{G_{123}^2}{H_{123}+\lambda}-\gamma \} \end{aligned}Gain=max{​Gain,H1​+λG12​​+H23​+λG232​​−H123​+λG1232​​−γ,H12​+λG122​​+H3​+λG32​​−H123​+λG1232​​−γ}​
{sk1,sk2,...,skl}\{s_{k1},s_{k2},...,s_{kl}\}{sk1​,sk2​,...,skl​}
Dk={(x1k,h1),(x2k,h2),...,(xnk,hn)}D_k=\{(x_{1k},h_1),(x_{2k},h_2),...,(x_{nk},h_n)\}Dk​={(x1k​,h1​),(x2k​,h2​),...,(xnk​,hn​)}

表示所有样本的第kkk个特征值及二阶导数。

则可以定义一个排序函数如下,该Rank函数的输入为某个特征值z,计算的是该特征所有可取值中小于z的特征值的总权重占总的所有可取值的总权重和的比例,输出为一个比例值,类似于概率密度函数f(x)f(x)f(x)的积分F(x)F(x)F(x),变化范围由0到1。

rk:R→[0,+∞) asrk(z)=1∑(x,h)∈Dkh∑(x,h)∈Dk,x<zh\begin{aligned} &r_k:\mathbb{R}\rightarrow [0,+\infty)\ \text{as}\\ &r_k(z)=\frac{1}{\sum_{(x,h)\in D_k}h}\sum_{(x,h)\in D_k, x<z}h \end{aligned}​rk​:R→[0,+∞) asrk​(z)=∑(x,h)∈Dk​​h1​(x,h)∈Dk​,x<z∑​h​

该函数表示特征值k小于z的实例比例。目标就是寻找候选分裂点集{sk1,sk2,...,skl}\{s_{k1}, s_{k2}, ..., s_{kl}\}{sk1​,sk2​,...,skl​}。希望得到的分位点满足如下条件:

∣rk(sk,j)−rk(sk,j+1)∣<ϵ, sk1=mini xik, skl=maxi xik|r_k(s_{k,j})-r_k(s_{k,j+1})|<\epsilon,\ s_{k1}=\mathop{\text{min}}_{i}\ x_{ik},\ s_{kl}=\mathop{\text{max}}_{i}\ x_{ik}∣rk​(sk,j​)−rk​(sk,j+1​)∣<ϵ, sk1​=mini​ xik​, skl​=maxi​ xik​

sk1s_{k1}sk1​是特征k的取值中最小的值xikx_{ik}xik​,skls_{kl}skl​是特征k的取值中最大的值xikx_{ik}xik​,这是分位数缩略图要求需要保留原序列中的最小值和最大值。这里ϵ\epsilonϵ是近似因子或者说是扫描步幅,按照步幅ϵ\epsilonϵ挑选出特征k的取值候选点,组成候选点集。这意味着大概有1/ϵ1/\epsilon1/ϵ个分位点。

这里每个数据点的权重hih_ihi​,从图上看可能更明显一些。为什么每个数据点都用二阶代数hih_ihi​作为权重进行加权分位呢?

L~(t)=∑i=1n[gift(xi)+12hift2(xi)]+Ω(ft)=∑i=1n12hi(ft(xi)−gi/hi)2+Ω(ft)+Constant\begin{aligned} \tilde{L}^{(t)}&=\sum_{i=1}^n\left[ g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i) \right]+\Omega(f_t)\\ &=\sum_{i=1}^n\frac{1}{2}h_i(f_t(x_i)-g_i/h_i)^2+\Omega(f_t)+\text{Constant} \end{aligned}L~(t)​=i=1∑n​[gi​ft​(xi​)+21​hi​ft2​(xi​)]+Ω(ft​)=i=1∑n​21​hi​(ft​(xi​)−gi​/hi​)2+Ω(ft​)+Constant​

上式就是一个加权平方误差,权重为hih_ihi​,label 为−gi/hi-g_i/h_i−gi​/hi​。可以看出hih_ihi​有对loss加权的作用,所以可以将特征k的取值权重看成对应的hih_ihi​。

Loss(y,y^)=(y−y^)2Loss(y, \hat{y})=(y-\hat{y})^2Loss(y,y^​)=(y−y^​)2

,则h=2,那么实际上是不带权。 如果损失函数是log loss,则h=pred⋅(1−pred)h=pred\cdot (1-pred)h=pred⋅(1−pred),这是个开口朝下的一元二次函数,所以最大值在0.5。当pred在0.5附近,这个值是非常不稳定的,很容易误判,h作为权重则因此变大,那么直方图划分,这部分就会被切分的更细:

XGBoost还特别设计了针对稀疏数据的算法,假设样本的第iii个特征缺失时,无法利用该特征对样本进行划分,系统将实例分到默认方向的叶子节点。每个分支都有两个默认方向,最佳的默认方向可以从训练数据中学习到。算法如下:

利用局部近似算法对分裂节点的贪心算法优化,取适当的ϵ\epsilonϵ时,可以保持算法的性能且提高算法的运算速度。

在损失函数中加入了L1/L2L_1/L_2L1​/L2​项,控制模型的复杂度,提高模型的鲁棒性。

灵活性更强:GBDT以CART作为基分类器,XGBoost不仅支持CART还支持线性分类器,(使用线性分类器的XGBoost相当于带L1L_1L1​和L2L_2L2​正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题))。此外,XGBoost工具支持自定义损失函数,只需函数支持一阶和二阶求导;

正则化:XGBoost在目标函数中加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、叶子节点权重的L2L_2L2​范式。正则项降低了模型的方差,使学习出来的模型更加简单,有助于防止过拟合;

传统GBDT以CART作为基分类器,XGBoost还支持线性分类器,这个时候XGBoost相当于带L1L_1L1​和L2L_2L2​正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)

GBDT使用了square loss的一阶求导,并且没有树本身因素的正则化部分。XGBoost使用了square loss的一阶和二阶求导,使用了树叶子节点(T)和叶子权重(∣∣w∣∣2||w||^2∣∣w∣∣2)作为正则化。这里是XGBoost最精髓的部分,它将基于样本的loss转化为了基于叶子节点的loss,即完成了参数的转变,这样才能 将loss部分和正则部分都转为叶子节点T的目标方程。

XGBoost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2L_2L2​模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是XGBoost优于传统GBDT的一个特性

boosting不是一种串行的结构吗?怎么并行的?注意XGBoost的并行不是tree粒度的并行,XGBoost也是一次迭代完才能进行下一次迭代的(第ttt次迭代的代价函数里包含了前面t−1t-1t−1次迭代的预测值)。XGBoost的并行是在特征粒度上的(树的粒度上是串行)。

陈天奇论文《XGBoost: A Scalable Tree Boosting System》
陈天奇演讲PPT《Introduction to Boosted Trees 》
GBM之GBRT总结(陈天奇论文演讲PPT翻译版)
XGBoost官网
牛顿法
GBDT算法原理与系统设计简介
xgboost的原理没你想像的那么难
gitlinux上的博客:XGBoost
xgboost导读和实战
通俗、有逻辑的写一篇说下Xgboost的原理
为啥Xgboost比GradientBoost好那么多?
XGBoost浅入浅出
XGboost: A Scalable Tree Boosting System论文及源码导读
XGBoost解读(2)--近似分割算法
机器学习算法中 GBDT 和 XGBOOST 的区别有哪些?
30分钟看懂xgboost的基本原理
xgboost 实战以及源代码分析
XGBoost缺失值引发的问题及其深度分析
灵魂拷问,你看过Xgboost原文吗?
GBDT、XGBoost、LightGBM 的使用及参数调优
详解《XGBoost: A Scalable Tree Boosting System》
XGBoost超详细推导
史上最详细的XGBoost实战
XGBoost Documentation
左手论文 右手代码 深入理解网红算法XGBoost
从Boosting到BDT再到GBDT
再从GBDT到XGBoost!
XGBoost从原理到调参
返回顶层目录
返回上层目录
XGBoost概述
模型
损失函数
正则项
牛顿法
损失函数的二阶泰勒展开
损失函数求导得最优值
优化算法
XGBoost的增益函数
树结点分裂方法(split finding)
暴力枚举(Basic Exact Greedy Algorithm)
近似算法(Approximate Algo for Split Finding)
加权分位点(Weighted Quantile Sketch)
结点分裂时多机并行
稀疏感知分割(缺失值处理)
XGBoost的系统设计
分块并行(Column Block for Parallel Learning)
缓存优化(Cache Aware Access)
核外块计算(Blocks for Out-of-core Computation)
XGBoost的其他特性
XGBoost总结
XGBoost优点
XGBoost缺点
XGBoost和GradientBoost的比较
Xgboost使用经验总结
xgboost-paper
xgboost-paper
predict-play-computer-game
Gi-Hi
newton-methed-to-newton-boosting
exact-greedy-algorithm-for-split-finding
global-local-variant
approximate-algorithm-for-split-finding
approximate-algorithm-examplee
approximate-algorithm-second-order-weights-select-split
approximate-algorithm-second-order-weights-select-split-2
missing-value
sparsity-aware-spilit-finding
column-block
column-block-2
user-define-loss-function-example