如何为 xgboost 实施增量训练?

Posted

技术标签:

【中文标题】如何为 xgboost 实施增量训练?【英文标题】:How can I implement incremental training for xgboost? 【发布时间】:2016-10-30 23:54:18 【问题描述】:

问题是由于火车数据大小,我的火车数据无法放入 RAM。所以我需要一种方法,首先在整个火车数据集上构建一棵树,计算残差构建另一棵树等等(就像梯度提升树一样)。显然,如果我在某个循环中调用 model = xgb.train(param, batch_dtrain, 2) - 这将无济于事,因为在这种情况下,它只会为每个批次重建整个模型。

【问题讨论】:

来自xgboost repo 的示例:github.com/dmlc/xgboost/blob/master/tests/python/… 【参考方案1】:

在第一批训练后尝试保存您的模型。然后,在连续运行时,为 xgb.train 方法提供已保存模型的文件路径。

这是我进行的一个小实验,以说服自己它有效:

首先,将波士顿数据集拆分为训练集和测试集。 然后将训练集分成两半。 用前半部分拟合一个模型,得到一个作为基准的分数。 然后用后半部分拟合两个模型;一个模型将具有附加参数 xgb_model。如果传入额外的参数没有影响,那么我们希望他们的分数是相似的.. 但是,幸运的是,新模型的性能似乎比第一个要好得多。

import xgboost as xgb
from sklearn.cross_validation import train_test_split as ttsplit
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error as mse

X = load_boston()['data']
y = load_boston()['target']

# split data into training and testing sets
# then split training set in half
X_train, X_test, y_train, y_test = ttsplit(X, y, test_size=0.1, random_state=0)
X_train_1, X_train_2, y_train_1, y_train_2 = ttsplit(X_train, 
                                                     y_train, 
                                                     test_size=0.5,
                                                     random_state=0)

xg_train_1 = xgb.DMatrix(X_train_1, label=y_train_1)
xg_train_2 = xgb.DMatrix(X_train_2, label=y_train_2)
xg_test = xgb.DMatrix(X_test, label=y_test)

params = 'objective': 'reg:linear', 'verbose': False
model_1 = xgb.train(params, xg_train_1, 30)
model_1.save_model('model_1.model')

# ================= train two versions of the model =====================#
model_2_v1 = xgb.train(params, xg_train_2, 30)
model_2_v2 = xgb.train(params, xg_train_2, 30, xgb_model='model_1.model')

print(mse(model_1.predict(xg_test), y_test))     # benchmark
print(mse(model_2_v1.predict(xg_test), y_test))  # "before"
print(mse(model_2_v2.predict(xg_test), y_test))  # "after"

# 23.0475232194
# 39.6776876084
# 27.2053239482

参考:https://github.com/dmlc/xgboost/blob/master/python-package/xgboost/training.py

【讨论】:

我知道 model_2_v2 的性能比同时使用两个数据集的模型差。但是 model_2_v2 比 model_1 差,这很奇怪,因为我们提供了 model_1 没有看到的新数据集,但最后 model_2_v2 表现更差......似乎提升树不是执行增量学习的最佳方式。 @pikachau 您是否尝试过使用 model_1 而不是“experiment.model”? 这可能是因为数据集非常小(样本大小 = 150)。对于更大的数据集,我认为 model_2_v2 应该优于 model_1。哦,experiment.model == model_1;我应该说得更清楚! model_2_v_2 的结果是否应该与在整个训练集(train_1 和 train_2)上训练的模型相同? XGBoost 的主要维护者在这里被引用说这不是正确的用法,不会导致预期的行为。 XGBoost 似乎无法进行迭代训练。 github.com/dmlc/xgboost/issues/3055#issuecomment-359648107。另见datascience.stackexchange.com/questions/47510/… 另见datascience.stackexchange.com/questions/47510/… ***.com/questions/48366379/…【参考方案2】:

现在(0.6 版?)有一个 process_update 参数可能会有所帮助。这是一个实验:

import pandas as pd
import xgboost as xgb
from sklearn.model_selection import ShuffleSplit
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error as mse

boston = load_boston()
features = boston.feature_names
X = boston.data
y = boston.target

X=pd.DataFrame(X,columns=features)
y = pd.Series(y,index=X.index)

