Repository: optuna/optuna

Issue:
Replace deprecated arguments with callback-ish way in lightgbm test
<!-- Please write a clear and concise description of the feature proposal. -->

## Motivation

Since lightgbm v3.3.0, train/cv's arguments have been deprecated as described in https://github.com/optuna/optuna/issues/3013. It would be great to replace such deprecated arguments with recommended way by lightgbm.


## Description

Replace the deprecated arguments with callback-ish way. The target lines are given in https://github.com/optuna/optuna/pull/3014#issuecomment-949362020.

See also https://github.com/optuna/optuna-examples/pull/64/files for the concrete replacement.

<!-- Please write a detailed description of the new feature. -->

## Alternatives (optional)

<!-- Please write a clear and concise description of any alternative solutions or features you've considered. -->

## Additional context (optional)

We do not need to care about the older lightgbm version. 



Discussion:
This issue has not seen any recent activity.
This issue has not seen any recent activity.
This issue was closed automatically because it had not seen any recent activity. If you want to discuss it, you can reopen it freely.

Resolving patch:
diff --git a/optuna/integration/_lightgbm_tuner/optimize.py b/optuna/integration/_lightgbm_tuner/optimize.py
--- a/optuna/integration/_lightgbm_tuner/optimize.py
+++ b/optuna/integration/_lightgbm_tuner/optimize.py
@@ -301,7 +301,13 @@ def __init__(
 
     def _get_cv_scores(self, cv_results: Dict[str, List[float]]) -> List[float]:
         metric = self._get_metric_for_objective()
-        val_scores = cv_results["{}-mean".format(metric)]
+        metric_key = f"{metric}-mean"
+        # The prefix "valid " is added to metric name since LightGBM v4.0.0.
+        val_scores = (
+            cv_results[metric_key]
+            if metric_key in cv_results
+            else cv_results["valid " + metric_key]
+        )
         return val_scores
 
     def __call__(self, trial: optuna.trial.Trial) -> float:
@@ -347,6 +353,7 @@ def __init__(
         self,
         params: Dict[str, Any],
         train_set: "lgb.Dataset",
+        callbacks: List[Callable[..., Any]],
         num_boost_round: int = 1000,
         fobj: Optional[Callable[..., Any]] = None,
         feval: Optional[Callable[..., Any]] = None,
@@ -354,7 +361,6 @@ def __init__(
         categorical_feature: str = "auto",
         early_stopping_rounds: Optional[int] = None,
         verbose_eval: Optional[Union[bool, int, str]] = None,
-        callbacks: Optional[List[Callable[..., Any]]] = None,
         time_budget: Optional[int] = None,
         sample_size: Optional[int] = None,
         study: Optional[optuna.study.Study] = None,
@@ -368,23 +374,32 @@ def __init__(
         _imports.check()
 
         params = copy.deepcopy(params)
+        if fobj is not None:
+            if "objective" not in params:
+                params["objective"] = fobj
+            else:
+                warnings.warn(
+                    "Objective function is specified by param['objective'] and therefore `fobj`"
+                    " will be ignored.",
+                    UserWarning,
+                )
 
         # Handling alias metrics.
         _handling_alias_metrics(params)
 
         if early_stopping_rounds is not None:
-            if callbacks is None:
-                callbacks = []
-            callbacks.append(lgb.early_stopping(stopping_rounds=early_stopping_rounds))
+            callbacks.append(
+                lgb.early_stopping(
+                    stopping_rounds=early_stopping_rounds, verbose=bool(verbose_eval)
+                )
+            )
 
         args = [params, train_set]
         kwargs: Dict[str, Any] = dict(
             num_boost_round=num_boost_round,
-            fobj=fobj,
             feval=feval,
             feature_name=feature_name,
             categorical_feature=categorical_feature,
-            verbose_eval=verbose_eval,
             callbacks=callbacks,
             time_budget=time_budget,
             sample_size=sample_size,
@@ -818,9 +833,26 @@ def __init__(
         *,
         optuna_seed: Optional[int] = None,
     ) -> None:
+        if callbacks is None:
+            callbacks = []
+
+        if evals_result is not None:
+            callbacks.append(lgb.record_evaluation(evals_result))
+
+        if learning_rates is not None:
+            callbacks.append(lgb.reset_parameter(learning_rate=learning_rates))
+
+        if verbose_eval == "warn":
+            verbose_eval = False if callbacks else True
+        if verbose_eval is True:
+            callbacks.append(lgb.log_evaluation())
+        elif isinstance(verbose_eval, int):
+            callbacks.append(lgb.log_evaluation(verbose_eval))
+
         super().__init__(
             params,
             train_set,
+            callbacks=callbacks,
             num_boost_round=num_boost_round,
             fobj=fobj,
             feval=feval,
@@ -828,7 +860,6 @@ def __init__(
             categorical_feature=categorical_feature,
             early_stopping_rounds=early_stopping_rounds,
             verbose_eval=verbose_eval,
-            callbacks=callbacks,
             time_budget=time_budget,
             sample_size=sample_size,
             study=study,
@@ -841,8 +872,6 @@ def __init__(
 
         self.lgbm_kwargs["valid_sets"] = valid_sets
         self.lgbm_kwargs["valid_names"] = valid_names
-        self.lgbm_kwargs["evals_result"] = evals_result
-        self.lgbm_kwargs["learning_rates"] = learning_rates
         self.lgbm_kwargs["keep_training_booster"] = keep_training_booster
 
         self._best_booster_with_trial_number: Optional[Tuple[lgb.Booster, int]] = None
@@ -984,17 +1013,25 @@ def __init__(
         *,
         optuna_seed: Optional[int] = None,
     ) -> None:
+        if callbacks is None:
+            callbacks = []
+
+        if verbose_eval is True:
+            callbacks.append(lgb.log_evaluation(show_stdv=show_stdv))
+        elif isinstance(verbose_eval, int):
+            callbacks.append(lgb.log_evaluation(period=verbose_eval, show_stdv=show_stdv))
+
         super().__init__(
             params,
             train_set,
-            num_boost_round,
+            callbacks=callbacks,
+            num_boost_round=num_boost_round,
             fobj=fobj,
             feval=feval,
             feature_name=feature_name,
             categorical_feature=categorical_feature,
             early_stopping_rounds=early_stopping_rounds,
             verbose_eval=verbose_eval,
-            callbacks=callbacks,
             time_budget=time_budget,
             sample_size=sample_size,
             study=study,
@@ -1009,7 +1046,6 @@ def __init__(
         self.lgbm_kwargs["nfold"] = nfold
         self.lgbm_kwargs["stratified"] = stratified
         self.lgbm_kwargs["shuffle"] = shuffle
-        self.lgbm_kwargs["show_stdv"] = show_stdv
         self.lgbm_kwargs["seed"] = seed
         self.lgbm_kwargs["fpreproc"] = fpreproc
         self.lgbm_kwargs["return_cvbooster"] = return_cvbooster
diff --git a/optuna/integration/lightgbm.py b/optuna/integration/lightgbm.py
--- a/optuna/integration/lightgbm.py
+++ b/optuna/integration/lightgbm.py
@@ -92,7 +92,10 @@ def _find_evaluation_result(
     ) -> Optional[List]:
         for evaluation_result in env.evaluation_result_list:
             valid_name, metric, current_score, is_higher_better = evaluation_result[:4]
-            if valid_name != target_valid_name or metric != self._metric:
+            # The prefix "valid " is added to metric name since LightGBM v4.0.0.
+            if valid_name != target_valid_name or (
+                metric != "valid " + self._metric and metric != self._metric
+            ):
                 continue
 
             return evaluation_result


Test patch:
diff --git a/tests/integration_tests/lightgbm_tuner_tests/test_optimize.py b/tests/integration_tests/lightgbm_tuner_tests/test_optimize.py
--- a/tests/integration_tests/lightgbm_tuner_tests/test_optimize.py
+++ b/tests/integration_tests/lightgbm_tuner_tests/test_optimize.py
@@ -56,7 +56,7 @@ def current_iteration(self) -> int:
 @contextlib.contextmanager
 def turnoff_cv(metric: str = "binary_logloss") -> Generator[None, None, None]:
     unexpected_value = 0.5
-    dummy_results = {"{}-mean".format(metric): [unexpected_value]}
+    dummy_results = {"valid {}-mean".format(metric): [unexpected_value]}
 
     with mock.patch("lightgbm.cv", return_value=dummy_results):
         yield