当前位置: 首页 > news >正文

xgboost算法_【模型篇】XGBoost模型

d72b40e38d410c53f4e0402dd652e226.png

XGBoost全称 “Extreme Gradient Boosting“,陈天奇大佬提出来的梯度提升模型。

Part A: 目标函数推导

目标函数的基本形式

模型对于某个样本的预测值为:

是基学习器,最终模型是多个基学习器

最初的目标函数可以写成

:是前t-1个集成学习器对样本的预测值

:是当前学习器对样本的预测值

:是第t个学习器的正则项

对目标函数进行泰勒二阶展开

:为
函数对
的一阶导数

:为
函数对
的二阶导数

正则项

接下来,将正则项具体化为如下的式子

表示叶子节点的个数,模型中将叶子节点的个数作为L1正则项,将叶子节点的权重值作为L2正则项。

这个叶子节点的权重,有的资料上成为叶子节点上输出的score,实际上就是预测值。

化简

并将原本单独的样本按照最终所在的叶子节点进行归类,令

表示样本
被划分到编号为j的叶子节点上

最终形式

对待求变量求导

求偏导并令其等于0,我们就能得到最终的
的解析解如下:

目标函数的值

代入原来的目标函数,我们就能得到原来目标函数的最终值。

这个式子表示在给定的决策树q下的目标函数的值,一定要注意,括号里加个q表示这是确定的一个树。

Part B: 分裂点的选择

分裂依据

由面我们推倒出Xgboost 目标函数(损失函数)的表达形式为

其实际意义表示按照特定分裂点(即按照某种树的结构)分裂后产生的损失值

其中

表示被分到编号为j的个叶子节点的样本

这样,我们可以得到样本群

依据某个特征值分裂后的损失函数的减小值
,将其作为分裂时的依据。

其中:

表示分裂后形成的两拨样本

有了节点分裂的依据,那么我们在基学习器中就可以生成树的结构,也就如何选择分裂点。

论文中共介绍了三种分裂点选择方法,

  • 第一种“Exact Greedy Algorithm for Split Finding”,是一种利用穷举法选择最佳的分裂节点
  • 第二种“Approximate Algorithm for Split Finding”,通过加权分位数方法近似选择最佳的分裂节点
  • 第三种“Sparsity-aware Split Finding”,针对稀疏特征的分裂点选择法

(一)Exact Greedy Algorithm for Split Finding 精确贪心算法

82f5e2194684323325ee3aa86b2823f6.png

算法如上图所示,核心思想如下

  • 两个for循环,第一个for遍历所有特征,第二个for找出最佳的特征值作为分裂点
  • 选分裂点的依据score为分裂前后损失函数的减少量
  • 第二个for循环中,先对数据按照特征值进行排序,这样做的目的为了后面一次遍历就能求出所有分裂点的score值。
    只需要在当前的基础上进行加减,不需要再扫描所有数据
  • 贪心算法体现在,当前分裂点的选择只考虑能使得当前损失函数减少量最大

(二)Approximate Algorithm for Split Finding 近似分割算法

精确贪心算法虽然很强大,但是当数据无法完全加载到内存中或者在分布式的条件下,这种基于穷举的分裂点寻找方法效率就会非常低下。于是作者提出了一种近似分割的算法,这种算法首先通过加权分位数的算法选出了一些可能的分裂点,然后再遍历这些较少的分裂点来找到最佳分裂点。

加权分位数算法

近似分割算法的核心是加权分位数算法,首先介绍分位数点。比如有下面10个样本数据,已经排好序了

分位数点

value: 1 1 2 3 4 5 5 6 7 8
rank :  1 2 3 4 5 6 7 8 9 10

那么0.3分数点的值是多少呢,对应的rank为10*0.3 =3,对应的值为2。这就是分位数点的基本思想,我们只要选出一些合适的分位数点然后遍历然后取遍历他们,就能达到和穷举法相同的精确程度。

加权分位数

