跳到内容

XGBoost for R 简介

引言

XGBoost 是一个优化过的分布式梯度提升库,设计目标是高度高效灵活可移植。它在 梯度提升 框架下实现了机器学习算法。XGBoost 提供了一种并行树提升(也称为 GBDT、GBM)方法,能够快速准确地解决许多数据科学问题。同一段代码可以在主要的分布式环境(Hadoop、SGE、MPI)上运行,并且能够解决超过十亿样本量的问题。

关于梯度提升概念的介绍,请参阅教程 提升树简介,在 XGBoost 的在线文档中。

有关 XGBoost 的特性和用法的更多详情,请参阅 在线文档,其中包含更多教程、示例和详细信息。

这篇简短的指南概述了 XGBoost 的 R 接口的基本用法,假设读者对使用梯度提升决策树进行统计建模背后的基本概念有一定的了解。

构建预测模型

在其核心,XGBoost 由一个 C++ 库组成,该库提供对不同编程语言的绑定,包括 R。XGBoost 的 R 包提供了一个惯用的接口,类似于其他使用 x/y 设计的统计建模包的接口,以及一个更低级的接口,它更直接地与底层核心库交互,并类似于其他语言绑定(如 Python)的接口,此外还提供了各种辅助工具来与模型对象交互,例如通过绘制特征重要性或将它们转换为其他格式。

主要的函数是 xgboost(x, y, ...),它在观测到的协变量/特征/预测变量“x”和响应变量“y”数据上调用 XGBoost 模型构建过程 - 这对于 glmnetncvreg 等包的用户来说应该很熟悉

library(xgboost)
data(ToothGrowth)

y <- ToothGrowth$supp # the response which we want to model/predict
x <- ToothGrowth[, c("len", "dose")] # the features from which we want to predct it
model <- xgboost(x, y, nthreads = 1, nrounds = 2)
model
## XGBoost model object
## Call:
##   xgboost(x = x, y = y, nrounds = 2, nthreads = 1)
## Objective: binary:logistic
## Number of iterations: 2
## Number of features: 2
## Classes: OJ, VC

在这种情况下,提供的“y”响应变量是一个“factor”类型,包含两个类别(“OJ”和“VC”)- 因此,XGBoost 基于特征“x”为其构建了一个二元分类模型,通过寻找最大似然估计值(类似于 R 的 glm 函数中的 family="binomial" 模型),通过从两棵决策树的总和(来自 nrounds=2)中获得的规则桶,然后我们可以从中预测概率、对数几率、最高似然类等

predict(model, x[1:6, ], type = "response") # probabilities for y's last level ("VC")
##         1         2         3         4         5         6 
## 0.6596265 0.5402158 0.6596265 0.6596265 0.6596265 0.4953500
predict(model, x[1:6, ], type = "raw")      # log-odds
##           1           2           3           4           5           6 
##  0.66163027  0.16121151  0.66163027  0.66163027  0.66163027 -0.01860031
predict(model, x[1:6, ], type = "class")    # class with highest probability
## [1] VC VC VC VC VC OJ
## Levels: OJ VC

与遵循 GLM 理论中“families”和“links”概念以拟合不同响应分布类型的 R 的 glm 函数相比,XGBoost 遵循更简单的“目标”概念,将两者混合为一个,并且就像 glm 一样,允许通过一个通用框架对各种不同的响应分布类型(例如, 离散选择、实数值、计数、截尾测量等)进行建模。

XGBoost 将根据响应变量的对象类别自动确定合适的目标(可以为分类传递因子,为回归传递数值向量,为生存分析传递 survival 包的 Surv 对象等 - 有关更多详情,请参阅 ?xgboost),但这可以通过 objective 参数根据所需的模型类型手动控制

data(mtcars)

y <- mtcars$mpg
x <- mtcars[, -1]
model_gaussian <- xgboost(x, y, nthreads = 1, nrounds = 2) # default is squared loss (Gaussian)
model_poisson <- xgboost(x, y, objective = "count:poisson", nthreads = 1, nrounds = 2)
model_abserr <- xgboost(x, y, objective = "reg:absoluteerror", nthreads = 1, nrounds = 2)

注意:目标必须与“y”响应变量的类型匹配 - 例如,离散选择的分类目标需要“factor”类型,而实数值数据的回归模型需要“numeric”类型。

模型参数

XGBoost 模型允许对其构建方式进行很大程度的控制。从本质上讲,梯度提升决策树集成能够捕获数据特征与响应变量之间非常复杂的模式,这也意味着如果控制不当,它们可能会出现过拟合。

为了获得最佳结果,需要为建模数据找到合适的参数。请注意,XGBoost 不会根据数据调整其默认超参数,不同的数据集将需要截然不同的超参数才能获得最佳预测性能。

例如,对于像“TootGrowth”这样的小数据集,它只有两个特征和 60 个观测值,XGBoost 的默认设置是过度的,会导致严重的过拟合 - 对于此类数据,可能希望使用更小的树(即, 更保守的决策规则,捕获更简单的模式)并且数量更少,例如。

可以通过向 xgboost() 传递附加参数来控制参数。有关可控制的参数的详细信息,请参阅 ?xgb.params

y <- ToothGrowth$supp
x <- ToothGrowth[, c("len", "dose")]
model_conservative <- xgboost(
    x, y, nthreads = 1,
    nrounds = 5,
    max_depth = 2,
    reg_lambda = 0.5,
    learning_rate = 0.15
)
pred_conservative <- predict(
    model_conservative,
    x
)
pred_conservative[1:6] # probabilities are all closer to 0.5 now
##         1         2         3         4         5         6 
## 0.6509258 0.4822042 0.6509258 0.6509258 0.6509258 0.4477925

XGBoost 还允许计算提升轮次中模型质量的评估指标,提供了各种各样的内置指标可供使用。可以自动留出一部分数据用作评估集,然后可以从中直观地监控训练进度和过拟合情况

xgboost(
    x, y, nthreads = 1,
    eval_set = 0.2,
    monitor_training = TRUE,
    verbosity = 1,
    eval_metric = c("auc", "logloss"),
    nrounds = 5,
    max_depth = 2,
    reg_lambda = 0.5,
    learning_rate = 0.15
)
## [1]  train-auc:0.757391  train-logloss:0.663578  eval-auc:0.757143   eval-logloss:0.676700 
## [2]  train-auc:0.757391  train-logloss:0.641348  eval-auc:0.757143   eval-logloss:0.657340 
## [3]  train-auc:0.757391  train-logloss:0.623876  eval-auc:0.757143   eval-logloss:0.641617 
## [4]  train-auc:0.757391  train-logloss:0.609999  eval-auc:0.757143   eval-logloss:0.628671 
## [5]  train-auc:0.757391  train-logloss:0.598893  eval-auc:0.757143   eval-logloss:0.617898
## XGBoost model object
## Call:
##   xgboost(x = x, y = y, nrounds = 5, max_depth = 2, learning_rate = 0.15, 
##     reg_lambda = 0.5, verbosity = 1, monitor_training = TRUE, 
##     eval_set = 0.2, eval_metric = c("auc", "logloss"), nthreads = 1)
## Objective: binary:logistic
## Number of iterations: 5
## Number of features: 2
## Classes: OJ, VC

检查模型对象

XGBoost 模型对象主要由指向 C++ 对象的指针组成,大部分信息保存在其中,并且通过包中的实用函数和方法进行接口交互,但也包含一些可以通过 attributes() 函数检索(和添加新属性)的 R 属性

attributes(model)
## $call
## xgboost(x = x, y = y, nrounds = 2, nthreads = 1)
## 
## $params
## $params$objective
## [1] "binary:logistic"
## 
## $params$nthread
## [1] 1
## 
## $params$verbosity
## [1] 0
## 
## $params$seed
## [1] 0
## 
## $params$validate_parameters
## [1] TRUE
## 
## 
## $names
## [1] "ptr"
## 
## $class
## [1] "xgboost"     "xgb.Booster"
## 
## $metadata
## $metadata$y_levels
## [1] "OJ" "VC"
## 
## $metadata$n_targets
## [1] 1

除了 R 属性(可以是任意 R 对象)之外,它可能还会保留一些标准化的 C 级属性,可以进行访问和修改(但这些属性只能是 JSON 格式)

## list()

(对于此模型,这些属性是空的)

… 但通常,当需要从模型对象中获取某些信息时,通常会希望通过内置的实用函数来完成。一些例子