# split data into training and testing sets
rs = ShuffleSplit(test_size=0.3, n_splits=1, random_state=0)
for train_idx,test_idx in rs.split(X):  # this looks silly
    pass

train_split = round(len(train_idx) / 2)
train1_idx = train_idx[:train_split]
train2_idx = train_idx[train_split:]
X_train = X.loc[train_idx]
X_train_1 = X.loc[train1_idx]
X_train_2 = X.loc[train2_idx]
X_test = X.loc[test_idx]
y_train = y.loc[train_idx]
y_train_1 = y.loc[train1_idx]
y_train_2 = y.loc[train2_idx]
y_test = y.loc[test_idx]

xg_train_0 = xgb.DMatrix(X_train, label=y_train)
xg_train_1 = xgb.DMatrix(X_train_1, label=y_train_1)
xg_train_2 = xgb.DMatrix(X_train_2, label=y_train_2)
xg_test = xgb.DMatrix(X_test, label=y_test)

params = 'objective': 'reg:linear', 'verbose': False
model_0 = xgb.train(params, xg_train_0, 30)
model_1 = xgb.train(params, xg_train_1, 30)
model_1.save_model('model_1.model')
model_2_v1 = xgb.train(params, xg_train_2, 30)
model_2_v2 = xgb.train(params, xg_train_2, 30, xgb_model=model_1)

params.update('process_type': 'update',
               'updater'     : 'refresh',
               'refresh_leaf': True)
model_2_v2_update = xgb.train(params, xg_train_2, 30, xgb_model=model_1)

print('full train\t',mse(model_0.predict(xg_test), y_test)) # benchmark
print('model 1 \t',mse(model_1.predict(xg_test), y_test))  
print('model 2 \t',mse(model_2_v1.predict(xg_test), y_test))  # "before"
print('model 1+2\t',mse(model_2_v2.predict(xg_test), y_test))  # "after"
print('model 1+update2\t',mse(model_2_v2_update.predict(xg_test), y_test))  # "after"

输出:

full train   17.8364309709
model 1      24.2542132108
model 2      25.6967017352
model 1+2    22.8846455135
model 1+update2  14.2816257268

【讨论】:

哪个是最终模型或我应该使用哪个? 您想要 MSE 最低的模型。但请注意 1+update2 如何低于完整的火车!我不清楚为什么会这样,所以我会怀疑这个结果并运行一个包含更多折叠的简历。 这似乎不适用于'objective': 'binary:logistic' 如果我用两次迭代训练,我得到的 AUC 为 0.66 和 0.68 的连续迭代。然后,当使用完全相同的数据训练下一个小批量时,我得到完全相同的 AUC。我希望,在继续使用现有模型时,AUC 会进一步改进或者可能保持不变。从获得相同的 AUC 后,我得出结论,它不会继续学习,而是重新开始。 'process_type': 'update' - 在 xgboost 1.9 中出现错误【参考方案3】:

我创建了a gist of jupyter notebook 来证明可以增量训练 xgboost 模型。我使用波士顿数据集来训练模型。我做了 3 个实验——一次学习,迭代一次学习,迭代增量学习。在增量训练中,我将波士顿数据以 50 大小的批次传递给模型。

要点在于,您必须多次迭代数据才能使模型收敛到一次性(所有数据)学习所达到的准确度。

这里是用xgboost做迭代增量学习的对应代码。

batch_size = 50
iterations = 25
model = None
for i in range(iterations):
    for start in range(0, len(x_tr), batch_size):
        model = xgb.train(
            'learning_rate': 0.007,
            'update':'refresh',
            'process_type': 'update',
            'refresh_leaf': True,
            #'reg_lambda': 3,  # L2
            'reg_alpha': 3,  # L1
            'silent': False,
        , dtrain=xgb.DMatrix(x_tr[start:start+batch_size], y_tr[start:start+batch_size]), xgb_model=model)

        y_pr = model.predict(xgb.DMatrix(x_te))
        #print('    MSE itr@: '.format(int(start/batch_size), sklearn.metrics.mean_squared_error(y_te, y_pr)))
    print('MSE itr@: '.format(i, sklearn.metrics.mean_squared_error(y_te, y_pr)))

