From 032d0fa461d2eb75c822bb94ba0ea7e09d36527b Mon Sep 17 00:00:00 2001 From: James Lamb Date: Wed, 24 Apr 2024 23:51:11 -0500 Subject: [PATCH 1/5] [python-package] respect 'verbose' setting when using custom objective function (fixes #6014) --- src/io/config.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/io/config.cpp b/src/io/config.cpp index e25bb6d4fd70..7434ced344e6 100644 --- a/src/io/config.cpp +++ b/src/io/config.cpp @@ -40,9 +40,25 @@ void GetFirstValueAsInt(const std::unordered_map>& params) { - int verbosity = Config().verbosity; - GetFirstValueAsInt(params, "verbose", &verbosity); - GetFirstValueAsInt(params, "verbosity", &verbosity); + + int verbosity = 1; + + // if "verbosity" was found in params, prefer that to any other aliases + const auto verbosity_iter = params.find("verbosity"); + if (verbosity_iter != params.end()) { + GetFirstValueAsInt(params, "verbosity", &verbosity); + } else { + // if "verbose" was found in params and "verbosity" was not, use that value + const auto verbose_iter = params.find("verbose"); + if (verbose_iter != params.end()) { + GetFirstValueAsInt(params, "verbose", &verbosity); + } else { + // if "verbosity" and "verbose" were both missing from params, don't modify LightGBM's log level + return; + } + } + + // otherwise, update LightGBM's log level based on the passed-in value if (verbosity < 0) { LightGBM::Log::ResetLogLevel(LightGBM::LogLevel::Fatal); } else if (verbosity == 0) { From fd10d509e0f40a37335123468a8f8b3517c9c466 Mon Sep 17 00:00:00 2001 From: James Lamb Date: Sat, 27 Apr 2024 23:24:43 -0500 Subject: [PATCH 2/5] fix test --- tests/python_package_test/test_basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python_package_test/test_basic.py b/tests/python_package_test/test_basic.py index 92f5593ef7c1..48355163f3ef 100644 --- a/tests/python_package_test/test_basic.py +++ b/tests/python_package_test/test_basic.py @@ -384,7 +384,7 @@ def test_add_features_does_not_fail_if_initial_dataset_has_zero_informative_feat arr_a = np.zeros((100, 1), dtype=np.float32) arr_b = np.random.normal(size=(100, 5)) - dataset_a = lgb.Dataset(arr_a).construct() + dataset_a = lgb.Dataset(arr_a, params={"verbose": 0}).construct() expected_msg = ( "[LightGBM] [Warning] There are no meaningful features which satisfy " "the provided configuration. Decreasing Dataset parameters min_data_in_bin " From 3bb7e86fd3930d1f3b45b88ec3a5f405168189bc Mon Sep 17 00:00:00 2001 From: James Lamb Date: Mon, 8 Jul 2024 23:06:43 -0500 Subject: [PATCH 3/5] update tests --- tests/python_package_test/test_engine.py | 17 ++++++++++++++++- tests/python_package_test/test_utilities.py | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/python_package_test/test_engine.py b/tests/python_package_test/test_engine.py index 7d7ae5c9fb01..a437ff81017d 100644 --- a/tests/python_package_test/test_engine.py +++ b/tests/python_package_test/test_engine.py @@ -1469,6 +1469,7 @@ def test_parameters_are_loaded_from_model_file(tmp_path, capsys, rng): "metric": ["l2", "rmse"], "num_leaves": 5, "num_threads": 1, + "verbosity": 0, } model_file = tmp_path / "model.txt" orig_bst = lgb.train(params, ds, num_boost_round=1, categorical_feature=[1, 2]) @@ -4274,11 +4275,25 @@ def test_verbosity_and_verbose(capsys): "verbosity": 0, } lgb.train(params, ds, num_boost_round=1) - expected_msg = "[LightGBM] [Warning] verbosity is set=0, verbose=1 will be ignored. " "Current value: verbosity=0" + expected_msg = "[LightGBM] [Warning] verbosity is set=0, verbose=1 will be ignored. Current value: verbosity=0" stdout = capsys.readouterr().out assert expected_msg in stdout +def test_verbosity_is_respected_when_using_custom_objective(capsys): + X, y = make_synthetic_regression() + ds = lgb.Dataset(X, y) + params = { + "objective": mse_obj, + "nonsense": 123, + "num_leaves": 3, + } + lgb.train({**params, "verbosity": -1}, ds, num_boost_round=1) + assert capsys.readouterr().out == "" + lgb.train({**params, "verbosity": 0}, ds, num_boost_round=1) + assert "[LightGBM] [Warning] Unknown parameter: nonsense" in capsys.readouterr().out + + @pytest.mark.parametrize("verbosity_param", lgb.basic._ConfigAliases.get("verbosity")) @pytest.mark.parametrize("verbosity", [-1, 0]) def test_verbosity_can_suppress_alias_warnings(capsys, verbosity_param, verbosity): diff --git a/tests/python_package_test/test_utilities.py b/tests/python_package_test/test_utilities.py index 3359d060e109..440a717ea1db 100644 --- a/tests/python_package_test/test_utilities.py +++ b/tests/python_package_test/test_utilities.py @@ -31,7 +31,7 @@ def dummy_metric(_, __): eval_records = {} callbacks = [lgb.record_evaluation(eval_records), lgb.log_evaluation(2), lgb.early_stopping(10)] lgb.train( - {"objective": "binary", "metric": ["auc", "binary_error"]}, + {"objective": "binary", "metric": ["auc", "binary_error"], "verbose": 1}, lgb_train, num_boost_round=10, feval=dummy_metric, From b63e7c97435796f6aca6df00863dcb90c104ef10 Mon Sep 17 00:00:00 2001 From: James Lamb Date: Mon, 8 Jul 2024 23:23:30 -0500 Subject: [PATCH 4/5] add tests --- tests/python_package_test/test_sklearn.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/python_package_test/test_sklearn.py b/tests/python_package_test/test_sklearn.py index d8aee94f3802..7ae640d32907 100644 --- a/tests/python_package_test/test_sklearn.py +++ b/tests/python_package_test/test_sklearn.py @@ -1290,6 +1290,19 @@ def test_max_depth_warning_is_never_raised(capsys, estimator_class, max_depth): assert "Provided parameters constrain tree depth" not in capsys.readouterr().out +def test_verbosity_is_respected_when_using_custom_objective(capsys): + X, y = make_synthetic_regression() + params = { + "objective": objective_ls, + "nonsense": 123, + "num_leaves": 3, + } + lgb.LGBMRegressor(**params, verbosity=-1, n_estimators=1).fit(X, y) + assert capsys.readouterr().out == "" + lgb.LGBMRegressor(**params, verbosity=0, n_estimators=1).fit(X, y) + assert "[LightGBM] [Warning] Unknown parameter: nonsense" in capsys.readouterr().out + + @pytest.mark.parametrize("estimator_class", [lgb.LGBMModel, lgb.LGBMClassifier, lgb.LGBMRegressor, lgb.LGBMRanker]) def test_getting_feature_names_in_np_input(estimator_class): # input is a numpy array, which doesn't have feature names. LightGBM adds From eb0bb15f0b5f2e4ec612430275171c70315aeb5f Mon Sep 17 00:00:00 2001 From: James Lamb Date: Mon, 8 Jul 2024 23:41:33 -0500 Subject: [PATCH 5/5] linting --- src/io/config.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/io/config.cpp b/src/io/config.cpp index db24525c98e0..c63de70fc16b 100644 --- a/src/io/config.cpp +++ b/src/io/config.cpp @@ -40,7 +40,6 @@ void GetFirstValueAsInt(const std::unordered_map>& params) { - int verbosity = 1; // if "verbosity" was found in params, prefer that to any other aliases