-
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
Divide metric into train_metric and valid_metric #1912
Changes from all commits
bc0ea4a
7ce4d59
e1f2bba
ab23d73
9327ffb
b0e7a5c
4a98ec0
a276abb
bea2851
e0ca9e0
95729b2
452d21f
6892408
27ab0b9
c6062de
a3fb1d6
9d5882b
b137ad5
2fb6066
f11dc46
b89c1ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -330,7 +330,8 @@ def fit(self, X, y, | |
sample_weight=None, init_score=None, group=None, | ||
eval_set=None, eval_names=None, eval_sample_weight=None, | ||
eval_class_weight=None, eval_init_score=None, eval_group=None, | ||
eval_metric=None, early_stopping_rounds=None, verbose=True, | ||
eval_metric=None, eval_train_metric=None, eval_valid_metric=None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that these new args are really needed. One can achieve the needed result in their rare case with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think they are not rere cases, because CatBoost, which is one of the most famous GBDT library and was acepted in NIPS 2018, supports setting of individual metrics to training and validation. cf. However, as you you pointed, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @guolinke What do you think? My position is that corresponding keys in I remember we were getting rid of some arguments in favor of their aliases in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, I think to have 3 similar parameters is confusing. BTW, |
||
early_stopping_rounds=None, verbose=True, | ||
feature_name='auto', categorical_feature='auto', callbacks=None): | ||
"""Build a gradient boosting model from the training set (X, y). | ||
|
||
|
@@ -363,6 +364,12 @@ def fit(self, X, y, | |
If callable, it should be a custom evaluation metric, see note below for more details. | ||
In either case, the ``metric`` from the model parameters will be evaluated and used as well. | ||
Default: 'l2' for LGBMRegressor, 'logloss' for LGBMClassifier, 'ndcg' for LGBMRanker. | ||
eval_train_metric : string, list of strings, callable or None, optional (default=None) | ||
metric(s) to be evaluated on the train set passed into ``eval_set`` | ||
if ``metric`` is set, ``eval_train_metric`` will be overwritten | ||
eval_valid_metric : string, list of strings, callable or None, optional (default=None) | ||
metric(s) to be evaluated on the valid set passed into ``eval_set`` | ||
if ``metric`` is set, ``eval_valid_metric`` will be overwritten | ||
early_stopping_rounds : int or None, optional (default=None) | ||
Activates early stopping. The model will train until the validation score stops improving. | ||
Validation score needs to improve at least every ``early_stopping_rounds`` round(s) | ||
|
@@ -460,24 +467,12 @@ def fit(self, X, y, | |
feval = _eval_function_wrapper(eval_metric) | ||
else: | ||
feval = None | ||
# register default metric for consistency with callable eval_metric case | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, but I'm talking about the Python side (we had many issues about inconsistent behavior at Python side and this code solved them). So, this code should be leaved as is to prevent the future changes in current behavior. Please see this our old conversation (setting default metric only at cpp side is not enough): #1589 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @henry0312 Please fix this (leave old code) to get this PR merged.
to support this new behavior
I believe that this is the most painless and not new-odd-behavior-introducing way to support new train/valid metric support 😃 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @henry0312 @guolinke I've decided to dive into the "metrics hell" again and started to write a big test which reflects the current behavior of metrics handling: 8265469 ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PR has been created: #1954. @henry0312 I hope it will help you abandon the idea to create more arguments for metrics 😄 |
||
original_metric = self._objective if isinstance(self._objective, string_type) else None | ||
if original_metric is None: | ||
# try to deduce from class instance | ||
if isinstance(self, LGBMRegressor): | ||
original_metric = "l2" | ||
elif isinstance(self, LGBMClassifier): | ||
original_metric = "multi_logloss" if self._n_classes > 2 else "binary_logloss" | ||
elif isinstance(self, LGBMRanker): | ||
original_metric = "ndcg" | ||
# overwrite default metric by explicitly set metric | ||
for metric_alias in ['metric', 'metrics', 'metric_types']: | ||
if metric_alias in params: | ||
original_metric = params.pop(metric_alias) | ||
# concatenate metric from params (or default if not provided in params) and eval_metric | ||
original_metric = [original_metric] if isinstance(original_metric, (string_type, type(None))) else original_metric | ||
eval_metric = [eval_metric] if isinstance(eval_metric, (string_type, type(None))) else eval_metric | ||
params['metric'] = set(original_metric + eval_metric) | ||
if eval_metric: | ||
# for backward compatibility, `metric` is prefered to `train_metric` and `valid_metric` | ||
params['metric'] = eval_metric | ||
else: | ||
params['train_metric'] = eval_train_metric | ||
params['valid_metric'] = eval_valid_metric | ||
|
||
if not isinstance(X, DataFrame): | ||
_X, _y = _LGBMCheckXY(X, y, accept_sparse=True, force_all_finite=False, ensure_min_samples=2) | ||
|
@@ -670,7 +665,8 @@ class LGBMRegressor(LGBMModel, _LGBMRegressorBase): | |
def fit(self, X, y, | ||
sample_weight=None, init_score=None, | ||
eval_set=None, eval_names=None, eval_sample_weight=None, | ||
eval_init_score=None, eval_metric=None, early_stopping_rounds=None, | ||
eval_init_score=None, eval_metric=None, eval_train_metric=None, | ||
eval_valid_metric=None, early_stopping_rounds=None, | ||
verbose=True, feature_name='auto', categorical_feature='auto', callbacks=None): | ||
"""Docstring is inherited from the LGBMModel.""" | ||
super(LGBMRegressor, self).fit(X, y, sample_weight=sample_weight, | ||
|
@@ -679,6 +675,8 @@ def fit(self, X, y, | |
eval_sample_weight=eval_sample_weight, | ||
eval_init_score=eval_init_score, | ||
eval_metric=eval_metric, | ||
eval_train_metric=eval_train_metric, | ||
eval_valid_metric=eval_valid_metric, | ||
early_stopping_rounds=early_stopping_rounds, | ||
verbose=verbose, feature_name=feature_name, | ||
categorical_feature=categorical_feature, | ||
|
@@ -697,6 +695,7 @@ def fit(self, X, y, | |
sample_weight=None, init_score=None, | ||
eval_set=None, eval_names=None, eval_sample_weight=None, | ||
eval_class_weight=None, eval_init_score=None, eval_metric=None, | ||
eval_train_metric=None, eval_valid_metric=None, | ||
early_stopping_rounds=None, verbose=True, | ||
feature_name='auto', categorical_feature='auto', callbacks=None): | ||
"""Docstring is inherited from the LGBMModel.""" | ||
|
@@ -712,15 +711,17 @@ def fit(self, X, y, | |
ova_aliases = ("multiclassova", "multiclass_ova", "ova", "ovr") | ||
if self._objective not in ova_aliases and not callable(self._objective): | ||
self._objective = "multiclass" | ||
if eval_metric in ('logloss', 'binary_logloss'): | ||
eval_metric = "multi_logloss" | ||
elif eval_metric in ('error', 'binary_error'): | ||
eval_metric = "multi_error" | ||
for _metric in (eval_metric, eval_train_metric, eval_valid_metric): | ||
if _metric in ('logloss', 'binary_logloss'): | ||
_metric = "multi_logloss" | ||
elif _metric in ('error', 'binary_error'): | ||
_metric = "multi_error" | ||
else: | ||
if eval_metric in ('logloss', 'multi_logloss'): | ||
eval_metric = 'binary_logloss' | ||
elif eval_metric in ('error', 'multi_error'): | ||
eval_metric = 'binary_error' | ||
for _metric in (eval_metric, eval_train_metric, eval_valid_metric): | ||
if _metric in ('logloss', 'multi_logloss'): | ||
_metric = 'binary_logloss' | ||
elif _metric in ('error', 'multi_error'): | ||
_metric = 'binary_error' | ||
|
||
if eval_set is not None: | ||
if isinstance(eval_set, tuple): | ||
|
@@ -738,6 +739,8 @@ def fit(self, X, y, | |
eval_class_weight=eval_class_weight, | ||
eval_init_score=eval_init_score, | ||
eval_metric=eval_metric, | ||
eval_train_metric=eval_train_metric, | ||
eval_valid_metric=eval_valid_metric, | ||
early_stopping_rounds=early_stopping_rounds, | ||
verbose=verbose, feature_name=feature_name, | ||
categorical_feature=categorical_feature, | ||
|
@@ -825,7 +828,8 @@ def fit(self, X, y, | |
sample_weight=None, init_score=None, group=None, | ||
eval_set=None, eval_names=None, eval_sample_weight=None, | ||
eval_init_score=None, eval_group=None, eval_metric=None, | ||
eval_at=[1], early_stopping_rounds=None, verbose=True, | ||
eval_train_metric=None, eval_valid_metric=None, eval_at=[1], | ||
early_stopping_rounds=None, verbose=True, | ||
feature_name='auto', categorical_feature='auto', callbacks=None): | ||
"""Docstring is inherited from the LGBMModel.""" | ||
# check group data | ||
|
@@ -851,6 +855,8 @@ def fit(self, X, y, | |
eval_sample_weight=eval_sample_weight, | ||
eval_init_score=eval_init_score, eval_group=eval_group, | ||
eval_metric=eval_metric, | ||
eval_train_metric=eval_train_metric, | ||
eval_valid_metric=eval_valid_metric, | ||
early_stopping_rounds=early_stopping_rounds, | ||
verbose=verbose, feature_name=feature_name, | ||
categorical_feature=categorical_feature, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the reload is deleted?
I think this will somehow affect the performance.