##    Feature      Gain     Cover Frequency
##     <char>     <num>     <num>     <num>
## 1:     len 0.7444265 0.6830449 0.7333333
## 2:    dose 0.2555735 0.3169551 0.2666667
##      Tree  Node     ID Feature Split    Yes     No Missing        Gain
##     <int> <int> <char>  <char> <num> <char> <char>  <char>       <num>
##  1:     0     0    0-0     len  19.7    0-1    0-2     0-2  5.88235283
##  2:     0     1    0-1    dose   1.0    0-3    0-4     0-4  2.50230217
##  3:     0     2    0-2    dose   2.0    0-5    0-6     0-6  2.50230217
##  4:     0     3    0-3     len   8.2    0-7    0-8     0-8  5.02710962
##  5:     0     4    0-4    Leaf    NA   <NA>   <NA>    <NA>  0.36000001
##  6:     0     5    0-5    Leaf    NA   <NA>   <NA>    <NA> -0.36000001
##  7:     0     6    0-6     len  29.5    0-9   0-10    0-10  0.93020594
##  8:     0     7    0-7    Leaf    NA   <NA>   <NA>    <NA>  0.36000001
##  9:     0     8    0-8     len  10.0   0-11   0-12    0-12  0.60633492
## 10:     0     9    0-9     len  24.5   0-13   0-14    0-14  0.78028417
## 11:     0    10   0-10    Leaf    NA   <NA>   <NA>    <NA>  0.15000001
## 12:     0    11   0-11    Leaf    NA   <NA>   <NA>    <NA> -0.30000001
## 13:     0    12   0-12     len  13.6   0-15   0-16    0-16  2.92307687
## 14:     0    13   0-13    Leaf    NA   <NA>   <NA>    <NA>  0.06666667
## 15:     0    14   0-14    Leaf    NA   <NA>   <NA>    <NA> -0.17142859
## 16:     0    15   0-15    Leaf    NA   <NA>   <NA>    <NA>  0.20000002
## 17:     0    16   0-16    Leaf    NA   <NA>   <NA>    <NA> -0.30000001
## 18:     1     0    1-0     len  19.7    1-1    1-2     1-2  3.51329851
## 19:     1     1    1-1    dose   1.0    1-3    1-4     1-4  1.63309026
## 20:     1     2    1-2    dose   2.0    1-5    1-6     1-6  1.65485406
## 21:     1     3    1-3     len   8.2    1-7    1-8     1-8  3.56799269
## 22:     1     4    1-4    Leaf    NA   <NA>   <NA>    <NA>  0.28835031
## 23:     1     5    1-5    Leaf    NA   <NA>   <NA>    <NA> -0.28835031
## 24:     1     6    1-6     len  26.7    1-9   1-10    1-10  0.22153124
## 25:     1     7    1-7    Leaf    NA   <NA>   <NA>    <NA>  0.30163023
## 26:     1     8    1-8     len  11.2   1-11   1-12    1-12  0.25236940
## 27:     1     9    1-9     len  24.5   1-13   1-14    1-14  0.44972166
## 28:     1    10   1-10    Leaf    NA   <NA>   <NA>    <NA>  0.05241550
## 29:     1    11   1-11    Leaf    NA   <NA>   <NA>    <NA> -0.21860033
## 30:     1    12   1-12    Leaf    NA   <NA>   <NA>    <NA> -0.03878851
## 31:     1    13   1-13    Leaf    NA   <NA>   <NA>    <NA>  0.05559399
## 32:     1    14   1-14    Leaf    NA   <NA>   <NA>    <NA> -0.13160129
##      Tree  Node     ID Feature Split    Yes     No Missing        Gain
##         Cover
##         <num>
##  1: 15.000000
##  2:  7.500000
##  3:  7.500000
##  4:  4.750000
##  5:  2.750000
##  6:  2.750000
##  7:  4.750000
##  8:  1.500000
##  9:  3.250000
## 10:  3.750000
## 11:  1.000000
## 12:  1.000000
## 13:  2.250000
## 14:  1.250000
## 15:  2.500000
## 16:  1.250000
## 17:  1.000000
## 18: 14.695991
## 19:  7.308470
## 20:  7.387520
## 21:  4.645680
## 22:  2.662790
## 23:  2.662790
## 24:  4.724730
## 25:  1.452431
## 26:  3.193249
## 27:  2.985818
## 28:  1.738913
## 29:  1.472866
## 30:  1.720383
## 31:  1.248612
## 32:  1.737206
##         Cover

其他特性

XGBoost 在其传统的梯度提升框架之上支持许多附加特性,其中包括

  • 构建具有以下特征的决策树模型,例如每个特征的单调性约束或交互约束。
  • 计算个体预测中的特征贡献。
  • 使用自定义目标和自定义评估指标。
  • 拟合线性模型。
  • 在 GPU 上拟合模型和/或在无法完全放入内存的数据上(“外部存储器”)。

请参阅在线文档 - 特别是教程部分 - 以大致了解 XGBoost 提供的更多功能。

低级接口

除了 xgboost(x, y, ...) 函数之外,XGBoost 还提供了一个更低级的接口,通过 xgb.train() 函数来创建模型对象,该函数类似于 XGBoost 其他语言绑定中的 xgb.train 函数。

这个 xgb.train() 接口暴露了更多的功能(例如用户提供的回调函数或外部内存数据支持),并且与 xgboost() 函数接口相比,执行更少的数据验证和类型转换。

