1. 说明
当我们的训练数据非常多,并且还在不断增加时,每次都用全量训练,数据过多,时间过长,此时就可以使用增量训练:用新增的数据微调校正模型。
2. 全量与增量的差异
在使用增量训练时,最关心的问题是:全量和增量的差别,从而确定增量训练的使用场景。
假设有200条数据,第一次训练150条,第二次训练50条,和直接用200条训练的差异在于:在第二次训练50条时,前150条数据已经不存在了,模型更拟合于后面的数据。如果我们定期增量训练,那么离当前时间越近的数据对模型影响越大,这也是我们想要的结果。但如果最后一批数据质量非常差,就可能覆盖之前的正确实例的训练结果,把模型带偏。
同理,如果我们按时间把数据分成几部分,然后按从早到晚的顺序多次训练模型,每个模型在上一个模型基础上训练,也间接地参加了后期实例的权重。
Xgboost提供两种增量训练的方式,一种是在当前迭代树的基础上增加新树,原树不变;另一种是当前迭代树结构不变,重新计算叶节点权重,同时也可增加新树。
对于已存在的决策树,早期训练的实例决定了模型的结构(选择哪些特征及分裂点),后期的实例决定最终的结果(叶节点的权重和新加入的树)。
综上,两个重点:第一,模型训练了一半,突然换了一批完全不同的数据继续训练,早期数据不再能再校正模型;第二,树一旦形成,结构就不再变化,后续的训练只能增加新树和重新计算前树的节点权重。
我觉得,还是尽量用全量数据训练,如果数据太多,必须增量时,尽量保证增量数据的质量和数量(均匀分布),以免带偏模型。
3. 例程
xgboost源码中有增量训练的例程:tests/python/test_training_continuation.py,其中核心部分稍做修改如下:
# -*- coding: utf-8 -*-
import xgboost as xgb
from sklearn.datasets import load_digits # 训练数据
xgb_params_01 = {}
digits_2class = load_digits(2)
X_2class = digits_2class['data']
y_2class = digits_2class['target']
dtrain_2class = xgb.DMatrix(X_2class, label=y_2class)
gbdt_03 = xgb.train(xgb_params_01, dtrain_2class, num_boost_round=3) # 训练三棵树的模型
print(gbdt_03.get_dump()) # 显示模型
gbdt_03a = xgb.train(xgb_params_01, dtrain_2class, num_boost_round=7, xgb_model=gbdt_03) # 在原模型基础上继续训练
print(gbdt_03a.get_dump())
4. 分析
训练函数train()中有个参数xgb_model,可填写旧模型路径,或者模型指针,指定该参数后,新模型在旧模型的基础上训练。
从代码上看,增量训练的逻辑主要在python层面,和普通的训练模型比,只是在c++底层实现了用旧模型填充learner,而非初始化learner。
回想一下前篇讨论过的训练过程:代入实例预测->对比预测结果和实际结果差异(误差函数)及误差方向(误差函数导数)->添加新决策树改进模型。继续训练也是如此。
从上面代码的输出结果可以看到,第一次dump的决策树是三棵,第二次dump出十棵(第一次三棵加第二次七棵)其中的前三棵与之前完全一样。就是说增量训练后,原来模型中的所有树都没变,只是在后面追加了更多的树。
如果在params中设置’process_type’:’update’,’update’:’refresh,’refresh_leaf’:True,则前三棵树结构不变,叶节点权重改变,最终结果一共七棵树(更新前三棵,新建后四棵)。