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”响应变量是具有两个类别(“OJ”和“VC”)的“factor”类型——因此,XGBoost 基于特征“x”为其构建了一个二分类模型,通过对两棵决策树(来自 nrounds=2)的总和获得的规则桶,找到最大似然估计(类似于 R 的 glm 函数中的 family="binomial" 模型),从中我们可以预测概率、log-odds、最高似然类别等。

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 遵循更简单的“objectives”概念,将两者结合为一个,并且就像 glm 一样,通过一个通用框架允许对非常不同类型的响应分布进行建模。

XGBoost 将根据响应变量的对象类别自动确定合适的 objective(分类可以传递 factors,回归可以传递 numeric 向量,生存分析可以传递 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)

注意:objective 必须与“y”响应变量的类型匹配——例如,离散选择的分类 objectives 需要“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.803322	train-logloss:0.647686	eval-auc:0.656250	eval-logloss:0.723275 
## [2]	train-auc:0.803322	train-logloss:0.615024	eval-auc:0.656250	eval-logloss:0.727078 
## [3]	train-auc:0.834790	train-logloss:0.598620	eval-auc:0.640625	eval-logloss:0.728885 
## [4]	train-auc:0.834790	train-logloss:0.584008	eval-auc:0.640625	eval-logloss:0.732156 
## [5]	train-auc:0.834790	train-logloss:0.559742	eval-auc:0.640625	eval-logloss:0.740185
## 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 格式)。

xgb.attributes(model)
## list()

(对于此模型它们是空的)

... 但通常,当需要从模型对象中获取某些内容时,通常会通过内置的实用函数来实现。一些示例:

xgb.importance(model)
##    Feature      Gain     Cover Frequency
##     <char>     <num>     <num>     <num>
## 1:     len 0.7444265 0.6830449 0.7333333
## 2:    dose 0.2555735 0.3169551 0.2666667
xgb.model.dt.tree(model)
##      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 还支持许多附加特性,包括但不限于:

  • 构建具有每特征单调性约束或交互约束等特征的决策树模型。

  • 计算个体预测中的特征贡献。

  • 使用自定义 objectives 和自定义评估指标。

  • 拟合线性模型。

  • 在 GPU 和/或不适合 RAM 的数据(“外部内存”)上拟合模型。

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

底层接口

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

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

两个接口的一些主要区别:

  • 与接受 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 时,它不会将 factor 的 levels 添加为估计概率的列名。

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 包中的实用函数适用于这两种模型类——例如:

xgb.importance(model)
##    Feature      Gain     Cover Frequency
##     <char>     <num>     <num>     <num>
## 1:     len 0.7444265 0.6830449 0.7333333
## 2:    dose 0.2555735 0.3169551 0.2666667
xgb.importance(booster)
##                            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() 接口未暴露的功能——例如,如果您的数据集不适合计算机的 RAM,仍然可以通过 xgb.ExtMemDMatrix() 以批处理方式加载数据,从而构建 DMatrix。