Python训练自定义损失函数与评价指标的XGBoost,并在Java环境调用模型

1. XGBoost自定义损失函数与评价指标(python)

  • 在python中,有两种调用xgboost模型的方法,分别成为xgboost原生接口方法,和xgboost的sklearn接口方法。
  • 损失函数就是指目标函数,也就是深度学习中常说的loss目标,是一个可以优化的函数。
  • 评价指标是指在训练过程中关注的评价模型质量的指标,如f1、auc等。

以下以多分类问题为例,分别解释。

1.1 xgboost原生接口方法

该方法使用xgboost原生接口,一般使用形式为:

import xgboost as xgb

dtrain = xgb.DMatrix(Train_data, label=Train_label)
dval = xgb.DMatrix(Val_data, label=Val_label)
params = {'booster': 'gbtree',
              'num_class': 8,
              'seed': 777,
              'objective': 'multi:softprob',
              'eta': 0.05,
              'gamma': 0.1,
              'min_child_weight': 3,
              'max_depth': 5,
              'lambda': 10,
              'subsample': 0.8,
              'colsample_bytree': 0.4,
              'colsample_bylevel': 0.7,
              'tree_method': 'exact'
              }
watchlist = [(dtrain, 'train'), (dval, 'eval')]
evals_result = {}
xgboost_clf = xgb.train(params=params, dtrain=dtrain, num_boost_round=250, evals=watchlist, evals_result=evals_result)

1.1.1 自定义损失函数

xgboost原生接口的损失函数”objective“可以使用模型里面自带的。对于多分类问题来说,可以使用"multi:softmax"和“multi:prob"。前者输出标签,后者输出多分类概率。当需要自定义损失函数时,只需要将参数中的”objective“指定为自定义的损失函数名即可。举一个栗子:

def custom_loss(y_pred, dTrain):
	# 获取真实标签, 是一个一维的
    y_true = dtrain.get_label()
    # 由于模型输进来的y_pred为 [样本数量,类别数量] 形状的,因此根据我的需要,我将真实标签修改为[样本数量,类别数量] 形状的one-hot矩阵
    y_true_one_hot = np.eye(8)[y_true.astype(int)]
	# 模型输出没有过激活函数,这里自己加了一个softmax过程
    y_pred = np.exp(y_pred) * (1 / np.expand_dims(np.sum(np.exp(y_pred), axis=1), axis=-1))
    # 自定义一阶导grad和二阶导hess的求解方法。如果自定义的损失函数过于复杂,只知道怎么写损失函数但不会求一阶和二阶导,可以使用from scipy.misc import derivative等进行辅助求解。
    grad = y_pred - y_true_one_hot
    hess = y_pred * (1 - y_pred)
    return grad, hess

然后将参数中的”objective“改为自定义的损失函数:

params = {'booster': 'gbtree',
              'num_class': 8,
              'seed': 777,
              'objective': custom_loss,
              'eta': 0.05,
              'gamma': 0.1,
              'min_child_weight': 3,
              'max_depth': 5,
              'lambda': 10,
              'subsample': 0.8,
              'colsample_bytree': 0.4,
              'colsample_bylevel': 0.7,
              'tree_method': 'exact'
              }

也可以写进xgb.train里面:

xgboost_clf = xgb.train(params=params, dtrain=dtrain, obj=custom_loss, num_boost_round=250, evals=watchlist, evals_result=evals_result)

1.1.2 自定义评价指标

首先需要写自定义的函数体:

def my_acc(y_pred, dTrain):
	# 首先获取真实标签
	y_true = dtrain.get_label()
	# 由于模型输出的y_pred为概率,因此需要将其转化成预测标签
	y_pred = np.argmax(y_pred, axis=1)
	# 写自己需要的评价指标
    acc = round(accuracy_score(y_true, y_pred), 6)
    # 注意,此处需要返回评价指标名字和评价指标的值
    return 'acc', acc

在xgb.train中添加一个参数custom_metric,