因为我们要均分的是loss,而不是样本的数量,而每个样本对loss的贡献可能是不一样的,按样本均分会导致loss分布不均匀,取到的分位点会有偏差。所以要在每个样本前面加个权重。而目标函数又可以化简为如下的形式,所谓我们可以将二阶偏导

作为权重。

样本的权重定义好之后,我们就能定义rank函数

表示第k个特征中特征值小于z的样本所占的加权比例。

举个例子来理解加权分位点:

8a184e19367ba9114ade2a84a5cf6177.png

99对应的分位点就是(0.1 * 6+0.4+0.2)/(0.1 * 6+0.4+0.2+0.6) = 2/3

这样我们就能选出一些候选分割点

,并让他们满足下面的条件:

这样,我们就选出了大约

候选分割点,对这些分割点进行枚举遍历,选出最佳的分割点即可。

需要注意是:引入的分割不一定会使得情况变好,因为在引入分割的同时也引入新叶子的惩罚项。所以通常需要设定一个阈值,如果引入的分割带来的增益小于一个阀值的时候,我们可以剪掉这个分割。此外在XGBoost的具体实践中,通常会设置树的深度来控制树的复杂度,避免单个树过于复杂带来的过拟合问题。

关于上述两个算法的小结

QA:为什么近似分割算法比精确贪心算法要快?

首先我们得捋一下这两个寻找最佳分裂点的时候都有哪些公共的时间开销

  • 预排序:每个特征都是按照排序好的顺序存储的,这一部分在存储的时候就已经完成了
  • 计算所有样本的一阶导G和二阶导L,这个过程只需要进行一次
  • 对样本的G和L累加求和,这个过程也只需要进行一次,为了后续做差加速
    • 精确贪心算法中是将所有样本G、L累加
    • 近似分割算法中是按桶累加
  • 算法中的两个for循环,第一个循环是遍历所有特征,这一步两个算法相同

