XGBoost R 教程
介绍
XGBoost 是 eXtreme Gradient Boosting 包的缩写。
本Vignette的目的是向您展示如何使用 XGBoost 构建模型并进行预测。
这是 @friedman2000additive 和 @friedman2001greedy 提出的梯度提升框架的一个高效且可扩展的实现。包含两个求解器:
线性 模型 ;
树学习 算法。
它支持多种目标函数,包括 回归、分类 和 排序。该包设计为可扩展的,因此用户也可以轻松定义自己的目标函数。
它有几个特点:
速度:它可以在 Windows 和 Linux 上使用 OpenMP 自动进行并行计算。通常情况下,它的速度比经典的
gbm
快 10 倍以上。输入类型:它接受几种输入数据类型:
密集 矩阵: R 的 密集 矩阵, 即
matrix
;稀疏 矩阵: R 的 稀疏 矩阵, 即
Matrix::dgCMatrix
;数据文件:本地数据文件 ;
xgb.DMatrix
: 它自己的类(推荐)。
稀疏性:它接受 稀疏 输入,适用于 树提升器 和 线性提升器,并且针对 稀疏 输入进行了优化;
自定义:它支持自定义的目标函数和评估函数。
安装
GitHub 版本
对于每周更新的版本(强烈推荐),从 GitHub 安装:
install.packages("drat", repos="https://cran.rstudio.com")
drat:::addRepo("dmlc")
install.packages("xgboost", repos="http://dmlc.ml/drat/", type = "source")
Windows 用户需要先安装 Rtools。
CRAN 版本
版本 0.4-2 已在 CRAN 上发布,您可以通过以下方式安装它:
install.packages("xgboost")
以前可用的版本可以从 CRAN 档案 获取
学习
在本教程中,我们将加载 XGBoost 包。
require(xgboost)
数据集展示
在这个例子中,我们的目标是预测一个蘑菇是否可以食用(就像在许多教程中一样,示例数据与你日常生活中使用的相同 :-)。
蘑菇数据引自UCI机器学习库。@Bache+Lichman:2013。
数据集加载
我们将加载包中嵌入的 agaricus
数据集,并将它们链接到变量。
数据集已经分为:
train
: 将用于构建模型;test
: 将用于评估我们模型的质量。
为什么要将数据集 分成 两部分?
在第一部分我们将构建我们的模型。在第二部分我们将希望测试它并评估其质量。如果不划分数据集,我们将在算法已经见过的数据上测试模型。
data(agaricus.train, package='xgboost')
data(agaricus.test, package='xgboost')
train <- agaricus.train
test <- agaricus.test
在现实世界中,如何划分
train
和test
数据取决于你。如何进行划分超出了本文的范围,然而caret
包可能会 有所帮助。
每个变量是一个包含两个元素的 列表
,即 标签
和 数据
:
str(train)
## List of 2
## $ data :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
## .. ..@ i : int [1:143286] 2 6 8 11 18 20 21 24 28 32 ...
## .. ..@ p : int [1:127] 0 369 372 3306 5845 6489 6513 8380 8384 10991 ...
## .. ..@ Dim : int [1:2] 6513 126
## .. ..@ Dimnames:List of 2
## .. .. ..$ : NULL
## .. .. ..$ : chr [1:126] "cap-shape=bell" "cap-shape=conical" "cap-shape=convex" "cap-shape=flat" ...
## .. ..@ x : num [1:143286] 1 1 1 1 1 1 1 1 1 1 ...
## .. ..@ factors : list()
## $ label: num [1:6513] 1 0 0 1 0 0 0 1 0 0 ...
label
是我们数据集的结果,这意味着它是我们将尝试预测的二元 分类。
让我们探索我们数据集的维度。
dim(train$data)
## [1] 6513 126
dim(test$data)
## [1] 1611 126
这个数据集非常小,以避免使 R 包过于庞大,然而 XGBoost 被设计为能够非常高效地管理大型数据集。
如下所示,data
存储在一个 dgCMatrix
中,这是一个 稀疏 矩阵,而 label
向量是一个 numeric
向量({0,1}
):
class(train$data)[1]
## [1] "dgCMatrix"
class(train$label)
## [1] "numeric"
使用 XGBoost 的基本训练
这一步是整个过程中对我们模型质量最关键的部分。
基础培训
我们正在使用 train
数据。如上所述,data
和 label
都存储在一个 list
中。
在 稀疏 矩阵中,包含 0
的单元格不会存储在内存中。因此,在一个主要由 0
组成的数据集中,内存大小会减少。这种情况非常常见。
我们将使用以下参数训练决策树模型:
objective = "binary:logistic"
: 我们将训练一个二分类模型;max.depth = 2
: 树不会很深,因为我们的情况非常简单;nthread = 2
: 我们将要使用的CPU线程数;nrounds = 2
: 数据将进行两次处理,第二次处理将进一步减少真实值与预测值之间的差异,从而增强模型。
bstSparse <- xgboost(data = train$data, label = train$label, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic")
## [0] train-error:0.046522
## [1] train-error:0.022263
你的特征与
标签
之间的关系越复杂,你需要的遍历次数就越多。
参数变化
密集矩阵
或者,你可以将你的数据集放入一个 密集 矩阵中,即一个基本的 R 矩阵。
bstDense <- xgboost(data = as.matrix(train$data), label = train$label, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic")
## [0] train-error:0.046522
## [1] train-error:0.022263
xgb.DMatrix
XGBoost 提供了一种将它们分组到 xgb.DMatrix
中的方法。你甚至可以添加其他元数据。这对于我们将要探索的最先进功能将非常有用。
dtrain <- xgb.DMatrix(data = train$data, label = train$label)
bstDMatrix <- xgboost(data = dtrain, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic")
## [0] train-error:0.046522
## [1] train-error:0.022263
详细选项
XGBoost 有几个功能可以帮助你查看内部的学习进度。其目的是帮助你设置最佳参数,这是模型质量的关键。
查看训练进度最简单的方法之一是设置 verbose
选项(更多高级技巧请参见下方)。
# verbose = 0, no message
bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic", verbose = 0)
# verbose = 1, print evaluation metric
bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic", verbose = 1)
## [0] train-error:0.046522
## [1] train-error:0.022263
# verbose = 2, also print information about tree
bst <- xgboost(data = dtrain, max.depth = 2, eta = 1, nthread = 2, nrounds = 2, objective = "binary:logistic", verbose = 2)
## [11:41:01] amalgamation/../src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 6 extra nodes, 0 pruned nodes, max_depth=2
## [0] train-error:0.046522
## [11:41:01] amalgamation/../src/tree/updater_prune.cc:74: tree pruning end, 1 roots, 4 extra nodes, 0 pruned nodes, max_depth=2
## [1] train-error:0.022263
使用 XGBoost 进行基本预测
执行预测
我们构建的模型的目的是对新数据进行分类。如前所述,我们将使用 test
数据集进行这一步骤。
pred <- predict(bst, test$data)
# size of the prediction vector
print(length(pred))
## [1] 1611
# limit display of predictions to the first 10
print(head(pred))
## [1] 0.28583017 0.92392391 0.28583017 0.28583017 0.05169873 0.92392391
这些数字看起来不像 二元分类 {0,1}
。在使用这些结果之前,我们需要进行一个简单的转换。
将回归转换为二元分类
XGBoost 唯一做的事情是 回归。XGBoost 使用 标签
向量来构建其 回归 模型。
我们如何使用 回归 模型来进行二元分类?
如果我们考虑回归应用于我们数据的含义,我们得到的数字是数据将被分类为 1
的概率。因此,我们将设定规则,如果特定数据的此概率 > 0.5
,则该观察结果被分类为 1
(否则为 0
)。
prediction <- as.numeric(pred > 0.5)
print(head(prediction))
## [1] 0 1 0 0 0 1
测量模型性能
为了衡量模型性能,我们将计算一个简单的指标,即 平均误差。
err <- mean(as.numeric(pred > 0.5) != test$label)
print(paste("test-error=", err))
## [1] "test-error= 0.0217256362507759"
注意,算法在模型构建过程中没有接触过
test
数据。
步骤解释:
as.numeric(pred > 0.5)
应用了我们的规则,即当概率(<=> 回归 <=> 预测)> 0.5
时,观察结果被分类为1
,否则为0
;probabilityVectorPreviouslyComputed != test$label
计算真实数据与计算概率之间的误差向量;mean(vectorOfErrors)
计算 平均误差 本身。
最重要的是要记住,要进行分类,只需对 标签
进行回归,然后应用一个阈值。
多类 分类的工作方式类似。
这个指标是 0.02 ,非常低:我们的 yummly 蘑菇模型工作得很好!
高级功能
以下大部分功能已经实现,以帮助您通过更好地理解其内容来改进您的模型。
数据集准备
对于以下高级功能,我们需要按照上述说明将数据放入 xgb.DMatrix
中。
dtrain <- xgb.DMatrix(data = train$data, label=train$label)
dtest <- xgb.DMatrix(data = test$data, label=test$label)
使用 xgb.train 衡量学习进度
xgboost
(简单) 和 xgb.train
(高级) 函数都可以训练模型。
xgb.train
的特殊功能之一是能够在每轮学习后跟踪学习进度。由于提升的工作方式,存在一个回合数过多导致过拟合的时刻。你可以将此功能视为交叉验证方法的表亲。以下技术将帮助你避免过拟合或优化学习时间,即尽早停止学习。
衡量模型学习进展的一种方法是向 XGBoost 提供一个已经分类的第二个数据集。因此,它可以在第一个数据集上学习,并在第二个数据集上测试其模型。在学习过程中,每轮之后都会测量一些指标。
在某种程度上,它类似于我们上面用平均误差所做的。主要区别在于,上面是在构建模型之后,而现在是在构建过程中测量误差。
为了这个示例的目的,我们使用 watchlist
参数。它是一个 xgb.DMatrix
的列表,每个都带有一个名称。
watchlist <- list(train=dtrain, test=dtest)
bst <- xgb.train(data=dtrain, max.depth=2, eta=1, nthread = 2, nrounds=2, watchlist=watchlist, objective = "binary:logistic")
## [0] train-error:0.046522 test-error:0.042831
## [1] train-error:0.022263 test-error:0.021726
XGBoost 在每一轮都计算了上述相同的平均误差指标(我们将 nrounds
设置为 2,这就是为什么我们有两行)。显然,train-error
数值与训练数据集(算法从中学习的那个)相关,而 test-error
数值与测试数据集相关。
训练误差和测试误差相关的指标非常相似,在某种程度上,这是有道理的:我们从训练数据集中学到的东西与测试数据集中的观察结果相匹配。
如果你用自己的数据集没有得到这样的结果,你应该考虑你是如何将数据集划分为训练集和测试集的。可能有些地方需要修正。再次,caret
包可能会 帮助。
为了更好地理解学习进展,您可能需要一些特定的指标,甚至可以使用多个评估指标。
bst <- xgb.train(data=dtrain, max.depth=2, eta=1, nthread = 2, nrounds=2, watchlist=watchlist, eval.metric = "error", eval.metric = "logloss", objective = "binary:logistic")
## [0] train-error:0.046522 train-logloss:0.233376 test-error:0.042831 test-logloss:0.226686
## [1] train-error:0.022263 train-logloss:0.136658 test-error:0.021726 test-logloss:0.137874
eval.metric
允许我们在每一轮监控两个新的指标,logloss
和error
。
线性提升
到目前为止,我们所进行的所有学习都是基于提升树的。XGBoost 实现了第二种算法,基于线性提升。与之前的命令唯一的区别是 booster = "gblinear"
参数(以及移除 eta
参数)。
bst <- xgb.train(data=dtrain, booster = "gblinear", nthread = 2, nrounds=2, watchlist=watchlist, eval.metric = "error", eval.metric = "logloss", objective = "binary:logistic")
## [0] train-error:0.024720 train-logloss:0.184616 test-error:0.022967 test-logloss:0.184234
## [1] train-error:0.004146 train-logloss:0.069885 test-error:0.003724 test-logloss:0.068081
在这种情况下,线性提升 比基于决策树的算法略微获得更好的性能指标。
在简单的情况下,这会发生是因为没有什么比线性算法更适合捕捉线性链接了。然而,决策树在捕捉预测变量和结果之间的非线性链接方面要好得多。因为没有银弹,我们建议你用你自己的数据集检查这两种算法,以便了解应该使用哪种算法。
操作 xgb.DMatrix
保存 / 加载
与保存模型类似,xgb.DMatrix
对象(它同时包含数据集和结果)也可以使用 xgb.DMatrix.save
函数保存。
xgb.DMatrix.save(dtrain, "dtrain.buffer")
## [1] TRUE
# to load it in, simply call xgb.DMatrix
dtrain2 <- xgb.DMatrix("dtrain.buffer")
## [11:41:01] 6513x126 matrix with 143286 entries loaded from dtrain.buffer
bst <- xgb.train(data=dtrain2, max.depth=2, eta=1, nthread = 2, nrounds=2, watchlist=watchlist, objective = "binary:logistic")
## [0] train-error:0.046522 test-error:0.042831
## [1] train-error:0.022263 test-error:0.021726
信息提取
可以使用 getinfo
函数从 xgb.DMatrix
中提取信息。接下来我们将提取 label
数据。
label = getinfo(dtest, "label")
pred <- predict(bst, dtest)
err <- as.numeric(sum(as.integer(pred > 0.5) != label))/length(label)
print(paste("test-error=", err))
## [1] "test-error= 0.0217256362507759"
从学习到的模型中查看特征重要性/影响
特征重要性与 R gbm 包的相对影响(rel.inf)相似。
importance_matrix <- xgb.importance(model = bst)
print(importance_matrix)
xgb.plot.importance(importance_matrix = importance_matrix)
从模型中查看树
你可以使用 xgb.dump
将你学习的树转储到文本文件中。
xgb.dump(bst, with_stats = TRUE)
## [1] "booster[0]"
## [2] "0:[f28<-1.00136e-05] yes=1,no=2,missing=1,gain=4000.53,cover=1628.25"
## [3] "1:[f55<-1.00136e-05] yes=3,no=4,missing=3,gain=1158.21,cover=924.5"
## [4] "3:leaf=1.71218,cover=812"
## [5] "4:leaf=-1.70044,cover=112.5"
## [6] "2:[f108<-1.00136e-05] yes=5,no=6,missing=5,gain=198.174,cover=703.75"
## [7] "5:leaf=-1.94071,cover=690.5"
## [8] "6:leaf=1.85965,cover=13.25"
## [9] "booster[1]"
## [10] "0:[f59<-1.00136e-05] yes=1,no=2,missing=1,gain=832.545,cover=788.852"
## [11] "1:[f28<-1.00136e-05] yes=3,no=4,missing=3,gain=569.725,cover=768.39"
## [12] "3:leaf=0.784718,cover=458.937"
## [13] "4:leaf=-0.96853,cover=309.453"
## [14] "2:leaf=-6.23624,cover=20.4624"
你可以使用 ```xgb.plot.tree`` 绘制模型中的树。
xgb.plot.tree(model = bst)
如果你为
fname
参数提供一个路径,你可以将树保存到你的硬盘上。
保存和加载模型
也许你的数据集很大,训练模型需要花费很多时间?也许你不喜欢一遍又一遍地重复同样的任务?在这些非常罕见的情况下,你会想要保存你的模型并在需要时加载它。
对你有帮助的是,XGBoost 实现了这些功能。
# save model to binary local file
xgb.save(bst, "xgboost.model")
## [1] TRUE
xgb.save
函数在一切顺利时应返回 TRUE,否则会崩溃。
一个有趣的测试,看看我们保存的模型与原始模型有多么相同,就是比较两者的预测结果。
# load binary model to R
bst2 <- xgb.load("xgboost.model")
pred2 <- predict(bst2, test$data)
# And now the test
print(paste("sum(abs(pred2-pred))=", sum(abs(pred2-pred))))
## [1] "sum(abs(pred2-pred))= 0"
结果是
0
?我们很好!
在某些非常特定的情况下,例如当你想从 caret
包中控制 XGBoost 时,你可能希望将模型保存为 R 二进制向量。请参见下文了解如何操作。
# save model to R's raw vector
rawVec <- xgb.save.raw(bst)
# print class
print(class(rawVec))
## [1] "raw"
# load binary model to R
bst3 <- xgb.load(rawVec)
pred3 <- predict(bst3, test$data)
# pred3 should be identical to pred
print(paste("sum(abs(pred3-pred))=", sum(abs(pred3-pred))))
## [1] "sum(abs(pred3-pred))= 0"
又是
0
?看起来XGBoost
工作得相当不错!