xgboost_clf = xgb.train(params=params, dtrain=dtrain, num_boost_round=250, custom_metric=my_acc, evals=watchlist, evals_result=evals_result)

1.2 基于sklearn的接口

该方法使用xgboost的sklearn接口,一般使用形式为:

from xgboost.sklearn import XGBClassifier

xgboost_clf = XGBClassifier(min_child_weight=3,max_depth=5,
                                 objective='multi:softprob', num_class=8, eval_metric='merror', subsample=0.8, learning_rate=0.05, n_estimators=100, random_state=777,
                                 colsample_bytree=0.4)
eval_set = [(Train_data, Train_label), (Val_data, Val_label)]
xgboost_clf.fit(Train_data, Train_label, eval_set=eval_set, verbose=True)

类似于原生接口,XGBClassifier中的objective参数和eval_metric分别对应着损失函数和评价指标,可以通过修改这两个参数为自定义的函数名来达到自定义的目的。

但是,基于sklearn接口的自定义函数体需要注意一些,与原生接口的写法稍有不一致。

1.2.1 自定义损失函数

# ********注意!!!这里的模型输入给损失函数的输入就不一致, 且y_pred在后,真实标签在前。********
def custom_loss(y_true, y_pred):
	# ********注意!!!y_true直接为一维的真实标签,不需要再手动获取********
    # 由于模型输进来的y_pred为 [样本数量,类别数量] 形状的,因此根据我的需要,我将真实标签修改为[样本数量,类别数量] 形状的one-hot矩阵
    y_true_one_hot = np.eye(8)[y_true.astype(int)]
	# 模型输出没有过激活函数,这里自己加了一个softmax过程
    y_pred = np.exp(y_pred) * (1 / np.expand_dims(np.sum(np.exp(y_pred), axis=1), axis=-1))
    # 自定义一阶导grad和二阶导hess的求解方法。如果自定义的损失函数过于复杂,只知道怎么写损失函数但不会求一阶和二阶导,可以使用from scipy.misc import derivative等进行辅助求解。
    grad = y_pred - y_true_one_hot
    hess = y_pred * (1 - y_pred)
    return grad, hess

1.2.2 自定义评价指标

# ********注意!!!这里的模型输入给评价函数的输入就不一致, 且y_pred在后,真实标签在前。********
def my_acc(y_true, y_pred):
	# ********注意!!!y_true直接为一维的真实标签,不需要再手动获取********
	# 由于模型输出的y_pred为概率,因此需要将其转化成预测标签
	y_pred = np.argmax(y_pred, axis=1)
	# 写自己需要的评价指标
    acc = round(accuracy_score(y_true, y_pred), 6)
    # ********注意!!!此处只需要返回评价指标的值********
    return acc

2. 在java环境中调用模型

2.1 在python保存和加载模型

# 保存模型
xgboost_clf.save_model("./model/xgboost_v1.0.json")
# 加载模型
model = xgb.Booster()
model.load_model('model/xgboost_v1.0.json')

此处需要注意的是,由于我们的损失函数和评价指标都是自定义的函数,因此如果需要加载模型后继续对模型进行训练优化,必须保证在加载模型的同一个py文件中,存在自定义的损失函数和评价指标函数,否则无法使用。

2.2 在java中加载模型

首先需要安装好import ml.dmlc.xgboost4j.java库,可以通过maven等进行导入(此处如有需要请自行百度)

import ml.dmlc.xgboost4j.java.XGBoost;

private Booster model = null;
model = XGBoost.loadModel(“model/xgboost_v1.0.json”);
  • 可能会报错:/workspace/include/xgboost/json.h:73: Invalid cast, from Integer to Boolean。
  • 这大概是因为python中的xgboost版本太高,与java中的版本不兼容,导致json文件中数据类型不一致。
  • 观察json文件,可以发现其中所有的default_left字段为list[1,0,0,1,0] 这样的,将其中的1全部改成“True”,0全部改成“False",即可。

你可能感兴趣的:(机器学习,python,机器学习,开发语言,java)