两种接口之间的一些主要区别

  • 与将 matrixdata.frame 等 R 对象作为输入的 xgboost() 不同,xgb.train() 函数使用 XGBoost 自己的数据容器,称为“DMatrix”,可以通过 xgb.DMatrix() 函数从 R 对象创建它。请注意,还有其他“DMatrix”构造函数,例如“xgb.QuantileDMatrix()”,对于某些用例可能更有益。
  • 一个“DMatrix”对象可能包含特征/协变量、响应变量、观测权重、基础边距等多种混合;并且与 xgboost() 不同,要求其输入已经编码为 XGBoost 在后台使用的表示形式 - 例如,虽然 xgboost() 可以接受一个 factor 对象作为“y”,但 xgb.DMatrix() 则要求二元响应变量以由零和一组成的向量形式传递。
  • 超参数在 xgboost() 中作为函数参数传递,而在 xgb.train() 中则作为命名列表传递。
  • xgb.train() 接口保留关于其输入的元数据较少 - 例如,在调用 predict 时,它不会将因子水平作为列名添加到估计概率中。

xgb.train() 的使用示例

data("agaricus.train")
dmatrix <- xgb.DMatrix(
    data = agaricus.train$data,  # a sparse CSC matrix ('dgCMatrix')
    label = agaricus.train$label, # zeros and ones
    nthread = 1
)
booster <- xgb.train(
    data = dmatrix,
    nrounds = 10,
    params = xgb.params(
        objective = "binary:logistic",
        nthread = 1,
        max_depth = 3
    )
)

data("agaricus.test")
dmatrix_test <- xgb.DMatrix(agaricus.test$data, nthread = 1)
pred_prob <- predict(booster, dmatrix_test)
pred_raw <- predict(booster, dmatrix_test, outputmargin = TRUE)

xgb.train() 生成的模型对象具有类 xgb.Booster,而由 xgboost() 生成的模型对象具有类 xgboost,它是 xgb.Booster 的子类。它们的 predict 方法也接受不同的参数 - 例如,predict.xgboost 有一个 type 参数,而 predict.xgb.Booster 通过二元参数控制这一点 - 但由于 xgboostxgb.Booster 的子类,如果需要,可以在 xgboost 对象上调用 xgb.Booster 的方法。

XGBoost R 包中的实用函数适用于这两种模型类 - 例如

##    Feature      Gain     Cover Frequency
##     <char>     <num>     <num>     <num>
## 1:     len 0.7444265 0.6830449 0.7333333
## 2:    dose 0.2555735 0.3169551 0.2666667
##                            Feature         Gain        Cover  Frequency
##                             <char>        <num>        <num>      <num>
##  1:                      odor=none 0.6083687503 0.3459792871 0.16949153
##  2:                stalk-root=club 0.0959684807 0.0695742744 0.03389831
##  3:                     odor=anise 0.0645662853 0.0777761744 0.10169492
##  4:                    odor=almond 0.0542574659 0.0865120182 0.10169492
##  5:               bruises?=bruises 0.0532525762 0.0535293301 0.06779661
##  6:              stalk-root=rooted 0.0471992509 0.0610565707 0.03389831
##  7:        spore-print-color=green 0.0326096192 0.1418126308 0.16949153
##  8:                      odor=foul 0.0153302980 0.0103517575 0.01694915
##  9: stalk-surface-below-ring=scaly 0.0126892940 0.0914230316 0.08474576
## 10:                gill-size=broad 0.0066973198 0.0345993858 0.10169492
## 11:                   odor=pungent 0.0027091458 0.0032193586 0.01694915
## 12:           population=clustered 0.0025750464 0.0015616374 0.03389831
## 13:  stalk-color-below-ring=yellow 0.0016913567 0.0173903519 0.01694915
## 14:        spore-print-color=white 0.0012798160 0.0008031107 0.01694915
## 15:             gill-spacing=close 0.0008052948 0.0044110809 0.03389831

虽然 xgboost() 旨在提供一个用户友好的接口,但在许多情况下,应优先选择 xgb.train() 接口 - 例如

  • 对于延迟敏感的应用程序(例如, 在实时服务模型时),xgb.train() 会有速度优势,因为它执行更少的验证、转换以及元数据的后处理。
  • 如果您正在开发依赖于 XGBoost 的 R 包,xgb.train() 将提供更稳定的接口(不易发生变化),并且时间和内存开销更低。
  • 如果您需要 xgboost() 接口未暴露的功能,- 例如,如果您的数据集无法完全放入计算机的内存中,如果数据通过 xgb.ExtMemDMatrix() 分批加载,仍然可以从中构建 DMatrix。