Sintonização do hiperparâmetro com Optuna & MLflow¶
Os hiperparâmetros de ajuste são um passo crítico no ciclo de vida do CD4ML para evitar overfitting e otimizar a precisão da previsão. Neste caderno, automatizamos a varredura do hiperparámetro sobre a força de regularização do nosso formato fechado RidgeDemandRegressor usando Optuna.
Por que Optuna?¶
- Pesquisa eficiente: Em vez da pesquisa em grade, Optuna usa um amostrador Parzen Estimator (TPE) estruturado em árvore para sugerir valores, concentrando-se em regiões promissoras no espaço de registro.
- Integração: Realizamos 25 ensaios, registrando cada seleção de parâmetros e as métricas de validação resultantes para MLflow como uma corrida aninhada dentro de um dos pais
hyperparameter_tuningpara preservar a história da experiência.
from pathlib import Path
import matplotlib.pyplot as plt
import mlflow
from nyc_taxi_demand_forecasting.configuration import ProjectConfigLoader
from nyc_taxi_demand_forecasting.models.tuning import DemandModelTuner
# Load project configuration
config_path = Path("../configs/project.yaml")
config = ProjectConfigLoader().load(config_path)
# Execute Optuna study search
tuner = DemandModelTuner()
result = tuner.select_best(
dataset_path=config.features.training_dataset_path,
target_column=config.training.target_column,
test_size=config.training.test_size,
feast_config=config.feast,
mlflow_config=config.mlflow,
)
print("Tuning completed successfully!")
print(f"Optimal Alpha regularization: {result.alpha:.4f}")
print(
f"Best Validation Mean Absolute Error (MAE): {result.validation_mae:.4f}"
)
Stream
/home/gabriel-menezes/Documents/repos/mlops/.venv/lib/python3.11/site-packages/ibis/backends/duckdb/__init__.py:332: DeprecationWarning: fetch_arrow_table() is deprecated, use to_arrow_table() instead.
meta = result.fetch_arrow_table()
Stream
Tuning completed successfully!
Optimal Alpha regularization: 9917.0641
Best Validation Mean Absolute Error (MAE): 10.9974
Recuperar e Plotizar Métricas de MLflow¶
Podemos consultar o MLflow Tracking Server usando mlflow.search_runs para visualizar o desempenho em todo o nosso espaço hiperparâmetro.
# Retrieve all trial runs under the active experiment
experiment = mlflow.get_experiment_by_name("nyc_taxi_demand_forecasting")
runs = mlflow.search_runs(
experiment_ids=[experiment.experiment_id],
filter_string="tags.mlflow.runName LIKE 'tune_ridge_trial_%'",
)
# Extract parameters and metrics
runs["alpha"] = runs["params.alpha"].astype(float)
runs["mae"] = runs["metrics.mae"].astype(float)
runs = runs.sort_values("start_time").reset_index(drop=True)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Plot 1: Optimization History
ax1.plot(
runs.index + 1,
runs["mae"],
marker="o",
color="#1f77b4",
linewidth=2,
)
best_idx = runs["mae"].idxmin()
best_mae = runs.loc[best_idx, "mae"]
best_alpha = runs.loc[best_idx, "alpha"]
ax1.axhline(best_mae, color="red", linestyle="--", alpha=0.7)
ax1.annotate(
f"Best Trial #{best_idx + 1}\nMAE: {best_mae:.4f}",
xy=(best_idx + 1, best_mae),
xytext=(20, 25),
textcoords="offset points",
arrowprops=dict(facecolor="black", shrink=0.08, width=1, headwidth=6),
fontsize=10,
)
ax1.set_title("Optimization History (MAE over Trials)", fontsize=12)
ax1.set_xlabel("Trial Number", fontsize=10)
ax1.set_ylabel("Validation MAE", fontsize=10)
ax1.grid(True, alpha=0.3)
# Plot 2: Parameter Search Space
ax2.scatter(
runs["alpha"],
runs["mae"],
color="#e6550d",
s=60,
alpha=0.8,
edgecolors="k",
)
ax2.set_xscale("log")
ax2.axvline(best_alpha, color="red", linestyle="--", alpha=0.7)
ax2.annotate(
f"Best Alpha: {best_alpha:.4f}",
xy=(best_alpha, best_mae),
xytext=(20, 25),
textcoords="offset points",
arrowprops=dict(facecolor="black", shrink=0.08, width=1, headwidth=6),
fontsize=10,
)
ax2.set_title("Parameter Search Space (MAE vs Alpha)", fontsize=12)
ax2.set_xlabel("Regularization Weight (Alpha, log-scale)", fontsize=10)
ax2.set_ylabel("Validation MAE", fontsize=10)
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
