Update Python custom objective demo. (#5981)

This commit is contained in:
Jiaming Yuan 2020-08-05 12:27:19 +08:00 committed by GitHub
parent 1149a7a292
commit 9c93531709
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 23 deletions

View File

@ -1,28 +1,28 @@
import os
import numpy as np
import xgboost as xgb
### ###
# advanced: customized loss function # advanced: customized loss function
# #
import os
import numpy as np
import xgboost as xgb
print('start running example to used customized objective function') print('start running example to used customized objective function')
CURRENT_DIR = os.path.dirname(__file__) CURRENT_DIR = os.path.dirname(__file__)
dtrain = xgb.DMatrix(os.path.join(CURRENT_DIR, '../data/agaricus.txt.train')) dtrain = xgb.DMatrix(os.path.join(CURRENT_DIR, '../data/agaricus.txt.train'))
dtest = xgb.DMatrix(os.path.join(CURRENT_DIR, '../data/agaricus.txt.test')) dtest = xgb.DMatrix(os.path.join(CURRENT_DIR, '../data/agaricus.txt.test'))
# note: for customized objective function, we leave objective as default # note: what we are getting is margin value in prediction you must know what
# note: what we are getting is margin value in prediction # you are doing
# you must know what you are doing param = {'max_depth': 2, 'eta': 1, 'objective': 'reg:logistic'}
param = {'max_depth': 2, 'eta': 1}
watchlist = [(dtest, 'eval'), (dtrain, 'train')] watchlist = [(dtest, 'eval'), (dtrain, 'train')]
num_round = 2 num_round = 10
# user define objective function, given prediction, return gradient and second # user define objective function, given prediction, return gradient and second
# order gradient this is log likelihood loss # order gradient this is log likelihood loss
def logregobj(preds, dtrain): def logregobj(preds, dtrain):
labels = dtrain.get_label() labels = dtrain.get_label()
preds = 1.0 / (1.0 + np.exp(-preds)) preds = 1.0 / (1.0 + np.exp(-preds)) # transform raw leaf weight
grad = preds - labels grad = preds - labels
hess = preds * (1.0 - preds) hess = preds * (1.0 - preds)
return grad, hess return grad, hess
@ -31,20 +31,31 @@ def logregobj(preds, dtrain):
# user defined evaluation function, return a pair metric_name, result # user defined evaluation function, return a pair metric_name, result
# NOTE: when you do customized loss function, the default prediction value is # NOTE: when you do customized loss function, the default prediction value is
# margin. this may make builtin evaluation metric not function properly for # margin, which means the prediction is score before logistic transformation.
# example, we are doing logistic loss, the prediction is score before logistic
# transformation the builtin evaluation error assumes input is after logistic
# transformation Take this in mind when you use the customization, and maybe
# you need write customized evaluation function
def evalerror(preds, dtrain): def evalerror(preds, dtrain):
labels = dtrain.get_label() labels = dtrain.get_label()
preds = 1.0 / (1.0 + np.exp(-preds)) # transform raw leaf weight
# return a pair metric_name, result. The metric name must not contain a # return a pair metric_name, result. The metric name must not contain a
# colon (:) or a space since preds are margin(before logistic # colon (:) or a space
# transformation, cutoff at 0) return 'my-error', float(sum(labels != (preds > 0.5))) / len(labels)
return 'my-error', float(sum(labels != (preds > 0.0))) / len(labels)
py_evals_result = {}
# training with customized objective, we can also do step by step training # training with customized objective, we can also do step by step training
# simply look at xgboost.py's implementation of train # simply look at xgboost.py's implementation of train
bst = xgb.train(param, dtrain, num_round, watchlist, obj=logregobj, py_params = param.copy()
feval=evalerror) py_params.update({'disable_default_eval_metric': True})
py_logreg = xgb.train(py_params, dtrain, num_round, watchlist, obj=logregobj,
feval=evalerror, evals_result=py_evals_result)
evals_result = {}
params = param.copy()
params.update({'eval_metric': 'error'})
logreg = xgb.train(params, dtrain, num_boost_round=num_round, evals=watchlist,
evals_result=evals_result)
for i in range(len(py_evals_result['train']['my-error'])):
np.testing.assert_almost_equal(py_evals_result['train']['my-error'],
evals_result['train']['error'])

View File

@ -197,9 +197,9 @@ class TestModels(unittest.TestCase):
assert np.all(np.abs(predt_2 - predt_1) < 1e-6) assert np.all(np.abs(predt_2 - predt_1) < 1e-6)
def test_custom_objective(self): def test_custom_objective(self):
param = {'max_depth': 2, 'eta': 1, 'verbosity': 0} param = {'max_depth': 2, 'eta': 1, 'objective': 'reg:logistic'}
watchlist = [(dtest, 'eval'), (dtrain, 'train')] watchlist = [(dtest, 'eval'), (dtrain, 'train')]
num_round = 2 num_round = 10
def logregobj(preds, dtrain): def logregobj(preds, dtrain):
labels = dtrain.get_label() labels = dtrain.get_label()
@ -210,10 +210,12 @@ class TestModels(unittest.TestCase):
def evalerror(preds, dtrain): def evalerror(preds, dtrain):
labels = dtrain.get_label() labels = dtrain.get_label()
preds = 1.0 / (1.0 + np.exp(-preds))
return 'error', float(sum(labels != (preds > 0.5))) / len(labels) return 'error', float(sum(labels != (preds > 0.5))) / len(labels)
# test custom_objective in training # test custom_objective in training
bst = xgb.train(param, dtrain, num_round, watchlist, logregobj, evalerror) bst = xgb.train(param, dtrain, num_round, watchlist, obj=logregobj,
feval=evalerror)
assert isinstance(bst, xgb.core.Booster) assert isinstance(bst, xgb.core.Booster)
preds = bst.predict(dtest) preds = bst.predict(dtest)
labels = dtest.get_label() labels = dtest.get_label()
@ -230,7 +232,8 @@ class TestModels(unittest.TestCase):
labels = dtrain.get_label() labels = dtrain.get_label()
return 'error', float(sum(labels == (preds > 0.0))) / len(labels) return 'error', float(sum(labels == (preds > 0.0))) / len(labels)
bst2 = xgb.train(param, dtrain, num_round, watchlist, logregobj, neg_evalerror, maximize=True) bst2 = xgb.train(param, dtrain, num_round, watchlist, logregobj,
neg_evalerror, maximize=True)
preds2 = bst2.predict(dtest) preds2 = bst2.predict(dtest)
err2 = sum(1 for i in range(len(preds2)) err2 = sum(1 for i in range(len(preds2))
if int(preds2[i] > 0.5) != labels[i]) / float(len(preds2)) if int(preds2[i] > 0.5) != labels[i]) / float(len(preds2))