oracle-ads 2.11.8__py3-none-any.whl → 2.11.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. ads/aqua/__init__.py +1 -1
  2. ads/aqua/{base.py → app.py} +27 -7
  3. ads/aqua/cli.py +59 -17
  4. ads/aqua/common/__init__.py +5 -0
  5. ads/aqua/{decorator.py → common/decorator.py} +14 -8
  6. ads/aqua/common/enums.py +69 -0
  7. ads/aqua/{exception.py → common/errors.py} +28 -0
  8. ads/aqua/{utils.py → common/utils.py} +171 -79
  9. ads/aqua/config/config.py +18 -0
  10. ads/aqua/constants.py +51 -33
  11. ads/aqua/data.py +15 -26
  12. ads/aqua/evaluation/__init__.py +8 -0
  13. ads/aqua/evaluation/constants.py +53 -0
  14. ads/aqua/evaluation/entities.py +170 -0
  15. ads/aqua/evaluation/errors.py +71 -0
  16. ads/aqua/{evaluation.py → evaluation/evaluation.py} +122 -370
  17. ads/aqua/extension/__init__.py +2 -0
  18. ads/aqua/extension/aqua_ws_msg_handler.py +97 -0
  19. ads/aqua/extension/base_handler.py +0 -7
  20. ads/aqua/extension/common_handler.py +12 -6
  21. ads/aqua/extension/deployment_handler.py +70 -4
  22. ads/aqua/extension/errors.py +10 -0
  23. ads/aqua/extension/evaluation_handler.py +5 -3
  24. ads/aqua/extension/evaluation_ws_msg_handler.py +43 -0
  25. ads/aqua/extension/finetune_handler.py +41 -3
  26. ads/aqua/extension/model_handler.py +56 -4
  27. ads/aqua/extension/models/__init__.py +0 -0
  28. ads/aqua/extension/models/ws_models.py +69 -0
  29. ads/aqua/extension/ui_handler.py +65 -4
  30. ads/aqua/extension/ui_websocket_handler.py +124 -0
  31. ads/aqua/extension/utils.py +1 -1
  32. ads/aqua/finetuning/__init__.py +7 -0
  33. ads/aqua/finetuning/constants.py +17 -0
  34. ads/aqua/finetuning/entities.py +102 -0
  35. ads/aqua/{finetune.py → finetuning/finetuning.py} +162 -136
  36. ads/aqua/model/__init__.py +8 -0
  37. ads/aqua/model/constants.py +46 -0
  38. ads/aqua/model/entities.py +266 -0
  39. ads/aqua/model/enums.py +26 -0
  40. ads/aqua/{model.py → model/model.py} +401 -309
  41. ads/aqua/modeldeployment/__init__.py +8 -0
  42. ads/aqua/modeldeployment/constants.py +26 -0
  43. ads/aqua/{deployment.py → modeldeployment/deployment.py} +288 -227
  44. ads/aqua/modeldeployment/entities.py +142 -0
  45. ads/aqua/modeldeployment/inference.py +75 -0
  46. ads/aqua/ui.py +88 -8
  47. ads/cli.py +55 -7
  48. ads/common/serializer.py +2 -2
  49. ads/config.py +2 -1
  50. ads/jobs/builders/infrastructure/dsc_job.py +49 -6
  51. ads/model/datascience_model.py +21 -1
  52. ads/model/deployment/model_deployment.py +11 -0
  53. ads/model/model_metadata.py +17 -6
  54. ads/opctl/operator/lowcode/anomaly/README.md +0 -2
  55. ads/opctl/operator/lowcode/anomaly/__main__.py +3 -3
  56. ads/opctl/operator/lowcode/anomaly/environment.yaml +0 -2
  57. ads/opctl/operator/lowcode/anomaly/model/automlx.py +2 -2
  58. ads/opctl/operator/lowcode/anomaly/model/autots.py +1 -1
  59. ads/opctl/operator/lowcode/anomaly/model/base_model.py +13 -17
  60. ads/opctl/operator/lowcode/anomaly/operator_config.py +2 -0
  61. ads/opctl/operator/lowcode/anomaly/schema.yaml +1 -2
  62. ads/opctl/operator/lowcode/anomaly/utils.py +3 -2
  63. ads/opctl/operator/lowcode/common/transformations.py +2 -1
  64. ads/opctl/operator/lowcode/common/utils.py +1 -1
  65. ads/opctl/operator/lowcode/forecast/README.md +1 -3
  66. ads/opctl/operator/lowcode/forecast/__main__.py +3 -18
  67. ads/opctl/operator/lowcode/forecast/const.py +2 -0
  68. ads/opctl/operator/lowcode/forecast/environment.yaml +1 -2
  69. ads/opctl/operator/lowcode/forecast/model/arima.py +1 -0
  70. ads/opctl/operator/lowcode/forecast/model/automlx.py +7 -4
  71. ads/opctl/operator/lowcode/forecast/model/autots.py +1 -0
  72. ads/opctl/operator/lowcode/forecast/model/base_model.py +38 -22
  73. ads/opctl/operator/lowcode/forecast/model/factory.py +33 -4
  74. ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +15 -1
  75. ads/opctl/operator/lowcode/forecast/model/ml_forecast.py +234 -0
  76. ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +9 -1
  77. ads/opctl/operator/lowcode/forecast/model/prophet.py +1 -0
  78. ads/opctl/operator/lowcode/forecast/model_evaluator.py +147 -0
  79. ads/opctl/operator/lowcode/forecast/operator_config.py +2 -1
  80. ads/opctl/operator/lowcode/forecast/schema.yaml +7 -2
  81. ads/opctl/operator/lowcode/forecast/utils.py +18 -44
  82. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/METADATA +9 -12
  83. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/RECORD +86 -61
  84. ads/aqua/job.py +0 -29
  85. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/LICENSE.txt +0 -0
  86. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/WHEEL +0 -0
  87. {oracle_ads-2.11.8.dist-info → oracle_ads-2.11.10.dist-info}/entry_points.txt +0 -0