不同的开销在于,两个算法中第二个for循环中:

  • 精确贪心算法遍历的所有样本 O(#feature x # samples)
  • 近似贪心算法遍历了所有桶 : O(#feature x # bins )

因为桶的数目远小于样本数,所以得以加速

(三)Sparsity-aware Split Finding 稀疏特征处理

fc5c39eadfedea3e8e626e7491cd005b.png

Xgboost 在处理带缺失值的特征时,先对非缺失的样本进行排序,对该特征缺失的样本先不处理,然后在遍历每个分裂点时,将这些缺失样本分别划入左子树和右子树来计算损失然后求最优。

如果训练样本中没有缺失值,而预测过程中出现了缺失值,那么样本会被默认分到右子树。

Part C: 分块并行与缓存优化

XGBoost的并行不是树粒度的而是特征粒度的,随机森林就是树粒度的并行。

寻找分裂点的时候,算法中先是遍历所有特征再遍历每个特征下的所有值。

遍历特征下所有值时要求值是排序好的,这样就可以使用差加速。

如果不排序,那么计算分类时候的损失函数减少量就没法达到O(1)的复杂度,因为二叉树的分裂是> x,分到a子树这样的形式。

f1982b5a80b5c14e5fd4b692b230f084.png

在建树的过程中,最耗时是找最优的切分点,而这个过程中,最耗时的部分是将数据排序。为了减少排序的时间,提出Block结构存储数据。

  • Block中的数据以稀疏格式CSC进行存储
  • Block中的特征进行排序(不对缺失值排序,**排序只有一次**)
  • Block 中特征还需存储指向样本的索引,这样才能根据特征的值来取梯度。
  • 一个Block中存储一个或多个特征的值

个人理解是,这个block是原样本的一种映射,在这个block里,"样本"是按照列存储的,其实他存储的是列,而不是样本。因为样本是按照行来组织的,block中存储的是原样本的各个排序后的列。

所以就要有列中的每个元素与原样本之间的映射关系,因为在分裂节点的选择时,不仅要遍历某个特征(即列)中的所有元素,还要用到原样本的梯度(一阶导和二阶导), 所以就要通过列中的元素找到原样本。

按照block存储的好处就是,不同列之间可以并行查找,并且因为预排序了所以使得分裂节点查找时更快。坏处是空间大了一倍

缓存优化

在分块并行中,block存储了排序的列,并建立和原来样本的一一映射,这样可以通过索来找到原始样本获得梯度,但是原始样本是存放是按照列值的原始顺序的(相邻内存的样本他们对应的列值可能不是连续的,而我们现在根据排序后的列值来找原样本,那么肯定会出现在内存上的跳跃式查找,就非连续内存访问,可能导致CPU cache命中率降低。

CPU cache命中率低对于加权分位数选择分裂点的方法没太大影响,因为其选择分裂点的时候本来就是跳跃着选的,但是对于精确贪心算法的效率影响就非常大了,因为其要遍历所有样本。

下图中,calculate 上下两个部分表示连续列值上计算G和H但是其对应的样本不连续。

红色字说明了连续列值对应的样本不连续性。

dc21ff7744dd4552e29aea8fb60342d0.png

原论文中说

A naive implementation of split enumeration introduces immediate read/write dependency between the accumulation and the non-continuous memory fetch operation

通过降低读写的依赖性来解决cache miss的问题

1.对于精确贪心算法,对每个线程分配一个连续的缓存空间,预取接下来要读取的数据,这样就降低了直接从内存读取并且cache miss消耗的时间。

2. 对于近似分割算法,选取适当的block大小即可(2^16 * each_sample_size)

###### 参考资料

『我爱机器学习』集成学习(三)XGBoost - 细语呢喃

https://arxiv.org/pdf/1603.02754.pdf

相关文章:

  • sqlserver2000内存突破4g_荣耀Play4T系列发布:麒麟810加持! 4G时代的终结者
  • 我的垃圾培训造就众多高中学历者高薪就业
  • hdmi接口有什么用_当贝投影仪HDMI(ARC)接口是什么意思?
  • English中一些常用近意词的区别
  • qq修改实名认证已达上限_王者荣耀实名认证修改方法
  • js怎么写返回的值赋给某个hidden的值_如何写一个Android inline hook框架
  • consider的用法
  • axios下载大文件_python 小文件下载、大文件下载、异步批量下载 教程
  • kafka maven没有下载_kafka是什么?kafka仅仅是属于消息 中间件吗?
  • 玩转“网上邻居”之故障分析
  • kafka原理_Kafka 原理简介
  • 共享上网完全攻略
  • python3 循环写入一对多键值对_深入理解 EF Core:EF Core 写入数据时发生了什么?
  • cousin的意思
  • linux 信号_Linux 可重入、异步信号安全和线程安全
  • 2018一半小结一波
  • avalon2.2的VM生成过程
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • IOS评论框不贴底(ios12新bug)
  • js算法-归并排序(merge_sort)
  • Koa2 之文件上传下载
  • node.js
  • SpiderData 2019年2月23日 DApp数据排行榜
  • vue-cli在webpack的配置文件探究
  • 构建二叉树进行数值数组的去重及优化
  • 记录:CentOS7.2配置LNMP环境记录
  • 技术胖1-4季视频复习— (看视频笔记)
  • 七牛云假注销小指南
  • 手写双向链表LinkedList的几个常用功能
  • 微信支付JSAPI,实测!终极方案
  • 智能网联汽车信息安全
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​ArcGIS Pro 如何批量删除字段
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (06)Hive——正则表达式
  • (8)STL算法之替换
  • (C#)一个最简单的链表类
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (剑指Offer)面试题34:丑数
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (九)信息融合方式简介
  • (四)鸿鹄云架构一服务注册中心
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转载)(官方)UE4--图像编程----着色器开发
  • *1 计算机基础和操作系统基础及几大协议
  • .NET : 在VS2008中计算代码度量值
  • .Net IOC框架入门之一 Unity
  • .net mvc 获取url中controller和action
  • .net wcf memory gates checking failed