y_pr = model.predict(xgb.DMatrix(x_te))
print('MSE at the end: '.format(sklearn.metrics.mean_squared_error(y_te, y_pr)))

XGBoost 版本:0.6

【讨论】:

感谢您的笔记本 - 我想知道仅在模型性能高于内部 for 循环中的阈值时才增加是否有意义?所以我们不会增加每一步,而是模型做得好的一些步骤?另外你的超参数调整策略是什么?【参考方案4】:

看起来您除了再次致电您的xgb.train(....) 之外不需要任何其他东西,但提供上一批的模型结果:

# python
params =  # your params here
ith_batch = 0
n_batches = 100
model = None
while ith_batch < n_batches:
    d_train = getBatchData(ith_batch)
    model = xgb.train(params, d_train, xgb_model=model)
    ith_batch += 1

这是基于https://xgboost.readthedocs.io/en/latest/python/python_api.html

【讨论】:

【参考方案5】:

如果您的问题与数据集大小有关,并且您并不真正需要增量学习(例如,您不是在处理流式应用程序),那么您应该查看 Spark 或 Flink。

这两个框架可以利用磁盘内存在具有小 RAM 的非常大的数据集上进行训练。两个框架都在内部处理内存问题。虽然 Flink 先解决了这个问题,但 Spark 在最近的版本中已经赶上了。

看看:

“XGBoost4J:Spark、Flink 和 Dataflow 中的便携式分布式 XGBoost”:http://dmlc.ml/2016/03/14/xgboost4j-portable-distributed-xgboost-in-spark-flink-and-dataflow.html Spark 集成:http://dmlc.ml/2016/10/26/a-full-integration-of-xgboost-and-spark.html

【讨论】:

由于某种原因(至少对我而言),您的两个链接都重定向到“softcloudtech.com/cloud-computing”。我认为有正确的xgboost.ai/2016/03/14/… 和xgboost.ai/2016/10/26/…。还要感谢我需要训练 CNN 输出的大量特征的建议,所以这会有所帮助!【参考方案6】:

对于 paulperry 的代码,如果将一行从“train_split = round(len(train_idx) / 2)”更改为“train_split = len(train_idx) - 50”。模型 1+update2 将从 14.2816257268 更改为 45.60806270012028。并且很多“leaf=0”导致转储文件。

当更新样本集相对较小时,更新模型不好。 对于 binary:logistic,当更新样本集只有一个类时,更新的模型是不可用的。

【讨论】:

【参考方案7】:

我没有测试过的一个可能的解决方案是使用一个 dask 数据帧,它的行为应该与 pandas 数据帧相同,但(我假设)利用磁盘并读入和读出 RAM。这里有一些有用的链接。 this link 提到如何将它与 xgboost 一起使用,另请参阅 also see。 此外,这里还有an experimental options from XGBoost,但它“尚未准备好投入生产”

【讨论】:

【参考方案8】:
Hey guys you can use my simple code for incremental model training with xgb base class :

    batch_size = 10000000


    X_train="your pandas training DataFrame" 
    y_train="Your lables"
    
    #Store eval results
    evals_result=
    Deval = xgb.DMatrix(X_valid, y_valid)
    eval_sets = [(Dtrain, 'train'), (Deval, 'eval')]
    for start in range(0, n, batch_size):
           model = xgb.train('refresh_leaf': True, 
                         'process_type': 'default', 
                         'max_depth': 5, 
                         'objective': 'reg:squarederror', 
                         'num_parallel_tree': 2,
                        'learning_rate':0.05,
                        'n_jobs':-1,
                        dtrain=xgb.DMatrix(X_train, y_train), evals=eval_sets, early_stopping_rounds=5,num_boost_round=100,evals_result=evals_result,xgb_model=model) 

【讨论】:

以上是关于如何为 xgboost 实施增量训练?的主要内容,如果未能解决你的问题,请参考以下文章

XGBRegressor梯度提升回归xgboos 决策树回归

机器学习 gbdt-xgboost 决策树提升

Xgboost 处理不平衡的分类数据

推荐算法与量化交易-A-5-5:XGBoost-lightGBM“集成提升树模型”算法-基于模型算法

为啥 Xgboost 中的功能不匹配错误

kaggle 房价预测经典文章