@@ -8,10 +8,10 @@ import json
8
8
  import logging
9
9
  import os
10
10
  import sys
11
- from abc import ABC, abstractclassmethod, abstractmethod
11
+ from abc import ABC, abstractmethod
12
12
  from dataclasses import dataclass, field, fields
13
13
  from pathlib import Path
14
- from typing import Dict, List, Tuple
14
+ from typing import Dict, List, Tuple, Union, Optional, Any
15
15
 
16
16
  import ads.dataset.factory as factory
17
17
  import fsspec
@@ -41,6 +41,8 @@ METADATA_DESCRIPTION_LENGTH_LIMIT = 255
41
41
  _METADATA_EMPTY_VALUE = "NA"
42
42
  CURRENT_WORKING_DIR = "."
43
43
 
44
+ _sentinel = object()
45
+
44
46
 
45
47
  class MetadataSizeTooLarge(ValueError):
46
48
  """Maximum allowed size for model metadata has been exceeded.
@@ -727,13 +729,18 @@ class ModelMetadata(ABC):
727
729
  """Initializes Model Metadata."""
728
730
  self._items = set()
729
731
 
730
- def get(self, key: str) -> ModelMetadataItem:
732
+ def get(
733
+ self, key: str, value: Optional[Any] = _sentinel
734
+ ) -> Union[ModelMetadataItem, Any]:
731
735
  """Returns the model metadata item by provided key.
732
736
 
733
737
  Parameters
734
738
  ----------
735
739
  key: str
736
740
  The key of model metadata item.
741
+ value: (str, optional)
742
+ A value to return if the specified key does not exist. Defaults to `object()`.
743
+ If default value not specified, the ValueError will be returned.
737
744
 
738
745
  Returns
739
746
  -------
@@ -750,7 +757,11 @@ class ModelMetadata(ABC):
750
757
  for item in self._items:
751
758
  if item.key.lower() == key.lower():
752
759
  return item
753
- raise ValueError(f"The metadata with {key} not found.")
760
+
761
+ if value is _sentinel:
762
+ raise ValueError(f"The metadata with {key} not found.")
763
+
764
+ return value
754
765
 
755
766
  def reset(self) -> None:
