-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom median objective function in lightgbm.cv() #6620
Comments
Hey. Thanks for using LightGBM. Can you try setting the condition to greater equal? i.e. grad = np.where(residual >= 0, 0.5, -0.5) |
@jmoralez tried setting to
Log:
When tried to debug the median_loss objective at the execution of lgb.cv(), the pred are all zero as seen in the screenshot: With the obective='regression' the model gets trained normally. Logs are below:
|
When using a custom objective LightGBM sets the init score as 0 and if it doesn't find a gain with any split you may be left with a single tree with only the root, you can verify this if you use the trees_to_dataframe method. If you're able to provide a reproducible example we can assist further. The following seems to train normally: import lightgbm as lgb
import numpy as np
from sklearn.datasets import make_regression
def median_loss(preds, train_data: lgb.Dataset):
y_true = train_data.get_label()
residual = preds - y_true
grad = np.where(residual >= 0, 0.5, -0.5)
hess = np.ones_like(grad) # Hessian is constant for median pinball loss
return grad, hess
X, y = make_regression(n_samples=1000, n_features=2)
dtrain = lgb.Dataset(X, y)
params={"objective": median_loss, 'num_leaves': 32, 'verbosity': -1, 'metrics': 'l2'}
cv_hist = lgb.cv(
params,
dtrain,
num_boost_round=10,
nfold=2,
stratified=False,
callbacks=[lgb.log_evaluation(1)],
)
# [1] cv_agg's valid l2: 15698.8 + 269.489
# [2] cv_agg's valid l2: 15689.7 + 269.239
# [3] cv_agg's valid l2: 15680.5 + 268.99
# [4] cv_agg's valid l2: 15671.4 + 268.741
# [5] cv_agg's valid l2: 15662.2 + 268.491
# [6] cv_agg's valid l2: 15653.1 + 268.242
# [7] cv_agg's valid l2: 15644 + 267.993
# [8] cv_agg's valid l2: 15634.8 + 267.744
# [9] cv_agg's valid l2: 15625.7 + 267.495
# [10] cv_agg's valid l2: 15616.6 + 267.246 |
@jmoralez
Unzip the file to test.bin |
Did you inspect the produced trees? |
You mean to get the model from lgb.train after lgb.cv and inspect the trees? If so, yes there seem to be only root. The hyper_params from the lgb.cv() and BayesianOptimization returns
The model is trained with these hyper params and yields:
Does this indicate that the median_loss objective is not good for the dataset? |
That means LightGBM isn't able to find a split that satisfies the constraints you've set ( This doesn't seem to be an issue within LightGBM or your custom loss, I'm pretty sure you'd get the same result if you used the built-in loss (single tree with only the root which predicts the init score). If you have very few samples you could try getting more data or reducing the constraints (in case 16 is your minimum |
@jmoralez The hyper parameter boundaries for tuning are shown below:
And the built-in regression objective gives the best hyper parameters by bayesian hyper param tuning with lgb.cv() cross validation:
And there are >1 trees
The issue occurs only while using custom loss function where it cannot find a split and only predicts the init score 0. |
@jmoralez is there anything i am missing out here? |
What are you returning as the trial's score? As I said, when using a custom objective, LightGBM starts boosting from zero, which might hurt the convergence. Can you try the approach in #5114 (comment) by setting the init score in your dataset (to the target's median in this case), adding it back to your predictions and then computing your metric on that? If you're using a built-in metric it won't work because it won't take into account the init scores. |
LightGBM version 4.0.0
The objective='regression' trains to predict the mean representation of the data. And i am interested to train to predict median representation of the actual values. Infact, a quanitle model with alpha=0.5 will solve the problem. However, the quantile model does not work with
monotone_constraints
parameter which is essential in our case. Therefore, a custommedian_loss
is used as objective passed to the params.params={ "objective": median_loss, }, cv_result = lgb.cv(params, dtrain, nfold=n_folds, stratified=False, return_cvbooster=True)
Debugging shows that all predictions during the lgb.cv step are 0's and therefore the gradients are uniform. It might not be providing LightGBM with sufficient gradient information to make meaningful splits.
Does anyone have a suggestion on how to train the model effectively with medain_loss custom objective or with a quantile objective preserving the monotonic constraint. @jameslamb @vladv14
The text was updated successfully, but these errors were encountered: