今天来讨论一个问题,就是xgboost是否能支持类似多任务学习的事情呢?之前听到这个问题还是挺惊奇的,这竟然也可以,今天查阅了部分相关的资料,发现竟然真的可以,我们知道深度学习中的多任务学习是通过一个共享层,建立各个任务之间的关系,通过构建多个任务的帕累托最优解,从而解决多任务的问题,而xgb选择了使用树一条路走到黑,接下来咱们来看看xgb是如何实现多任务学习的。
全量训练和增量训练
这里要先来看两个概念,一个是全量学习,一种是增量学习。全量学习顾名思义就是使用你所有的1000条数据进行训练,模型的目标也是拟合你这1000条数据的结果,而增量学习是指,先用500条数据进行学习,然后在用另外500条数据进行学习,当用后500条学习的时候,复用的是前500条的结果,并且不再关注前500条的结果,这样就将参数带过来了。对于一些对时序依赖比较严重的金融性任务,一般会采用后面这种训练方式。知道了这个背景知识以后,再来介绍xgb支持训练的两种方式。
- 一种是在当前迭代树的基础上增加新树,原树不变;
- 一种是当前迭代树结构不变,重新计算叶节点权重,同时也可增加新树。
增加新树的方式
下面给一段增加新树的方式进行训练的demo。
import xgboost as xgb
import numpy as np
from sklearn.model_selection import train_test_split
# 假设你有一只股票的数据,包括5天、10天、15天、20天的收益率
# 数据维度为 (样本数量, 特征数量),这里以 100 个样本和 3 个特征为例
np.random.seed(42)
data = np.random.rand(100, 3)
returns_5days = np.random.rand(100)
returns_10days = np.random.rand(100)
returns_15days = np.random.rand(100)
returns_20days = np.random.rand(100)
# 分别划分每个任务的训练集和验证集
data_5days, data_5days_val, returns_5days, returns_5days_val = train_test_split(data, returns_5days, test_size=0.2, random_state=42)
data_10days, data_10days_val, returns_10days, returns_10days_val = train_test_split(data, returns_10days, test_size=0.2, random_state=42)
data_15days, data_15days_val, returns_15days, returns_15days_val = train_test_split(data, returns_15days, test_size=0.2, random_state=42)
data_20days, data_20days_val, returns_20days, returns_20days_val = train_test_split(data, returns_20days, test_size=0.2, random_state=42)
# 创建每个任务的DMatrix对象
dtrain_5days = xgb.DMatrix(data_5days, label=returns_5days)
dval_5days = xgb.DMatrix(data_5days_val, label=returns_5days_val)
dtrain_10days = xgb.DMatrix(data_10days, label=returns_10days)
dval_10days = xgb.DMatrix(data_10days_val, label=returns_10days_val)
dtrain_15days = xgb.DMatrix(data_15days, label=returns_15days)
dval_15days = xgb.DMatrix(data_15days_val, label=returns_15days_val)
dtrain_20days = xgb.DMatrix(data_20days, label=returns_20days)
dval_20days = xgb.DMatrix(data_20days_val, label=returns_20days_val)
# 设置XGBoost参数
params = {
'objective': 'reg:squarederror', # 回归问题使用'reg:squarederror'
'eval_metric': 'rmse', # 均方根误差作为评估指标
'max_depth': 3,
}
# 训练每个任务的模型
num_boost_round = 100
best_iteration_list = list()
num_boost_round = 300
model_5days = xgb.train(params, dtrain_5days, num_boost_round=num_boost_round, evals=[(dtrain_5days, 'train'), (dval_5days, 'val')], early_stopping_rounds=10)
best_iteration_list.append(model_5days.best_iteration)
model_10days = xgb.train(params, dtrain_10days,xgb_model=model_5days,num_boost_round=num_boost_round, evals=[(dtrain_10days, 'train'), (dval_10days, 'val')], early_stopping_rounds=10)
best_iteration_list.append(model_10days.best_iteration)
model_15days = xgb.train(params, dtrain_15days, xgb_model=model_10days,num_boost_round=num_boost_round, evals=[(dtrain_15days, 'train'), (dval_15days, 'val')], early_stopping_rounds=10)
best_iteration_list.append(model_15days.best_iteration)
model_20days = xgb.train(params, dtrain_20days, xgb_model=model_15days,num_boost_round=num_boost_round, evals=[(dtrain_20days, 'train'), (dval_20days, 'val')], early_stopping_rounds=10)
best_iteration_list.append(model_20days.best_iteration)
model_20days.save_model('./data/models/regression_all_data.model')
这里是一个背景,想预测一只股票的5天后的收益,10天后的收益以及15和20天的收益,很明显这个是一个相关的任务,先训练5天的模型,然后不断的使用新的数据在老的模型上进行增加新的树,这样训练下来的整个模型具备预测5天、10天以及15天以后的结果。
更新权重的方式
这样类似的同样给出更新权重的方式。
import xgboost as xgb
from sklearn.datasets import load_digits # 训练数据
from sklearn.model_selection import train_test_split
X, y = load_digits(n_class=2, return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, shuffle=True, stratify=y,
random_state=100)
# dtrain = xgb.DMatrix(X_train, label=y_train)
# dtest = xgb.DMatrix(X_test, label=y_test)
print("-+-" * 25)
params1 = {'tree_method': 'hist', "n_estimators": 3}
model1 = xgb.XGBClassifier(**params1)
model1.fit(X_train, y_train)
print(len(model1.get_booster().get_dump()))
for leaf in model1.get_booster().get_dump():
print(leaf)
print("-+-" * 25)
params2 = {'tree_method': 'hist', "n_estimators": 3}
model2 = xgb.XGBClassifier(**params2)
model2.fit(X_test, y_test, xgb_model=model1.get_booster())
print(len(model2.get_booster().get_dump()))
for leaf in model2.get_booster().get_dump():
print(leaf)
print("-+-" * 25)
params3 = {'tree_method': 'hist', "n_estimators": 3}
params3["updater"] = "refresh"
params3["process_type"] = "update"
params3["refresh_leaf"] = True
# 则3棵树结构不变,叶节点权重改变,最终结果一共3棵树
# 特别注意这里的num_boost_round <=原始模型的boost_nums 否则汇报错
model3 = xgb.XGBClassifier(**params3)
model3.fit(X_test, y_test, xgb_model=model1.get_booster())
print(len(model3.get_booster().get_dump()))
for leaf in model3.get_booster().get_dump():
print(leaf)
这里最重要的几行代码就是
params3["updater"] = "refresh"
params3["process_type"] = "update"
params3["refresh_leaf"] = True
这是xgb的高级选项。接下来咱们一一看看都是解决什么问题的
updater
updater: 用于指定 XGBoost 中的更新器(Updater),它控制模型的更新方式。在这里,将 updater 设置为 “refresh” 表示使用增量训练,即在已有模型的基础上进行更新。
process_type
process_type: 指定处理的类型。在这里,将 process_type 设置为 “update” 表示执行增量更新。
refresh_leaf
refresh_leaf: 设置为 True 表示更新树的叶子节点。
这样的设置表明你可能在已有的模型基础上进行增量训练,而且是通过更新叶子节点的方式进行的。增量训练通常用于在已有模型的基础上,通过使用新的数据进行进一步的学习,而不必重新从头开始训练整个模型。
总而言之
不知道到这里你是否有些惊讶,用了好久的xgb,还不知道能够实现这样的功能,其实往往是业务的需求驱动能够让我们能够在技术上进一步拓宽视野。技术和业务要双迭代才能制造生产力。