diff --git a/.gitignore b/.gitignore index 0840f1ac28..36ca8d5296 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ docs_env .venv .env uv.lock +*mlruns/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ab6b94ad6..e68c1cc30f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ but cannot always guarantee backwards compatibility. Changes that may **break co **Improved** +- 🚀🚀 Added a custom MLflow model flavor for Darts, available under `darts.utils.mlflow`. It provides an MLflow integration for any Darts `ForecastingModel` (statistical, scikit-learn-like, and PyTorch-based). [#3022](https://github.com/unit8co/darts/pull/3022) by [Jakub Chłapek](https://github.com/jakubchlapek), [Zhihao Dai](https://github.com/daidahao) and [Michel Zeller](https://github.com/mizeller). + - Added `save_model()`, `load_model()`, and `log_model()` to persist and reload Darts models as MLflow models, including model and covariate metadata. + - Added `autolog()` for model parameters, series/covariate metadata, optional model artifacts, PyTorch epoch metrics, and backtest metrics. Nested historical-forecast fits are suppressed. + - Darts metrics are logged with shape-aware keys; multi-series and window-level backtest details are available in `metrics_per_series.json`. + - Added a new notebook for [MLflow quickstart](https://unit8co.github.io/darts/examples/29-MLflow-quickstart.html) with detailed usage examples. + - Note: model serving and deployment (MLflow's `pyfunc` flavor, model signatures, and input examples) are not yet supported. - Added support for per-timestep (non-aggregated) encoder and decoder variable importances in `TFTExplainer`, exposed as `TimeSeries` via `TFTExplainabilityResult.get_encoder_importance_over_time()` and `get_decoder_importance_over_time()`. [#3170](https://github.com/unit8co/darts/pull/3170) by [exactml](https://github.com/exactml). - Calling `TFTModel.fit_from_dataset()` on a dataset that does not have future covariates now raises an informative exception. [#3149](https://github.com/unit8co/darts/pull/3149) by [YOON KIWOONG](https://github.com/kiwoongyoon). - 🔴 Percentage and range-based metrics (`ape`, `mape`, `sape`, `smape`, `wmape`, `ope`, `arre`, `marre`, `coefficient_of_variation`) no longer raise a hard `ValueError` when the denominator is exactly zero. A new `zero_division` parameter controls the behavior: [#3122](https://github.com/unit8co/darts/pull/3122) by [Mahimn](https://github.com/mahimn01). @@ -26,6 +32,8 @@ but cannot always guarantee backwards compatibility. Changes that may **break co **Dependencies** +- Added `mlflow>=3.0` to the optional dependency group, enabling the MLflow integration. [#3022](https://github.com/unit8co/darts/pull/3022) by [Jakub Chłapek](https://github.com/jakubchlapek), [Zhihao Dai](https://github.com/daidahao) and [Michel Zeller](https://github.com/mizeller). + ### For developers of the library: ## [0.46.1](https://github.com/unit8co/darts/tree/0.46.1) (2026-07-20) diff --git a/INSTALL.md b/INSTALL.md index f52f9c2708..302c7d7421 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -22,6 +22,12 @@ Some models have additional dependencies that are not included in the `all` inst | `NeuralForecastModel` | neuralforecast>=3.0.0 | | `TiRexModel` | tirex-ts>=1.4.0 | +Some optional integrations also require additional dependencies: + +| Integration | Dependencies | +|-------------------------|--------------| +| `darts.utils.mlflow` | mlflow>=3.0 | + ## From conda-forge Create a conda environment (e.g., for Python 3.11): diff --git a/darts/metrics/utils.py b/darts/metrics/utils.py index 2f893c2bcd..3bdd974b20 100644 --- a/darts/metrics/utils.py +++ b/darts/metrics/utils.py @@ -155,6 +155,22 @@ def wrapper_classification_support(*args, **kwargs): return wrapper_classification_support +_metric_callbacks: list[Callable] = [] + + +def register_metric_callback(callback: Callable) -> None: + """Register a callback to be invoked after every top-level metric call.""" + _metric_callbacks.append(callback) + + +def unregister_metric_callback(callback: Callable) -> None: + """Remove a previously registered metric callback, if present.""" + try: + _metric_callbacks.remove(callback) + except ValueError: + pass + + def multi_ts_support(func) -> Callable[..., METRIC_OUTPUT_TYPE]: """ This decorator further adapts the metrics that took as input two (or three for scaled metrics with `insample`) @@ -169,6 +185,9 @@ def multi_ts_support(func) -> Callable[..., METRIC_OUTPUT_TYPE]: @wraps(func) def wrapper_multi_ts_support(*args, **kwargs): + original_args = args + original_kwargs = dict(kwargs) + actual_series = ( kwargs["actual_series"] if "actual_series" in kwargs else args[0] ) @@ -312,6 +331,10 @@ def wrapper_multi_ts_support(*args, **kwargs): elif series_seq_type == SeriesType.SINGLE: vals = vals[0] + # invoke registered callbacks (e.g. MLflow autologging) + for cb in _metric_callbacks: + cb(func=func, result=vals, args=original_args, kwargs=original_kwargs) + # flatten along series axis if n series == 1 return vals diff --git a/darts/models/forecasting/pl_forecasting_module.py b/darts/models/forecasting/pl_forecasting_module.py index 95dfa48235..80fef92fd5 100644 --- a/darts/models/forecasting/pl_forecasting_module.py +++ b/darts/models/forecasting/pl_forecasting_module.py @@ -450,8 +450,11 @@ def _update_metrics(self, output, target, metrics): pred = output.squeeze(dim=-1) # torch metrics require 2D targets of shape (batch size * ocl, num targets) - target = target.reshape(-1, self.n_targets) - pred = pred.reshape(-1, self.n_targets) + # contiguous() is needed because model outputs can be non-contiguous views + # (e.g. NBEATS slices the last dimension), and some torchmetrics implementations + # call .view() internally which requires a contiguous tensor. + target = target.reshape(-1, self.n_targets).contiguous() + pred = pred.reshape(-1, self.n_targets).contiguous() metrics.update(pred, target) diff --git a/darts/tests/conftest.py b/darts/tests/conftest.py index bf288461a8..e0f48db067 100644 --- a/darts/tests/conftest.py +++ b/darts/tests/conftest.py @@ -32,6 +32,7 @@ def _package_available(*names: str) -> bool: PLOTLY_AVAILABLE = _package_available("plotly") IPYTHON_AVAILABLE = _package_available("IPython") TIREX_AVAILABLE = _package_available("tirex") +MLFLOW_AVAILABLE = _package_available("mlflow") tfm_kwargs: dict[str, Any] = { "pl_trainer_kwargs": { diff --git a/darts/tests/optional_deps/test_mlflow.py b/darts/tests/optional_deps/test_mlflow.py new file mode 100644 index 0000000000..38c6a7b736 --- /dev/null +++ b/darts/tests/optional_deps/test_mlflow.py @@ -0,0 +1,1975 @@ +import logging +import os + +import numpy as np +import pandas as pd +import pytest + +import darts.metrics as dm +import darts.metrics.metrics as dmm +import darts.utils.timeseries_generation as tg +from darts import TimeSeries +from darts.models.forecasting.forecasting_model import ( + ForecastingModel, + GlobalForecastingModel, +) +from darts.tests.conftest import MLFLOW_AVAILABLE, TORCH_AVAILABLE, tfm_kwargs_dev + +if not MLFLOW_AVAILABLE: + pytest.skip( + f"MLflow not available. {__name__} tests will be skipped.", + allow_module_level=True, + ) + +import mlflow +from mlflow.utils.autologging_utils.client import MlflowAutologgingQueueingClient + +from darts.models import ( + ExponentialSmoothing, + LinearRegressionModel, + NaiveSeasonal, + RegressionEnsembleModel, +) +from darts.utils.mlflow import ( + _build_metric_keys, + _flush_logged_metrics, + _infer_metric_axes, + _log_backtest_metrics, + autolog, + load_model, + log_model, + save_model, +) + +if TORCH_AVAILABLE: + from darts.models import NBEATSModel + + +@pytest.fixture +def mlflow_tracking(tmpdir_fn): + """Set up MLflow tracking with a temporary database.""" + mlflow.set_tracking_uri(f"sqlite:///{tmpdir_fn}/mlflow.db") + return mlflow.tracking.MlflowClient() + + +@pytest.fixture +def autolog_context(): + """Context manager to safely enable/disable autolog for a test. + + Usage: + with autolog_context(): # default autolog + with autolog_context(log_training_metrics=True): # custom kwargs + """ + from contextlib import contextmanager + + @contextmanager + def _autolog_context(**kwargs): + autolog(disable=True) # clean state + autolog(**kwargs) # enable with custom kwargs + try: + yield + finally: + autolog(disable=True) # clean up + + return _autolog_context + + +def assert_mlflow_artifacts_exist(path: str, is_torch: bool = False): + """Assert that all required MLflow artifact files exist.""" + assert os.path.exists(os.path.join(path, "MLmodel")) + assert os.path.exists(os.path.join(path, "conda.yaml")) + assert os.path.exists(os.path.join(path, "requirements.txt")) + assert os.path.exists(os.path.join(path, "python_env.yaml")) + + if is_torch: + assert os.path.exists(os.path.join(path, "model.pt")) + assert os.path.exists(os.path.join(path, "model.pt.ckpt")) + else: + assert os.path.exists(os.path.join(path, "model.pkl")) + + +def assert_predictions_equal( + model1: ForecastingModel, + model2: ForecastingModel, + n: int, + decimal: int = 4, + is_global: bool = True, + series: TimeSeries | None = None, + past_covariates: TimeSeries | None = None, + future_covariates: TimeSeries | None = None, +): + """Assert that two models produce equivalent predictions. If series is provided, + it will be passed to the second model's predict method (for global models that require it).""" + if is_global: + assert isinstance(model1, GlobalForecastingModel) + assert isinstance(model2, GlobalForecastingModel) + pred1 = model1.predict( + n=n, + series=series, + past_covariates=past_covariates, + future_covariates=future_covariates, + ) + pred2 = model2.predict( + n=n, + series=series, + past_covariates=past_covariates, + future_covariates=future_covariates, + ) + else: + pred1 = model1.predict(n=n) + pred2 = model2.predict(n=n) + + np.testing.assert_array_almost_equal( + pred1.values(), pred2.values(), decimal=decimal + ) + + +class TestMLflow: + ts_univariate = tg.linear_timeseries( + start_value=10, end_value=50, length=50 + ).astype("float32") + ts_multivariate = ts_univariate.stack(ts_univariate * 1.5) + ts_with_static = ts_univariate.with_static_covariates( + pd.DataFrame({"static_feat": [1.0]}) + ) + ts_past_cov = tg.sine_timeseries(length=62).astype("float32") + ts_future_cov = tg.constant_timeseries(value=1.0, length=62).astype("float32") + # binary classification series with values {0.0, 1.0} + ts_binary = tg.constant_timeseries(value=0.0, length=50).with_values( + np.random.default_rng(42) + .choice([0.0, 1.0], size=50) + .astype(np.float32) + .reshape(-1, 1) + ) + + def test_save_load_statistical_model(self, tmpdir_fn): + """Test save/load round-trip for statistical model""" + model = ExponentialSmoothing() + model.fit(self.ts_univariate) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + + assert_mlflow_artifacts_exist(model_path, is_torch=False) + + loaded_model = load_model(f"file://{model_path}") + assert_predictions_equal(model, loaded_model, n=5, is_global=False) + + def test_save_load_regression_model(self, tmpdir_fn): + """Test save/load round-trip for regression model""" + model = LinearRegressionModel(lags=5) + model.fit(self.ts_univariate) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + + assert_mlflow_artifacts_exist(model_path, is_torch=False) + + loaded_model = load_model(f"file://{model_path}") + assert_predictions_equal(model, loaded_model, n=3, series=self.ts_univariate) + + @pytest.mark.skipif(not TORCH_AVAILABLE, reason="requires torch") + def test_save_load_torch_model(self, tmpdir_fn): + """Test save/load round-trip for torch model""" + model = NBEATSModel( + input_chunk_length=4, output_chunk_length=2, n_epochs=1, **tfm_kwargs_dev + ) + model.fit(self.ts_univariate) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + + assert_mlflow_artifacts_exist(model_path, is_torch=True) + + # save(clean=True) strips pl_trainer_kwargs; explicitly restore accelerator + # so Lightning doesn't default to MPS on Github macOS runner + loaded_model = load_model( + f"file://{model_path}", + pl_trainer_kwargs=tfm_kwargs_dev.get("pl_trainer_kwargs", {}), + ) + assert_predictions_equal(model, loaded_model, n=2, series=self.ts_univariate) + + def test_log_model_basic(self, mlflow_tracking): + """Test basic log_model functionality""" + model = ExponentialSmoothing() + model.fit(self.ts_univariate) + + with mlflow.start_run(): + log_info = log_model(model, name="model") + + loaded_model = load_model(log_info.model_uri) + assert_predictions_equal(model, loaded_model, n=5, is_global=False) + + def test_log_model_with_covariates(self, mlflow_tracking): + """Test that covariate info is logged with correct values""" + model = LinearRegressionModel(lags=5, lags_past_covariates=3) + model.fit(self.ts_univariate[:40], past_covariates=self.ts_past_cov[:40]) + + with mlflow.start_run(): + log_model(model, name="model") + run_id = mlflow.active_run().info.run_id + + loaded_model = load_model(f"runs:/{run_id}/model") + assert_predictions_equal( + model, + loaded_model, + n=5, + series=self.ts_univariate[:40], + past_covariates=self.ts_past_cov, + ) + + def test_log_model_with_all_covariate_types(self, mlflow_tracking): + """Test logging model with past, future, and static covariates""" + # use a model that supports all covariate types + model = LinearRegressionModel( + lags=5, lags_past_covariates=3, lags_future_covariates=[0, 1] + ) + model.fit( + self.ts_with_static[:40], + past_covariates=self.ts_past_cov[:40], + future_covariates=self.ts_future_cov[:50], + ) + + with mlflow.start_run(): + log_model(model, name="model") + run_id = mlflow.active_run().info.run_id + + loaded_model = load_model(f"runs:/{run_id}/model") + assert_predictions_equal( + model, + loaded_model, + n=5, + series=self.ts_with_static[:40], + past_covariates=self.ts_past_cov, + future_covariates=self.ts_future_cov, + ) + + def test_autolog_enable_disable(self, mlflow_tracking, autolog_context): + """Test autolog can be enabled and disabled""" + with autolog_context(): + with mlflow.start_run(): + model = ExponentialSmoothing() + model.fit(self.ts_univariate) + + runs = mlflow.search_runs() + assert len(runs) == 1, "Expected exactly one run after autolog fit" + + # verify the run has expected content + last_run = runs.iloc[0] + assert last_run["tags.model_class"] == "ExponentialSmoothing" + assert last_run["tags.mlflow.runName"] is not None + + # after context exits, autolog should be disabled + model2 = ExponentialSmoothing() + model2.fit(self.ts_univariate) + + runs_after_disable = mlflow.search_runs() + assert len(runs_after_disable) == 1, ( + "No new run should be created after disable" + ) + + def test_autolog_parameters(self, mlflow_tracking, autolog_context): + """Test that autolog logs model parameters""" + with autolog_context(): + with mlflow.start_run(): + model = ExponentialSmoothing(seasonal_periods=12) + model.fit(self.ts_univariate) + + runs = mlflow.search_runs() + assert len(runs) == 1 + + last_run = runs.iloc[0] + assert last_run["params.seasonal_periods"] == "12" + assert last_run["tags.model_class"] == "ExponentialSmoothing" + + def test_autolog_model_params_json_artifact(self, mlflow_tracking, autolog_context): + """The model_params.json artifact mirrors model.model_params, preserving + JSON-native types and falling back to str() for non-serializable values + (e.g. enums) instead of the flat params store's blanket stringification.""" + with autolog_context(): + with mlflow.start_run() as run: + model = ExponentialSmoothing(seasonal_periods=12) + model.fit(self.ts_univariate) + + params = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/model_params.json" + ) + assert params["seasonal_periods"] == 12 + assert params["trend"] == str(model.model_params["trend"]) + + @pytest.mark.skipif(not TORCH_AVAILABLE, reason="requires torch") + def test_autolog_torch_metrics(self, mlflow_tracking, autolog_context): + """Test that autolog logs training metrics for torch models""" + with autolog_context(): + with mlflow.start_run(): + model = NBEATSModel( + input_chunk_length=4, + output_chunk_length=2, + n_epochs=2, + **tfm_kwargs_dev, + ) + train, val = self.ts_univariate.split_before(0.7) + model.fit(train, val_series=val) + + runs = mlflow.search_runs() + assert len(runs) == 1, "Expected exactly one run" + last_run = runs.iloc[0] + last_run_id = last_run["run_id"] + assert last_run["tags.model_class"] == "NBEATSModel" + + client = mlflow.tracking.MlflowClient() + + # check train_loss metrics + train_metrics = client.get_metric_history(last_run_id, "train_loss") + assert len(train_metrics) > 0, "Expected train_loss metrics to be logged" + assert len(train_metrics) <= 2, "Expected at most 2 epochs of train_loss" + + for m in train_metrics: + assert np.isfinite(m.value), f"train_loss is not finite: {m.value}" + assert m.value >= 0, f"train_loss is negative: {m.value}" + assert m.step >= 0, "Metric step should be non-negative" + + val_metrics = client.get_metric_history(last_run_id, "val_loss") + if val_metrics: + for m in val_metrics: + assert np.isfinite(m.value), f"val_loss is not finite: {m.value}" + assert m.value >= 0, f"val_loss is negative: {m.value}" + + @pytest.mark.skipif(not TORCH_AVAILABLE, reason="requires torch") + def test_autolog_pytorch_autolog_enabled(self, mlflow_tracking, autolog_context): + """Test that autolog enables mlflow.pytorch.autolog and logs per-epoch + train_loss, val_loss, and custom torch_metrics with finite non-negative values.""" + import torchmetrics + from mlflow.utils.autologging_utils import autologging_is_disabled + + n_epochs = 2 + + def assert_metric(history, key): + assert len(history) > 0, f"{key} not logged" + assert len(history) <= n_epochs, f"too many {key} entries" + assert all(np.isfinite(m.value) and m.value >= 0 for m in history) + + with autolog_context(): + assert not autologging_is_disabled("pytorch") + + with mlflow.start_run(): + model = NBEATSModel( + input_chunk_length=4, + output_chunk_length=2, + n_epochs=n_epochs, + torch_metrics=torchmetrics.MeanAbsoluteError(), + **tfm_kwargs_dev, + ) + train, val = self.ts_univariate.split_before(0.7) + model.fit(train, val_series=val) + + runs = mlflow.search_runs() + assert len(runs) == 1 + run_id = runs.iloc[0]["run_id"] + assert runs.iloc[0]["tags.model_class"] == "NBEATSModel" + + client = mlflow.tracking.MlflowClient() + assert_metric(client.get_metric_history(run_id, "train_loss"), "train_loss") + assert_metric(client.get_metric_history(run_id, "val_loss"), "val_loss") + # custom torch_metrics: in normal use both train_/val_ prefixes are logged, but + # fast_dev_run suppresses the Lightning logger during traininge + assert_metric( + client.get_metric_history(run_id, "val_MeanAbsoluteError"), + "val_MeanAbsoluteError", + ) + + assert autologging_is_disabled("pytorch") + + def test_autolog_series_info_single_series(self, mlflow_tracking, autolog_context): + """The series_info.json artifact reports the target series' component + names/count, plus covariate usage, count, and names, for a + single-series fit.""" + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel(lags=5, lags_past_covariates=3) + model.fit( + self.ts_univariate[:40], past_covariates=self.ts_past_cov[:40] + ) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["series"]["count"] == self.ts_univariate.n_components + assert series_info["series"]["names"] == self.ts_univariate.components.tolist() + assert series_info["past_covariates"]["used"] is True + assert series_info["past_covariates"]["count"] == 1 + assert ( + series_info["past_covariates"]["names"] + == self.ts_past_cov.components.tolist() + ) + assert series_info["future_covariates"]["used"] is False + assert series_info["static_covariates"]["used"] is False + + def test_autolog_series_info_multi_series(self, mlflow_tracking, autolog_context): + """Fitting on a list of series doesn't leave past_covariates unreported: + `model.past_covariate_series` stays None for a multi-series fit, so the + info must come from the actual `fit()` call arguments instead.""" + series = [self.ts_univariate, self.ts_univariate * 1.2] + past_covs = [self.ts_past_cov[:50], self.ts_past_cov[:50]] + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel(lags=5, lags_past_covariates=3) + model.fit(series, past_covariates=past_covs) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["past_covariates"]["used"] is True + assert series_info["past_covariates"]["count"] == 1 + assert ( + series_info["past_covariates"]["names"] + == self.ts_past_cov.components.tolist() + ) + + def test_autolog_series_info_add_encoders(self, mlflow_tracking, autolog_context): + """Covariates generated purely via `add_encoders` (no explicit + covariate argument passed to `fit()`) are still reported in + `series_info.json`.""" + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel( + lags=5, + lags_future_covariates=[0], + add_encoders={"datetime_attribute": {"future": ["month"]}}, + ) + model.fit(self.ts_univariate[:40]) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + expected_names = model.encoders.future_components.tolist() + assert expected_names # sanity check: encoders actually generated something + assert series_info["future_covariates"]["used"] is True + assert series_info["future_covariates"]["count"] == len(expected_names) + assert series_info["future_covariates"]["names"] == expected_names + assert series_info["past_covariates"]["used"] is False + + def test_autolog_series_info_static_is_global( + self, mlflow_tracking, autolog_context + ): + """`is_global` is based on row count vs. the number of series + components, not the number of static-covariate columns.""" + # one shared row over 2 components -> global + global_target = self.ts_multivariate.with_static_covariates( + pd.DataFrame({"a": [1.0], "b": [2.0]}) + ) + with autolog_context(): + with mlflow.start_run() as run: + LinearRegressionModel(lags=5).fit(global_target) + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["static_covariates"]["is_global"] is True + + # one row per component (2 components, 2 rows) -> component-specific + per_component_target = self.ts_multivariate.with_static_covariates( + pd.DataFrame({"a": [1.0, 2.0]}) + ) + with autolog_context(): + with mlflow.start_run() as run: + LinearRegressionModel(lags=5).fit(per_component_target) + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["static_covariates"]["is_global"] is False + + def test_autolog_historical_forecasts_series_info_covariates( + self, mlflow_tracking, autolog_context + ): + """HF without a prior fit() still reports explicit covariates and static + covariates in series_info / tags (outer model stays unfitted).""" + target = self.ts_univariate.with_static_covariates( + pd.DataFrame({"static_feat": [1.0]}) + ) + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel(lags=5, lags_past_covariates=3) + model.historical_forecasts( + series=target, + past_covariates=self.ts_past_cov, + forecast_horizon=1, + retrain=True, + start=0.5, + ) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["past_covariates"]["used"] is True + assert ( + series_info["past_covariates"]["names"] + == self.ts_past_cov.components.tolist() + ) + assert series_info["static_covariates"]["used"] is True + assert series_info["static_covariates"]["names"] == ["static_feat"] + tags = mlflow.tracking.MlflowClient().get_run(run.info.run_id).data.tags + assert tags["model_uses_past_covariates"] == "True" + assert tags["model_uses_static_covariates"] == "True" + + def test_autolog_historical_forecasts_series_info_add_encoders( + self, mlflow_tracking, autolog_context + ): + """HF with add_encoders (no explicit cov args) marks future covariates + as used even though the outer model remains unfitted.""" + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel( + lags=5, + lags_future_covariates=[0], + add_encoders={"datetime_attribute": {"future": ["month"]}}, + ) + model.historical_forecasts( + series=self.ts_univariate, + forecast_horizon=1, + retrain=True, + start=0.5, + ) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["future_covariates"]["used"] is True + tags = mlflow.tracking.MlflowClient().get_run(run.info.run_id).data.tags + assert tags["model_uses_future_covariates"] == "True" + + def test_autolog_backtest_series_info_covariates( + self, mlflow_tracking, autolog_context + ): + """backtest(retrain=True) without a prior fit() reports covariates via + the internal historical_forecasts path.""" + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel(lags=5, lags_past_covariates=3) + model.backtest( + series=self.ts_univariate, + past_covariates=self.ts_past_cov, + forecast_horizon=1, + retrain=True, + start=0.5, + metric=dm.mae, + ) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["past_covariates"]["used"] is True + assert ( + series_info["past_covariates"]["names"] + == self.ts_past_cov.components.tolist() + ) + + def test_autolog_historical_forecasts_logs_model_setup( + self, mlflow_tracking, autolog_context + ): + """historical_forecasts(retrain=True) without a prior fit() still logs + model params and series_info (but not the model artifact).""" + with autolog_context(): + with mlflow.start_run() as run: + model = NaiveSeasonal(K=1) + model.historical_forecasts( + series=self.ts_univariate, forecast_horizon=1, retrain=True + ) + + params = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/model_params.json" + ) + assert params["K"] == 1 + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert series_info["series"]["names"] == self.ts_univariate.components.tolist() + client = mlflow.tracking.MlflowClient() + artifact_paths = [a.path for a in client.list_artifacts(run.info.run_id)] + assert "model" not in artifact_paths + assert ( + client.get_run(run.info.run_id).data.tags["model_class"] == "NaiveSeasonal" + ) + + def test_autolog_historical_forecasts_retrain_false_skips_model_setup( + self, mlflow_tracking, autolog_context + ): + """historical_forecasts(retrain=False) does not log model setup.""" + model = LinearRegressionModel(lags=5) + model.fit(self.ts_univariate[:40]) + with autolog_context(): + with mlflow.start_run() as run: + model.historical_forecasts( + series=self.ts_univariate, + forecast_horizon=1, + retrain=False, + start=0.5, + ) + + client = mlflow.tracking.MlflowClient() + artifact_paths = [a.path for a in client.list_artifacts(run.info.run_id)] + assert "model_params.json" not in artifact_paths + assert "series_info.json" not in artifact_paths + + def test_autolog_historical_forecasts_overwrites_fit_setup( + self, mlflow_tracking, autolog_context + ): + """fit() then historical_forecasts(retrain=True) in the same run + overwrites model_params / series_info with the HF call's series.""" + with autolog_context(): + with mlflow.start_run() as run: + model = LinearRegressionModel(lags=5) + model.fit(self.ts_univariate[:30]) + model.historical_forecasts( + series=self.ts_multivariate, + forecast_horizon=1, + retrain=True, + start=0.5, + ) + + series_info = mlflow.artifacts.load_dict( + f"runs:/{run.info.run_id}/series_info.json" + ) + assert ( + series_info["series"]["names"] == self.ts_multivariate.components.tolist() + ) + + def test_multivariate_with_all_covariate_types(self, mlflow_tracking): + """Test saving/loading multivariate series with all covariate types""" + # create multivariate target with static covariates + target = self.ts_multivariate.with_static_covariates( + pd.DataFrame({"static_feat_1": [1.0], "static_feat_2": [2.0]}) + ) + + model = LinearRegressionModel( + lags=5, lags_past_covariates=3, lags_future_covariates=[0, 1] + ) + model.fit( + target[:40], + past_covariates=self.ts_past_cov[:40], + future_covariates=self.ts_future_cov[:50], + ) + + with mlflow.start_run(): + log_model(model, name="model") + run_id = mlflow.active_run().info.run_id + + loaded_model = load_model(f"runs:/{run_id}/model") + assert_predictions_equal( + loaded_model, + model, + n=5, + series=target[:40], + past_covariates=self.ts_past_cov, + future_covariates=self.ts_future_cov, + ) + + @pytest.mark.skipif(not TORCH_AVAILABLE, reason="requires torch") + def test_pytorch_autolog_with_existing_callbacks( + self, mlflow_tracking, autolog_context + ): + """Test pytorch autolog works when model already has callbacks""" + # create model with existing callback + if TORCH_AVAILABLE: + import pytorch_lightning as pl + + existing_callback = pl.callbacks.EarlyStopping(monitor="train_loss") + else: + pytest.skip("PyTorch Lightning not available") + + with autolog_context(): + model = NBEATSModel( + input_chunk_length=4, + output_chunk_length=2, + n_epochs=2, + pl_trainer_kwargs={ + **tfm_kwargs_dev.get("pl_trainer_kwargs", {}), + "callbacks": [existing_callback], + }, + **{k: v for k, v in tfm_kwargs_dev.items() if k != "pl_trainer_kwargs"}, + ) + + train, val = self.ts_univariate.split_before(0.7) + model.fit(train, val_series=val) + + # verify existing callback is still present (not removed by autolog) + callbacks = model.trainer_params.get("callbacks", []) + has_existing = any( + isinstance(cb, pl.callbacks.EarlyStopping) for cb in callbacks + ) + assert has_existing, "Existing EarlyStopping callback should be preserved" + + # verify metrics were still logged via mlflow.pytorch.autolog + runs = mlflow.search_runs() + assert len(runs) >= 1, "Expected at least one run" + last_run_id = runs.iloc[0]["run_id"] + client = mlflow.tracking.MlflowClient() + train_metrics = client.get_metric_history(last_run_id, "train_loss") + assert len(train_metrics) > 0, ( + "Expected train_loss metrics to be logged via pytorch autolog" + ) + + def test_autolog_multiple_fits(self, mlflow_tracking, autolog_context): + """Test that multiple fits with autolog create separate runs""" + with autolog_context(): + # since managed_run=True, subsequent fits will reuse the existing run, + # so we explicitly start runs for each fit + with mlflow.start_run(): + model2 = LinearRegressionModel(lags=5) + model2.fit(self.ts_univariate) + with mlflow.start_run(): + model1 = ExponentialSmoothing() + model1.fit(self.ts_univariate) + + runs = mlflow.search_runs() + assert len(runs) == 2, "Expected two separate runs for two fits" + + # verify different model classes logged + model_classes = set(runs["tags.model_class"]) + assert "ExponentialSmoothing" in model_classes + assert "LinearRegressionModel" in model_classes + + def test_autolog_ensemble_model_fit_logs_once( + self, mlflow_tracking, autolog_context + ): + """Fitting a composite/ensemble model must log exactly one model artifact + for the outer model, not one per sub-model too. + + RegressionEnsembleModel.fit() internally calls fit() on each of its + forecasting_models. Since every ForecastingModel subclass is patched + independently, an unguarded patch would re-trigger autologging for each + inner fit() call as well as the outer one. + """ + model = RegressionEnsembleModel( + forecasting_models=[ + NaiveSeasonal(K=12), + LinearRegressionModel(lags=12), + ], + regression_train_n_points=12, + ) + + with autolog_context(log_models=True): + with mlflow.start_run() as run: + model.fit(self.ts_univariate) + + logged_models = mlflow_tracking.search_logged_models( + experiment_ids=[run.info.experiment_id] + ) + assert len(logged_models) == 1, ( + f"Expected exactly one logged model, got {len(logged_models)}: " + f"{[m.name for m in logged_models]}" + ) + assert logged_models[0].name == "RegressionEnsembleModel" + + @pytest.mark.skipif(not TORCH_AVAILABLE, reason="requires torch") + def test_autolog_torch_model_multiple_fits(self, mlflow_tracking, autolog_context): + """Test autolog with multiple fits of a torch model""" + with autolog_context(): + with mlflow.start_run(): + model1 = NBEATSModel( + input_chunk_length=4, + output_chunk_length=2, + n_epochs=1, + **tfm_kwargs_dev, + ) + train, val = self.ts_univariate.split_before(0.7) + model1.fit(train, val_series=val) + + with mlflow.start_run(): + model2 = LinearRegressionModel(lags=5) + model2.fit(self.ts_univariate) + + runs = mlflow.search_runs() + assert len(runs) == 2, "Expected two separate runs for two fits" + + for _, run in runs.iterrows(): + assert run["tags.model_class"] in [ + "NBEATSModel", + "LinearRegressionModel", + ] + assert run["tags.mlflow.runName"] is not None + + def test_save_load_preserves_series_metadata(self, tmpdir_fn): + """Test that save/load preserves multivariate and static covariate structure""" + target = self.ts_multivariate.with_static_covariates( + pd.DataFrame({"stat1": [1.0], "stat2": [2.0]}) + ) + + model = LinearRegressionModel(lags=5) + model.fit(target) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + + loaded_model = load_model(f"file://{model_path}") + + pred_original = model.predict(n=3) + pred_loaded = loaded_model.predict(n=3, series=target) + + # verify multivariate structure preserved + assert pred_original.width == pred_loaded.width == 2, ( + "Should maintain 2 components" + ) + assert pred_original.n_components == pred_loaded.n_components == 2 + + np.testing.assert_array_almost_equal( + pred_original.values(), pred_loaded.values(), decimal=4 + ) + + def test_load_nonexistent_model(self): + """Test that loading nonexistent model raises appropriate error""" + with pytest.raises(Exception): + load_model("runs:/fake_run_id/model") + + def test_load_invalid_uri_fails(self): + """Test that loading with invalid URI raises an error""" + with pytest.raises(Exception): + load_model("invalid://bad/uri") + + with pytest.raises(Exception): + load_model("file:///nonexistent/path/to/model") + + def test_load_corrupted_mlmodel_fails(self, tmpdir_fn): + """Test that loading with corrupted MLmodel file fails""" + # save a valid model + model = LinearRegressionModel(lags=5) + model.fit(self.ts_univariate) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + + # corrupt the MLmodel file + mlmodel_path = os.path.join(model_path, "MLmodel") + with open(mlmodel_path, "w") as f: + f.write("corrupted content that is not valid YAML {[[") + + # loading should fail + with pytest.raises(Exception): + load_model(f"file://{model_path}") + + def test_load_missing_model_file_fails(self, tmpdir_fn): + """Test that loading with missing model data file fails""" + # save a valid model + model = LinearRegressionModel(lags=5) + model.fit(self.ts_univariate) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + + # remove the model data file + model_data_path = os.path.join(model_path, "model.pkl") + os.remove(model_data_path) + + # loading should fail + with pytest.raises(Exception): + load_model(f"file://{model_path}") + + @pytest.mark.parametrize( + "model_cls,fit_kwargs", + [ + (ExponentialSmoothing, {}), + (LinearRegressionModel, {"lags": 5}), + ], + ) + def test_save_load_multiple_models(self, tmpdir_fn, model_cls, fit_kwargs): + """Test save/load for multiple model types""" + if fit_kwargs: + model = model_cls(**fit_kwargs) + else: + model = model_cls() + + model.fit(self.ts_univariate) + + model_path = os.path.join(tmpdir_fn, "test_model") + save_model(model, model_path) + loaded = load_model(f"file://{model_path}") + + # Only pass series for global models (LinearRegressionModel) + # Local models (ExponentialSmoothing) don't need it + if isinstance(model, GlobalForecastingModel): + assert_predictions_equal(model, loaded, n=5, series=self.ts_univariate) + else: + assert_predictions_equal(model, loaded, n=5, is_global=False) + + @pytest.mark.parametrize( + "series,series_name", + [ + ("ts_multivariate", "multivariate"), + ("ts_with_static", "static_covariates"), + ], + ) + def test_save_load_with_special_series(self, tmpdir_fn, series, series_name): + """Test save/load with multivariate and static covariate series""" + test_series = getattr(self, series) + + model = LinearRegressionModel(lags=5) + model.fit(test_series) + + model_path = os.path.join(tmpdir_fn, f"test_model_{series_name}") + save_model(model, model_path) + + loaded_model = load_model(f"file://{model_path}") + + assert_predictions_equal(model, loaded_model, n=3, series=test_series) + + # verify the series dimensions are preserved + pred_original = model.predict(n=3) + pred_loaded = loaded_model.predict(n=3, series=test_series) + assert pred_original.width == pred_loaded.width + + def test_autolog_metric_logging_scalar(self, mlflow_tracking, autolog_context): + """Calling a darts metric inside an active run logs a scalar to MLflow.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + result = dm.mae(self.ts_univariate, self.ts_univariate * 1.1) + + run_data = mlflow.get_run(run.info.run_id).data + assert "mae" in run_data.metrics, "mae should be logged to MLflow" + assert np.isfinite(run_data.metrics["mae"]) + assert np.isscalar(result) + assert np.isfinite(float(result)) + + def test_autolog_metric_repeated_call(self, mlflow_tracking, autolog_context): + """Calling the same metric twice overwrites the value (last-value-wins).""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + dm.rmse(self.ts_univariate, self.ts_univariate * 1.1) + dm.rmse(self.ts_univariate, self.ts_univariate * 1.2) + + run_data = mlflow.get_run(run.info.run_id).data + assert "rmse" in run_data.metrics, "rmse should be logged to MLflow" + + def test_autolog_metric_per_component(self, mlflow_tracking, autolog_context): + """Non-scalar metric results logged per-component as {name}_{component_name}. + + ts_multivariate = ts_univariate.stack(ts_univariate * 1.5), whose component + names are ['linear', 'linear_1']. With component_reduction=None the result + is a 1-D array (one value per component), so the expected keys are + 'mae_linear' and 'mae_linear_1'. + """ + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + dm.mae( + self.ts_multivariate, + self.ts_multivariate * 1.1, + component_reduction=None, + ) + + run_data = mlflow.get_run(run.info.run_id).data + assert "mae_linear" in run_data.metrics, ( + "Component 'linear' should be logged as mae_linear" + ) + assert "mae_linear_1" in run_data.metrics, ( + "Component 'linear_1' should be logged as mae_linear_1" + ) + assert np.isfinite(run_data.metrics["mae_linear"]) + assert np.isfinite(run_data.metrics["mae_linear_1"]) + + def test_autolog_metric_no_active_run(self, mlflow_tracking, autolog_context): + """Calling a metric without an active run does not raise and returns correctly.""" + with autolog_context(log_metrics=True): + # called outside any start_run — must not raise + result = dm.mse(self.ts_univariate, self.ts_univariate * 1.1) + + assert np.isscalar(result) + assert np.isfinite(float(result)) + + def test_autolog_metric_returns_correct_value( + self, mlflow_tracking, autolog_context + ): + """The patched metric returns the same value whether inside or outside a run.""" + with autolog_context(log_metrics=True): + pred = self.ts_univariate * 1.05 + + with mlflow.start_run(): + result_inside = dm.mae(self.ts_univariate, pred) + + # call outside a run — no logging, same computation + result_outside = dm.mae(self.ts_univariate, pred) + + np.testing.assert_almost_equal(result_inside, result_outside, decimal=6) + assert np.isfinite(result_inside) + + def test_autolog_log_metrics_false(self, mlflow_tracking, autolog_context): + """autolog(log_metrics=False) leaves metrics unpatched — nothing is logged.""" + with autolog_context(log_metrics=False): + with mlflow.start_run() as run: + dm.mape(self.ts_univariate, self.ts_univariate * 1.1) + + run_data = mlflow.get_run(run.info.run_id).data + assert "mape" not in run_data.metrics, ( + "mape should NOT be logged when log_metrics=False" + ) + + def test_autolog_metric_any_import_path_logs( + self, mlflow_tracking, autolog_context + ): + """Metrics log identically regardless of which module path was used to + call them. The mlflow hook lives inside `multi_ts_support` (baked into + the function itself) rather than patching a specific module + attribute, and `darts.metrics.mae` and `darts.metrics.metrics.mae` are + the same function object. + """ + with autolog_context(log_metrics=True): + with mlflow.start_run() as run_public: + dm.mae(self.ts_univariate, self.ts_univariate * 1.1) + with mlflow.start_run() as run_impl: + dmm.mae(self.ts_univariate, self.ts_univariate * 1.1) + + assert "mae" in mlflow.get_run(run_public.info.run_id).data.metrics + assert "mae" in mlflow.get_run(run_impl.info.run_id).data.metrics + + def test_autolog_metric_import_order_independent( + self, mlflow_tracking, autolog_context + ): + """A metric imported via `from darts.metrics import ` *before* + autolog() is enabled still logs correctly, since the hook lives inside + the metric's own `multi_ts_support` decorator rather than patching a + module attribute after the fact.""" + from darts.metrics import mae as mae_imported_before_autolog + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + mae_imported_before_autolog( + self.ts_univariate, self.ts_univariate * 1.1 + ) + + assert "mae" in mlflow.get_run(run.info.run_id).data.metrics + + def test_autolog_metric_internal_composite_call_not_double_logged( + self, mlflow_tracking, autolog_context + ): + """rmse calls mse internally via `_get_wrapped_metric`, which bypasses + `multi_ts_support` entirely, so autologging must fire once (for rmse), + not twice (rmse and the internal mse call).""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + dm.rmse(self.ts_univariate, self.ts_univariate * 1.1) + + metrics = mlflow.get_run(run.info.run_id).data.metrics + assert "rmse" in metrics + assert "mse" not in metrics + + def test_autolog_metric_per_timestep(self, mlflow_tracking, autolog_context): + """A per-timestep metric (ae) logs one value per timestep across MLflow steps. + + time_reduction=None (ae's default) means the result keeps a per-timestep + axis, which is mapped to the MLflow step (mirroring the backtest path) + rather than being mislabeled as per-component. + """ + train = self.ts_univariate[:40] + model = LinearRegressionModel(lags=4) + model.fit(train) + pred = model.predict(n=10) + actual = self.ts_univariate[40:] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.ae(actual, pred) + + ref = np.asarray(ref, dtype=float) # shape (n_timesteps,) + history = mlflow_tracking.get_metric_history(run.info.run_id, "ae") + assert len(history) == len(ref), "Expected one step per timestep" + steps = sorted(m.step for m in history) + assert steps == list(range(len(ref))) + logged = [m.value for m in sorted(history, key=lambda m: m.step)] + np.testing.assert_allclose(logged, ref, atol=1e-5) + + def test_autolog_metric_aligns_time_axis_by_forecast_position( + self, mlflow_tracking, autolog_context + ): + """Each series starts at forecast position zero.""" + ts_long = self.ts_univariate # length 50 + ts_short = ts_long[10:] # length 40, same end, starts 10 steps later + + # distinct constant error per series, so a step's mean reveals exactly + # which series contributed to it + pred_long = ts_long + 1.0 + pred_short = ts_short + 2.0 + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + dm.ae([ts_long, ts_short], [pred_long, pred_short]) + + history = mlflow_tracking.get_metric_history(run.info.run_id, "ae") + by_step = {m.step: m.value for m in history} + assert len(by_step) == 50 + for step in range(40): + assert by_step[step] == pytest.approx(1.5, abs=1e-4), step + for step in range(40, 50): + assert by_step[step] == pytest.approx(1.0, abs=1e-4), step + + rows = self._read_per_series_table(run.info.run_id) + steps_by_series = {0: set(), 1: set()} + for r in rows: + steps_by_series[r["series_index"]].add(r["step"]) + assert steps_by_series[0] == set(range(50)) + assert steps_by_series[1] == set(range(40)) + + def test_autolog_metric_quantile(self, mlflow_tracking, autolog_context): + """A quantile metric (mql) logs one key per quantile with matching values.""" + train = self.ts_univariate[:40] + model = LinearRegressionModel( + lags=4, likelihood="quantile", quantiles=[0.1, 0.5, 0.9] + ) + model.fit(train) + pred = model.predict(n=10, num_samples=200) + actual = self.ts_univariate[40:] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.mql(actual, pred, q=[0.1, 0.5, 0.9]) + + ref = np.asarray(ref, dtype=float) # shape (n_quantiles,) + m = mlflow.get_run(run.info.run_id).data.metrics + for i, key in enumerate(("mql_q0_100", "mql_q0_500", "mql_q0_900")): + assert key in m, f"Expected quantile key {key}" + assert m[key] == pytest.approx(ref[i], abs=1e-5) + + def test_autolog_metric_quantile_interval(self, mlflow_tracking, autolog_context): + """A quantile interval metric (miw) logs one key per interval.""" + train = self.ts_univariate[:40] + model = LinearRegressionModel( + lags=4, likelihood="quantile", quantiles=[0.1, 0.5, 0.9] + ) + model.fit(train) + pred = model.predict(n=10, num_samples=200) + actual = self.ts_univariate[40:] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.miw(actual, pred, q_interval=(0.1, 0.9)) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert "miw_qi_80_000" in m + assert m["miw_qi_80_000"] == pytest.approx(float(ref), abs=1e-5) + + def test_autolog_metric_multi_series(self, mlflow_tracking, autolog_context): + """A list of series logs the mean over series; per-series values go to a table.""" + series = [self.ts_univariate, self.ts_univariate * 1.2] + pred = [s * 1.1 for s in series] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.mae(series, pred) + + ref = np.asarray(ref, dtype=float) # shape (n_series,) + m = mlflow.get_run(run.info.run_id).data.metrics + # aggregate = mean over series, no per-series _s{i} keys + assert m["mae"] == pytest.approx(float(np.mean(ref)), abs=1e-5) + assert not any(k.startswith("mae_s") for k in m) + # granular per-series breakdown written to a table artifact + rows = self._read_per_series_table(run.info.run_id) + by_series = {int(row["series_index"]): float(row["value"]) for row in rows} + assert by_series == pytest.approx({0: ref[0], 1: ref[1]}, abs=1e-5) + + def test_autolog_metric_multi_series_custom_agg_func( + self, mlflow_tracking, autolog_context + ): + """autolog()'s agg_func controls how per-series values are aggregated + into the single logged metric (default np.mean).""" + # 3 series with distinct, asymmetric per-series errors so median != mean + series = [self.ts_univariate * f for f in (1.0, 1.2, 5.0)] + pred = [s * 1.1 for s in series] + + with autolog_context(log_metrics=True, agg_func=np.median): + with mlflow.start_run() as run: + ref = dm.mae(series, pred) + + ref = np.asarray(ref, dtype=float) + assert float(np.median(ref)) != pytest.approx(float(np.mean(ref)), abs=1e-3) + m = mlflow.get_run(run.info.run_id).data.metrics + assert m["mae"] == pytest.approx(float(np.median(ref)), abs=1e-5) + + def test_autolog_metric_multi_series_per_component( + self, mlflow_tracking, autolog_context + ): + """A list of multivariate series with component_reduction=None logs the + per-component mean over series; the CSV carries one row per (component, series).""" + series = [self.ts_multivariate, self.ts_multivariate * 1.2] + pred = [s * 1.1 for s in series] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.mae(series, pred, component_reduction=None) + + ref = np.asarray(ref, dtype=float) # shape (n_series, n_components) + m = mlflow.get_run(run.info.run_id).data.metrics + # aggregate per component = mean over series, no per-series _s{i} keys + assert m["mae_linear"] == pytest.approx(float(ref[:, 0].mean()), abs=1e-5) + assert m["mae_linear_1"] == pytest.approx(float(ref[:, 1].mean()), abs=1e-5) + assert not any(k.endswith(("_s0", "_s1")) for k in m) + # granular CSV: one row per (component, series) + rows = self._read_per_series_table(run.info.run_id) + got = { + (row["key"], int(row["series_index"])): float(row["value"]) for row in rows + } + assert got[("mae_linear", 0)] == pytest.approx(ref[0, 0], abs=1e-5) + assert got[("mae_linear", 1)] == pytest.approx(ref[1, 0], abs=1e-5) + assert got[("mae_linear_1", 0)] == pytest.approx(ref[0, 1], abs=1e-5) + assert got[("mae_linear_1", 1)] == pytest.approx(ref[1, 1], abs=1e-5) + + def test_autolog_metric_name_override(self, mlflow_tracking, autolog_context): + """The metric `name` kwarg overrides only the metric-name token in the key, + keeping the backtest prefix and the quantile/axis suffixes.""" + actual = self.ts_univariate + train = self.ts_univariate[:40] + qmodel = self._fit_qlr(train) + pred = qmodel.predict(n=10, num_samples=200) + target = self.ts_univariate[40:] + + with autolog_context(log_metrics=True): + # direct call: name replaces the metric token; suffix (_q0_500) preserved + with mlflow.start_run() as run_direct: + dm.mae(actual, actual * 1.1, name="custom") + dm.mql(target, pred, q=0.5, name="myq") + # backtest: name replaces the metric token; backtest_ prefix preserved + with mlflow.start_run() as run_bt: + self._fit_lr().backtest( + self.ts_univariate, + metric=dm.mae, + metric_kwargs={"name": "custom"}, + retrain=False, + stride=10, + ) + + direct = mlflow.get_run(run_direct.info.run_id).data.metrics + assert "custom" in direct + assert "mae" not in direct, "default metric name should be replaced" + assert "myq_q0_500" in direct, "quantile suffix should be preserved" + + bt = mlflow.get_run(run_bt.info.run_id).data.metrics + assert "backtest_custom" in bt + assert "backtest_mae" not in bt, "default metric name should be replaced" + + def test_autolog_metric_multi_series_classification_labels_explicit( + self, mlflow_tracking, autolog_context + ): + """f1 with label_reduction=None and explicit labels on a list of binary + series logs the per-label mean over series and writes the per-series table. + + ``labels`` must be explicit (``label_reduction=None`` without it raises, + since the number of output labels can't be determined ahead of time).""" + # two independent binary series (same classes, deterministic) + binary1 = tg.constant_timeseries(value=0.0, length=50).with_values( + np.array([0.0, 1.0] * 25, dtype=np.float32).reshape(-1, 1) + ) + binary2 = tg.constant_timeseries(value=0.0, length=50).with_values( + np.array([1.0, 0.0] * 25, dtype=np.float32).reshape(-1, 1) + ) + series = [binary1, binary2] + pred = series # perfect predictions → f1 == 1.0 per label per series + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.f1(series, pred, label_reduction=None, labels=[0, 1]) + + ref = [np.asarray(r, dtype=float).flatten() for r in ref] + m = mlflow.get_run(run.info.run_id).data.metrics + # aggregate per label = mean over series, no per-series _s{i} keys + assert m["f1_label0"] == pytest.approx( + float(np.mean([ref[0][0], ref[1][0]])), abs=1e-5 + ) + assert m["f1_label1"] == pytest.approx( + float(np.mean([ref[0][1], ref[1][1]])), abs=1e-5 + ) + assert not any(k.endswith(("_s0", "_s1")) for k in m) + # granular CSV: one row per (label, series) + rows = self._read_per_series_table(run.info.run_id) + got = { + (row["key"], int(row["series_index"])): float(row["value"]) for row in rows + } + for i in range(2): + assert got[("f1_label0", i)] == pytest.approx(ref[i][0], abs=1e-5) + assert got[("f1_label1", i)] == pytest.approx(ref[i][1], abs=1e-5) + + def test_autolog_metric_component_count_mismatch_allowed_when_reduced( + self, mlflow_tracking, autolog_context + ): + """Default mae reduces components to scalars, so mixed component counts + are valid and log under a single aggregated key.""" + series = [self.ts_univariate, self.ts_multivariate] + pred = [s * 1.1 for s in series] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = dm.mae(series, pred) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert m["mae"] == pytest.approx(float(np.mean(ref)), abs=1e-5) + + def test_autolog_metric_component_count_mismatch_raises( + self, mlflow_tracking, autolog_context + ): + """When components are preserved, mixed component counts raise rather + than taking names from the first series and mislabeling the rest.""" + series = [self.ts_univariate, self.ts_multivariate] + pred = [s * 1.1 for s in series] + + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + with pytest.raises(ValueError, match="same number of components"): + dm.mae(series, pred, component_reduction=None) + + assert not mlflow.get_run(run.info.run_id).data.metrics + + def test_autolog_metric_size_mismatch_raises( + self, mlflow_tracking, autolog_context, caplog + ): + """When the inferred C-axis size doesn't divide the result, logging raises + (and logs an error), propagating out of the public metric call — the + metric callback isn't invoked through MLflow's safe_patch, so nothing + catches it. No metrics are written for the failed call.""" + actual = self.ts_univariate[40:] + # mae with component_reduction=None on a univariate series produces shape (T,), + # which is size T — divisible by c_size=1 (1 component × 1 quantile), so we + # need to force a mismatch. We do that by monkey-patching _infer_metric_axes + # to report has_comp_axis=True with a fake 3-component count, making c_size=3 + # while the actual result is shape (T,). + train = self.ts_univariate[:40] + model = LinearRegressionModel(lags=4) + model.fit(train) + pred = model.predict(n=10) + + import unittest.mock as mock + + from darts.utils import mlflow as mlflow_utils + + fake_axes = (False, True, ["_c0", "_c1", "_c2"]) + with mock.patch.object( + mlflow_utils, "_infer_metric_axes", return_value=fake_axes + ): + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + with caplog.at_level(logging.ERROR, logger="darts"): + with pytest.raises(ValueError, match="not divisible"): + dm.mae(actual, pred) + + assert any("not divisible" in record.message for record in caplog.records), ( + "Expected a 'not divisible' error to be logged when axes don't match" + ) + # no metrics should have been written for the (faked) mismatched call + run_data = mlflow.get_run(run.info.run_id).data.metrics + assert not any("mae" in k for k in run_data), ( + "No mae metrics should be logged when the size check fails" + ) + + def test_autolog_metric_per_series_table_schema_and_single_series_skip( + self, mlflow_tracking, autolog_context + ): + """The per-series table has the expected schema for multi-series input, and no + artifact is written for single-series input (mean == the value itself).""" + # multi-series: artifact exists with the documented columns + multi = [self.ts_univariate, self.ts_univariate * 1.2] + pred_multi = [s * 1.1 for s in multi] + with autolog_context(log_metrics=True): + with mlflow.start_run() as run_multi: + dm.mae(multi, pred_multi) + + rows = self._read_per_series_table(run_multi.info.run_id) + assert list(rows[0].keys()) == [ + "key", + "series_index", + "step", + "window_index", + "value", + ] + assert {int(r["series_index"]) for r in rows} == {0, 1} + + # single-series: no per-series table artifact should be created + single = self.ts_univariate + pred_single = single * 1.1 + with autolog_context(log_metrics=True): + with mlflow.start_run() as run_single: + dm.mae(single, pred_single) + + artifacts = mlflow_tracking.list_artifacts(run_single.info.run_id) + assert not any(a.path == "metrics_per_series.json" for a in artifacts), ( + "Single-series input should not write a per-series table artifact" + ) + + def test_autolog_backtest_scalar(self, mlflow_tracking, autolog_context): + """Default (reduced) backtest of a single univariate series logs one scalar.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr().backtest( + self.ts_univariate, metric=dm.mae, retrain=False, stride=10 + ) + + run_data = mlflow.get_run(run.info.run_id).data + assert "backtest_mae" in run_data.metrics + assert run_data.metrics["backtest_mae"] == pytest.approx(float(ref), abs=1e-5) + + def test_autolog_backtest_per_window_steps(self, mlflow_tracking, autolog_context): + """reduction=None logs per-window values at end-relative steps.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr().backtest( + self.ts_univariate, + metric=dm.mae, + retrain=False, + stride=10, + reduction=None, + ) + + history = mlflow_tracking.get_metric_history(run.info.run_id, "backtest_mae") + assert len(history) > 1, "Expected multiple per-window steps" + steps = sorted(m.step for m in history) + assert steps == list(range(-len(history), 0)) + logged = [m.value for m in sorted(history, key=lambda m: m.step)] + np.testing.assert_allclose(logged, np.asarray(ref, dtype=float), atol=1e-5) + + def test_autolog_backtest_per_component(self, mlflow_tracking, autolog_context): + """component_reduction=None logs one key per component name.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr(self.ts_multivariate).backtest( + self.ts_multivariate, + metric=dm.mae, + retrain=False, + stride=10, + metric_kwargs={"component_reduction": None}, + ) + + run_data = mlflow.get_run(run.info.run_id).data + assert "backtest_mae_linear" in run_data.metrics + assert "backtest_mae_linear_1" in run_data.metrics + ref = np.asarray(ref, dtype=float) + assert run_data.metrics["backtest_mae_linear"] == pytest.approx( + ref[0], abs=1e-5 + ) + assert run_data.metrics["backtest_mae_linear_1"] == pytest.approx( + ref[1], abs=1e-5 + ) + + def test_autolog_backtest_multi_metric(self, mlflow_tracking, autolog_context): + """Multiple metrics are logged under one key each.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr().backtest( + self.ts_univariate, + metric=[dm.mae, dm.rmse], + retrain=False, + stride=10, + ) + + run_data = mlflow.get_run(run.info.run_id).data + assert "backtest_mae" in run_data.metrics + assert "backtest_rmse" in run_data.metrics + assert run_data.metrics["backtest_mae"] == pytest.approx( + float(ref[0]), abs=1e-5 + ) + assert run_data.metrics["backtest_rmse"] == pytest.approx( + float(ref[1]), abs=1e-5 + ) + + def test_autolog_backtest_multi_series(self, mlflow_tracking, autolog_context): + """A list of series logs the mean over series; per-series values go to a table.""" + series = [self.ts_univariate, self.ts_univariate * 1.2] + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr(series).backtest( + series, metric=dm.mae, retrain=False, stride=10 + ) + + run_data = mlflow.get_run(run.info.run_id).data + # aggregate = mean over series, no per-series _s{i} keys + assert run_data.metrics["backtest_mae"] == pytest.approx( + float(np.mean(ref)), abs=1e-5 + ) + assert not any(k.startswith("backtest_mae_s") for k in run_data.metrics) + # granular per-series breakdown written to a table artifact + rows = self._read_per_series_table(run.info.run_id) + by_series = {int(row["series_index"]): float(row["value"]) for row in rows} + assert by_series == pytest.approx( + {0: float(ref[0]), 1: float(ref[1])}, abs=1e-5 + ) + + def test_autolog_backtest_multi_series_custom_agg_func(self, mlflow_tracking): + """autolog()'s agg_func also controls the backtest() aggregation + (default np.mean). Calls _log_backtest_metrics directly with a + fabricated result so the per-series values are exact.""" + backtest_args = { + "metric": dm.mae, + "metric_kwargs": {}, + "series": [self.ts_univariate, self.ts_univariate, self.ts_univariate], + "forecast_horizon": 1, + "reduction": np.mean, + "last_points_only": True, + } + result = [1.0, 2.0, 100.0] # asymmetric -> median != mean + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + _log_backtest_metrics( + client, run.info.run_id, result, backtest_args, agg_func=np.median + ) + client.flush(synchronous=True) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert m["backtest_mae"] == pytest.approx(2.0) + + def test_autolog_backtest_per_timestep_scalar( + self, mlflow_tracking, autolog_context + ): + """A per-timestep metric (ae) under default reduction collapses to one scalar.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr().backtest( + self.ts_univariate, metric=dm.ae, retrain=False, stride=10 + ) + + history = mlflow_tracking.get_metric_history(run.info.run_id, "backtest_ae") + assert len(history) == 1, "Default reduction should yield a single value" + assert history[0].value == pytest.approx(float(ref), abs=1e-5) + + def test_autolog_backtest_per_timestep_per_window( + self, mlflow_tracking, autolog_context + ): + """Window-level ae results aggregate per horizon step in the chart and + remain available with their window index in the detailed table.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + ref = self._fit_lr().backtest( + self.ts_univariate, + metric=dm.ae, + retrain=False, + stride=10, + forecast_horizon=4, + reduction=None, + ) + + ref = np.asarray(ref, dtype=float) # shape (n_windows, forecast_horizon) + history = mlflow_tracking.get_metric_history(run.info.run_id, "backtest_ae") + assert len(history) == 4, "Expected one step per forecast horizon timestep" + logged = [m.value for m in sorted(history, key=lambda m: m.step)] + np.testing.assert_allclose(logged, np.nanmean(ref, axis=0), atol=1e-5) + + rows = self._read_per_series_table(run.info.run_id) + assert len(rows) == ref.size + for row in rows: + assert row["key"] == "backtest_ae" + assert row["window_index"] in range(-len(ref), 0) + + def test_autolog_backtest_historical_forecasts_horizon_inferred( + self, mlflow_tracking, autolog_context + ): + """When `historical_forecasts` is user-supplied, `backtest()` ignores the + `forecast_horizon` argument, autologging must infer the true window + length from the historical forecasts themselves, not from the (unused, + defaulted) `forecast_horizon` argument.""" + model = self._fit_lr() + hf = model.historical_forecasts( + self.ts_univariate, + retrain=False, + stride=10, + forecast_horizon=4, + last_points_only=False, + ) + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + # forecast_horizon is not passed (defaults to 1) and would be + # wrong; the real horizon (4) must come from `hf`. + ref = model.backtest( + self.ts_univariate, + historical_forecasts=hf, + metric=dm.ae, + reduction=None, + ) + + ref = np.asarray(ref, dtype=float) # shape (n_windows, forecast_horizon) + history = mlflow_tracking.get_metric_history(run.info.run_id, "backtest_ae") + assert len(history) == 4, "Expected one step per forecast horizon timestep" + logged = [m.value for m in sorted(history, key=lambda m: m.step)] + np.testing.assert_allclose(logged, np.nanmean(ref, axis=0), atol=1e-5) + + def test_autolog_backtest_quantile(self, mlflow_tracking, autolog_context): + """A quantile metric (mql) logs one key per quantile.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + self._fit_qlr().backtest( + self.ts_univariate, + metric=dm.mql, + metric_kwargs={"q": [0.1, 0.5, 0.9]}, + retrain=False, + stride=10, + num_samples=200, + ) + + m = mlflow.get_run(run.info.run_id).data.metrics + for key in ( + "backtest_mql_q0_100", + "backtest_mql_q0_500", + "backtest_mql_q0_900", + ): + assert key in m, f"Expected quantile key {key}" + assert np.isfinite(m[key]) + + def test_autolog_backtest_mixed_degenerate_axes_keep_metric_names( + self, mlflow_tracking, autolog_context + ): + """Metrics whose differing axes have size one keep their metric names.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + self._fit_lr().backtest( + self.ts_univariate, + metric=[dm.mae, dm.ae], + retrain=False, + stride=10, + ) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert "backtest_mae" in m + assert "backtest_ae" in m + assert not any(k.startswith("backtest_metrics_") for k in m) + + def test_autolog_backtest_classification_labels_in_data( + self, mlflow_tracking, autolog_context + ): + """f1 with explicit labels present in the series logs finite per-label keys.""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + self._fit_lr(self.ts_binary).backtest( + self.ts_binary, + metric=dm.f1, + metric_kwargs={"label_reduction": None, "labels": [0, 1]}, + retrain=False, + stride=10, + ) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert "backtest_f1_label0" in m + assert "backtest_f1_label1" in m + assert np.isfinite(m["backtest_f1_label0"]) + assert np.isfinite(m["backtest_f1_label1"]) + + def test_autolog_backtest_classification_labels_not_in_data( + self, mlflow_tracking, autolog_context + ): + """f1 with explicit labels absent from the series still creates the keys, but + the scores are NaN (the labels never appear in any window).""" + with autolog_context(log_metrics=True): + with mlflow.start_run() as run: + self._fit_lr(self.ts_binary).backtest( + self.ts_binary, + metric=dm.f1, + metric_kwargs={"label_reduction": None, "labels": [5, 10]}, + retrain=False, + stride=10, + ) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert "backtest_f1_label5" in m + assert "backtest_f1_label10" in m + assert np.isnan(m["backtest_f1_label5"]) + assert np.isnan(m["backtest_f1_label10"]) + + def test_log_backtest_metrics_component_count_mismatch_allowed_when_reduced( + self, mlflow_tracking + ): + """Default mae reduces components to scalars, so mixed component counts + aggregate normally. Calls _log_backtest_metrics directly.""" + backtest_args = { + "metric": dm.mae, + "metric_kwargs": {}, + "series": [self.ts_univariate, self.ts_multivariate], + "forecast_horizon": 1, + "reduction": np.mean, + "last_points_only": True, + } + result = [1.0, 3.0] + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + _log_backtest_metrics(client, run.info.run_id, result, backtest_args) + client.flush(synchronous=True) + + m = mlflow.get_run(run.info.run_id).data.metrics + assert m["backtest_mae"] == pytest.approx(2.0) + + def test_log_backtest_metrics_component_count_mismatch_raises( + self, mlflow_tracking + ): + """When components are preserved, mixed component counts raise rather + than taking names from the first series only. + + Calls _log_backtest_metrics directly so the raise is not swallowed by + MLflow's safe_patch wrapper. + """ + backtest_args = { + "metric": dm.mae, + "metric_kwargs": {"component_reduction": None}, + "series": [self.ts_univariate, self.ts_multivariate], + "forecast_horizon": 1, + "reduction": np.mean, + "last_points_only": True, + } + result = [1.0, np.array([1.0, 2.0])] + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + with pytest.raises(ValueError, match="same number of components"): + _log_backtest_metrics(client, run.info.run_id, result, backtest_args) + + assert not mlflow.get_run(run.info.run_id).data.metrics + + def test_log_backtest_metrics_unknown_labels_raises(self, mlflow_tracking): + """label_reduction=None without explicit labels raises rather than + inferring class names from the series at runtime. + + This is tested by calling _log_backtest_metrics directly so the raise + is not swallowed by MLflow's safe_patch wrapper. + """ + ts_bin = tg.constant_timeseries(value=0.0, length=10) + backtest_args = { + "metric": dm.f1, + "metric_kwargs": {"label_reduction": None}, + "series": ts_bin, + "forecast_horizon": 1, + "reduction": np.mean, + "last_points_only": True, + } + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + with pytest.raises(ValueError, match="requires explicit `labels`"): + _log_backtest_metrics( + client, run.info.run_id, np.array([0.5]), backtest_args + ) + + def test_log_backtest_metrics_label_count_mismatch(self, mlflow_tracking): + """When the explicit label count does not divide the metric output size, + logging raises rather than silently producing incomplete metrics. + + This is tested by calling _log_backtest_metrics directly so the raise is + not swallowed by MLflow's safe_patch wrapper. + """ + ts_3class = tg.constant_timeseries(value=0.0, length=50) + + # Simulate a backtest result with only 2 entries — as if the metric was + # evaluated on windows that only contained 2 of the 3 explicit labels. + # axis_size is 3 (len(labels)) but result has 2 → mismatch. + fake_result = np.array([0.8, 0.6], dtype=float) + + backtest_args = { + "metric": dm.f1, + "metric_kwargs": {"label_reduction": None, "labels": [0, 1, 2]}, + "series": ts_3class, + "forecast_horizon": 1, + "reduction": np.mean, # not None → has_windows=False → single window + "last_points_only": True, + } + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + with pytest.raises(ValueError, match="not divisible"): + _log_backtest_metrics( + client, run.info.run_id, fake_result, backtest_args + ) + + assert not mlflow.get_run(run.info.run_id).data.metrics + + def test_log_backtest_metrics_aligns_windows_by_end_date(self, mlflow_tracking): + """A shorter series' window axis aligns from the end, not the start, + so its negative steps overlap the tail of a longer series. Calls + _log_backtest_metrics directly with a fabricated result so the window + counts per series are exact.""" + backtest_args = { + "metric": dm.mae, + "metric_kwargs": {}, + "series": [self.ts_univariate, self.ts_univariate], + "forecast_horizon": 1, + "reduction": None, + "last_points_only": False, + } + # series 0 has 5 windows; series 1 (shorter, later-starting) has 3 + result = [np.array([1.0, 2.0, 3.0, 4.0, 5.0]), np.array([10.0, 20.0, 30.0])] + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + _log_backtest_metrics(client, run.info.run_id, result, backtest_args) + client.flush(synchronous=True) + + history = mlflow_tracking.get_metric_history(run.info.run_id, "backtest_mae") + logged = {m.step: m.value for m in history} + assert logged == pytest.approx({-5: 1.0, -4: 2.0, -3: 6.5, -2: 12.0, -1: 17.5}) + + rows = self._read_per_series_table(run.info.run_id) + by_step = {(r["series_index"], r["step"]): r["value"] for r in rows} + assert by_step[(0, -5)] == pytest.approx(1.0) + assert by_step[(0, -1)] == pytest.approx(5.0) + assert by_step[(1, -3)] == pytest.approx(10.0) + assert by_step[(1, -1)] == pytest.approx(30.0) + assert (1, -5) not in by_step + assert (1, -4) not in by_step + + def test_log_backtest_metrics_aligns_last_points_only_time_axis( + self, mlflow_tracking + ): + """last_points_only stitches windows into one series scored per real + timestep -- a separate code path from the window-axis case above that + needs the same end-date alignment.""" + backtest_args = { + "metric": dm.ae, + "metric_kwargs": {}, + "series": [self.ts_univariate, self.ts_univariate], + "forecast_horizon": 1, + "reduction": None, + "last_points_only": True, + } + # series 0 has 5 timesteps; series 1 (shorter, later-starting) has 3 + result = [np.array([1.0, 2.0, 3.0, 4.0, 5.0]), np.array([10.0, 20.0, 30.0])] + + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + _log_backtest_metrics(client, run.info.run_id, result, backtest_args) + client.flush(synchronous=True) + + history = mlflow_tracking.get_metric_history(run.info.run_id, "backtest_ae") + logged = {m.step: m.value for m in history} + assert logged == pytest.approx({-5: 1.0, -4: 2.0, -3: 6.5, -2: 12.0, -1: 17.5}) + + @staticmethod + def _read_per_series_table(run_id): + """Load the run's consolidated per-series metric table into row dicts.""" + df = mlflow.load_table( + artifact_file="metrics_per_series.json", run_ids=[run_id] + ) + return df.to_dict("records") + + def _fit_lr(self, series=None): + """Fit and return a fresh LinearRegressionModel (no active run).""" + model = LinearRegressionModel(lags=4) + model.fit(series if series is not None else self.ts_univariate) + return model + + def _fit_qlr(self, series=None): + """Fit and return a fresh quantile LinearRegressionModel (no active run).""" + model = LinearRegressionModel( + lags=4, likelihood="quantile", quantiles=[0.1, 0.5, 0.9] + ) + model.fit(series if series is not None else self.ts_univariate) + return model + + +@pytest.mark.parametrize( + "metric_name, metric_kwargs, expected", + [ + ("mae", {}, dict(has_time_axis=False, has_comp_axis=False, axis_size=1)), + ("ae", {}, dict(has_time_axis=True, has_comp_axis=False, axis_size=1)), + ("mae", {"component_reduction": None}, dict(has_comp_axis=True)), + ], +) +def test_infer_metric_axes_reductions(metric_name, metric_kwargs, expected): + has_time_axis, has_comp_axis, axis_labels = _infer_metric_axes( + getattr(dm, metric_name), metric_kwargs + ) + actual = { + "has_time_axis": has_time_axis, + "has_comp_axis": has_comp_axis, + "axis_size": len(axis_labels), + } + for attr, value in expected.items(): + assert actual[attr] == value + + +def test_infer_metric_axes_quantiles(): + _, _, axis_labels = _infer_metric_axes(dm.mql, {"q": [0.1, 0.5, 0.9]}) + assert axis_labels == ["_q0.100", "_q0.500", "_q0.900"] + + +def test_infer_metric_axes_quantile_interval(): + has_time, _, axis_labels = _infer_metric_axes(dm.iw, {"q_interval": (0.1, 0.9)}) + assert axis_labels == ["_qi_80.000"] + assert has_time is True + + +def test_infer_metric_axes_unknown_labels_raises(): + """label_reduction=None with no explicit labels cannot determine the number + of output labels ahead of time, so this raises rather than falling back.""" + with pytest.raises(ValueError, match="requires explicit `labels`"): + _infer_metric_axes(dm.f1, {"label_reduction": None}) + + +def test_build_metric_keys_components_and_quantiles(): + """Shared key builder expands components x quantile suffixes per metric.""" + metric_axes = [ + (False, True, ["_q0.100", "_q0.900"]), + (False, True, ["_label0", "_label1"]), + ] + c_size, keys = _build_metric_keys( + ["mae", "f1"], + ["temp", "hum"], + has_comp_axis=True, + metric_axes=metric_axes, + prefix="backtest_", + ) + assert c_size == 4 + assert keys == [ + [ + "backtest_mae_temp_q0_100", + "backtest_mae_temp_q0_900", + "backtest_mae_hum_q0_100", + "backtest_mae_hum_q0_900", + ], + [ + "backtest_f1_temp_label0", + "backtest_f1_temp_label1", + "backtest_f1_hum_label0", + "backtest_f1_hum_label1", + ], + ] + + +def test_build_metric_keys_no_components_no_prefix(): + """Without components, each metric gets one key per axis label.""" + metric_axes = [(False, False, ["_q0.500"])] + c_size, keys = _build_metric_keys( + ["mql"], + ["ignored"], + has_comp_axis=False, + metric_axes=metric_axes, + ) + assert c_size == 1 + assert keys == [["mql_q0_500"]] + + +def test_flush_logged_metrics_aggregates_and_writes_table(mlflow_tracking): + """Cells are aggregated with agg_func; supplied table rows are persisted.""" + agg = { + ("mae", 0): [1.0, 3.0], + ("mae", 1): [10.0, 30.0], + } + rows = [ + {"key": "mae", "series_index": 0, "step": 0, "value": 1.0}, + {"key": "mae", "series_index": 1, "step": 0, "value": 3.0}, + {"key": "mae", "series_index": 0, "step": 1, "value": 10.0}, + {"key": "mae", "series_index": 1, "step": 1, "value": 30.0}, + ] + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + _flush_logged_metrics( + client, run.info.run_id, agg, agg_func=np.mean, table_rows=rows + ) + client.flush(synchronous=True) + + history0 = mlflow_tracking.get_metric_history(run.info.run_id, "mae") + logged = {m.step: m.value for m in history0} + assert logged[0] == pytest.approx(2.0) + assert logged[1] == pytest.approx(20.0) + table = mlflow.load_table( + artifact_file="metrics_per_series.json", run_ids=[run.info.run_id] + ) + assert len(table) == 4 + + +def test_flush_logged_metrics_skips_table_without_rows(mlflow_tracking): + """Omitting table rows logs the aggregate only.""" + agg = {("mae", 0): [1.5]} + with mlflow.start_run() as run: + client = MlflowAutologgingQueueingClient() + _flush_logged_metrics(client, run.info.run_id, agg, agg_func=np.mean) + client.flush(synchronous=True) + + assert mlflow.get_run(run.info.run_id).data.metrics["mae"] == pytest.approx(1.5) + artifacts = mlflow_tracking.list_artifacts(run.info.run_id) + assert not any(a.path == "metrics_per_series.json" for a in artifacts) diff --git a/darts/utils/mlflow.py b/darts/utils/mlflow.py new file mode 100644 index 0000000000..d59aecd524 --- /dev/null +++ b/darts/utils/mlflow.py @@ -0,0 +1,1698 @@ +""" +MLflow Integration +------------------ + +Custom MLflow model flavor for Darts forecasting models. Supports saving, loading, +and logging any Darts ``ForecastingModel`` (statistical, ML-based, and PyTorch-based) +to MLflow, as well as automatic logging (``autolog()``) of: + +- Model parameters and data metadata: The model creation parameters, target series, and covariate usage information. +- Model storage: The trained model artifact after each ``fit()`` call when ``log_models=True`` (default ``False``). +- Metrics: + - The score for each metric called inside an active MLflow run. + - The score(s) from a ``backtest()`` call. + - Per-epoch training/validation metrics for PyTorch-based models. + +See the `MLflow quickstart example `_ +for an end-to-end walkthrough. + +To keep auto-logged metrics comparable across runs, use the same evaluation +time frame, forecast horizon, and evaluation start date for every +``backtest()`` / metric call you intend to compare. +""" + +import inspect +import re +import sys +import threading +from collections.abc import Callable +from operator import itemgetter +from pathlib import Path +from typing import Any + +from darts.logging import raise_log +from darts.typing import TimeSeriesLike + +try: + import mlflow +except ImportError: + raise_log( + ImportError( + "The `mlflow` module could not be imported. To enable MLflow support " + "in Darts, follow the detailed instructions in the installation guide: " + "https://github.com/unit8co/darts/blob/master/INSTALL.md" + ) + ) + +import numpy as np +import pandas as pd +import yaml +from mlflow.entities import LoggedModel +from mlflow.models import Model, ModelInputExample, ModelSignature +from mlflow.models.model import MLMODEL_FILE_NAME +from mlflow.models.utils import _save_example +from mlflow.tracking.artifact_utils import _download_artifact_from_uri +from mlflow.tracking.fluent import _initialize_logged_model +from mlflow.utils import _get_fully_qualified_class_name +from mlflow.utils.autologging_utils import ( + autologging_integration, + get_autologging_config, +) +from mlflow.utils.autologging_utils.client import MlflowAutologgingQueueingClient +from mlflow.utils.autologging_utils.safety import safe_patch +from mlflow.utils.class_utils import _get_class_from_string +from mlflow.utils.environment import ( + _CONDA_ENV_FILE_NAME, + _CONSTRAINTS_FILE_NAME, + _PYTHON_ENV_FILE_NAME, + _REQUIREMENTS_FILE_NAME, + _mlflow_conda_env, + _process_conda_env, + _process_pip_requirements, + _PythonEnv, + _validate_env_arguments, +) +from mlflow.utils.file_utils import write_to +from mlflow.utils.model_utils import ( + _add_code_from_conf_to_system_path, + _get_flavor_configuration, + _validate_and_copy_code_paths, + _validate_and_prepare_target_save_path, +) +from mlflow.utils.requirements_utils import _get_pinned_requirement + +import darts +from darts import TimeSeries +from darts.logging import get_logger, raise_log +from darts.metrics.utils import ( + _LabelReduction, + register_metric_callback, + unregister_metric_callback, +) +from darts.models.forecasting.forecasting_model import ForecastingModel +from darts.utils.ts_utils import ( + SeriesType, + get_series_seq_type, + get_single_series, + series2seq, +) +from darts.utils.utils import TORCH_AVAILABLE + +if TORCH_AVAILABLE: + from darts.models.forecasting.torch_forecasting_model import ( + TorchForecastingModel, + ) + +logger = get_logger(__name__) + +FLAVOR_NAME = "darts" + + +_MODEL_FILE_STAT = "model.pkl" +_MODEL_FILE_TORCH = "model.pt" + +# Thread-local flags used by _patched_fit to suppress nested/re-entrant +# autologging: in_historical_forecasts covers historical_forecasts' internal +# fit() calls, in_fit covers nested fit() calls (e.g. ensembles, super()), +# so only the outermost call logs. +_autolog_state = threading.local() + + +def save_model( + model: ForecastingModel, + path: str, + conda_env: dict | str | None = None, + code_paths: list[str] | None = None, + mlflow_model: Model | None = None, + signature: ModelSignature | None = None, + input_example: ModelInputExample | None = None, + pip_requirements: list[str] | None = None, + extra_pip_requirements: list[str] | None = None, + metadata: dict[str, Any] | None = None, +) -> None: + """Save a Darts forecasting model in MLflow format. + + Produces an MLflow model directory at ``path`` containing: + + - The serialized Darts model (delegated to the model's own ``save()`` method). + - An ``MLmodel`` YAML file with flavor metadata. + - ``conda.yaml`` and ``requirements.txt`` environment files. + + Parameters + ---------- + model + A fitted Darts ``ForecastingModel`` instance. + path + Local filesystem path where the model directory will be created. + conda_env + A conda environment specification (dict or path to a ``conda.yaml``). + If ``None``, a default environment is generated. + code_paths + A list of local filesystem paths to Python file dependencies (or directories + containing file dependencies). These files are prepended to the system path + when the model is loaded. + mlflow_model + Optional MLflow Model object to use for saving. When provided (typically by + ``Model.log()``), this model instance is used instead of creating a new one. + signature + *Unsupported, see notes.* An ``mlflow.models.ModelSignature`` instance describing model input/output. + Use ``mlflow.models.infer_signature()`` to automatically generate from example inputs. + input_example + *Unsupported, see notes.* An example input for the model (used by MLflow UI). + pip_requirements + A list of pip requirement strings. Overrides ``conda_env`` pip section + when provided. + extra_pip_requirements + A list of additional pip requirement strings to add to the model's environment, + in addition to the default requirements. + metadata + Optional dictionary of custom metadata to store in the ``MLmodel`` file. + + Notes + ----- + Signature and input_example params are currently not supported, as they + are used to support serving and input validation in the MLflow pyfunc flavor, + which is not implemented for Darts models. They are accepted as params for + simplifying potential future extensibility, and to keep in line with MLflow API + conventions. + """ + if not isinstance(model, ForecastingModel): + raise_log( + ValueError( + "Model must be an instance of darts.models.forecasting.ForecastingModel." + ) + ) + + _validate_env_arguments(conda_env, pip_requirements, extra_pip_requirements) + + path = Path(path).resolve() + _validate_and_prepare_target_save_path(str(path)) + code_dir_subpath = _validate_and_copy_code_paths(code_paths, str(path)) + + is_torch = _is_torch_model(model) + + # clean=True excludes any timeseries or callbacks from the model file + model_file = _MODEL_FILE_TORCH if is_torch else _MODEL_FILE_STAT + model.save(str(path / model_file), clean=True) + + model_class = _get_fully_qualified_class_name(model) + + if mlflow_model is None: + mlflow_model = Model() + + if signature is not None: + mlflow_model.signature = signature + + if input_example is not None: + _save_example(mlflow_model, input_example, str(path)) + + if metadata is not None: + mlflow_model.metadata = metadata + + mlflow_model.add_flavor( + FLAVOR_NAME, + darts_version=darts.__version__, + data=model_file, + model_class=model_class, + code=code_dir_subpath, + ) + mlflow_model.save(str(path / MLMODEL_FILE_NAME)) + + if pip_requirements is None: + default_reqs = get_default_pip_requirements() + # TODO: `infer_pip_requirements` requires `pyfunc` flavor to be implemented. + # inferred_reqs = infer_pip_requirements(path, FLAVOR_NAME, fallback=default_reqs) + # default_reqs = sorted(set(inferred_reqs).union(default_reqs)) + else: + default_reqs = None + conda_env, pip_requirements, pip_constraints = ( + _process_pip_requirements( + default_reqs, pip_requirements, extra_pip_requirements + ) + if conda_env is None + else _process_conda_env(conda_env) + ) + + with open(path / _CONDA_ENV_FILE_NAME, "w") as f: + yaml.safe_dump(conda_env, stream=f, default_flow_style=False) + + if pip_constraints: + write_to(str(path / _CONSTRAINTS_FILE_NAME), "\n".join(pip_constraints)) + + write_to(str(path / _REQUIREMENTS_FILE_NAME), "\n".join(pip_requirements)) + _PythonEnv.current().to_yaml(str(path / _PYTHON_ENV_FILE_NAME)) + + +def load_model( + model_uri: str, + dst_path: str | None = None, + **kwargs, +) -> ForecastingModel: + """Load a Darts model from an MLflow model URI. + + Parameters + ---------- + model_uri + An MLflow model URI, e.g. ``"runs://model"``, + ``"models://"``, or a local ``file:///...`` path. + dst_path + Optional local path for downloading remote artifacts. + **kwargs + Additional keyword arguments forwarded to the model's ``load()`` method + (e.g. ``map_location`` for a `TorchForecastingModel`). + + Returns + ------- + ForecastingModel + The loaded Darts forecasting model. + """ + local_path = _download_artifact_from_uri( + artifact_uri=model_uri, output_path=dst_path + ) + + flavor_conf = _get_flavor_configuration( + model_path=local_path, flavor_name=FLAVOR_NAME + ) + _add_code_from_conf_to_system_path(local_path, flavor_conf) + + model_cls_str = flavor_conf.get("model_class", None) + model_cls = _get_class_from_string(model_cls_str) + + if not issubclass(model_cls, ForecastingModel): + raise_log( + ValueError( + f"Cannot load model: class `{model_cls_str}` is not a subclass of `ForecastingModel`." + ) + ) + + model_path = Path(local_path) / flavor_conf["data"] + + return model_cls.load(str(model_path), **kwargs) + + +def log_model(model: ForecastingModel, **kwargs): + """Log a Darts model to the current MLflow run, using the Darts MLflow flavor. + + This is a thin wrapper around ``mlflow.models.Model.log()`` that supplies + the Darts flavor for saving/loading; every other argument is forwarded + as-is. See the `MLflow documentation + `_ + for the full list of accepted parameters (e.g. ``name``, + ``registered_model_name``, ``conda_env``, ``pip_requirements``, + ``metadata``, ``tags``, ...). + + Parameters + ---------- + model + A fitted Darts ``ForecastingModel`` instance. + **kwargs + Forwarded to ``mlflow.models.Model.log()``. Use ``name`` to set the + run-relative artifact path. ``artifact_path`` parameter is deprecated + by MLflow and not exposed here. + + Returns + ------- + ModelInfo + MLflow ModelInfo object containing model_uri, run_id, artifact_path, + model_id, timestamps, and other metadata about the logged model. + + Notes + ----- + ``signature`` and ``input_example`` are currently not supported, as they + are used to support serving and input validation in the MLflow pyfunc + flavor, which is not implemented for Darts models. + """ + # MLflow still requires "artifact_path" to be provided (it has no default), + # but it is deprecated in favour of "name". Accept it via kwargs for + # compatibility, defaulting to None so callers can use "name" alone. + artifact_path = kwargs.pop("artifact_path", None) + return Model.log( + artifact_path, + flavor=sys.modules[__name__], + model=model, + **kwargs, + ) + + +def autolog( + log_models: bool = False, + log_params: bool = True, + log_metrics: bool = True, + log_torch_metrics: bool = True, + agg_func: Callable = np.mean, + disable: bool = False, + silent: bool = False, +) -> None: + """Enable (or disable) automatic MLflow logging for Darts. + + When enabled, the following functionalities emit detailed logs: + + - Calling ``ForecastingModel.fit()`` inside an active MLflow run (e.g. within + ``with mlflow.start_run():``); does nothing if no run is active: + + - Logs model creation parameters (``model.model_params``), both as MLflow + params and as a ``model_params.json`` artifact. + - Logs target series info and covariate usage information (past, future, + and static covariates) as a ``series_info.json`` artifact. + - Stores the trained model artifact when ``log_models=True`` (default: + ``False``). + - Logs per-epoch training and validation metrics for PyTorch-based models. + - Calling ``ForecastingModel.historical_forecasts(retrain=True)`` inside an + active MLflow run; does nothing if no run is active or ``retrain`` is not + ``True``: + + - Logs the same model creation parameters and ``series_info.json`` as + ``fit()`` (overwriting any prior ``fit()`` artifacts in the same run). + - Does not log the trained model artifact; call ``log_model()`` manually + if needed. + - Calling any Darts metric inside an active MLflow run; does nothing if no + run is active: + + - Logs the result of that metric call as an MLflow metric. More information + in the notes below. + - Calling ``ForecastingModel.backtest()`` inside an active MLflow run; does + nothing if no run is active: + + - Logs all evaluation metrics under ``backtest_*`` keys. More information + in the notes below. + + .. note:: + + Logged metric keys follow the pattern + ``{metric_name}{component}{quantile_or_label}``, where each part is + included only when the corresponding axis is present: + + - ``metric_name`` – the metric function name, or the ``name`` metric + keyword argument when provided. + - ``component`` – the component name when ``component_reduction=None``. + - ``quantile_or_label``, e.g.: + + - ``_q0.500`` for quantile metrics with keyword argument ``q=[0.5]`` + - ``_qi_80.000`` for quantile interval metrics with keyword argument + ``q_interval=[(0.1, 0.9)]`` (80% interval between quantiles 0.1 and + 0.9). + - ``_label1`` for classification metrics with keyword argument + ``labels`` when ``label_reduction=None``. + + Per-timestep metrics (``time_reduction=None``) are charted across the + MLflow ``step``. + + When ``series_reduction`` is set on a metric call, results are already + aggregated across series inside the metric itself, so the + cross-series aggregation described below does not apply. + + For a list of series, the logged metric is aggregated over all series + using ``agg_func``. The detailed per-series metrics / backtest metrics + are logged under a single ``metrics_per_series.json`` table + artifact. + + When components are preserved (``component_reduction=None``), all + series scored together must have the same number of components; names + are taken from the first series. + + Metric values are only comparable across runs when the evaluation + settings match. Use the same evaluation time frame, forecast horizon, + and evaluation start date for every ``backtest()`` / metric call you + intend to compare. + + Parameters + ---------- + log_models + If ``True``, log the trained model artifact after ``fit()``. Defaults to + ``False``. + log_params + If ``True`` (default), log model creation parameters. + log_metrics + If ``True`` (default), log the result of any Darts metric call made + inside an active MLflow run. + log_torch_metrics + If ``True`` (default), enable ``mlflow.pytorch.autolog(log_models=False)`` + around PyTorch-based model training to automatically log per-epoch + training and validation metrics. Only effective for PyTorch-based models. + agg_func + Function used to aggregate a metric's per-series values into the + single value logged for a list of series (e.g. ``np.mean``, the + default, or ``np.median``). Called as ``agg_func(values)`` on a list + of floats. + disable + If ``True``, restore the original ``fit()`` methods and stop + autologging. + silent + If ``True`` (default ``False``), suppress all event logging and warnings from + MLflow during autologging. + """ + # Enable/disable mlflow.pytorch.autolog for per-epoch metrics on torch models. + # This must happen outside the @autologging_integration-decorated _autolog() + # because that decorator short-circuits _autolog()'s body entirely when + # disable=True, so a call placed inside it would never run. Unlike + # mlflow.sklearn, which exposes a private, undecorated _autolog(flavor_name=...) + # that other flavors (e.g. xgboost) call to tag its patches under their own + # integration name for cleanup, mlflow.pytorch has no such hook: its autolog() + # hardcodes its own patches under "pytorch", so Darts can't fold pytorch's + # patch lifecycle into its own and must call mlflow.pytorch.autolog() directly. + if log_torch_metrics and not disable: + try: + import mlflow.pytorch + + mlflow.pytorch.autolog( + log_models=False, + log_datasets=False, + checkpoint=False, + silent=silent, + ) + except ImportError: + pass + elif disable: + try: + import mlflow.pytorch + + mlflow.pytorch.autolog(disable=True) + except (ImportError, Exception): + pass + + # Register/unregister the metric-logging callback with darts.metrics.utils + # directly, rather than via mlflow's safe_patch on each darts.metrics + # attribute (which is import-order sensitive) + unregister_metric_callback(_mlflow_metric_callback) + if log_metrics and not disable: + register_metric_callback(_mlflow_metric_callback) + + _autolog( + log_models=log_models, + log_params=log_params, + log_metrics=log_metrics, + agg_func=agg_func, + disable=disable, + silent=silent, + ) + + +def _get_forecasting_models(): + """Find all ``ForecastingModel`` subclasses currently loaded in memory. + + Traverses ``__subclasses__()``, avoiding force-importing all of the forecasting + models. + + Returns: + A list of (name, class) tuples for all matching classes. + """ + seen: set[type] = set() + stack = [ForecastingModel] + while stack: + current = stack.pop() + for sub in current.__subclasses__(): + if sub not in seen: + seen.add(sub) + stack.append(sub) + + classes = [(cls.__name__, cls) for cls in seen] + return sorted(classes, key=itemgetter(0)) + + +@autologging_integration(FLAVOR_NAME) +def _autolog( + log_models: bool = True, + log_params: bool = True, + log_metrics: bool = True, + agg_func: Callable = np.mean, + disable: bool = False, + silent: bool = False, +) -> None: + """Internal autolog implementation decorated with ``@autologging_integration``. + + Handles patching of Darts ``ForecastingModel.fit()`` and metric functions. + The ``mlflow.pytorch.autolog`` coordination is handled by the public + ``autolog()`` wrapper because the decorator short-circuits on + ``disable=True``. + """ + + def _patched_fit(original, self, *args, **kwargs): + """Patch function for ForecastingModel.fit() autologging. + + Logs model parameters, class, and covariates; optionally logs the + model artifact when ``log_models=True``. + + Parameters + ---------- + original + The original fit method being patched. + self + The model instance (ForecastingModel or TorchForecastingModel). + args + Positional arguments passed to fit. + kwargs + Keyword arguments passed to fit. + + Returns + ------- + ForecastingModel + The result of calling the original fit method. + """ + # Create a training session to track the training process and log information + autologging_client = MlflowAutologgingQueueingClient() + + if getattr(_autolog_state, "in_historical_forecasts", False): + return original(self, *args, **kwargs) + + # handle nested fit() calls + if getattr(_autolog_state, "in_fit", False): + return original(self, *args, **kwargs) + + # Track which model is active so metric patches can prefix their keys + _autolog_state.current_model_name = type(self).__name__ + + _autolog_state.in_fit = True + try: + result = original(self, *args, **kwargs) + finally: + _autolog_state.in_fit = False + + active_run = mlflow.active_run() + if active_run is None: + return result + run_id = active_run.info.run_id + + fit_args = inspect.signature(original).bind(self, *args, **kwargs).arguments + _log_model_setup( + self, + autologging_client, + run_id, + series=fit_args["series"], + past_covariates=fit_args.get("past_covariates"), + future_covariates=fit_args.get("future_covariates"), + log_params=log_params, + ) + + param_logging_ops = autologging_client.flush(synchronous=False) + + if log_models: + model_name = type(self).__name__ + model: LoggedModel = _initialize_logged_model( + name=model_name, flavor=FLAVOR_NAME + ) + try: + registered_model_name = get_autologging_config( + flavor_name=FLAVOR_NAME, + config_key="registered_model_name", + default_value=None, + ) + log_model( + result, + name=model_name, + registered_model_name=registered_model_name, + model_id=model.model_id, + ) + except Exception: + raise_log( + ValueError( + f"Failed to autolog model artifact for {type(self).__name__}." + ) + ) + + param_logging_ops.await_completion() + + return result + + def _patched_historical_forecasts(original, self, *args, **kwargs): + """Suppress per-iteration fit() autologging; log model setup once when + ``retrain=True``. + + Sets a thread-local flag so ``_patched_fit`` skips autologging for the + internal ``fit()`` calls. When ``retrain is True`` and an MLflow run is + active, logs model tags, creation parameters, and series info once after + the call (overwriting any prior ``fit()`` artifacts in the same run). + Does not start a run and does not log the trained model artifact. + """ + _autolog_state.in_historical_forecasts = True + try: + result = original(self, *args, **kwargs) + finally: + _autolog_state.in_historical_forecasts = False + + active_run = mlflow.active_run() + if active_run is None: + return result + + bound = inspect.signature(ForecastingModel.historical_forecasts).bind( + self, *args, **kwargs + ) + bound.apply_defaults() + if bound.arguments["retrain"] is not True: + return result + + autologging_client = MlflowAutologgingQueueingClient() + _log_model_setup( + self, + autologging_client, + active_run.info.run_id, + series=bound.arguments["series"], + past_covariates=bound.arguments.get("past_covariates"), + future_covariates=bound.arguments.get("future_covariates"), + log_params=log_params, + ) + autologging_client.flush(synchronous=False).await_completion() + return result + + def _patched_backtest(original, self, *args, **kwargs): + """Wrap ``backtest`` to log metric result(s) to the active MLflow run. + + Delegates to ``_log_backtest_metrics``, which infers result shape from + the metric signature and logs every cell under a descriptive key. + """ + _autolog_state.in_backtest = True + try: + result = original(self, *args, **kwargs) + finally: + _autolog_state.in_backtest = False + + active_run = mlflow.active_run() + if not log_metrics or active_run is None: + return result + + bound = inspect.signature(ForecastingModel.backtest).bind(self, *args, **kwargs) + bound.apply_defaults() + backtest_args = bound.arguments + + autologging_client = MlflowAutologgingQueueingClient() + _log_backtest_metrics( + autologging_client=autologging_client, + run_id=active_run.info.run_id, + result=result, + backtest_args=backtest_args, + agg_func=agg_func, + ) + autologging_client.flush(synchronous=False).await_completion() + return result + + # patch `fit()` for all forecasting models + for _, cls in _get_forecasting_models(): + safe_patch( + FLAVOR_NAME, + cls, + "fit", + _patched_fit, + ) + + # patch `historical_forecasts()` for all forecasting models so that the + # N internal fit() calls don't each log, and so that retrain=True calls + # log model setup once + for _, cls in _get_forecasting_models(): + safe_patch( + FLAVOR_NAME, + cls, + "historical_forecasts", + _patched_historical_forecasts, + ) + + # patch `backtest()` for all forecasting models to log metric results + for _, cls in _get_forecasting_models(): + safe_patch( + FLAVOR_NAME, + cls, + "backtest", + _patched_backtest, + ) + + +def get_default_pip_requirements(): + """Return the default pip requirements for logging a Darts model. + + Returns + ------- + list[str] + A list of pip requirement strings. + """ + reqs = [_get_pinned_requirement("darts")] + return reqs + + +def get_default_conda_env(): + """Return a default conda environment dict for a Darts model. + + Returns + ------- + dict + A conda environment specification dictionary. + """ + return _mlflow_conda_env( + additional_pip_deps=get_default_pip_requirements(), + ) + + +def _infer_covariate_usage( + model: ForecastingModel, + series: TimeSeriesLike, + past_covariates: TimeSeriesLike | None, + future_covariates: TimeSeriesLike | None, +) -> tuple[bool, bool, bool]: + """Infer past/future/static covariate usage from model state and call args. + + After ``historical_forecasts(retrain=True)`` the outer model is still + unfitted (training happens on internal copies), so ``model.uses_*`` stays + ``False``. Fall back to call args / ``add_encoders`` / static covariates on + ``series``, gated by ``supports_*`` / ``considers_static_covariates``. + """ + # encoder keys like "datetime_attribute" map to {"past": ..., "future": ...}; + # non-dict values ("tz", "transformer") are ignored by the isinstance check + enc_types = { + cov + for val in (model.add_encoders or {}).values() + if isinstance(val, dict) + for cov in ("past", "future") + if cov in val + } + first_series = get_single_series(series) + uses_past = model.uses_past_covariates or ( + model.supports_past_covariates + and (past_covariates is not None or "past" in enc_types) + ) + uses_future = model.uses_future_covariates or ( + model.supports_future_covariates + and (future_covariates is not None or "future" in enc_types) + ) + uses_static = model.uses_static_covariates or ( + first_series is not None + and first_series.static_covariates is not None + and model.supports_static_covariates + and model.considers_static_covariates + ) + return uses_past, uses_future, uses_static + + +def _get_model_info_tags( + model: ForecastingModel, + series: TimeSeriesLike, + past_covariates: TimeSeriesLike | None = None, + future_covariates: TimeSeriesLike | None = None, +) -> dict[str, Any]: + """ + Returns: + A dictionary of MLflow run tag keys and values describing the specified model. + """ + uses_past, uses_future, uses_static = _infer_covariate_usage( + model, series, past_covariates, future_covariates + ) + return { + "model_class": model.__class__.__name__, + "model_reference": ( + model.__class__.__module__ + "." + model.__class__.__name__ + ), + "model_likelihood": ( + model.likelihood.__class__.__name__ + if model.likelihood is not None + else None + ), + "model_uses_past_covariates": uses_past, + "model_uses_future_covariates": uses_future, + "model_uses_static_covariates": uses_static, + } + + +def _log_model_setup( + model: ForecastingModel, + autologging_client: MlflowAutologgingQueueingClient, + run_id: str, + series: TimeSeriesLike, + past_covariates: TimeSeriesLike | None = None, + future_covariates: TimeSeriesLike | None = None, + *, + log_params: bool = True, +) -> None: + """Log model tags, creation parameters, and series info to an active run. + + Shared by ``fit()`` and ``historical_forecasts(retrain=True)`` autologging. + Does not log the trained model artifact. + """ + autologging_client.set_tags( + run_id=run_id, + tags=_get_model_info_tags( + model, + series=series, + past_covariates=past_covariates, + future_covariates=future_covariates, + ), + ) + if log_params: + autologging_client.log_params(run_id=run_id, params=model.model_params) + mlflow.log_dict(model.model_params, "model_params.json") + _log_series_info( + model, + series=series, + past_covariates=past_covariates, + future_covariates=future_covariates, + ) + + +def _log_series_info( + model: ForecastingModel, + series: TimeSeriesLike, + past_covariates: TimeSeriesLike | None, + future_covariates: TimeSeriesLike | None, +) -> None: + """Log target series and covariate usage information to MLflow. + + Extracts information about the target series, and about past, future, + and static covariates used during training and logs them as a JSON + artifact for easy filtering, comparison, and documentation. + + Logs: + - Target series: component count and names + - Past / future covariates: usage, count, and names, including both + explicitly-passed covariates and any generated by ``add_encoders`` + - Static covariates: usage, count, names, and whether they are global + - Artifact: complete metadata as ``series_info.json`` + + Parameters + ---------- + model + A fitted Darts forecasting model instance. + series + The ``series`` argument passed to ``fit()``: a single ``TimeSeries`` + or a ``Sequence[TimeSeries]``. + past_covariates + The past covariate argument passed to ``fit()``, or + ``None``. + future_covariates + The future covariate covariate argument passed to ``fit()``, or + ``None``. + """ + first_series = get_single_series(series) + uses_past, uses_future, uses_static = _infer_covariate_usage( + model, series, past_covariates, future_covariates + ) + series_info = { + "series": { + "count": first_series.n_components, + "names": first_series.components.tolist(), + }, + "past_covariates": _extract_covariate_metadata( + uses_past, + get_single_series(past_covariates), + "components", + encoded_names=model.encoders.past_components, + ), + "future_covariates": _extract_covariate_metadata( + uses_future, + get_single_series(future_covariates), + "components", + encoded_names=model.encoders.future_components, + ), + } + + static_covariates = ( + first_series.static_covariates if first_series is not None else None + ) + series_info["static_covariates"] = _extract_covariate_metadata( + uses_static, static_covariates, "columns" + ) + if uses_static and static_covariates is not None: + # static covariates are global (one shared row) unless there is one row + # per series component, in which case they are component-specific + series_info["static_covariates"]["is_global"] = ( + len(static_covariates) != first_series.n_components + ) + + # log complete information as JSON artifact + mlflow.log_dict(series_info, "series_info.json") + + +def _is_torch_model(model) -> bool: + """Check if a model is a `TorchForecastingModel`. + + Parameters + ---------- + model + A Darts forecasting model instance. + + Returns + ------- + bool + True if the model is a `TorchForecastingModel`, False otherwise. + """ + return TORCH_AVAILABLE and isinstance(model, TorchForecastingModel) + + +def _extract_covariate_metadata( + uses: bool, + single_cov: TimeSeries | pd.DataFrame | None, + names_attr: str, + encoded_names: pd.Index | list[str] | None = None, +) -> dict: + """Extract metadata for a single covariate type from its (already + singular) value. + + Parameters + ---------- + uses + Whether the model uses this covariate type. + single_cov + The covariate's value for one series: a ``TimeSeries`` (past/future + covariates) or a static-covariates ``DataFrame``, or ``None``. + names_attr : str + Attribute holding the feature names ("components" for a + ``TimeSeries``, "columns" for a static-covariates ``DataFrame``). + encoded_names + Additional covariate names generated by encoders (``add_encoders``), + appended to the names extracted from ``single_cov``. Ignored when + ``uses`` is ``False``. + + Returns + ------- + dict + Dictionary with keys: "used" (bool), "count" (int), "names" (list). + """ + info = {"used": False, "count": 0, "names": []} + + if uses: + info["used"] = True + names = list(getattr(single_cov, names_attr)) if single_cov is not None else [] + if encoded_names is not None: + names = names + list(encoded_names) + info["names"] = names + info["count"] = len(names) + + return info + + +def _sanitize_mlflow_key(name: str) -> str: + """Sanitize a string for use as an MLflow metric key. + + Replaces any character that is not alphanumeric, a hyphen, or an + underscore with an underscore, so component names become valid + MLflow keys. + + Parameters + ---------- + name + The raw name to sanitize. + + Returns + ------- + str + A string safe for use as an MLflow metric key. + """ + return re.sub(r"[^\w-]", "_", name) + + +def _build_metric_keys( + metric_names: list[str], + components: list[str], + has_comp_axis: bool, + metric_axes: list[tuple[bool, bool, list[str]]], + *, + prefix: str = "", +) -> tuple[int, list[list[str]]]: + """Build sanitized MLflow metric keys for each metric x component x axis label. + + Parameters + ---------- + metric_names + One sanitized metric-name token per metric. + components + Component names from the first series (used only when ``has_comp_axis``). + has_comp_axis + Whether components are preserved in the metric output. + metric_axes + Per-metric ``(has_time_axis, has_comp_axis, axis_labels)`` tuples from + ``_infer_metric_axes``. Axis size is taken from the first entry. + prefix + Optional key prefix (e.g. ``"backtest_"``). + + Returns + ------- + tuple[int, list[list[str]]] + ``(c_size, keys)`` where ``c_size`` is + ``(n_components if has_comp_axis else 1) * axis_size`` and ``keys[m][c]`` + is the sanitized key for metric ``m`` and flat component/axis index ``c``. + """ + axis_size = len(metric_axes[0][2]) + c_size = (len(components) if has_comp_axis else 1) * axis_size + keys: list[list[str]] = [] + for m, metric_name in enumerate(metric_names): + axis_labels = metric_axes[m][2] + keys_m = [] + for c in range(c_size): + # c is a flat index into the (n_components x axis_size) C axis: + # c = comp_i * axis_size + axis_idx + component_index, axis_idx = divmod(c, axis_size) + comp_part = ( + "_" + _sanitize_mlflow_key(components[component_index]) + if has_comp_axis + else "" + ) + keys_m.append( + _sanitize_mlflow_key( + f"{prefix}{metric_name}{comp_part}{axis_labels[axis_idx]}" + ) + ) + keys.append(keys_m) + return c_size, keys + + +def _log_per_series_table(rows: list[dict]) -> None: + """Append the granular per-series metric breakdown to a single, run-wide + table artifact. + + Each row is a single metric cell for one series, with columns ``key`` (the + aggregate MLflow key, without any series suffix), ``series_index``, ``step`` + (the time or window index charted by MLflow), ``window_index`` (the source + backtest window, or ``None``), and ``value``. All calls within a run append + to the same ``metrics_per_series.json`` artifact. + Used when more than one series is scored, since the logged metric keys + only carry the aggregate over series. + + Parameters + ---------- + rows + One dict per metric cell with keys ``key``, ``series_index``, ``step``, + ``window_index``, and ``value``. + """ + if not rows: + return + df = pd.DataFrame(rows).sort_values(["key", "series_index", "step"]) + mlflow.log_table(data=df, artifact_file="metrics_per_series.json") + + +def _flush_logged_metrics( + autologging_client: MlflowAutologgingQueueingClient, + run_id: str, + agg: dict[tuple[str, int], list[float]], + agg_func: Callable, + table_rows: list[dict] | None = None, +) -> None: + """Aggregate per-series cells, log MLflow metrics, and optionally write the + per-series table artifact. + + Parameters + ---------- + autologging_client + MLflow autologging client used to queue metric writes. + run_id + ID of the active MLflow run. + agg + Map of ``(key, step) -> list of per-series float values``. + agg_func + Aggregation over the per-series values for each ``(key, step)``. + table_rows + Granular cells for ``metrics_per_series.json``. ``None`` skips writing + the table artifact. + """ + metrics_by_step: dict[int, dict[str, float]] = {} + for (key, step), values in agg.items(): + metrics_by_step.setdefault(step, {})[key] = float(agg_func(values)) + for step, metrics in metrics_by_step.items(): + autologging_client.log_metrics(run_id=run_id, metrics=metrics, step=step) + + if table_rows is not None: + _log_per_series_table(table_rows) + + +def _log_backtest_metrics( + autologging_client: MlflowAutologgingQueueingClient, + run_id: str, + result, + backtest_args: dict, + agg_func: Callable = np.mean, +) -> None: + """Log backtest metric result(s) to MLflow. + + Reshapes each per-series result to a canonical ``(W, T, C, M)`` layout + (windows, timesteps, components x quantiles, metrics) inferred from the + metric signatures and ``backtest_args``, logging every cell under a + descriptive key with the time axis (or window axis when time is reduced) + mapped to the MLflow ``step``. A metric's ``name`` entry in + ``metric_kwargs`` overrides the metric-name token in the key (the + ``backtest_`` prefix and axis suffixes are preserved). + + Shape inference respects all kwargs that affect output dimensions: + + - ``time_reduction`` – collapses the time axis (``T=1``). + - ``component_reduction`` – collapses the component axis (``C=1``). + - ``series_reduction`` – if other than ``None``, windows are already aggregated + inside the metric, so ``W=1`` regardless of ``backtest.reduction``. + - ``q`` / ``q_interval`` – expand the component axis with one entry per + quantile / interval. + - ``labels`` - expand each component with one entry per label along + the component axis. + - ``label_reduction`` – collapses the labels along the component axis. + value; ``labels`` only restricts which classes are scored. + - ``reduction=None`` – no aggregation across windows -> one value per window. + - ``last_points_only`` – collapses all windows into one TimeSeries before scoring, + so there is effectively only one window regardless of reduction. + + + When more than one series is scored, the logged value is ``agg_func`` + applied over series for each cell, and the granular per-series breakdown + is appended to the run's ``metrics_per_series.json`` table artifact + (shared with ``_log_metric_result``). For a single series the aggregate + is just the value itself and no artifact is written. When components + are preserved (``component_reduction=None``), all series scored together + must have the same number of components; names are taken from the first + series. + + Series can have different lengths and intersect in different ways, + so the time axis is aligned from the end rather than the start: a shorter + series lines up on its last value instead of its first. To represent this, + the time axis is mapped to the MLflow ``step`` as ``t - t_size`` when + ``has_time_axis`` is ``True``. + + Raises + ------ + ValueError + On a shape/size mismatch between the metric result and the inferred + axes, when ``component_reduction=None`` and series in a sequence have + different numbers of components, or when ``label_reduction=None`` is + requested without explicit ``labels``. + + Parameters + ---------- + autologging_client + MLflow autologging client used to queue metric writes. + run_id + ID of the active MLflow run. + result + Return value of ``backtest()``. + backtest_args + Bound arguments of the ``backtest()`` call (from + ``inspect.BoundArguments.arguments`` after ``apply_defaults``). + agg_func + Function used to aggregate a metric's per-series values into the + single value logged for a list of series. Called as + ``agg_func(values)`` on a list of floats. + """ + metric = backtest_args.get("metric") + metric = metric if isinstance(metric, list) else [metric] + metric_kwargs = backtest_args.get("metric_kwargs") or {} + metric_kwargs = ( + metric_kwargs if isinstance(metric_kwargs, list) else [metric_kwargs] + ) + # backtest accepts a single dict that applies to all metrics; broadcast it + if len(metric_kwargs) != len(metric): + metric_kwargs = [metric_kwargs[0]] * len(metric) + # the `name` entry in metric_kwargs overrides the metric-name token in the key + metric_names = [ + _sanitize_mlflow_key( + metric_kwargs[i].get("name") or getattr(m, "__name__", f"metric_{i}") + ) + for i, m in enumerate(metric) + ] + n_metrics = len(metric) + + # reduction=None means no aggregation across windows -> one value per window. + # last_points_only collapses all windows into one TimeSeries before scoring, + # so there is effectively only one window regardless of reduction. + has_windows = backtest_args.get("reduction") is None and not backtest_args.get( + "last_points_only", False + ) + + # series_reduction inside the metric itself already aggregates across windows, + # so the result has no window axis even when backtest.reduction is None. + metric_0_params = inspect.signature(metric[0]).parameters + if "series_reduction" in metric_0_params: + effective_sr = metric_kwargs[0].get( + "series_reduction", metric_0_params["series_reduction"].default + ) + if effective_sr is not None: + has_windows = False + + # check the dim axes from the metric kwargs for each + metric_axes = [_infer_metric_axes(m, kw) for m, kw in zip(metric, metric_kwargs)] + has_time_axis, has_comp_axis, _ = metric_axes[0] + + series = backtest_args.get("series") + forecast_horizon = backtest_args.get("forecast_horizon") + historical_forecasts = backtest_args.get("historical_forecasts") + last_points_only = backtest_args.get("last_points_only", False) + + # if last_points_only is True, has_windows will be False, so fc_hzn is not needed + if historical_forecasts is not None and not last_points_only: + first_series_hf = ( + historical_forecasts + if get_series_seq_type(series) == SeriesType.SINGLE + else historical_forecasts[0] + ) + forecast_horizon = len(first_series_hf[0]) + + series_seq = series2seq(series) + results = [result] if get_series_seq_type(series) == SeriesType.SINGLE else result + # component names are only used when the metric preserves components + if has_comp_axis: + n_components = {s.n_components for s in series_seq} + if len(n_components) > 1: + raise_log( + ValueError( + "Backtest metric logging failed: all series must have the same " + f"number of components, got {sorted(n_components)}. Consider " + f"setting a metric `component_reduction`, or make sure all series " + f"have the same number of components." + ) + ) + + # agg maps (key, step) -> per-series values, aggregated into the logged metric. + agg: dict[tuple[str, int], list[float]] = {} + rows: list[dict] = [] + + # component names/count from the first series (all series share n_components) + comps = series_seq[0].components.tolist() + c_size, base_keys = _build_metric_keys( + metric_names, + comps, + has_comp_axis, + metric_axes, + prefix="backtest_", + ) + + # first pass: reshape each series' result into a canonical (W, T, C, M) + # array, recording its window-axis length for the alignment pass below. + series_shapes = [] + for r in results: + arr = np.asarray(r, dtype=float) + # after stripping C and M axes, rest = W*T (or W or T alone) + rest, extra = divmod(arr.size, c_size * n_metrics) + if extra: + raise_log( + ValueError( + f"Backtest metric logging failed: result size ({arr.size}) " + f"is not divisible by c_size * n_metrics ({c_size} * " + f"{n_metrics} = {c_size * n_metrics}). The metric output " + "shape does not match the inferred axes." + ) + ) + + # both time and window axes present: backtest returns (W*T*C*M,) in C order so we can + # recover W and T only if forecast_horizon is known (T = forecast_horizon) + if has_time_axis and has_windows: + t_size, w_size = forecast_horizon, rest // forecast_horizon + elif has_time_axis: + t_size, w_size = rest, 1 + elif has_windows: + t_size, w_size = 1, rest + else: + if rest != 1: + raise_log( + ValueError( + f"Backtest metric logging failed: expected a single " + f"scalar per component/metric after reduction, but got " + f"{rest} elements. Check time_reduction and " + "component_reduction defaults." + ) + ) + t_size, w_size = 1, 1 + + series_shapes.append(( + t_size, + w_size, + arr.reshape(w_size, t_size, c_size, n_metrics), + )) + + # align the calendar-relative axes from the end + max_w_size = max((w_size for _, w_size, _ in series_shapes), default=0) + t_axis_is_calendar = has_time_axis and not has_windows and last_points_only + + for series_index, (t_size, w_size, canonical) in enumerate(series_shapes): + w_offset = max_w_size - w_size if has_windows else 0 + for m in range(n_metrics): + for c in range(c_size): + key = base_keys[m][c] + if has_time_axis and has_windows: + # Keep window-level values in the detailed table, but aggregate + # windows into one chart value per horizon step for this series. + for t in range(t_size): + values = canonical[:, t, c, m] + agg.setdefault((key, t), []).append(float(np.nanmean(values))) + for w, value in enumerate(values): + window_index = w + w_offset + rows.append({ + "key": key, + "series_index": series_index, + "step": t, + "window_index": window_index - max_w_size, + "value": float(value), + }) + continue + + for w in range(w_size): + aligned_w = w + w_offset + for t in range(t_size): + # MLflow step maps to the axis the UI should chart: + # horizon when present, otherwise end-relative calendar axis + if has_time_axis: + step = t - t_size if t_axis_is_calendar else t + else: + step = aligned_w - max_w_size + value = float(canonical[w, t, c, m]) + agg.setdefault((key, step), []).append(value) + rows.append({ + "key": key, + "series_index": series_index, + "step": step, + "window_index": None, + "value": value, + }) + + _flush_logged_metrics( + autologging_client, + run_id, + agg, + agg_func=agg_func, + table_rows=( + rows if len(series_seq) > 1 or (has_time_axis and has_windows) else None + ), + ) + + +def _infer_metric_axes(metric: Callable, metric_kwargs: dict) -> tuple: + """Infer a metric's output axes from its signature and ``metric_kwargs``. + + Covers ``time_reduction``, ``component_reduction``, ``q``, ``q_interval``, + and ``label_reduction`` / ``labels`` for classification metrics. + ``series_reduction`` is handled at the ``_log_backtest_metrics`` level. + + Parameters + ---------- + metric + A Darts metric callable. + metric_kwargs + Keyword arguments that will be forwarded to ``metric``. + + Returns + ------- + tuple + ``(has_time_axis, has_comp_axis, axis_labels)`` where + + - ``has_time_axis`` – ``True`` when ``time_reduction`` is ``None`` (i.e. a + per-timestep axis is present in the output). + - ``has_comp_axis`` – ``True`` when components are expanded (not collapsed to a scalar). + - ``axis_labels`` – one key suffix per quantile/interval/label entry. + + Raises + ------ + ValueError + If ``label_reduction=None`` is requested without explicit ``labels``. + """ + params = inspect.signature(metric).parameters + + def effective(param_name: str) -> Any: + """Return metric_kwargs value if present, else the signature default.""" + if param_name in metric_kwargs: + return metric_kwargs[param_name] + return params[param_name].default if param_name in params else None + + has_time_axis = "time_reduction" in params and effective("time_reduction") is None + has_comp_axis = ( + "component_reduction" in params and effective("component_reduction") is None + ) + + q_interval, q = metric_kwargs.get("q_interval"), metric_kwargs.get("q") + if "q_interval" in params and q_interval is not None: + intervals = np.atleast_2d(np.array(q_interval, dtype=float)) + axis_labels = [f"_qi_{100 * (hi - lo):.3f}" for lo, hi in intervals] + elif "q" in params and q is not None: + axis_labels = [f"_q{v:.3f}" for v in np.atleast_1d(np.array(q, dtype=float))] + elif "label_reduction" in params and getattr(metric, "__name__", ""): + label_reduction = effective("label_reduction") + if isinstance(label_reduction, _LabelReduction): + label_reduction = label_reduction.value + labels = metric_kwargs.get("labels") + # label_reduction=None means one output per label, but without explicit + # labels we can't know how many ahead of time + if label_reduction is None and labels is None: + raise_log( + ValueError( + "`label_reduction=None` requires explicit `labels` to be " + "passed for MLflow autologging (the number of output " + "labels cannot be determined ahead of time otherwise)." + ) + ) + axis_labels = ( + [f"_label{x}" for x in np.atleast_1d(labels)] + if label_reduction is None + else [""] + ) + else: + axis_labels = [""] + + return (has_time_axis, has_comp_axis, axis_labels) + + +def _log_metric_result( + autologging_client: MlflowAutologgingQueueingClient, + run_id: str, + metric_name: str, + result, + series, + has_time_axis: bool, + has_comp_axis: bool, + axis_labels: list[str], + series_reduced: bool = False, + agg_func: Callable = np.mean, +) -> None: + """Log a metric result to the active MLflow run. + + Reshapes each per-series result into a canonical ``(T, C)`` layout + (timesteps, components x quantiles/intervals/labels) inferred from the + metric signature and call kwargs by ``_infer_metric_axes``, logging every + cell under a descriptive key with the time axis mapped to the MLflow + ``step``. This mirrors ``_log_backtest_metrics`` (without the + window/``forecast_horizon`` split, since ``multi_ts_support`` returns a + clean per-series list). + + The logged MLflow key follows the pattern:: + + {metric_name}{component}{quantile_or_label} + + where each optional part is included only when the corresponding axis is + present: + + - ``component`` – ``_{component_name}`` when ``has_comp_axis``. + - ``quantile_or_label`` – e.g. ``_q0.500`` / ``_qi_80.000`` / ``_label1``. + + When more than one series is scored, the logged value is ``agg_func`` + applied over series for each cell, and the granular per-series breakdown + is appended to the run's ``metrics_per_series.json`` table artifact + (shared with ``_log_backtest_metrics``). For a single series the + aggregate is just the value itself and no artifact is written. + + Series can have different lengths and intersect in different ways, + so the time axis is aligned from the end rather than the start: a shorter + series lines up on its last value instead of its first. To represent this, + the time axis is mapped to the MLflow ``step`` as ``t - t_size`` when + ``has_time_axis`` is ``True``. + + Raises + ------ + ValueError + On a shape mismatch between the metric result and the inferred + axes, or when ``has_comp_axis`` is ``True`` and series in a sequence + have different numbers of components. + + Parameters + ---------- + metric_name + Metric name used as the MLflow key (the metric's ``name`` keyword + argument when provided, otherwise the metric function name). + result + The metric result to log. + series + The ``actual_series`` argument passed to the metric (single series or + ``Sequence[TimeSeries]``); used for component names and series count. + When ``has_comp_axis`` is ``True``, all series in a sequence must have + the same number of components; names are taken from the first series. + has_time_axis + ``True`` when the result carries a per-timestep axis (``time_reduction=None``). + has_comp_axis + ``True`` when components are expanded (``component_reduction=None``). + axis_labels + One key suffix per quantile/interval/label entry. + series_reduced + ``True`` when ``series_reduction`` collapsed the series axis inside the + metric, so the result has no leading series axis even for list input. + agg_func + Function used to aggregate a metric's per-series values into the + single value logged for a list of series. Called as + ``agg_func(values)`` on a list of floats. + """ + if series_reduced: + # series_reduction aggregated across series -> single result, no series axis + series_seq = [get_single_series(series)] + results = [result] + else: + series_seq = series2seq(series) + results = ( + [result] if get_series_seq_type(series) == SeriesType.SINGLE else result + ) + # component names are only used when the metric preserves components + if has_comp_axis: + n_components = {s.n_components for s in series_seq} + if len(n_components) > 1: + raise_log( + ValueError( + f"Metric logging failed for `{metric_name}`: all series must " + f"have the same number of components, got " + f"{sorted(n_components)}." + ) + ) + + # component names/count from the first series (all series share n_components) + comps = series_seq[0].components.tolist() + c_size, base_keys = _build_metric_keys( + [metric_name], + comps, + has_comp_axis, + [(has_time_axis, has_comp_axis, axis_labels)], + ) + keys = base_keys[0] + + # first pass: reshape each series' result into a canonical (T, C) array, + # recording its time-axis length for the alignment pass below. + series_shapes = [] + for r in results: + arr = np.asarray(r, dtype=float) + # after stripping the C axis, the remainder is the time axis (or scalar) + n_times, extra = divmod(arr.size, c_size) + if extra: + raise_log( + ValueError( + f"Metric logging failed for `{metric_name}`: result size " + f"({arr.size}) is not divisible by the inferred " + f"component/quantile size ({c_size}). The metric output " + "shape does not match the inferred axes." + ) + ) + + if has_time_axis: + t_size = n_times + elif n_times != 1: + raise_log( + ValueError( + f"Metric logging failed for `{metric_name}`: expected a " + f"single value per component/quantile after reduction, " + f"but got {n_times} elements. Check time_reduction and " + "component_reduction." + ) + ) + else: + t_size = 1 + + series_shapes.append((t_size, arr.reshape(t_size, c_size))) + + # agg maps (key, step) -> per-series values, aggregated into the logged metric. + agg: dict[tuple[str, int], list[float]] = {} + rows: list[dict] = [] + for series_index, (t_size, canonical) in enumerate(series_shapes): + for c, key in enumerate(keys): + for t in range(t_size): + # Forecast positions are zero-based: the first prediction is 0. + step = t if has_time_axis else 0 + value = float(canonical[t, c]) + agg.setdefault((key, step), []).append(value) + rows.append({ + "key": key, + "series_index": series_index, + "step": step, + "window_index": None, + "value": value, + }) + + _flush_logged_metrics( + autologging_client, + run_id, + agg, + agg_func=agg_func, + table_rows=rows if len(series_seq) > 1 else None, + ) + autologging_client.flush(synchronous=False).await_completion() + + +def _mlflow_metric_callback(func, result, args, kwargs) -> None: + """Metric callback registered with ``darts.metrics.utils`` for autologging. + + Invoked by ``multi_ts_support`` (the outermost decorator on every Darts + metric) after every top-level metric call, so it fires regardless of how + the metric was imported. It is not invoked for internal metric-to-metric + calls (e.g. ``rmse`` calling ``mse`` internally via ``_get_wrapped_metric``), + since those bypass ``multi_ts_support`` entirely. + + When an active MLflow run exists, infers the output axes from the metric + signature and call kwargs (via ``_infer_metric_axes``) and delegates to + ``_log_metric_result``, which logs each cell under a key built as:: + + {metric_name}{component}{quantile_or_label} + + where: + + - ``metric_name`` – the metric function name, or the ``name`` keyword + argument when provided (it overrides only this token). + - ``component`` – ``_{component_name}`` when ``component_reduction=None``. + - ``quantile_or_label`` – quantile/interval/label suffix (e.g. ``_q0.500``, + ``_qi_80.000``, ``_label1``) when applicable. + + When the input is a ``Sequence[TimeSeries]`` with more than one series, the + logged value is ``autolog()``'s ``agg_func`` applied over series, and the + per-series breakdown is appended to the run's ``metrics_per_series.json`` + table artifact instead of per-series keys. + + The per-timestep axis (``time_reduction=None``) is mapped to the MLflow + ``step``. + + Parameters + ---------- + func + The Darts metric function that was called (used for its name and + signature). + result + The metric's return value. + args + Positional arguments the metric was called with. + kwargs + Keyword arguments the metric was called with. + """ + active_run = mlflow.active_run() + if active_run is None: + return + + # backtest() calls metric functions internally; _patched_backtest + # handles logging the aggregated result, so skip here to avoid + # generating one flat key per window (series_gen_mape_0, _1, …). + if getattr(_autolog_state, "in_backtest", False): + return + + series = args[0] if len(args) > 0 else kwargs["actual_series"] + + autologging_client = MlflowAutologgingQueueingClient() + run_id = active_run.info.run_id + + # the `name` kwarg overrides the metric-name token in the logged key + key_name = _sanitize_mlflow_key(kwargs.get("name") or func.__name__) + + # infer output axes from the metric signature + call kwargs + has_time_axis, has_comp_axis, axis_labels = _infer_metric_axes(func, kwargs) + + # series_reduction collapses the series axis inside the metric, so the + # result has no leading series axis even for list input. + params = inspect.signature(func).parameters + series_reduced = False + if "series_reduction" in params: + effective_sr = kwargs.get( + "series_reduction", params["series_reduction"].default + ) + series_reduced = effective_sr is not None + + # _mlflow_metric_callback is a bare registered callback, not a closure over + # autolog()'s call kwargs, so agg_func is read back from the autologging + # config store that autolog() populated. + agg_func = get_autologging_config( + flavor_name=FLAVOR_NAME, config_key="agg_func", default_value=np.mean + ) + _log_metric_result( + autologging_client, + run_id, + key_name, + result, + series, + has_time_axis, + has_comp_axis, + axis_labels, + series_reduced=series_reduced, + agg_func=agg_func, + ) diff --git a/examples/29-MLflow-quickstart.ipynb b/examples/29-MLflow-quickstart.ipynb new file mode 100644 index 0000000000..b5a76dbb92 --- /dev/null +++ b/examples/29-MLflow-quickstart.ipynb @@ -0,0 +1,6143 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MLflow for Darts\n", + "\n", + "This notebook demonstrates how to use Darts with MLflow for experiment tracking, model versioning, and management.\n", + "If you are new to Darts, please check out the [Quickstart Guide](https://unit8co.github.io/darts/quickstart/00-quickstart.html) before proceeding.\n", + "\n", + "MLflow is an open-source platform for managing the end-to-end machine learning lifecycle. It provides tools for tracking experiments, packaging code into reproducible runs, sharing and deploying models, and managing model versions in a central registry. With Darts' MLflow integration, you can easily log forecasting models, compare experiments, and manage model versions throughout your forecasting workflow.\n", + "\n", + "For more details, see the [MLflow documentation](https://mlflow.org/docs/latest/index.html)." + ], + "id": "aeddb542" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installing MLflow\n", + "\n", + "MLflow is available as an optional dependency for Darts. Install it with:\n", + "\n", + "```bash\n", + "pip install \"mlflow>=3.0\"\n", + "```" + ], + "id": "f72894af" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup and Imports" + ], + "id": "42e3dcea" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:43.049165Z", + "iopub.status.busy": "2026-06-24T15:18:43.049088Z", + "iopub.status.idle": "2026-06-24T15:18:43.053693Z", + "shell.execute_reply": "2026-06-24T15:18:43.053435Z" + } + }, + "source": [ + "# fix python path if working locally\n", + "from utils import fix_pythonpath_if_working_locally\n", + "\n", + "fix_pythonpath_if_working_locally()\n", + "\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\", category=FutureWarning)" + ], + "execution_count": 2, + "outputs": [], + "id": "b346ce8f" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:43.054776Z", + "iopub.status.busy": "2026-06-24T15:18:43.054722Z", + "iopub.status.idle": "2026-06-24T15:18:46.599643Z", + "shell.execute_reply": "2026-06-24T15:18:46.599208Z" + } + }, + "source": [ + "%matplotlib inline\n", + "\n", + "import os\n", + "import tempfile\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import mlflow\n", + "import numpy as np\n", + "\n", + "import darts.metrics as metrics\n", + "from darts.datasets import AirPassengersDataset\n", + "from darts.models import ExponentialSmoothing, LinearRegressionModel, NBEATSModel\n", + "from darts.utils.mlflow import autolog, load_model, log_model, save_model" + ], + "execution_count": 3, + "outputs": [], + "id": "13b13fe4" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:46.600906Z", + "iopub.status.busy": "2026-06-24T15:18:46.600748Z", + "iopub.status.idle": "2026-06-24T15:18:46.634540Z", + "shell.execute_reply": "2026-06-24T15:18:46.634100Z" + } + }, + "source": [ + "# use darts plotting style\n", + "from darts import set_option\n", + "\n", + "set_option(\"plotting.use_darts_style\", True)" + ], + "execution_count": 4, + "outputs": [], + "id": "4d424e08" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## MLflow Setup\n", + "\n", + "First, let's configure MLflow tracking. We'll use a temporary directory for this example, however you can choose from any of the supported MLflow [tracking backends](https://mlflow.org/docs/latest/self-hosting/architecture/tracking-server/#backend-store) such as local filesystem, SQLite, PostgreSQL, MySQL, or cloud storage solutions like S3 or Azure Blob Storage." + ], + "id": "2f9c40d6" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:46.636640Z", + "iopub.status.busy": "2026-06-24T15:18:46.636565Z", + "iopub.status.idle": "2026-06-24T15:18:47.268439Z", + "shell.execute_reply": "2026-06-24T15:18:47.268081Z" + } + }, + "source": [ + "# temporary directory for MLflow tracking\n", + "tmpdir = tempfile.mkdtemp()\n", + "mlflow_db = os.path.join(tmpdir, \"mlflow.db\")\n", + "\n", + "mlflow.set_tracking_uri(f\"sqlite:///{mlflow_db}\")\n", + "mlflow.set_experiment(\"darts-quickstart\")\n", + "\n", + "print(f\"MLflow tracking URI: {mlflow.get_tracking_uri()}\")\n", + "print(f\"Experiment: {mlflow.get_experiment_by_name('darts-quickstart').name}\")" + ], + "execution_count": 5, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "2026/07/23 18:50:07 INFO mlflow.store.db.utils: Creating initial MLflow database tables...\n", + "2026/07/23 18:50:07 INFO mlflow.store.db.utils: Updating database tables\n", + "2026/07/23 18:50:08 INFO mlflow.tracking.fluent: Experiment with name 'darts-quickstart' does not exist. Creating a new experiment.\n" + ] + }, + { + "name": "stdout", +"output_type": "stream", + "text": [ + "MLflow tracking URI: sqlite:////var/folders/yr/3703qwtj56lcw6n91xm6tq_r0000gn/T/tmpw5bcp0ic/mlflow.db\n", + "Experiment: darts-quickstart\n" + ] + } + ], + "id": "88320df5" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Sample Data\n", + "\n", + "We'll use the classic AirPassengers dataset for this example." + ], + "id": "03d5209e" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:47.269628Z", + "iopub.status.busy": "2026-06-24T15:18:47.269548Z", + "iopub.status.idle": "2026-06-24T15:18:47.356588Z", + "shell.execute_reply": "2026-06-24T15:18:47.356201Z" + } + }, + "source": [ + "series = AirPassengersDataset().load()\n", + "train, val = series.split_before(0.75)\n", + "\n", + "print(f\"Training series: {len(train)} points\")\n", + "print(f\"Validation series: {len(val)} points\")\n", + "\n", + "series.plot()\n", + "plt.axvline(train.end_time(), color=\"red\", linestyle=\"--\", label=\"Train/Val split\")\n", + "plt.legend()\n", + "plt.title(\"AirPassengers Dataset\")\n", + "plt.show()" + ], + "execution_count": 6, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Training series: 107 points\n", + "Validation series: 37 points\n" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "data": { + "image/png": "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jVedgAkhDlKhQtpCSARSVpTs2rUrZIn2/fv3i1deeUV4WZ9k8eLFcr1w4cKiYsWKYfdHt14IKSUy8HoroM/M3XffLQ4fPiwfN2jQQPa1cROzKKHrhhBCUpuUFSWffPKJbEIHECyKxm/58uWTj1G9VGWDuM0ff/xhpOdeffXVliwHyloCgYEeNpFAGu5tt91mNNq74YYbZN8Zt6uowlWDYmqK66+/3tXzE0Jcoly5tIWQCKSkKDl37pz46KOP5DomfUzS48ePFw8++KB8DmLFbauBQllJlCixQjQunI8//ljccccdhiBr06aNLPeuUovd5o033hDlypUTzzzzjChVqpQn10AIcZgTJ9IWQiKQkqIETeyQQgvQiVYFd/bu3Vtky5ZNrn/wwQfi+PHjtp53zpw5sqndvn37bBUl6BVjJdgVAqBHjx6Giwfuk3HjxokcOXIIr2jRooX466+/bGn2RwghJLFJSVECwaF44IEHjHXEZsCKoGJL0FPGLuAOggB67rnnZHDpoUOHwooSvQZJJLCfcvNkZClB074nnnjCeNy3b1/ZbA/nIYQQQvxAyomSjRs3yiBPULZs2XSN5h599FFj/a233rIcOBoJWCRUDAca3N1+++3i9OnTQftAqKhOuZdffrlllwoa86lUWhzbbOFBgbIBAwYYj19++WXxv//9j5kuhBBCfEXKiRLEkmCSBr169UpnKUDxMJWGu379eunqsQNzwzz0tunatWuQ6IGVQ12bVdeNOa4E8TIovmYWYrD8AIgwWGuYeksIIcRvpJQogaVi+PDhch2xI926dQu5H1wbutsjXpARo4QCrDO5cuUysnxQKyWeeJJQcSVmFw5ShRVsdkcIcZ1rr01bCIlASomSr7/+Wuzdu1eut23bVvaVySj4snz58kZ11T179thmJXnkkUdkpk/mzGlD//rrrxtiJB5REq6yqy5K6tatG8M7IISQOBg3Lm0hJBVEya+//iozSRAncSJM2pmykoD77rsvw/0gGJAqC+BemTp1aszXBneMEiU4LgJpb7nlFilGFLCW4DxLliyRjyGWUF02GhBTkjt3brmO4yg3EPj555+NdavBs4QQQojbJIUogcBArZGnnnpKVKpUSYwZMyZdgCr62syaNUuuwwrSuHHjsMds3bp1kIUlVmD92LRpk1EcrHjx4nL9oYceEpdeeqlcR+8Z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", + "text/plain": [ + "
" + ] + } + } + ], + "id": "1596e07e" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic Model Logging\n", + "\n", + "Let's train a simple model and log it to MLflow manually." + ], + "id": "34858645" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:47.357581Z", + "iopub.status.busy": "2026-06-24T15:18:47.357516Z", + "iopub.status.idle": "2026-06-24T15:18:47.450374Z", + "shell.execute_reply": "2026-06-24T15:18:47.449925Z" + } + }, + "source": [ + "model = ExponentialSmoothing()\n", + "model.fit(train)\n", + "\n", + "predictions = model.predict(n=len(val))\n", + "\n", + "# calculate metrics you want to log to MLflow\n", + "mape_score = metrics.mape(val, predictions)\n", + "rmse_score = metrics.rmse(val, predictions)\n", + "\n", + "print(f\"Validation MAPE: {mape_score:.2f}%\")\n", + "print(f\"Validation RMSE: {rmse_score:.2f}\")\n", + "\n", + "train[-50:].plot(label=\"Training\")\n", + "val.plot(label=\"Actual\")\n", + "predictions.plot(label=\"Forecast\")\n", + "plt.legend()\n", + "plt.title(\"Exponential Smoothing Forecast\")\n", + "plt.show()" + ], + "execution_count": 7, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Validation MAPE: 7.86%\n", + "Validation RMSE: 34.77\n" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiUAAAHECAYAAADmnVpRAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlcelbwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAnupJREFUeJztnQV0G9fWhY/M7MR2HI7DjG3KTSlNm9Jr/zI3ZX7tK3Pfa19TfmVmZmZOmdNww4mDjp04MbP1r33lOxrJki3Zghl5f2t5eSyNxprRaO6ec/Y51+F0Op1CCCGEEBJl4qL9BgghhBBCAEUJIYQQQiwBRQkhhBBCLAFFCSGEEEIsAUUJIYQQQiwBRQkhhBBCLAFFCSGEEEIsAUUJIYQQQiwBRQkhNqKsrEzefPNNWb9+fURfGyuUlJSoY7Bp06Z21+XxIiTyONjRlUSK6upq+fjjj/0+37t3b9ltt926/AdSWloqX3/9tey6667Sp08fj+MxZ84cmTRpkrzxxhty5JFHBnWsgn1tXV2dLFmyRNatWyd5eXkycuRIycrKsvXxmzVrluy9997yySefyPTp09vcTmeOdSS+L9F4T5GkvLxcPv/8c9lxxx1lwIAB0X47JEIkROofEVJcXCxHHXWUDB8+XMaNG9fqgEyePJmiRESWLl2qjtM777wjhx12mMcx6tatmxxxxBHSr1+/sJ5QTz75pNxwww0SFxcnY8eOla1bt8rcuXPl8MMPl9tvv1369+9vy+MXDJE61h39vsS6KFmzZo3a/xdeeEFOPPHEaL8dEiEoSkjEOeSQQ+Suu+7ike8AAwcOVOmHcPLRRx/JmWeeKRdeeKH873//k4QE12Xi999/V4MDxImVRYmdjnUg8PtCuhIUJcSSILze2NioLshmVq9eLX/88Ydsv/32MmjQIOUR+Pbbb2XKlCnSs2dP+e2339RjEyZM8HuHi7QEtrFlyxa1zsSJE1VEQOO9zdmzZ8uGDRvU3WpBQYHPbTY3N8u8efPU3V337t1VyDk5OTnobSJV8s0336jln3/+WR0DgPVGjBihfA5ffPGF7LLLLtK3b19jfz744ANjG/i/ODaIcHSEt956S/2+9tprDUECdthhB/nxxx9l8+bNfvcLwgXpE+w/joMGxxs+DkTDsF5HPpdA1mvv+HnT1mfr61h35NxAlOnXX3+VlJQUlVIC77//vowfP15FQUIZIcJPamqqOs7Z2dkezyMVkpmZqfYHnwXee69evVSKCmBu1gULFqjvGNJ0+AyxLV8sWrRIVq5cqY4B9kOf601NTSpCpcHjSL1gHYfD0Wo7CxcuNL4zOF8zMjLU40VFRerYA5xTOHZg1KhRMmbMmJAdM2JB4CkhJBKsWrUKM1I7L7300nbXff7559W69957r/FYWVmZc/jw4c4RI0Y4y8vL1WPffPONWu+VV15x7rPPPs4pU6Y4J02a5IyPj3deffXVrbb71ltvOXv06OEcMGCAc9q0ac7c3Fy1vb/++stYR2/zzTffdB588MHO3Xff3bnddts54+LinLfeemurbf7888/qfWG7++23n9pefn6+8+OPPw56mz/88INz7733VuvusssuziOOOEL9vPPOO+p5vE8898Ybbxiv2bp1q7EefnAcMjMznRMmTHCuWbPGWM/Xa31x/vnnq/WWLl3a7uek9+vVV19V+77XXns5R44c6ezevbvzp59+cpaWljqnTp3q3HPPPZ2jRo1ypqSkON9///0OfS6BrNfe8Qvms/V1vII9N5566ilnWlqac9iwYc59991XfSbff/+92sadd94Zku9LUVGR+szxf3D8x44dq47zzJkzPdbDcTr00EOd999/v3P06NHqvZ977rnqudmzZzvHjBmjjieOK57H8ttvv+2xjT///NM5btw4Z3Z2tvpfO+20k9q3L7/8Uj1fU1PjcS7is8e6+OyXL19ubGfjxo3O7bffXv2P/fffX31v+/Xr53zggQfU83/88Yd6H9j/HXbYwdgevucktqEoIRFDX2QPOeQQdaH3/lmyZInH+meddZYzMTFRDW4AFyVceBcsWNBqkJg4caK6kGkee+wx9fgzzzxjPIYLb0JCgvPEE090NjQ0GAP6jjvu6OzZs6daNm8TF0O8RnPxxRer97N69WrjsZUrVzqzsrLUxb6yslI91tzcrAYSvNcVK1YEvU2IHKyrB1IzgQoL7AsGwAMPPDDo1/74449qkMXr8R4gBv2h92vy5MnO+fPnq8caGxvVYISB7bTTTnPOmTNHPd7U1KSEy6BBg4zjH8znEuh6bR2/YD6HtkRJIK//7bff1HG88MIL1TmhB+ODDjooKFHi6/uyePFiY73ddttN7b/5sbvvvlu99umnn/YQJTj2ODf1+4HwXL9+vTMnJ0d9NubP+sYbb3QmJSUZ37e1a9cqsQnRt3nzZmM9PP7JJ5/43Q/cQEC87LHHHsZjZ555prN///7GzQWoqKhwvvjii8bfOJ+wDy+88EKbx4nEFhQlJGLoiyyiCua7Kf3jfQddW1ur7kL79u3rvPbaa31eoPQgcdFFF7X6f4iYjB8/3vj71FNPdSYnJztLSko81vv222/VNvRdmt7m5Zdf7rHesmXL1ONPPvmk8dgFF1ygojLFxcUe6+KOEdGKq666KuhtdlSUYMD74osvVDQBzx9zzDHO1NRUJQbae603n376qbp7xaDqcDicgwcPViJx7ty5Huvp/briiit8RrouueQSj8dffvll9bgWMMF8LoGuF4goCeRzaEuUBPL6GTNmqHMAg60ZDLzBiBJf3xe9b7/++qvPbUF0QBQi+mEWJYhaaPGswTmKbRQWFno8DuGHiB/OcYB9xrmghXZbbNq0SUVP9Ll4yimnqO8JvhcAYhnREwhYf1CUdE3oKSGWNe4hHw2j4XbbbSe33HKLnHPOOX5d+MihewMPxBNPPKF8BfBG/PXXXzJkyBBV3mpmp512Uvlu5NjNwLdiRpclrl271ngMvoX8/HzltQAtQl/95OTkKJ9JsNvsSOnojBkzVC4fXhr4HxITE5W/oKamRnkacnNzg9rm/vvvr34qKipU3v+7776Thx56SJ5++ml5+eWXVVWEGXxGZrQHw9/j8H5oz0ugn0uwn19bdPZzCOT1eL/wsWifhL/Xdub7gv+hj4EZHA889swzzygPjvZ8jB49WtLT0z3WxTkMTweOH37M5zAe1+cwfDH4/AYPHuz3veJ/wST9yiuvKJ8NPD9JSUnKfwK/Cbws8N7ge3zCCSeocwDn0p577im77767hw+LdE0oSoilwUVOmxXbGljT0tJ8PoYLa0NDgxIl9fX1rS7IABdNDOL4X2a8jYJYD9TW1hqPYdDHdl988UWfQsnbbBrINoNl5syZ8t5778lPP/2khJgGRtX58+erY9BRYIzceeed1c8ZZ5yhjJlXXHFFK1Hib78C2d9AP5dgP7+26OznEOh++err4utc7Sj4H8DXcdGPYR092Pfo0aPVejiHYdT2dQ7j/IUQ1Pvm6/+Yueeee+Sll15S/WBgBtbceuutSkDpc/G4446TYcOGqf8J0y9uOrBt/EbVF+m6UJQQy1JVVaX6RMDhf+qpp6rBF83VDjjggFbrrlixotVjy5cvV6/VFQQo8cTdHi7A5qqOwsJCdeFGxUqw4IKNapRQlo76qlJoix9++EENHmZBAlBJ0RFQdeI96AJEfvB/UH2io0+hINDPJdD1gj1+4QLvF6LQG0QNQvk/wLJly1pFpfAYokoQlhpfxwbnMKppXn/9dZ/VThqICJznECe6GsbXuTh06FAPQeLvXIRo1xFOVDWdffbZctFFF6nvN7Zhlc+RRBa2mSeW5ayzzlIXcFwIcQe21157yUknnaRKCL3B3Zn5ThmC5NNPP5Wjjz7aeAzLKCPFumawbVwAzesGCkLVaHKFZmPeIEKD0sZg0REhiINAQCdclKXqu2aAXiLoatoREAlB2ssbhN6xXZRlhkqQBPO5BLpesMcvXCCahDSVuVwbPPvssyH7H1OnTlX7i9Qa0iNmEfDll1/KMccc0+42EAGrrKyU+++/v9Vz2ObGjRvV8mmnnaa+Y2ie541u249zEd8HpBQ1SCOi940Z7+8wIjhodIdICgSKlT5HElkYKSERBxcpX5EF5N516++HH35YeReeeuop1YcCIE+NngoYfL7//nsVstcgooLX4jlcYDFQIdVw0003GeucfPLJqv/J6aefrgZX9DvAwI0QMi606KUQLMj343/A74Jt7bHHHmrAXrx4sfJ4oPnYoYceGtQ2ccePu9L77rtP/Y2wtr8+GwB3l+gtgveCsDgiB++++66cd955cscddwS9T/ABnHvuufL444+r7cEzs2rVKiW8MGg89thjEkoC/VwCXS/Y4xcuTjnlFPW5HHvssXLJJZeo94W28bpXSSgiAUgFPffcc6q767777qs+L0TucN7heCAd0h777LOP8qxcfvnlyhuFv5HuwRQDb7/9tjq/sV08ju1dd911SvTg/yFqAtGF6Mall14qF1xwgerAeuCBB6r9xzxLaNOPx83vBecmRDRa/sOPA/GGzwt/oz8KQA8V7AOuBfgMcX1gn5LYh6KERAxcWCAewKuvvtrqedxlQVhs27ZNXRz//e9/q7szDVIxCDHfe++98tlnn8nBBx9sPIfUBVqgY+DEnRYukAgHm02GGATwfz/88EMVRUFzJhj30HDNnPrAXRveJy6K3uBx75bf119/vfrfuPjifWOgwDwxCGVjn4LdZnx8vNo/RCvwPrUnBoOqr9bn8HsgX487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", + "text/plain": [ + "
" + ] + } + } + ], + "id": "bc8f520d" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's log this model to MLflow.\n", + "\n", + "Tags can live in two places in MLflow 3:\n", + "\n", + "- **Run tags** (`mlflow.set_tags(...)`) – shown on the run page in the UI. This is also where `autolog()` writes its tags (`model_class`, …).\n", + "- **LoggedModel tags** (`log_model(..., tags=...)`) – attached to the logged model entity itself (open the model from the run's **Outputs** / Models view)." + ], + "id": "35dc864c" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:47.451366Z", + "iopub.status.busy": "2026-06-24T15:18:47.451297Z", + "iopub.status.idle": "2026-06-24T15:18:47.906785Z", + "shell.execute_reply": "2026-06-24T15:18:47.906380Z" + } + }, + "source": [ + "with mlflow.start_run(run_name=\"exponential-smoothing-baseline\") as run:\n", + " model_info = log_model(\n", + " model=model,\n", + " name=\"exponential-smoothing-model\",\n", + " # alternatively, use mlflow.set_tag(key, value) in the run\n", + " tags={\"model_type\": \"ExponentialSmoothing\", \"dataset\": \"AirPassengers\"},\n", + " )\n", + "\n", + " # log calculated metrics you want\n", + " mlflow.log_metric(\"mape\", mape_score)\n", + " mlflow.log_metric(\"rmse\", rmse_score)\n", + "\n", + " print(f\"Run ID: {run.info.run_id}\")\n", + " print(f\"Model URI: {model_info.model_uri}\")" + ], + "execution_count": 8, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "2026/07/23 18:50:08 WARNING mlflow.utils.environment: Failed to resolve installed pip version. ``pip`` will be added to conda.yaml environment spec without a version specifier.\n" + ] + }, + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Run ID: 19ce5c2862014f5a8b27d68cb5f41c34\n", + "Model URI: models:/m-aa41380f3e4746b8b517c25cf69b23ab\n" + ] + } + ], + "id": "61406cd9" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load the Model Back\n", + "\n", + "We can load the model from MLflow using its URI:" + ], + "id": "0728690e" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:47.907880Z", + "iopub.status.busy": "2026-06-24T15:18:47.907802Z", + "iopub.status.idle": "2026-06-24T15:18:47.914695Z", + "shell.execute_reply": "2026-06-24T15:18:47.914326Z" + } + }, + "source": [ + "loaded_model = load_model(model_info.model_uri)\n", + "\n", + "loaded_predictions = loaded_model.predict(n=len(val))\n", + "\n", + "# verify predictions match\n", + "predictions_match = np.allclose(predictions.values(), loaded_predictions.values())\n", + "print(f\"Loaded model predictions match: {predictions_match}\")" + ], + "execution_count": 9, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Loaded model predictions match: True\n" + ] + } + ], + "id": "cae35ffa" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Automatic Logging with `autolog()`\n", + "\n", + "`autolog()` patches darts models and metrics so that the following are logged automatically, with no extra code needed:\n", + "\n", + "- **`fit()`** – model creation parameters and covariate metadata (and the trained model artifact when ``log_models=True``; default ``False``).\n", + "- **Darts metric functions** – any call made inside an active MLflow run.\n", + "- **`backtest()`** – evaluation metrics under `backtest_*` keys.\n", + "- **`historical_forecasts()`** – patched so its internal per-window `fit()` calls don't each spawn their own logging.\n", + "- **PyTorch-based models** – per-epoch `train_loss` / `val_loss` via MLflow's PyTorch autologging (see the section below for details)." + ], + "id": "6bd4597c" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:47.915629Z", + "iopub.status.busy": "2026-06-24T15:18:47.915566Z", + "iopub.status.idle": "2026-06-24T15:18:50.020337Z", + "shell.execute_reply": "2026-06-24T15:18:50.019951Z" + } + }, + "source": [ + "autolog()\n", + "\n", + "with mlflow.start_run(run_name=\"linear-regression-autolog\") as run:\n", + " auto_model = LinearRegressionModel(lags=12)\n", + " auto_model.fit(train) # autolog logs params and covariate metadata\n", + "\n", + " auto_predictions = auto_model.predict(n=len(val))\n", + " # these metric calls happen inside the run, so they are logged automatically\n", + " auto_mape = metrics.mape(val, auto_predictions)\n", + " auto_rmse = metrics.rmse(val, auto_predictions)\n", + "\n", + "autolog(disable=True)\n", + "\n", + "# show logged metrics\n", + "logged = mlflow.tracking.MlflowClient().get_run(run.info.run_id).data.metrics\n", + "print(\"Logged metrics:\", {k: round(v, 4) for k, v in sorted(logged.items())})\n", + "\n", + "# plot\n", + "fig, ax = plt.subplots(figsize=(10, 4))\n", + "train[-36:].plot(label=\"Train\", ax=ax)\n", + "val.plot(label=\"Actual\", ax=ax)\n", + "auto_predictions.plot(\n", + " label=f\"Forecast (MAPE {auto_mape:.1f}%, RMSE {auto_rmse:.1f})\", ax=ax\n", + ")\n", + "ax.set_title(\"Linear Regression — autolog run\")\n", + "ax.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ], + "execution_count": 10, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Logged metrics: {'mape': 10.742, 'rmse': 51.182}\n" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "data": { + "image/png": "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tGjRwh7/8ssv5plnnkm38vnevXvtcceOHc2FF17Iyw8AAAAg6gi6M+Dxxx83u3bt8tKRGzduHP13BJk2cuRIu8ZeNIv9448/pjl4MmbMGHus+w8cOJBXHQAAAEAgCLoj9MEHH5iUlBR7XLx4cTNq1Khg3hFkmmaru3XrZo8PHTqU5hZimgV36edaD37eeefxqgMAAAAIBEF3BBTAPfDAA95lVbwuW7ZsMO8IskSz1meffbY9nj59ulm6dGnY7Uo9HzdunD0+44wzzBNPPMErDgAAACAwBN0RUJC9fv16e1ynTh1z7733BveOIEuUhTB06NA0txDT/uoaRJGHHnrIlC9fnlccAAAAQGAIutPx3XffmeHDh3tVsidMmMCe3LncPffcYy699FJ7/Pnnn5s33njDHm/evNlMmjTJHhcpUsT06dMnR39PAAAAAPEvroPuTz75xNx+++12/fWWLVsy/PhQKGT35D5y5Ii9/Nhjj3nBHGJnC7F+/fqZP//8086Au/3VNQNeqlSpHPwtAQAAACSCuAy6d+7caWc7r732WvPmm2+aF154we6p3aZNG7N48WIbTEdCM6SLFi2yxyq2NWjQoIB/c0RLw4YNTatWrezxtm3bbMG0119/3UtB1wAKAAAAAAQtroJurd2dPHmyueiii8wrr7wSdtvx48fNzJkzTb169czll19uJk6c6FWwTitw9wdm2pO7UKFCgf7+CG4LsWnTptk24LZ+U+ANAAAAAEGLm6B7zZo15oYbbjCdO3e2AbMULVrUBl7du3cPqza+evVqc99995kKFSrYwHrTpk0nPV/v3r3N77//bo/btm1rmjVrlo1/DaLhggsusO+9nyqbP/LII7zAAAAAALJFzAfdWqurAPnKK680H3/8sXd9u3btzLp160zPnj1t4PXDDz+YqVOn2urjzp49e+x6bwVnLVq0MAsXLrSz5R999JF59dVX7X3OPPNMM2bMmBz525B1AwYMCFu7rfXdGowBAAAAgOwQs0G31mXPnj3bVKtWzYwYMcIcO3bMXq8AWsGz9mj2bwd12mmnmfbt29viaqpo3alTJ3P66ad7zzVnzhzTpEkT+3wdO3b0HqfK5WwrFbuKFStmK87r/a9du3bYfusAAAAAELSYDLq19VPLli1NUlKSV5VcAXRycrJNHW/UqNEpH1+jRg279luPVVBdsWLFsC3C9PxSq1Ytm4aO2KZ2sm/fPjvgUrBgwZz+dQAAAAAkkJgLup9++mk7G62Zaadx48Y22B48eLA544wzIn4upR337dvXrulWkbX69et7t+XLl8/OkOpfxD7NdOfNG3PNHQAAAECMy29ijNbkOuXKlbPrrVXoLE+ePFna17l169b2RwXZ3n33Xbvd2BVXXBGl3xoAAAAAkIhiLugWzVh269bNDBkyxBY6i6bq1avbHwAAAAAAEi7oVjGsl156yVYrBwAAAAAgN4u5oFvFsFibCwAAAACIBTFXWYqAGwAAAAAQK2Iu6AYAAAAAIFYQdAMAAAAAEBCCbgAAAAAAAkLQDQAAAABAQAi6AQAAAAAICEE3AAAAAAABIegGAAAAACAgBN0AAAAAAASEoBsAAAAAgIAQdAMAAAAAEBCCbgAAAAAAApInFAqFgnpyAAAAAAASGTPdAAAAAAAEhKAbAAAAAICAEHQDAAAAABAQgm4AAAAAAAJC0A0AAAAAQEAIugEAAAAACAhBNwAAAAAAASHoBgAAAAAgIPlNjOjfv785evSoPe7YsaO55JJLwm7fvXu3eeutt8zPP/9sLrroInPrrbea0047zbu9X79+5vjx497lK664wtx+++0R347E8uSTT5r9+/fb4/bt25srr7wy7PY//vjDvPnmm+ann34yVapUMbfddps544wzvNsHDx5sDh486F2uVq2a6dSpU9hzfPPNN2bu3LkmFAqZtm3bmvPPPz/wvwu509NPP2127dplj1u3bm2uvvrqsNv//PNP27/98MMP5rzzzrNtslChQt7tw4YNM3v37vUuq03ed9999lh94tixY0/6P5OSkkydOnUC/KuQW40aNcr8+uuv9rh58+bm+uuvD7tdfZfa28aNG02FChXsd2GRIkW820eOHGl+++0377Lu88gjj3iXDxw4YGbOnGm+//57U7x4cXPzzTebypUrZ8vfhtxn3Lhx5scff7THjRo1sj9+hw8fNjNmzDAbNmwwZcuWtf2b2o2j/kv9mFO6dGnz+OOPR/x4JJZJkybZtiDq29TH+SmWUP/07bff2rak87eSJUuG3Wfr1q1m1qxZNrZo1qyZqVmzZtjtCxYsMJ988oltZ+3atTPnnHNONvxliHUxM9P9j3/8wzRu3NisWrXK7Nix46QT0g4dOthOWR+Mjz76yAwcODDsPh98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6V8HAIBci6AbAIBcZtu2bebrr7/2Ljdo0MAUKFDAu7xlyxazdu1a73KTJk0C+T327NljPv30U3tcp04dc+aZZ3q3LV682LzwwgsmX758BN0AAJwCQTcAALnMypUrzaBBg7zLycnJpnnz5t7lZ5991ixbtizwoHvz5s2mf//+9jglJcVUq1YtkP8HAIB4RtANAEAuN336dC/o3rRpk1m+fPkp779z506zbt06c+TIEVOhQgVTtWrVU6aG//jjj/anbNmy5sILL/QC7hUrVniP0f+pGfaSJUuaq6666qT/M7XnAAAABN0AAORqpUqVsgG0Zr+vvPJKM23aNBMKhez1O3bsCLvv8ePHzciRI82sWbNMmTJl7I8ed/HFF5vhw4fbANyfGq6ge968eTY4X7Nmjdm/f79p1aqVGThwoA2wv/rqK++5v/zyS7NhwwZzwQUXhAXd+l169uyZ6nMAAACqlwMAkKu1adPG/jt16lS7xlpBcpEiRcyNN9540n1fffVVM2PGDFO8eHF7/0mTJtkZ8m+//dY89thj5q+//gq7vy5fd911Zty4caZ79+72urffftv8+uuvpm7duqZr167efR988EEbuHfp0iXi5wAAAATdAADkag0bNrQz1h999JEZO3asOXz4sLn55ptNwYIFT7rv7NmzvaJnCszFVRbfuHGjWb169UmPady4sf23UqVK3nUZDZij8RwAAMQr9ukGACAXU3XwW2+91aaOv/POO/Zyu3btTrqfbt++fbs9LlGihHe91mD7q6Kf+NxFixa1x/nzZ67MSzSeAwCAeEbQDQBALpeUlOTNbNevX9+UK1cu1eC3cOHC9vjgwYPe9QcOHPCOXXAMAACyD0E3AAC5nPbHfuihh2yqeKdOndK837XXXmv/9e/x7Y6Vbn755Zdn6P897bTTwmbSAQBAxpEHBgBADLjtttvsz6n06NHDFk377rvvzJAhQ0yVKlXM5MmTbfA8YMAAb513pLRGW7Pnqkj+5ptvmq1bt9rq5foBAACRIegGACCXKV++vFcArVChQqneRwG1u49TunRpu6XYggULzDfffGOD7w4dOpimTZuaihUreverXLmyfaxS0h1VPHfPp2NRkD5hwgRbMX3Xrl12qzE9RkF3pM8BAECiyxPSBpsAAAAAACDqWNMNAAAAAEBACLoBAAAAAAgIQTcAAAAAAAEh6AYAAAAAICAE3QAAAAAABISgGwAAAACAgBB0AwAAAAAQEIJuAAAAAAACQtANAAAAAEBACLoBAAAAAAgIQTcAAAAAAAEh6AYAAAAAwATj/wH4ti+gzVt0TwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + } + } + ], + "id": "5ef7f73a" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Open the MLflow UI\n", + "\n", + "To experiment with the runs yourself, open the **MLflow UI** to explore them interactively. Run the command printed below in a separate terminal, then navigate to `http://localhost:5000`.\n", + "\n", + "The UI lets you:\n", + "- **Compare runs** side-by-side in the Experiments table\n", + "- **Inspect** individual run parameters, metrics, and logged artifacts\n", + "- **Visualize** metrics across runs with built-in charts\n", + "- **Register** models to the Model Registry for versioning" + ], + "id": "f484330f" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:50.021527Z", + "iopub.status.busy": "2026-06-24T15:18:50.021439Z", + "iopub.status.idle": "2026-06-24T15:18:50.023116Z", + "shell.execute_reply": "2026-06-24T15:18:50.022824Z" + } + }, + "source": [ + "print(\"Launch the MLflow UI with this command in your terminal:\\n\")\n", + "print(f\" mlflow ui --backend-store-uri {mlflow.get_tracking_uri()}\\n\")\n", + "print(\"Then open: http://localhost:5000\")" + ], + "execution_count": 11, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Launch the MLflow UI with this command in your terminal:\n", + "\n", + " mlflow ui --backend-store-uri sqlite:////var/folders/yr/3703qwtj56lcw6n91xm6tq_r0000gn/T/tmpw5bcp0ic/mlflow.db\n", + "\n", + "Then open: http://localhost:5000\n" + ] + } + ], + "id": "1a01cd2f" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> 📸 **Try it:** Open the UI, switch to the `darts-quickstart` experiment, and sort the **Table view** by `mape` to instantly see which model performs best. Click any run name to see its parameters, tags, and logged artifacts.\n", + ">\n", + "![Mlflow Overview](./static/images/mlflow_overview.png)" + ], + "id": "88b0d285" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Per-epoch Metrics with Torch Models\n", + "\n", + "For neural models, `autolog()` enables MLflow's PyTorch autologging, which records `train_loss` and `val_loss` at the end of every epoch.\n", + "\n", + "Pass `torch_metrics` to the model to add extra per-epoch metrics (e.g. MAE, MSE). They will appear prefixed as `train_MAE`, `val_MAE`, etc." + ], + "id": "e2b133bc" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:50.024161Z", + "iopub.status.busy": "2026-06-24T15:18:50.024087Z", + "iopub.status.idle": "2026-06-24T15:18:53.370946Z", + "shell.execute_reply": "2026-06-24T15:18:53.370531Z" + } + }, + "source": [ + "from torchmetrics import MeanAbsoluteError, MeanSquaredError, MetricCollection\n", + "\n", + "# per-epoch train_loss / val_loss are logged via MLflow's PyTorch autologging\n", + "autolog()\n", + "\n", + "with mlflow.start_run(run_name=\"nbeats-epoch-metrics\"):\n", + " nbeats = NBEATSModel(\n", + " input_chunk_length=24,\n", + " output_chunk_length=12,\n", + " n_epochs=10,\n", + " pl_trainer_kwargs={\"accelerator\": \"cpu\"},\n", + " # these are logged per epoch as train_MAE, val_MAE, train_MSE, val_MSE\n", + " torch_metrics=MetricCollection({\n", + " \"MAE\": MeanAbsoluteError(),\n", + " \"MSE\": MeanSquaredError(),\n", + " }),\n", + " random_state=42,\n", + " )\n", + " nbeats.fit(train, val_series=val)\n", + " nbeats_pred = nbeats.predict(n=len(val))\n", + " # metric calls inside the run are logged automatically (keys: mape, rmse)\n", + " nbeats_mape = metrics.mape(val, nbeats_pred)\n", + " nbeats_rmse = metrics.rmse(val, nbeats_pred)\n", + " print(f\"NBEATS MAPE: {nbeats_mape:.2f}%\")\n", + " print(f\"NBEATS RMSE: {nbeats_rmse:.2f}\")\n", + "\n", + "autolog(disable=True)" + ], + "execution_count": 53, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "INFO: GPU available: True (mps), used: False\n", + "INFO:lightning.pytorch.utilities.rank_zero:GPU available: True (mps), used: False\n", + "INFO: TPU available: False, using: 0 TPU cores\n", + "INFO:lightning.pytorch.utilities.rank_zero:TPU available: False, using: 0 TPU cores\n", + "INFO: HPU available: False, using: 0 HPUs\n", + "INFO:lightning.pytorch.utilities.rank_zero:HPU available: False, using: 0 HPUs\n", + "/Users/jakubchlapek/Desktop/projects/darts/.venv/lib/python3.11/site-packages/pytorch_lightning/trainer/setup.py:177: GPU available but not used. You can set it by doing `Trainer(accelerator='gpu')`.\n", + "\n", + " | Name | Type | Params | Mode \n", + "-------------------------------------------------------------\n", + "0 | criterion | MSELoss | 0 | train\n", + "1 | train_criterion | MSELoss | 0 | train\n", + "2 | val_criterion | MSELoss | 0 | train\n", + "3 | train_metrics | MetricCollection | 0 | train\n", + "4 | val_metrics | MetricCollection | 0 | train\n", + "5 | stacks | ModuleList | 6.2 M | train\n", + "-------------------------------------------------------------\n", + "6.2 M Trainable params\n", + "1.4 K Non-trainable params\n", + "6.2 M Total params\n", + "24.787 Total estimated model params size (MB)\n", + "400 Modules in train mode\n", + "0 Modules in eval mode\n" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "33ff56f2bbbb46ddb71bf5ac409bc3c7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Sanity Checking: | | 0/? [00:00 📸 **Try it:** Click on the `nbeats-epoch-metrics` run in the UI, then open the **Model metrics** tab. You'll see `train_loss`, `val_loss`, `train_MAE`, and `val_MAE` plotted as learning curves across epochs.\n", + ">\n", + "![Mlflow Charts](./static/images/mlflow_charts.png)" + ], + "id": "6fcc8f13" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Forecast Metrics\n", + "\n", + "With `log_metrics=True` (the default), `autolog()` patches every darts metric function. Any metric you call **inside an active run** is logged automatically with no extra arguments needed.\n", + "\n", + "The metric key is derived from the result: a scalar is logged under `{metric}` (e.g. calling `darts.metrics.mae(val, pred)` logs `mae`). You can always log a custom-named metric explicitly with `mlflow.log_metric()`.\n", + "\n", + "Logged metric values are only meaningful to compare across runs when the evaluation settings match. Use the same evaluation time frame, forecast horizon, and evaluation start date for every `backtest()` / metric call you intend to compare against one another." + ], + "id": "3f0828c2" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:53.372445Z", + "iopub.status.busy": "2026-06-24T15:18:53.372367Z", + "iopub.status.idle": "2026-06-24T15:18:53.470125Z", + "shell.execute_reply": "2026-06-24T15:18:53.469722Z" + } + }, + "source": [ + "# log_metrics=True (the default) patches every darts metric so that calls made\n", + "# inside an active run are logged automatically\n", + "autolog(log_metrics=True)\n", + "\n", + "with mlflow.start_run(run_name=\"linear-regression-full-metrics\") as run:\n", + " lr_model = LinearRegressionModel(lags=12)\n", + " lr_model.fit(train)\n", + " lr_pred = lr_model.predict(n=len(val))\n", + "\n", + " # each metric called here is auto-logged under its own name: mae, rmse, smape\n", + " metrics.mae(val, lr_pred)\n", + " metrics.rmse(val, lr_pred)\n", + " metrics.smape(val, lr_pred)\n", + " # you can still log a custom-named metric explicitly\n", + " mlflow.log_metric(\"manual_mape\", metrics.mape(val, lr_pred))\n", + " run_id = run.info.run_id\n", + "\n", + "autolog(disable=True)\n", + "\n", + "# show what was logged\n", + "client = mlflow.tracking.MlflowClient()\n", + "run_metrics = client.get_run(run_id).data.metrics\n", + "metric_names = sorted(run_metrics.keys())\n", + "print(f\"All logged metrics ({len(metric_names)}):\")\n", + "for name in metric_names:\n", + " print(f\" {name}: {run_metrics[name]:.4f}\")" + ], + "execution_count": 13, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "All logged metrics (5):\n", + " mae: 46.0220\n", + " manual_mape: 10.7420\n", + " mape: 10.7420\n", + " rmse: 51.1820\n", + " smape: 10.1015\n" + ] + } + ], + "id": "df8e85b6" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Metric Shape and Detailed Logging\n", + "\n", + "The logged key reflects the shape of the metric output, which `autolog()` infers from the metric and its keyword arguments. The general pattern is:\n", + "\n", + "`{metric_name}{component}{quantile_or_label}`\n", + "\n", + "- **Per-component** (`component_reduction=None`) adds the component name, e.g. `mae_`.\n", + "- **Quantile / interval** (`q`, `q_interval`) adds the quantile, e.g. `mql_q0.500`.\n", + "- **Per-label classification** (`label_reduction=None`) adds the class, e.g. `f1_label1`.\n", + "- **Per-timestep forecast metrics** (`time_reduction=None`) use zero-based steps, so the first predicted value is `0`. Backtest calendar-relative steps are negative and end at `-1`, aligning different series lengths on their shared end.\n", + "- **`series_reduction`**, when set on the metric call, aggregates across series inside the metric itself, so the mean-over-series logging described below does not apply.\n", + "\n", + "For `backtest()`, metric keys have a `backtest_` prefix. A time-dependent metric with `reduction=None` keeps its per-window values in the return value, but MLflow charts one value per horizon step: it applies `np.nanmean` over windows for each series, then aggregates across series. The detailed `metrics_per_series.json` table retains every source value with a `window_index`, including for a single-series backtest.\n", + "\n", + "When you score a list of series, the logged value is the mean over series and the full per-series breakdown is appended to the same run-wide table artifact, `metrics_per_series.json`, which you can read back with `mlflow.load_table()`.\n", + "\n", + "Passing `name=` to a metric overrides the `{metric_name}` token in the key while keeping the suffixes (e.g. `mql(..., q=0.5, name=\"foo\")` logs `foo_q0.500`)." + ], + "id": "b08e900a" + }, + { + "cell_type": "code", + "metadata": {}, + "source": [ + "# build a small multi-series example from the univariate AirPassengers data\n", + "series_list = [train, train * 1.2]\n", + "val_list = [val, val * 1.2]\n", + "\n", + "multi_model = LinearRegressionModel(lags=12)\n", + "multi_model.fit(series_list)\n", + "multi_preds = multi_model.predict(n=len(val), series=series_list)\n", + "\n", + "autolog(log_metrics=True)\n", + "\n", + "with mlflow.start_run(run_name=\"metric-shape-and-table\") as run:\n", + " # metric shape: a per-timestep metric (ae) logs one value per horizon step\n", + " # under a single key, charted across MLflow steps\n", + " single_pred = multi_preds[0] # train == series_list[0]\n", + " metrics.ae(val, single_pred)\n", + "\n", + " # multiple series: the logged value is the MEAN over series, and the full\n", + " # per-series breakdown is appended to the run's table artifact\n", + " per_series_mae = metrics.mae(val_list, multi_preds)\n", + "\n", + "autolog(disable=True)\n", + "\n", + "client = mlflow.tracking.MlflowClient()\n", + "run_id = run.info.run_id\n", + "\n", + "# aggregate metrics: mae is the mean over the two series\n", + "logged = client.get_run(run_id).data.metrics\n", + "print(\"Aggregate metrics:\", {k: round(v, 3) for k, v in sorted(logged.items())})\n", + "print(\"Mean MAE over series:\", round(float(np.mean(per_series_mae)), 3))\n", + "\n", + "# load the per-series table artifact\n", + "per_series_df = mlflow.load_table(\n", + " artifact_file=\"metrics_per_series.json\", run_ids=[run_id]\n", + ")\n", + "print(\"\\nPer-series breakdown (metrics_per_series.json):\")\n", + "per_series_df" + ], + "execution_count": 14, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Aggregate metrics: {'ae': 44.196, 'mae': 51.316}\n", + "Mean MAE over series: 51.316\n" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0305dca7a1b846f19da965d1b74319c5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Downloading artifacts: 0%| | 0/1 [00:00\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
keyseries_indexstepvalue
0mae0047.458874
1mae1055.172880
\n", + "" + ], + "text/plain": [ + " key series_index step value\n", + "0 mae 0 0 47.458874\n", + "1 mae 1 0 55.172880" + ] + } + } + ], + "id": "ecdbbf29" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Saving and Loading Models Locally\n", + "\n", + "You can also save and load models to/from local paths without MLflow runs." + ], + "id": "8511fc08" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:53.471252Z", + "iopub.status.busy": "2026-06-24T15:18:53.471178Z", + "iopub.status.idle": "2026-06-24T15:18:53.478682Z", + "shell.execute_reply": "2026-06-24T15:18:53.478330Z" + } + }, + "source": [ + "# Save model to local directory\n", + "local_model_path = os.path.join(tmpdir, \"my_model\")\n", + "save_model(model, path=local_model_path)\n", + "\n", + "print(\"\\nFiles in model directory:\")\n", + "for file in os.listdir(local_model_path):\n", + " print(f\" - {file}\")" + ], + "execution_count": 15, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "2026/07/23 18:50:10 WARNING mlflow.utils.environment: Failed to resolve installed pip version. ``pip`` will be added to conda.yaml environment spec without a version specifier.\n" + ] + }, + { + "name": "stdout", +"output_type": "stream", + "text": [ + "\n", + "Files in model directory:\n", + " - python_env.yaml\n", + " - requirements.txt\n", + " - MLmodel\n", + " - model.pkl\n", + " - conda.yaml\n" + ] + } + ], + "id": "645ef079" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:53.479679Z", + "iopub.status.busy": "2026-06-24T15:18:53.479604Z", + "iopub.status.idle": "2026-06-24T15:18:53.484973Z", + "shell.execute_reply": "2026-06-24T15:18:53.484638Z" + } + }, + "source": [ + "# Load model from local directory\n", + "local_loaded_model = load_model(f\"file://{local_model_path}\")\n", + "\n", + "# Test it\n", + "local_predictions = local_loaded_model.predict(n=5)\n", + "print(\"Loaded model successfully!\")\n", + "print(f\"Predictions shape: {local_predictions.values().shape}\")" + ], + "execution_count": 16, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Loaded model successfully!\n", + "Predictions shape: (5, 1)\n" + ] + } + ], + "id": "254ba153" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Querying Experiments\n", + "\n", + "You can programmatically query and compare runs." + ], + "id": "aab0d1e0" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:53.485914Z", + "iopub.status.busy": "2026-06-24T15:18:53.485859Z", + "iopub.status.idle": "2026-06-24T15:18:53.496380Z", + "shell.execute_reply": "2026-06-24T15:18:53.496048Z" + } + }, + "source": [ + "from mlflow.tracking import MlflowClient\n", + "\n", + "experiment = mlflow.get_experiment_by_name(\"darts-quickstart\")\n", + "\n", + "# get all runs\n", + "client = MlflowClient()\n", + "runs = client.search_runs(\n", + " experiment_ids=[experiment.experiment_id],\n", + " order_by=[\"metrics.mape ASC\"], # Sort by best MAPE\n", + ")\n", + "\n", + "print(f\"Found {len(runs)} runs in experiment '{experiment.name}':\\n\")\n", + "for i, run in enumerate(runs, 1):\n", + " run_name = run.data.tags.get(\"mlflow.runName\", \"Unnamed\")\n", + " mape_val = run.data.metrics.get(\"mape\", \"N/A\")\n", + " print(f\"{i}. {run_name}\")\n", + " print(f\" Run ID: {run.info.run_id}\")\n", + " print(f\" Validation MAPE: {mape_val}\")\n", + " print()" + ], + "execution_count": 17, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Found 5 runs in experiment 'darts-quickstart':\n", + "\n", + "1. exponential-smoothing-baseline\n", + " Run ID: 19ce5c2862014f5a8b27d68cb5f41c34\n", + " Validation MAPE: 7.864181481214469\n", + "\n", + "2. linear-regression-full-metrics\n", + " Run ID: 708095048f274913990375bbbd4310ad\n", + " Validation MAPE: 10.742044444678953\n", + "\n", + "3. linear-regression-autolog\n", + " Run ID: 36376b7a94454a13a4acba207459315e\n", + " Validation MAPE: 10.742044444678953\n", + "\n", + "4. nbeats-epoch-metrics\n", + " Run ID: f0d1b0ed9ac34232b5e36fe82a24a0a6\n", + " Validation MAPE: 13.639569217869525\n", + "\n", + "5. metric-shape-and-table\n", + " Run ID: 9be3e9d311b64d45b0204174835537cc\n", + " Validation MAPE: N/A\n", + "\n" + ] + } + ], + "id": "109a9812" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load the Best Model" + ], + "id": "22685423" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:53.497560Z", + "iopub.status.busy": "2026-06-24T15:18:53.497496Z", + "iopub.status.idle": "2026-06-24T15:18:53.572540Z", + "shell.execute_reply": "2026-06-24T15:18:53.572133Z" + } + }, + "source": [ + "from darts.models.forecasting.forecasting_model import GlobalForecastingModel\n", + "\n", + "if runs:\n", + " best_run = runs[0]\n", + " # the model is logged via MLflow's logged-model API, so resolve its model_id\n", + " # from the run outputs and load it with a models:/ URI\n", + " model_outputs = mlflow.get_run(best_run.info.run_id).outputs.model_outputs\n", + " best_model_uri = f\"models:/{model_outputs[0].model_id}\"\n", + "\n", + " print(f\"Loading best model from run: {best_run.data.tags.get('mlflow.runName')}\")\n", + " print(f\"Model URI: {best_model_uri}\")\n", + "\n", + " best_model = load_model(best_model_uri)\n", + " # global models (e.g. LinearRegression, NBEATS) need the series at predict time\n", + " # because we save with clean=True; local models (e.g. ExponentialSmoothing) don't\n", + " if isinstance(best_model, GlobalForecastingModel):\n", + " best_predictions = best_model.predict(n=len(val), series=train)\n", + " else:\n", + " best_predictions = best_model.predict(n=len(val))\n", + "\n", + " train[-50:].plot(label=\"Training\")\n", + " val.plot(label=\"Actual\")\n", + " best_predictions.plot(label=\"Best Model Forecast\")\n", + " plt.legend()\n", + " plt.title(\"Best Model Predictions\")\n", + " plt.show()" + ], + "execution_count": 18, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Loading best model from run: exponential-smoothing-baseline\n", + "Model URI: models:/m-aa41380f3e4746b8b517c25cf69b23ab\n" + ] + }, + { + "metadata": {}, + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiUAAAHECAYAAADmnVpRAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjExLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlcelbwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAoK1JREFUeJztnQd4U2UXx0+6001bSmmBsvcWZIkoiKLi+FRw4MC99544PgXX50IUEffeuFFQFAeisvcus3Tv3eZ7/u/te3OTJm2SZtybnt/z5OlNbpLee3OT93/P+Z/zmiwWi4UYhmEYhmECTEigN4BhGIZhGAawKGEYhmEYRhewKGEYhmEYRhewKGEYhmEYRhewKGEYhmEYRhewKGEYhmEYRhewKGEYhmEYRhewKGEYhmEYRhewKGEYxiOWL19OP/30k0ev/eGHH2jFihW6P/J79uyhTz75hMrLy9XHNm3aJB7zZt9JX7wnwxgRE3d0ZRhbfv75Z8rPz1fvh4WFUXp6OvXv359iY2N9eri+++47SkpKolGjRrm1rcnJyXTsscc2WY9B7vPPP6eGhgbq06cPDRo0yGvbetxxx1FRURH9888/br+2b9++NHDgQDEQu/NZREVFUY8ePahfv37kD1599VW6/PLLafv27dSzZ0/x2IMPPkgPPfQQ1dbWinPDVTZs2EBbtmyhs846q8k6T9+TYYINPvsZxo67776b1q1bRyeddJK4j4ECV7K5ubl077330u233+6zY3b11VfT6NGj6YMPPnB5W//66y8xWGdnZ1NCQoLN+l9++YXOPPNMsXzrrbfSU089RUb+LPLy8ujPP/8UAvH9998X4sbf4H/jmIaEuBdofuedd+jxxx93GA3x9D0ZJthgUcIwDkC0QnsVj4HkuuuuozvuuINGjhxJxxxzjG6OW1paGhUWFgohc+WVV9qse/3116lr164iDREsnwUiDogknXHGGUIs+pvp06eLm97fk2GMCMtyhnEBk8lE5557rlh2lq44cOCASL/ghqiFIw4ePCh8GEuXLqVDhw7ZrMPAW1FRQfv37xfLuC1ZsqTFbUN05NRTT6U33njD5vHS0lL69NNP6aKLLmr29RjYv/rqK5EqwWscUV9fLyIU3377rdN9c/dYeArSPqeddhpt3rxZpFUgyHCs8D8BIitffPGFTdoHx/XXX3+lL7/8ktavX+/0vXfs2EFff/01rV271iP/R2VlJf3+++/0zTffiPeSIJq1detWsSw/W9zk8W7uPRGhgwcHxz4rK6vJemwrUnSgqqpKnFs//vij088SxwJ+HmwjjiFSewyjG+ApYRjGyqhRoywZGRlNDsnChQsxYlg+/fRTm8eLioos06ZNs0RGRlrGjh1rOeqooyxRUVGWW2+91dLQ0CCeg7+XX365ePzoo4+2nHDCCZbOnTtbZsyYYamurhbPOfPMMy3R0dGWTp06iWXcbrvttha3tU+fPpZvvvlGbNuWLVvUda+++qolIiLCsnXrVrEO26MlKyvLMmbMGEtsbKxl4sSJlr59+4rl559/3uZ527dvF+sSExMtxx13nFieP3++ZdKkSZYjjjjC7WMBsM3YP08/iyuuuELs0+rVqy1///23WH7jjTcsp5xyimX06NGWHj16WJYvXy6e++yzz1ri4uLEduO4p6SkiP3ev3+/+n61tbWWmTNnWkJDQ8X/xO2MM86wzJ07V7w3joFk1qxZ4jG8RsszzzxjSUhIsHTr1k38n169eom/+fn5Yh32Ga+Tny1uBw4ccPqedXV1lptvvtkSHh5uGT58uDiW+DzPOussS0lJifq8G2+8URxvHIshQ4ZYJk+eLM6hpKQky59//mmzjZ988ol4vH///papU6daBg4caBk2bJhlzZo1LX4WDOMPWJQwjB0YkPDD/fHHH4vbe++9Z7n//vst7dq1s5x77rlisNCCgTo1NdWyceNG9bHff/9dDCYYjMCiRYvEoCMHSoBBGu+tHWAyMzMtZ599tsufiRQl2KaOHTta7rrrLnXduHHjxACWm5vbRJTU19dbBg8eLP7fnj171Mexn3juZ599Ju7jfTGAYUDPzs5WX3vVVVeJ19qLEleORWtFCUQcBvyYmBhLRUWFKkoGDBhg+e2338RzKisrLfv27RPCDOsgLiQFBQViu/HeEoiCkJAQy3fffac+huXevXu7JEpefvll8diTTz5ps62LFy+27N27Vyzfeeed4jmOcPSejzzyiHjsgw8+UB/766+/xH5Pnz7dRpTg+F544YWW4uJi8VhZWZkQHCNHjlSfh88S4uzaa6+1+d8bNmxQjxvDBBoWJQxjBwYrs9msXs3+5z//EVequPp84YUXbJ6LH3MMHC+++GKT43jBBReIgRvMmzdPPA9Ri+bwVJSA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", + "text/plain": [ + "
" + ] + } + } + ], + "id": "7b09db6a" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model Registry\n", + "\n", + "After comparing runs in the UI you can promote your best model to the **MLflow Model Registry** for versioning and lifecycle management (Staging → Production → Archived)." + ], + "id": "6f1ec89e" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-22T08:27:04.144511Z", + "iopub.status.busy": "2026-07-22T08:27:04.144439Z", + "iopub.status.idle": "2026-07-22T08:27:04.166315Z", + "shell.execute_reply": "2026-07-22T08:27:04.165897Z" + } + }, + "source": [ + "# register the best model (from the \"Load the Best Model\" section above)\n", + "result = mlflow.register_model(\n", + " model_uri=best_model_uri,\n", + " name=\"darts-air-passengers\",\n", + ")\n", + "print(f\"Registered version: {result.version}\")" + ], + "execution_count": 60, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Registered version: 1\n" + ] + }, + { + "name": "stdout", +"output_type": "stream", + "text": [ + "Successfully registered model 'darts-air-passengers'.\n", + "Created version '1' of model 'darts-air-passengers'.\n" + ] + } + ], + "id": "7c854bc4" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The registry is accessible in the MLflow UI under the **Models** tab, where you can add descriptions, set aliases, and compare versions side-by-side.\n", + "\n", + "> 📸 **Try it:** After running the cells above, open the Models tab in the UI and register one of the runs.\n", + ">\n", + "![Mlflow Charts](./static/images/mlflow_models.png)" + ], + "id": "da6cc83a" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Important Note: Custom Flavor\n", + "\n", + "Since Darts uses a custom MLflow flavor on its' side it's important to import the methods accordingly.\n", + "\n", + "**Always use:**\n", + "```python\n", + "from darts.utils.mlflow import load_model\n", + "model = load_model(model_uri)\n", + "```\n", + "\n", + "**Instead of:**\n", + "```python\n", + "import mlflow\n", + "model = mlflow.pyfunc.load_model(model_uri) # Will fail!\n", + "```\n", + "\n", + "This custom flavor is necessary to properly handle:\n", + "- TimeSeries objects\n", + "- Darts-specific model parameters\n", + "- Covariate handling (past, future, static)\n", + "- PyTorch model state preservation" + ], + "id": "7af49ea9" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup" + ], + "id": "40621b13" + }, + { + "cell_type": "code", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-24T15:18:53.573724Z", + "iopub.status.busy": "2026-06-24T15:18:53.573637Z", + "iopub.status.idle": "2026-06-24T15:18:53.575115Z", + "shell.execute_reply": "2026-06-24T15:18:53.574817Z" + } + }, + "source": [ + "# Uncomment to cleanup\n", + "# import shutil\n", + "# shutil.rmtree(tmpdir)\n", + "# print(f\"Cleaned up temporary directory: {tmpdir}\")\n", + "\n", + "print(f\"To cleanup manually, delete: {tmpdir}\")" + ], + "execution_count": 19, + "outputs": [ + { + "name": "stdout", +"output_type": "stream", + "text": [ + "To cleanup manually, delete: /var/folders/yr/3703qwtj56lcw6n91xm6tq_r0000gn/T/tmpw5bcp0ic\n" + ] + } + ], + "id": "51fc7c4a" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Final Remarks" + ], + "id": "ca40afc1" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Currently none of the model serving capabilities are implemented for Darts. While an API was provided (`input_example` and `signature` parameters for the `.log_model` and `.load_model`) to keep in line with MLflow API conventions, they are currently widely unsupported." + ], + "id": "c4c86a23" + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv (3.11.9)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "01ac1053789e45fab75e2cde894b417a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "02cefe41f8814ddf9de26418b26783c3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "03569ea4478d4d74b0ae4c5416c40d6d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "055bae1119db45caa5b6cd91921c76d2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_11dbeaae3ae640c3995e784abfda15c2", + "IPY_MODEL_4787d389023845a8ae398eb5d1155ab1", + "IPY_MODEL_cb3cebaf14cc445a8055d1aac2f0d05c" + ], + "layout": "IPY_MODEL_30a18f8c59754940a9785cd152a67daa", + "tabbable": null, + "tooltip": null + } + }, + "05d25d3b3bd9497f86849702e5a19515": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "071709416bab4673a582978e2e3592a3": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "07e8c0b16c2d430ea8bb6798e9b4f103": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_580c0814097844e186ef642094968d3a", + "placeholder": "​", + "style": "IPY_MODEL_ce45f3190357423a9563bfae253f2f16", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "0deaf695a8544725ac4904ff7388f489": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_32e9a01e1a2b4914989eda6f6832f989", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_20cca542fe2a4e19846e2e1cb07dd165", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "11dbeaae3ae640c3995e784abfda15c2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_97b2d3e5a7e847739b237fc9c5cdc3a9", + "placeholder": "​", + "style": "IPY_MODEL_dc6cccb142db43b7ac9849983a154a20", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "12524abe09304f9bb20bd9978ac2a44d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_dcb0a4350297443aa61691406923fb81", + "placeholder": "​", + "style": "IPY_MODEL_e53d8d156b1c4db6aff2ba8395bf5415", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 189.90it/s]" + } + }, + "13af542c17d64851a3ef54ce9610bb99": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "153af823c040405dbc075bf50a28ff17": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "16656416b10a43c8b81c6c4b2a02ec0b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "16ce59abc5bb4ffe978736017f60e21a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_07e8c0b16c2d430ea8bb6798e9b4f103", + "IPY_MODEL_f6f12cd135674388b0fc72167a0308eb", + "IPY_MODEL_81e281800a094e34b2e43021bea6fd11" + ], + "layout": "IPY_MODEL_8d829381b5034ed9807b86d514b1fdbb", + "tabbable": null, + "tooltip": null + } + }, + "19382158e51343918122512753ca25c7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "20cca542fe2a4e19846e2e1cb07dd165": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "252a64f9c6d440e2a3d3c3f62f44d764": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_f299c926a6a64a769d3db2e71725f8af", + "placeholder": "​", + "style": "IPY_MODEL_7d599e5710f4485a8f7580c7efca3bc3", + "tabbable": null, + "tooltip": null, + "value": " 3/3 [00:00<00:00, 19.89it/s, train_loss=768.0, val_loss=2.64e+3, val_MAE=39.40, val_MSE=2.64e+3, train_MAE=26.60, train_MSE=1.08e+3]" + } + }, + "27e25633e2a549dcbfafe1129839043d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2b154733d72e4e468c339683e83c30a7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2b197d3911154085bb86f0dbba2c8b98": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2c4ee939558d44a4a3bc9040a940693f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_71c1aa3663804753bf3a75a227412072", + "placeholder": "​", + "style": "IPY_MODEL_7e0ad83f116b4933a35870525656df5d", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 199.01it/s]" + } + }, + "2e727ab68ed64ee98bfb9d81f1e39c1d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_f69737793cc648bd9aadfc0dd4b16569", + "IPY_MODEL_579cbbe7cfb34e429bffd11ec774ba50", + "IPY_MODEL_fe40606eebe6464db0b7f86e5abe1997" + ], + "layout": "IPY_MODEL_c2906f9d5f47471ca6bf44d8754f8d30", + "tabbable": null, + "tooltip": null + } + }, + "2ec00b06939145ec95930e3c1490efcc": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_ce49dfd502074696aefcfa2cff18e4f2", + "placeholder": "​", + "style": "IPY_MODEL_19382158e51343918122512753ca25c7", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "30a18f8c59754940a9785cd152a67daa": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "32e9a01e1a2b4914989eda6f6832f989": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "331f7f90d7d049a6adc72c260c06570e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_4be51ee5aeac4ddf87ec0ca3ceb7b2cb", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_153af823c040405dbc075bf50a28ff17", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "33a28231831d473aa27d32e1c15a8989": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "35778f5ef201497d8d453772f80a7978": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "366e2af2827540728d29bfa6d931dbdb": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_2ec00b06939145ec95930e3c1490efcc", + "IPY_MODEL_fb7ccdb37f844e989de3f7a0b12b8924", + "IPY_MODEL_d04931a3bb0c47329cdd67701a7be0ad" + ], + "layout": "IPY_MODEL_f286ebc675c549678f70419f668a7baa", + "tabbable": null, + "tooltip": null + } + }, + "3799be676aea4d12b87992115e4b90ee": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3a36a442c4a94ab585d8522e57f45c34": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_8f2ac8fd36964f11bd556782c96b0f72", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_35778f5ef201497d8d453772f80a7978", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "3b44dec6a3354f7ebc4117e7d7f50b1b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_e742e68a86474f0db17ccb64db4264c9", + "placeholder": "​", + "style": "IPY_MODEL_cd4d74958b704246a2cb2462a784d502", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "3c3d34b732664135a79031e8bc9e2984": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_6eb691aae3a44a25b1e7471929151f47", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b676b1571a00427587827caac8158c14", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "3cde338fcaf548de8e8216a98e66f9bc": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_c325a187e8a54e639300ff5a29640f03", + "placeholder": "​", + "style": "IPY_MODEL_b4e804d6e4b44314a8847c2f5be857d4", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 195.74it/s]" + } + }, + "3dddd89019c849f0872e9a9700e14ad0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_425a21a3b7604c8cb49871c1b2850f68", + "placeholder": "​", + "style": "IPY_MODEL_e09f041d857346029cbba27fb36f009e", + "tabbable": null, + "tooltip": null, + "value": "Predicting DataLoader 0: 100%" + } + }, + "40b8f5c6c7844bd0bb071f043f97a639": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "40df96a9f08440a893ffe9d57d176e1f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "41e23a2458044b888fb76fd882428b39": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "100%" + } + }, + "42360fe280a14e44831c8e62f6ad249e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "425a21a3b7604c8cb49871c1b2850f68": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4297b4158e4f43da9226bfc00272f9c5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "43eab5ba00014d7ba62f97acd9e93eba": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "44c3cc7783594142a19ffad035eea9d0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_27e25633e2a549dcbfafe1129839043d", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_4f10caf0abca4aa59053c4cb374ea3d6", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "4787d389023845a8ae398eb5d1155ab1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_2b154733d72e4e468c339683e83c30a7", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_859b368a8a6d4500960a6e2285f4c016", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "480afa5d41c145d0ac88e2adaca7b1e6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_eeb45b37592a4a599da1857d3444382e", + "IPY_MODEL_44c3cc7783594142a19ffad035eea9d0", + "IPY_MODEL_3cde338fcaf548de8e8216a98e66f9bc" + ], + "layout": "IPY_MODEL_d4b49fabdd4543959d64e4e1c60dc360", + "tabbable": null, + "tooltip": null + } + }, + "4b3f0ca06bf54eb9bab4e081dabcf139": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4be51ee5aeac4ddf87ec0ca3ceb7b2cb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4f10caf0abca4aa59053c4cb374ea3d6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "50e4f1e4cd994abeae53d0f84b6afd44": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "522af0834c254f0eb2fe7c9efa74c5f6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "54aad655289741b59d3c83b8508e68b6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_b23efdc3bb5747af96dd5e539a14b5c8", + "IPY_MODEL_3c3d34b732664135a79031e8bc9e2984", + "IPY_MODEL_12524abe09304f9bb20bd9978ac2a44d" + ], + "layout": "IPY_MODEL_13af542c17d64851a3ef54ce9610bb99", + "tabbable": null, + "tooltip": null + } + }, + "55e102413ba54377b2fd1995b98c7858": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "579cbbe7cfb34e429bffd11ec774ba50": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_e6dfb5d14dfe4a1193e551f414ea393e", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_522af0834c254f0eb2fe7c9efa74c5f6", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "57cd2f911c6840da87c1d6c8a78521e5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "57db1d3f28d347878d9f48d0ceef5589": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_3b44dec6a3354f7ebc4117e7d7f50b1b", + "IPY_MODEL_0deaf695a8544725ac4904ff7388f489", + "IPY_MODEL_e12d00f1e1b94e749f22904d3cec2445" + ], + "layout": "IPY_MODEL_b4d7ddbd507e4e1681cc0888680abb5b", + "tabbable": null, + "tooltip": null + } + }, + "580c0814097844e186ef642094968d3a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "590ebae9a3664214be16b105941c7ee3": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "59413112366944be8e0d6600880dc1a4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "5dcf47d8b9c0420898fe491a81bab8af": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_3dddd89019c849f0872e9a9700e14ad0", + "IPY_MODEL_331f7f90d7d049a6adc72c260c06570e", + "IPY_MODEL_9b933a5d5aa14741904151b4ea7e3f50" + ], + "layout": "IPY_MODEL_cc7d97d8845d46ff9d946020b1b8e4cb", + "tabbable": null, + "tooltip": null + } + }, + "61734f2b95c34e13971fd65cd7da2e1b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "641b5ae5b9e7475db63a31e67b13b601": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "642a06d187834b96af41b74a25d13e1a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6da83aee80314ceb9ace5fa0c4c76529": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_9e33a121da334db899669d702f9d54ce", + "IPY_MODEL_7adbf399ea5e4a7c990cdff60674b593", + "IPY_MODEL_252a64f9c6d440e2a3d3c3f62f44d764" + ], + "layout": "IPY_MODEL_41e23a2458044b888fb76fd882428b39", + "tabbable": null, + "tooltip": null + } + }, + "6dad7db23e9e4397bfb93655cd8b08b9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_e794979a2793408791ee570d3af6f371", + "IPY_MODEL_b35ce184b43b48daaa39ccef748eeac6", + "IPY_MODEL_fb53a4348865461783df6d1e5b16386b" + ], + "layout": "IPY_MODEL_f03f6404137144b8b600f8131c78ac63", + "tabbable": null, + "tooltip": null + } + }, + "6eb691aae3a44a25b1e7471929151f47": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "71c1aa3663804753bf3a75a227412072": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "72125343cca3416fbd3f012232e63ae5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "761dbbe1bf4e4804a3649858898b78e9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "7adbf399ea5e4a7c990cdff60674b593": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_42360fe280a14e44831c8e62f6ad249e", + "max": 3, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_f60411b259344a9e9a892d9552ecdabf", + "tabbable": null, + "tooltip": null, + "value": 3 + } + }, + "7d599e5710f4485a8f7580c7efca3bc3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "7e0ad83f116b4933a35870525656df5d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "81e281800a094e34b2e43021bea6fd11": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_590ebae9a3664214be16b105941c7ee3", + "placeholder": "​", + "style": "IPY_MODEL_02cefe41f8814ddf9de26418b26783c3", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 174.77it/s]" + } + }, + "859b368a8a6d4500960a6e2285f4c016": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "86bfcc40b08440af98f64a732df1ea5f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "8766872fa61844458d70112fcf44bdb3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "8825c219393f4040890a639a17210bc5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "89dc0773756b4b7b93cce8e6cfe47202": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_924315df026a41fb97d7cc8488bb8503", + "placeholder": "​", + "style": "IPY_MODEL_86bfcc40b08440af98f64a732df1ea5f", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 100.83it/s]" + } + }, + "8d829381b5034ed9807b86d514b1fdbb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "8e6984bd5a00425bb9071deba84f4cea": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "8f2ac8fd36964f11bd556782c96b0f72": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "924315df026a41fb97d7cc8488bb8503": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "95daeef1bd3e4f77b883d3d71fcd1fac": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_be1dd700617141cf9517ee7ef1153746", + "placeholder": "​", + "style": "IPY_MODEL_59413112366944be8e0d6600880dc1a4", + "tabbable": null, + "tooltip": null, + "value": "Sanity Checking DataLoader 0: 100%" + } + }, + "97b2d3e5a7e847739b237fc9c5cdc3a9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9914744ed44845fd9d00727c7da93d32": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_9a20b5d724584e289863cf63a4c13259", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_05d25d3b3bd9497f86849702e5a19515", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "9a20b5d724584e289863cf63a4c13259": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9b1dd53c1fdd45dabf3660e9e0be1310": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_c255e442c01446699af3179f14eb8fc3", + "IPY_MODEL_3a36a442c4a94ab585d8522e57f45c34", + "IPY_MODEL_2c4ee939558d44a4a3bc9040a940693f" + ], + "layout": "IPY_MODEL_c7c373a0b1c14b809041651ffcdd4559", + "tabbable": null, + "tooltip": null + } + }, + "9b8b6898ef244336989227f5469a2d11": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9b933a5d5aa14741904151b4ea7e3f50": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_c99d9a52749b40628b8d148c64e75e88", + "placeholder": "​", + "style": "IPY_MODEL_761dbbe1bf4e4804a3649858898b78e9", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 88.28it/s]" + } + }, + "9e33a121da334db899669d702f9d54ce": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_642a06d187834b96af41b74a25d13e1a", + "placeholder": "​", + "style": "IPY_MODEL_40df96a9f08440a893ffe9d57d176e1f", + "tabbable": null, + "tooltip": null, + "value": "Epoch 9: 100%" + } + }, + "9fd5c0e984d04e4aa0e106ec89434720": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "a30948d8cfd6491484d6b63591b65a84": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "a5f3118f4f9040ebbdb1a7acae012087": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b1dc954934944e92914a1c129d8c7dcd": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b23efdc3bb5747af96dd5e539a14b5c8": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_3799be676aea4d12b87992115e4b90ee", + "placeholder": "​", + "style": "IPY_MODEL_8766872fa61844458d70112fcf44bdb3", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "b35ce184b43b48daaa39ccef748eeac6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_c198ffce01de4b7294649c1619b5b7e6", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_03569ea4478d4d74b0ae4c5416c40d6d", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "b4d7ddbd507e4e1681cc0888680abb5b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "b4e804d6e4b44314a8847c2f5be857d4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "b6625b52007b4434a7804cfd824452cf": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_95daeef1bd3e4f77b883d3d71fcd1fac", + "IPY_MODEL_9914744ed44845fd9d00727c7da93d32", + "IPY_MODEL_89dc0773756b4b7b93cce8e6cfe47202" + ], + "layout": "IPY_MODEL_e7198635809b4f56bd1a30b6cbe661cf", + "tabbable": null, + "tooltip": null + } + }, + "b676b1571a00427587827caac8158c14": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "be1dd700617141cf9517ee7ef1153746": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c198ffce01de4b7294649c1619b5b7e6": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c255e442c01446699af3179f14eb8fc3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_ce97bb2d961e4dc9bf44a646cb3d3aff", + "placeholder": "​", + "style": "IPY_MODEL_4297b4158e4f43da9226bfc00272f9c5", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "c2906f9d5f47471ca6bf44d8754f8d30": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "c325a187e8a54e639300ff5a29640f03": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c60add7b277344feaad65a884c7fbf37": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "c7c373a0b1c14b809041651ffcdd4559": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "c99d9a52749b40628b8d148c64e75e88": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "cb3cebaf14cc445a8055d1aac2f0d05c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_071709416bab4673a582978e2e3592a3", + "placeholder": "​", + "style": "IPY_MODEL_fffac86b0934453d88f52df4b9ac6dd6", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 180.77it/s]" + } + }, + "cc7d97d8845d46ff9d946020b1b8e4cb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "100%" + } + }, + "cd4d74958b704246a2cb2462a784d502": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "ce45f3190357423a9563bfae253f2f16": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "ce49dfd502074696aefcfa2cff18e4f2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ce97bb2d961e4dc9bf44a646cb3d3aff": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d04931a3bb0c47329cdd67701a7be0ad": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_50e4f1e4cd994abeae53d0f84b6afd44", + "placeholder": "​", + "style": "IPY_MODEL_a30948d8cfd6491484d6b63591b65a84", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 192.16it/s]" + } + }, + "d473da3081af4d4a810014019cd40b29": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_dcced12e0ae04e32a811f60680db24f2", + "IPY_MODEL_d9fc58b33d2645488a8c53af72f21e7a", + "IPY_MODEL_fe3a78bc7ec842b08aac1b4e0fe1ed29" + ], + "layout": "IPY_MODEL_9fd5c0e984d04e4aa0e106ec89434720", + "tabbable": null, + "tooltip": null + } + }, + "d4b49fabdd4543959d64e4e1c60dc360": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "d9fc58b33d2645488a8c53af72f21e7a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_72125343cca3416fbd3f012232e63ae5", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e19ea3de3e554c50a7300a7214c773f2", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "da4c6178632b4cfe96d2a9504cc3173b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "dc6cccb142db43b7ac9849983a154a20": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "dcb0a4350297443aa61691406923fb81": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "dcced12e0ae04e32a811f60680db24f2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_43eab5ba00014d7ba62f97acd9e93eba", + "placeholder": "​", + "style": "IPY_MODEL_55e102413ba54377b2fd1995b98c7858", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "e09f041d857346029cbba27fb36f009e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "e12d00f1e1b94e749f22904d3cec2445": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_641b5ae5b9e7475db63a31e67b13b601", + "placeholder": "​", + "style": "IPY_MODEL_40b8f5c6c7844bd0bb071f043f97a639", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 186.36it/s]" + } + }, + "e19ea3de3e554c50a7300a7214c773f2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "e53d8d156b1c4db6aff2ba8395bf5415": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "e6dfb5d14dfe4a1193e551f414ea393e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": "2", + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e7198635809b4f56bd1a30b6cbe661cf": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "e742e68a86474f0db17ccb64db4264c9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e794979a2793408791ee570d3af6f371": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_33a28231831d473aa27d32e1c15a8989", + "placeholder": "​", + "style": "IPY_MODEL_61734f2b95c34e13971fd65cd7da2e1b", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "ed4968eb8435434db1604e34ee657413": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "eeb45b37592a4a599da1857d3444382e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_b1dc954934944e92914a1c129d8c7dcd", + "placeholder": "​", + "style": "IPY_MODEL_8e6984bd5a00425bb9071deba84f4cea", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "f03f6404137144b8b600f8131c78ac63": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "f286ebc675c549678f70419f668a7baa": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": "inline-flex", + "flex": null, + "flex_flow": "row wrap", + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": "100%" + } + }, + "f299c926a6a64a769d3db2e71725f8af": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f60411b259344a9e9a892d9552ecdabf": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "f609670bc6b840ed9f8698af8c7615a0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "f69737793cc648bd9aadfc0dd4b16569": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_9b8b6898ef244336989227f5469a2d11", + "placeholder": "​", + "style": "IPY_MODEL_8825c219393f4040890a639a17210bc5", + "tabbable": null, + "tooltip": null, + "value": "Validation DataLoader 0: 100%" + } + }, + "f6f12cd135674388b0fc72167a0308eb": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_01ac1053789e45fab75e2cde894b417a", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_ed4968eb8435434db1604e34ee657413", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "fb53a4348865461783df6d1e5b16386b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_a5f3118f4f9040ebbdb1a7acae012087", + "placeholder": "​", + "style": "IPY_MODEL_f609670bc6b840ed9f8698af8c7615a0", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 191.28it/s]" + } + }, + "fb7ccdb37f844e989de3f7a0b12b8924": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_4b3f0ca06bf54eb9bab4e081dabcf139", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_c60add7b277344feaad65a884c7fbf37", + "tabbable": null, + "tooltip": null, + "value": 1 + } + }, + "fe3a78bc7ec842b08aac1b4e0fe1ed29": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_da4c6178632b4cfe96d2a9504cc3173b", + "placeholder": "​", + "style": "IPY_MODEL_57cd2f911c6840da87c1d6c8a78521e5", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 191.93it/s]" + } + }, + "fe40606eebe6464db0b7f86e5abe1997": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_2b197d3911154085bb86f0dbba2c8b98", + "placeholder": "​", + "style": "IPY_MODEL_16656416b10a43c8b81c6c4b2a02ec0b", + "tabbable": null, + "tooltip": null, + "value": " 1/1 [00:00<00:00, 177.56it/s]" + } + }, + "fffac86b0934453d88f52df4b9ac6dd6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/static/images/mlflow_charts.png b/examples/static/images/mlflow_charts.png new file mode 100644 index 0000000000..cdf6da6356 Binary files /dev/null and b/examples/static/images/mlflow_charts.png differ diff --git a/examples/static/images/mlflow_models.png b/examples/static/images/mlflow_models.png new file mode 100644 index 0000000000..6c935b91cd Binary files /dev/null and b/examples/static/images/mlflow_models.png differ diff --git a/examples/static/images/mlflow_overview.png b/examples/static/images/mlflow_overview.png new file mode 100644 index 0000000000..2d923379e2 Binary files /dev/null and b/examples/static/images/mlflow_overview.png differ diff --git a/pyproject.toml b/pyproject.toml index 52025e995c..6dafa73391 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -169,6 +169,7 @@ dev = [ ] torch-cpu = ["torch>=2.0.0"] optional = [ + "mlflow>=3.0", "onnx>=1.0.0", "onnxruntime>=1.24.1; python_version >= '3.11'", "onnxruntime<1.24.1; python_version < '3.11'", # 1.24.1 dropped python 3.10 support