756
767
  """Resets all model metadata items to empty values.
@@ -952,7 +963,7 @@ class ModelMetadata(ABC):
952
963
  def __len__(self):
953
964
  return len(self._items)
954
965
 
955
- @abstractclassmethod
966
+ @abstractmethod
956
967
  def _from_oci_metadata(cls, metadata_list):
957
968
  pass
958
969
 
@@ -967,7 +978,7 @@ class ModelMetadata(ABC):
967
978
  """
968
979
  pass
969
980
 
970
- @abstractclassmethod
981
+ @abstractmethod
971
982
  def from_dict(cls, data: Dict) -> "ModelMetadata":
972
983
  """Constructs an instance of `ModelMetadata` from a dictionary.
973
984
 
@@ -37,8 +37,6 @@ To run anomaly detection locally, create and activate a new conda environment (`
37
37
  ```yaml
38
38
  - report-creator
39
39
  - cerberus
40
- - oracle-automlx==23.4.1
41
- - oracle-automlx[classic]==23.4.1
42
40
  - "git+https://github.com/oracle/accelerated-data-science.git@feature/anomaly#egg=oracle-ads"
43
41
  ```
44
42
 
@@ -40,11 +40,11 @@ def operate(operator_config: AnomalyOperatorConfig) -> None:
40
40
  AnomalyOperatorModelFactory.get_model(
41
41
  operator_config, datasets
42
42
  ).generate_report()
43
- except Exception as e2:
43
+ except Exception as ee:
44
44
  logger.debug(
45
- f"Failed to backup forecast with error {e2.args}. Raising original error."
45
+ f"Failed to backup forecast with error {ee.args}. Raising original error."
46
46
  )
47
- raise e
47
+ raise ee
48
48
  else:
49
49
  raise e
50
50
 
@@ -7,6 +7,4 @@ dependencies:
7
7
  - pip:
8
8
  - report-creator
9
9
  - cerberus
10
- - oracle-automlx==23.4.1
11
- - oracle-automlx[classic]==23.4.1
12
10
  - "oracle-ads[anomaly]"
@@ -19,8 +19,8 @@ class AutoMLXOperatorModel(AnomalyOperatorBaseModel):
19
19
  @runtime_dependency(
20
20
  module="automlx",
21
21
  err_msg=(
22
- "Please run `pip3 install oracle-automlx==23.4.1` and "
23
- "`pip3 install oracle-automlx[classic]==23.4.1` "
22
+ "Please run `pip3 install oracle-automlx>=23.4.1` and "
23
+ "`pip3 install oracle-automlx[classic]>=23.4.1` "
24
24
  "to install the required dependencies for automlx."
25
25
  ),
26
26
  )
@@ -91,7 +91,7 @@ class AutoTSOperatorModel(AnomalyOperatorBaseModel):
91
91
  ),
92
92
  ]
93
93
  model_description = rc.Text(
94
- "The automlx model automatically pre-processes, selects and engineers "
94
+ "The autots model automatically pre-processes, selects and engineers "
95
95
  "high-quality features in your dataset, which then given to an automatically "
96
96
  "chosen and optimized machine learning model.."
97
97
  )
@@ -4,33 +4,29 @@
4
4
  # Copyright (c) 2023, 2024 Oracle and/or its affiliates.
5
5
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
6
 
7
+ import fsspec
8
+ import numpy as np
7
9
  import os
10
+ import pandas as pd
8
11
  import tempfile
9
12
  import time
10
13
  from abc import ABC, abstractmethod
11
- from typing import Tuple
12
-
13
- import fsspec
14
- import pandas as pd
15
- import numpy as np
16
14
  from sklearn import linear_model
15
+ from typing import Tuple
17
16
 
17
+ from ads.common.object_storage_details import ObjectStorageDetails
18
18
  from ads.opctl import logger
19
-
20
- from ..operator_config import AnomalyOperatorConfig, AnomalyOperatorSpec
21
- from .anomaly_dataset import AnomalyDatasets, AnomalyOutput, TestData
22
19
  from ads.opctl.operator.lowcode.anomaly.const import OutputColumns, SupportedMetrics
23
- from ..const import SupportedModels
20
+ from ads.opctl.operator.lowcode.anomaly.utils import _build_metrics_df, default_signer
24
21
  from ads.opctl.operator.lowcode.common.utils import (
25
22
  human_time_friendly,
26
23
  enable_print,
27
24
  disable_print,
28
25
  write_data,
29
- merge_category_columns,
30
- find_output_dirname,
31
26
  )
32
- from ads.opctl.operator.lowcode.anomaly.utils import _build_metrics_df, default_signer
33
- from ads.common.object_storage_details import ObjectStorageDetails
27
+ from .anomaly_dataset import AnomalyDatasets, AnomalyOutput, TestData
28
+ from ..const import SupportedModels
29
+ from ..operator_config import AnomalyOperatorConfig, AnomalyOperatorSpec
34
30
 
35
31
 
36
32
  class AnomalyOperatorBaseModel(ABC):
@@ -246,7 +242,7 @@ class AnomalyOperatorBaseModel(ABC):
246
242
  """Saves resulting reports to the given folder."""
247
243
  import report_creator as rc
248
244
 
249
- unique_output_dir = find_output_dirname(self.spec.output_directory)
245
+ unique_output_dir = self.spec.output_directory.url
250
246
 
251
247
  if ObjectStorageDetails.is_oci_path(unique_output_dir):
252
248
  storage_options = default_signer()
@@ -320,11 +316,11 @@ class AnomalyOperatorBaseModel(ABC):
320
316
  # Iterate over the full_data_dict items
321
317
  for target, df in self.datasets.full_data_dict.items():
322
318
  est = linear_model.SGDOneClassSVM(random_state=42)
323
- est.fit(df[target].values.reshape(-1, 1))
319
+ est.fit(df[self.spec.target_column].fillna(0).values.reshape(-1, 1))
324
320
  y_pred = np.vectorize(self.outlier_map.get)(
325
- est.predict(df[target].values.reshape(-1, 1))
321
+ est.predict(df[self.spec.target_column].fillna(0).values.reshape(-1, 1))
326
322
  )
327
- scores = est.score_samples(df[target].values.reshape(-1, 1))
323
+ scores = est.score_samples(df[self.spec.target_column].fillna(0).values.reshape(-1, 1))
328
324
 
329
325
  anomaly = pd.DataFrame(
330
326
  {date_column: df[date_column], OutputColumns.ANOMALY_COL: y_pred}
@@ -16,6 +16,7 @@ from ads.opctl.operator.common.operator_config import (
16
16
  InputData,
17
17
  )
18
18
  from .const import SupportedModels
19
+ from ads.opctl.operator.lowcode.common.utils import find_output_dirname
19
20
 
20
21
 
21
22
  @dataclass(repr=True)
@@ -79,6 +80,7 @@ class AnomalyOperatorSpec(DataClassSerializable):
79
80
 
80
81
  def __post_init__(self):
81
82
  """Adjusts the specification details."""
83
+ self.output_directory = self.output_directory or OutputDirectory(url=find_output_dirname(self.output_directory))
82
84
  self.report_file_name = self.report_file_name or "report.html"
83
85
  self.report_theme = self.report_theme or "light"
84
86
  self.inliers_filename = self.inliers_filename or "inliers.csv"
@@ -349,9 +349,8 @@ spec:
349
349
  model:
350
350
  type: string
351
351
  required: false
352
- default: automlx
352
+ default: autots
353
353
  allowed:
354
- - automlx
355
354
  - autots
356
355
  - auto
357
356
  meta:
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*--
3
3
 
4
- # Copyright (c) 2023 Oracle and/or its affiliates.
4
+ # Copyright (c) 2023, 2024 Oracle and/or its affiliates.
5
5
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
6
 
7
7
  import os
@@ -77,5 +77,6 @@ def default_signer(**kwargs):
77
77
 
78
78
  return default_signer(**kwargs)
79
79
 
80
+
80
81
  def select_auto_model(datasets, operator_config):
81
- return SupportedModels.AutoMLX
82
+ return SupportedModels.AutoTS
@@ -97,7 +97,8 @@ class Transformations(ABC):
97
97
  for value in merged_values:
98
98
  self._target_category_columns_map[value] = df[df[DataColumns.Series] == value][self.target_category_columns].drop_duplicates().iloc[0].to_dict()
99
99
 
100
- df = df.drop(self.target_category_columns, axis=1)
100
+ if self.target_category_columns != [DataColumns.Series]:
101
+ df = df.drop(self.target_category_columns, axis=1)
101
102
  return df
102
103
 
103
104
  def _format_datetime_col(self, df):
@@ -215,7 +215,7 @@ def human_time_friendly(seconds):
215
215
 
216
216
 
217
217
  def find_output_dirname(output_dir: OutputDirectory):
218
- if output_dir:
218
+ if output_dir and output_dir.url:
219
219
  return output_dir.url
220
220
  output_dir = "results"
221
221
 
@@ -38,9 +38,7 @@ To run forecasting locally, create and activate a new conda environment (`ads-fo
38
38
  - report-creator
39
39
  - cerberus
40
40
  - sktime
41
- - optuna==3.1.0
42
- - oracle-automlx==23.4.1
43
- - oracle-automlx[forecasting]==23.4.1
41
+ - optuna
44
42
  - oracle-ads>=2.9.0
45
43
  ```
46
44
 
@@ -24,24 +24,9 @@ def operate(operator_config: ForecastOperatorConfig) -> None:
24
24
  from .model.factory import ForecastOperatorModelFactory
25
25
 
26
26
  datasets = ForecastDatasets(operator_config)
27
- try:
28
- ForecastOperatorModelFactory.get_model(
29
- operator_config, datasets
30
- ).generate_report()
31
- except Exception as e:
32
- if operator_config.spec.model == "auto":
33
- logger.debug(
34
- f"Failed to forecast with error {e.args}. Trying again with model `prophet`."
35
- )
36
- operator_config.spec.model = "prophet"
37
- operator_config.spec.model_kwargs = dict()
38
- datasets = ForecastDatasets(operator_config)
39
- ForecastOperatorModelFactory.get_model(
40
- operator_config, datasets
41
- ).generate_report()
42
- else:
43
- raise
44
-
27
+ ForecastOperatorModelFactory.get_model(
28
+ operator_config, datasets
29
+ ).generate_report()
45
30
 
46
31
  def verify(spec: Dict, **kwargs: Dict) -> bool:
47
32
  """Verifies the forecasting operator config."""
@@ -14,6 +14,7 @@ class SupportedModels(str, metaclass=ExtendedEnumMeta):
14
14
  Prophet = "prophet"
15
15
  Arima = "arima"
16
16
  NeuralProphet = "neuralprophet"
17
+ MLForecast = "mlforecast"
17
18
  AutoMLX = "automlx"
18
19
  AutoTS = "autots"
19
20
  Auto = "auto"
@@ -86,3 +87,4 @@ DEFAULT_TRIALS = 10
86
87
  SUMMARY_METRICS_HORIZON_LIMIT = 10
87
88
  PROPHET_INTERNAL_DATE_COL = "ds"
88
89
  RENDER_LIMIT = 5000
90
+ AUTO_SELECT = "auto-select"
@@ -8,6 +8,7 @@ dependencies:
8
8
  - oracle-ads>=2.9.0
9
9
  - prophet
10
10
  - neuralprophet
11
+ - mlforecast
11
12
  - pmdarima
12
13
  - statsmodels
13
14
  - report-creator
@@ -16,6 +17,4 @@ dependencies:
16
17
  - shap
17
18
  - autots[additional]
18
19
  - optuna
19
- - oracle-automlx>=23.4.1
20
- - oracle-automlx[forecasting]>=23.4.1
21
20
  - fire
@@ -125,6 +125,7 @@ class ArimaOperatorModel(ForecastOperatorBaseModel):
125
125
  logger.debug("===========Done===========")
126
126
  except Exception as e:
127
127
  self.errors_dict[s_id] = {"model_name": self.spec.model, "error": str(e)}
128
+ logger.debug(f"Encountered Error: {e}. Skipping.")
128
129
 
129
130
  def _build_model(self) -> pd.DataFrame:
130
131
  full_data_dict = self.datasets.get_data_by_series()
@@ -62,8 +62,8 @@ class AutoMLXOperatorModel(ForecastOperatorBaseModel):
62
62
  @runtime_dependency(
63
63
  module="automlx",
64
64
  err_msg=(
65
- "Please run `pip3 install oracle-automlx==23.4.1` and "
66
- "`pip3 install oracle-automlx[forecasting]==23.4.1` "
65
+ "Please run `pip3 install oracle-automlx>=23.4.1` and "
66
+ "`pip3 install oracle-automlx[forecasting]>=23.4.1` "
67
67
  "to install the required dependencies for automlx."
68
68
  ),
69
69
  )
@@ -84,7 +84,7 @@ class AutoMLXOperatorModel(ForecastOperatorBaseModel):
84
84
  loglevel=logging.CRITICAL,
85
85
  )
86
86
  except Exception as e:
87
- logger.info("Ray already initialized")
87
+ logger.info(f"Error. Has Ray already been initialized? Skipping. {e}")
88
88
 
89
89
  full_data_dict = self.datasets.get_data_by_series()
90
90
 
@@ -113,7 +113,9 @@ class AutoMLXOperatorModel(ForecastOperatorBaseModel):
113
113
  data_i = self.drop_horizon(data)
114
114
  X_pred = self.get_horizon(data).drop(target, axis=1)
115
115
 
116
- logger.debug(f"Time Index Monotonic: {data_i.index.is_monotonic}")
116
+ logger.debug(
117
+ f"Time Index Monotonic: {data_i.index.is_monotonic_increasing}"
118
+ )
117
119
 
118
120
  if self.loaded_models is not None and s_id in self.loaded_models:
119
121
  model = self.loaded_models[s_id]
@@ -166,6 +168,7 @@ class AutoMLXOperatorModel(ForecastOperatorBaseModel):
166
168
  "model_name": self.spec.model,
167
169
  "error": str(e),
168
170
  }
171
+ logger.debug(f"Encountered Error: {e}. Skipping.")
169
172
 
170
173
  logger.debug("===========Forecast Generated===========")
171
174
 
@@ -209,6 +209,7 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
209
209
  "model_name": self.spec.model,
210
210
  "error": str(e),
211
211
  }
212
+ logger.debug(f"Encountered Error: {e}. Skipping.")
212
213
 
213
214
  logger.debug("===========Done===========")
214
215
 
@@ -4,31 +4,19 @@
4
4
  # Copyright (c) 2023, 2024 Oracle and/or its affiliates.
5
5
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
6
 
7
- import json
7
+ import fsspec
8
+ import numpy as np
8
9
  import os
10
+ import pandas as pd
9
11
  import tempfile
10
12
  import time
13
+ import traceback
11
14
  from abc import ABC, abstractmethod
12
15
  from typing import Tuple
13
- import traceback
14
-
15
- import fsspec
16
- import numpy as np
17
- import pandas as pd
18
16
 
19
- from ads.opctl.operator.lowcode.forecast.utils import (
20
- default_signer,
21
- evaluate_train_metrics,
22
- get_forecast_plots,
23
- _build_metrics_df,
24
- _build_metrics_per_horizon,
25
- load_pkl,
26
- write_pkl,
27
- _label_encode_dataframe,
28
- )
17
+ from ads.common.decorator.runtime_dependency import runtime_dependency
29
18
  from ads.common.object_storage_details import ObjectStorageDetails
30
19
  from ads.opctl import logger
31
-
32
20
  from ads.opctl.operator.lowcode.common.utils import (
33
21
  human_time_friendly,
34
22
  enable_print,
@@ -37,18 +25,28 @@ from ads.opctl.operator.lowcode.common.utils import (
37
25
  merged_category_column_name,
38
26
  datetime_to_seconds,
39
27
  seconds_to_datetime,
40
- find_output_dirname,
41
28
  )
29
+ from ads.opctl.operator.lowcode.forecast.model.forecast_datasets import TestData
30
+ from ads.opctl.operator.lowcode.forecast.utils import (
31
+ default_signer,
32
+ evaluate_train_metrics,
33
+ get_forecast_plots,
34
+ get_auto_select_plot,
35
+ _build_metrics_df,
36
+ _build_metrics_per_horizon,
37
+ load_pkl,
38
+ write_pkl,
39
+ _label_encode_dataframe,
40
+ )
41
+ from .forecast_datasets import ForecastDatasets
42
42
  from ..const import (
43
43
  SUMMARY_METRICS_HORIZON_LIMIT,
44
44
  SupportedMetrics,
45
45
  SupportedModels,
46
46
  SpeedAccuracyMode,
47
+ AUTO_SELECT
47
48
  )
48
49
  from ..operator_config import ForecastOperatorConfig, ForecastOperatorSpec
49
- from ads.common.decorator.runtime_dependency import runtime_dependency
50
- from .forecast_datasets import ForecastDatasets, ForecastOutput
51
- from ads.opctl.operator.lowcode.forecast.model.forecast_datasets import TestData
52
50
 
53
51
 
54
52
  class ForecastOperatorBaseModel(ABC):
@@ -250,6 +248,23 @@ class ForecastOperatorBaseModel(ABC):
250
248
  sec9 = rc.DataTable(self.eval_metrics, index=True)
251
249
  train_metrics_sections = [sec9_text, sec9]
252
250
 
251
+ backtest_sections = []
252
+ if self.spec.model == AUTO_SELECT:
253
+ output_dir = self.spec.output_directory.url
254
+ backtest_report_name = "backtest_stats.csv"
255
+ backtest_stats = pd.read_csv(f"{output_dir}/{backtest_report_name}")
256
+ average_dict = backtest_stats.mean().to_dict()
257
+ del average_dict['backtest']
258
+ best_model = min(average_dict, key=average_dict.get)
259
+ backtest_text = rc.Heading("Back Testing Metrics", level=2)
260
+ summary_text = rc.Text(
261
+ f"Overall, the average scores for the models are {average_dict}, with {best_model}"
262
+ f" being identified as the top-performing model during backtesting.")
263
+ backtest_table = rc.DataTable(backtest_stats, index=True)
264
+ liner_plot = get_auto_select_plot(backtest_stats)
265
+ backtest_sections = [backtest_text, backtest_table, summary_text, liner_plot]
266
+
267
+
253
268
  forecast_plots = []
254
269
  if len(self.forecast_output.list_series_ids()) > 0:
255
270
  forecast_text = rc.Heading(
@@ -276,6 +291,7 @@ class ForecastOperatorBaseModel(ABC):
276
291
  yaml_appendix = rc.Yaml(self.config.to_dict())
277
292
  report_sections = (
278
293
  [summary]
294
+ + backtest_sections
279
295
  + forecast_plots
280
296
  + other_sections
281
297
  + test_metrics_sections
@@ -409,7 +425,7 @@ class ForecastOperatorBaseModel(ABC):
409
425
  """Saves resulting reports to the given folder."""
410
426
  import report_creator as rc
411
427
 
412
- unique_output_dir = find_output_dirname(self.spec.output_directory)
428
+ unique_output_dir = self.spec.output_directory.url
413
429
 
414
430
  if ObjectStorageDetails.is_oci_path(unique_output_dir):
415
431
  storage_options = default_signer()
@@ -4,7 +4,7 @@
4
4
  # Copyright (c) 2023 Oracle and/or its affiliates.
5
5
  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
6
6
 
7
- from ..const import SupportedModels
7
+ from ..const import SupportedModels, AUTO_SELECT
8
8
  from ..operator_config import ForecastOperatorConfig
9
9
  from .arima import ArimaOperatorModel
10
10
  from .automlx import AutoMLXOperatorModel
@@ -12,8 +12,9 @@ from .autots import AutoTSOperatorModel
12
12
  from .base_model import ForecastOperatorBaseModel
13
13
  from .neuralprophet import NeuralProphetOperatorModel
14
14
  from .prophet import ProphetOperatorModel
15
- from ..utils import select_auto_model
16
15
  from .forecast_datasets import ForecastDatasets
16
+ from .ml_forecast import MLForecastOperatorModel
17
+ from ..model_evaluator import ModelEvaluator
17
18
 
18
19
  class UnSupportedModelError(Exception):
19
20
  def __init__(self, model_type: str):
@@ -32,6 +33,7 @@ class ForecastOperatorModelFactory:
32
33
  SupportedModels.Prophet: ProphetOperatorModel,
33
34
  SupportedModels.Arima: ArimaOperatorModel,
34
35
  SupportedModels.NeuralProphet: NeuralProphetOperatorModel,
36
+ SupportedModels.MLForecast: MLForecastOperatorModel,
35
37
  SupportedModels.AutoMLX: AutoMLXOperatorModel,
36
38
  SupportedModels.AutoTS: AutoTSOperatorModel
37
39
  }
@@ -61,8 +63,35 @@ class ForecastOperatorModelFactory:
61
63
  In case of not supported model.
62
64
  """
