Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,159 changes: 1,159 additions & 0 deletions examples/tutorials/try_optuna.ipynb

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
)
from openstef_models.utils.data_split import DataSplitter
from openstef_models.utils.feature_selection import Exclude, FeatureSelection, Include
from openstef_models.utils.tuning import TuningConfigMixin
from openstef_models.workflows.custom_forecasting_workflow import (
CustomForecastingWorkflow,
ForecastingCallback,
Expand All @@ -74,7 +75,7 @@
from openstef_core.datasets import ForecastDataset


class EnsembleForecastingWorkflowConfig(BaseConfig):
class EnsembleForecastingWorkflowConfig(TuningConfigMixin, BaseConfig):
"""Configuration for ensemble forecasting workflows."""

kind: Literal["ensemble"] = Field(default="ensemble", description="Discriminator tag for config type.")
Expand Down Expand Up @@ -280,6 +281,16 @@ class EnsembleForecastingWorkflowConfig(BaseConfig):
description="Optional metadata tags for experiment tracking.",
)

# Hyperparameter tuning (Optuna)
optuna_n_trials: int = Field(
default=50,
description="Number of Optuna trials to run during hyperparameter tuning.",
)
optuna_seed: int | None = Field(
default=None,
description="Random seed for Optuna sampler reproducibility. None disables seeding.",
)


def _checks(config: EnsembleForecastingWorkflowConfig) -> list[Transform[TimeSeriesDataset, TimeSeriesDataset]]:
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
to predict values outside the range of the training data.
"""

from typing import ClassVar, Literal, override
from typing import Annotated, ClassVar, Literal, override

import numpy as np
import pandas as pd
Expand All @@ -22,7 +22,6 @@
from openstef_core.datasets.mixins import LeadTime
from openstef_core.datasets.validated_datasets import ForecastDataset, ForecastInputDataset
from openstef_core.exceptions import InputValidationError, MissingExtraError, NotFittedError
from openstef_core.mixins.predictor import HyperParams
from openstef_core.utils.pandas import normalize_to_unit_sum
from openstef_models.explainability.mixins import ContributionsMixin, ExplainableForecaster
from openstef_models.models.forecasting.forecaster import Forecaster
Expand All @@ -32,26 +31,27 @@
get_objective_function,
xgb_prepare_target_for_objective,
)
from openstef_models.utils.tuning import CategoricalRange, FloatRange, IntRange, TunableHyperParams

try:
import xgboost as xgb
except ImportError as e:
raise MissingExtraError("xgboost", "openstef-models") from e


class GBLinearHyperParams(HyperParams):
class GBLinearHyperParams(TunableHyperParams):
"""Hyperparameter configuration for GBLinear forecaster."""

# Learning Parameters
n_steps: int = Field(
n_steps: Annotated[int, IntRange(50, 1000)] = Field(
default=500,
description="Number for steps (boosting rounds) to train the GBLinear model.",
)
updater: str = Field(
updater: Annotated[str, CategoricalRange(("shotgun", "coord_descent"))] = Field(
default="shotgun",
description="The updater to use for the GBLinear booster.",
)
learning_rate: float = Field(
learning_rate: Annotated[float, FloatRange(0.01, 0.5, log=True)] = Field(
default=0.15,
description="Step size shrinkage used to prevent overfitting. Range: [0,1]. Lower values require more boosting "
"rounds.",
Expand All @@ -68,15 +68,15 @@ class GBLinearHyperParams(HyperParams):
)

# Regularization
reg_alpha: float = Field(
reg_alpha: Annotated[float, FloatRange(1e-8, 1.0, log=True)] = Field(
default=0.0001, description="L1 regularization on weights. Higher values increase regularization. Range: [0,∞]"
)
reg_lambda: float = Field(
reg_lambda: Annotated[float, FloatRange(1e-8, 1.0, log=True)] = Field(
default=0.1, description="L2 regularization on weights. Higher values increase regularization. Range: [0,∞]"
)

# Feature selection
feature_selector: str = Field(
feature_selector: Annotated[str, CategoricalRange(("cyclic", "shuffle", "random", "greedy", "thrifty"))] = Field(
default="shuffle",
description="Feature selection method.",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
comprehensive hyperparameter control for production forecasting workflows.
"""

from typing import ClassVar, Literal, override
from typing import Annotated, ClassVar, Literal, override

import numpy as np
import pandas as pd
Expand All @@ -18,7 +18,6 @@

from openstef_core.datasets import ForecastDataset, ForecastInputDataset, TimeSeriesDataset
from openstef_core.exceptions import MissingExtraError, NotFittedError
from openstef_core.mixins import HyperParams
from openstef_core.utils.pandas import normalize_to_unit_sum
from openstef_models.explainability.mixins import ContributionsMixin, ExplainableForecaster
from openstef_models.models.forecasting.forecaster import Forecaster
Expand All @@ -28,14 +27,15 @@
get_objective_function,
xgb_prepare_target_for_objective,
)
from openstef_models.utils.tuning import CategoricalRange, FloatRange, IntRange, TunableHyperParams

try:
import xgboost as xgb
except ImportError as e:
raise MissingExtraError("xgboost", "openstef-models") from e


class XGBoostHyperParams(HyperParams):
class XGBoostHyperParams(TunableHyperParams):
"""XGBoost hyperparameters for gradient boosting tree models.

Configures tree-specific parameters for XGBoost gbtree booster. Provides
Expand Down Expand Up @@ -65,28 +65,28 @@ class XGBoostHyperParams(HyperParams):
"""

