R 语言版 XGBoost 简介

简介

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

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

与 R 的glm函数(它遵循广义线性模型 (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.771304	train-logloss:0.657748	eval-auc:0.700000	eval-logloss:0.686140 
## [2]	train-auc:0.831304	train-logloss:0.630729	eval-auc:0.528571	eval-logloss:0.689780 
## [3]	train-auc:0.831304	train-logloss:0.607955	eval-auc:0.528571	eval-logloss:0.683832 
## [4]	train-auc:0.887826	train-logloss:0.592823	eval-auc:0.528571	eval-logloss:0.682381 
## [5]	train-auc:0.887826	train-logloss:0.574939	eval-auc:0.528571	eval-logloss:0.679424
## 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 在其传统的梯度提升框架之上支持许多附加功能,包括但不限于:

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

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

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

  • 拟合线性模型。

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

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

低级接口

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

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

两个接口之间的一些关键区别:

  • 与接受 R 对象(如matrixdata.frame)作为输入的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 包中的实用函数适用于两种模型类——例如:

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。