63
65
  model_type = operator_config.spec.model
64
- if model_type == "auto":
65
- model_type = select_auto_model(datasets, operator_config)
66
+ if model_type == AUTO_SELECT:
67
+ model_type = cls.auto_select_model(datasets, operator_config)
68
+ operator_config.spec.model_kwargs = dict()
66
69
  if model_type not in cls._MAP:
67
70
  raise UnSupportedModelError(model_type)
68
71
  return cls._MAP[model_type](config=operator_config, datasets=datasets)
72
+
73
+ @classmethod
74
+ def auto_select_model(
75
+ cls, datasets: ForecastDatasets, operator_config: ForecastOperatorConfig
76
+ ) -> str:
77
+ """
78
+ Selects AutoMLX or Arima model based on column count.
79
+
80
+ If the number of columns is less than or equal to the maximum allowed for AutoMLX,
81
+ returns 'AutoMLX'. Otherwise, returns 'Arima'.
82
+
83
+ Parameters
84
+ ------------
85
+ datasets: ForecastDatasets
86
+ Datasets for predictions
87
+
88
+ Returns
89
+ --------
90
+ str
91
+ The type of the model.
92
+ """
93
+ all_models = operator_config.spec.model_kwargs.get("model_list", cls._MAP.keys())
94
+ num_backtests = operator_config.spec.model_kwargs.get("num_backtests", 5)
95
+ sample_ratio = operator_config.spec.model_kwargs.get("sample_ratio", 0.20)
96
+ model_evaluator = ModelEvaluator(all_models, num_backtests, sample_ratio)
97
+ return model_evaluator.find_best_model(datasets, operator_config)
@@ -86,7 +86,10 @@ class AdditionalData(AbstractData):
86
86
  pd.date_range(
87
87
  start=historical_data.get_max_time(),
88
88
  periods=spec.horizon + 1,
89
- freq=historical_data.freq,
89
+ freq=historical_data.freq
90
+ or pd.infer_freq(
91
+ historical_data.data.reset_index()[spec.datetime_column.name][-5:]
92
+ ),
90
93
  ),
91
94
  name=spec.datetime_column.name,
92
95
  )
@@ -135,6 +138,7 @@ class ForecastDatasets:
135
138
 
136
139
  self._horizon = config.spec.horizon
137
140
  self._datetime_column_name = config.spec.datetime_column.name
141
+ self._target_col = config.spec.target_column
138
142
  self._load_data(config.spec)
139
143
 
140
144
  def _load_data(self, spec):
@@ -158,6 +162,16 @@ class ForecastDatasets:
158
162
  on=[self._datetime_column_name, ForecastOutputColumns.SERIES],
159
163
  ).reset_index()
160
164
 
165
+ def get_all_data_long_forecast_horizon(self):
166
+ """Returns all data in long format for the forecast horizon."""
167
+ test_data = pd.merge(
168
+ self.historical_data.data,
169
+ self.additional_data.data,
170
+ how="outer",
171
+ on=[self._datetime_column_name, ForecastOutputColumns.SERIES],
172
+ ).reset_index()
173
+ return test_data[test_data[self._target_col].isnull()].reset_index(drop=True)
174
+
161
175
  def get_data_multi_indexed(self):
162
176
  return pd.concat(
163
177
  [