# Core Tree Boosting Parameters
n_estimators: int = Field(
n_estimators: Annotated[int, IntRange(50, 500)] = Field(
default=100,
description="Number of boosting rounds/trees to fit. Higher values may improve performance but "
"increase training time and risk overfitting.",
)
learning_rate: float = Field(
learning_rate: Annotated[float, FloatRange(0.01, 0.5, log=True)] = Field(
default=0.3,
alias="eta",
description="Step size shrinkage used to prevent overfitting. Range: [0,1]. Lower values require "
"more boosting rounds.",
)
max_depth: int = Field(
max_depth: Annotated[int, IntRange(1, 15)] = Field(
default=6,
description="Maximum depth of trees. Higher values capture more complex patterns but risk "
"overfitting. Range: [1,∞]",
)
min_child_weight: float = Field(
min_child_weight: Annotated[float, FloatRange(1.0, 10.0)] = Field(
default=1,
description="Minimum sum of instance weight (hessian) needed in a child. Higher values prevent "
"overfitting. Range: [0,∞]",
)
gamma: float = Field(
gamma: Annotated[float, FloatRange(0.0, 5.0)] = Field(
default=0,
alias="min_split_loss",
description="Minimum loss reduction required to make a split. Higher values make algorithm more "
Expand All @@ -103,10 +103,10 @@ class XGBoostHyperParams(HyperParams):
)

# Regularization
reg_alpha: float = Field(
reg_alpha: Annotated[float, FloatRange(1e-8, 10.0, log=True)] = Field(
default=0, description="L1 regularization on leaf weights. Higher values increase regularization. Range: [0,∞]"
)
reg_lambda: float = Field(
reg_lambda: Annotated[float, FloatRange(1e-8, 10.0, log=True)] = Field(
default=1, description="L2 regularization on leaf weights. Higher values increase regularization. Range: [0,∞]"
)
max_delta_step: float = Field(
Expand All @@ -119,7 +119,7 @@ class XGBoostHyperParams(HyperParams):
max_leaves: int = Field(
default=0, description="Maximum number of leaves. 0 means no limit. Only relevant when grow_policy='lossguide'."
)
grow_policy: Literal["depthwise", "lossguide"] = Field(
grow_policy: Annotated[Literal["depthwise", "lossguide"], CategoricalRange(("depthwise", "lossguide"))] = Field(
default="depthwise",
description="Controls how new nodes are added. 'depthwise' grows level by level, 'lossguide' adds leaves "
"with highest loss reduction.",
Expand All @@ -136,11 +136,11 @@ class XGBoostHyperParams(HyperParams):
)

# Subsampling Parameters
subsample: float = Field(
subsample: Annotated[float, FloatRange(0.5, 1.0)] = Field(
default=1.0,
description="Fraction of training samples used for each tree. Lower values prevent overfitting. Range: (0,1]",
)
colsample_bytree: float = Field(
colsample_bytree: Annotated[float, FloatRange(0.5, 1.0)] = Field(
default=1.0, description="Fraction of features used when constructing each tree. Range: (0,1]"
)
colsample_bylevel: float = Field(
Expand All @@ -151,7 +151,10 @@ class XGBoostHyperParams(HyperParams):
)

# Tree Construction Method
tree_method: Literal["auto", "exact", "hist", "approx", "gpu_hist"] = Field(
tree_method: Annotated[
Literal["auto", "exact", "hist", "approx", "gpu_hist"],
CategoricalRange(("auto", "hist", "approx")),
] = Field(
default="auto",
description="Tree construction algorithm. 'hist' is fastest for large datasets, 'exact' for small "
"datasets, 'approx' is deprecated.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@
Provides configurations and utilities for setting up forecasting workflows.
"""

from .forecasting_workflow import ForecastingWorkflowConfig, create_forecasting_workflow
from openstef_models.utils.tuning import TuningResult, fit_with_tuning, tune

from .forecasting_workflow import (
ForecastingWorkflowConfig,
create_forecasting_workflow,
)

__all__ = [
"ForecastingWorkflowConfig",
"TuningResult",
"create_forecasting_workflow",
"fit_with_tuning",
"tune",
]
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
)
from openstef_models.utils.data_split import DataSplitter
from openstef_models.utils.feature_selection import Exclude, FeatureSelection, Include
from openstef_models.utils.tuning import TuningConfigMixin
from openstef_models.workflows.custom_forecasting_workflow import (
CustomForecastingWorkflow,
ForecastingCallback,
Expand Down Expand Up @@ -100,7 +101,7 @@ def tags(self) -> dict[str, str]:
}


class ForecastingWorkflowConfig(BaseConfig): # PredictionJob
class ForecastingWorkflowConfig(TuningConfigMixin, BaseConfig): # PredictionJob
"""Configuration for forecasting workflows.

Defines all parameters needed to set up a forecasting model, including model type,
Expand Down Expand Up @@ -278,6 +279,16 @@ class ForecastingWorkflowConfig(BaseConfig): # PredictionJob
default=0, description="Verbosity level. 0=silent, 1=warning, 2=info, 3=debug"
)

# Hyperparameter tuning (Optuna)
optuna_n_trials: int = Field(
default=20,
description="Number of Optuna trials to run when any search-space field has tune=True.",
)
optuna_seed: int | None = Field(
default=42,
description="Random seed for the Optuna TPE sampler. Set to None to disable seeding.",
)

# Metadata
tags: dict[str, str] = Field(
default_factory=dict,
Expand Down
Loading
Loading