oracle-ads 2.12.11__py3-none-any.whl → 2.13.1__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.
- ads/aqua/__init__.py +7 -1
- ads/aqua/app.py +41 -27
- ads/aqua/client/client.py +48 -11
- ads/aqua/common/entities.py +28 -1
- ads/aqua/common/enums.py +32 -21
- ads/aqua/common/errors.py +3 -4
- ads/aqua/common/utils.py +10 -15
- ads/aqua/config/container_config.py +203 -0
- ads/aqua/config/evaluation/evaluation_service_config.py +5 -181
- ads/aqua/constants.py +1 -1
- ads/aqua/evaluation/constants.py +7 -7
- ads/aqua/evaluation/errors.py +3 -4
- ads/aqua/evaluation/evaluation.py +4 -4
- ads/aqua/extension/base_handler.py +4 -0
- ads/aqua/extension/model_handler.py +41 -27
- ads/aqua/extension/models/ws_models.py +5 -6
- ads/aqua/finetuning/constants.py +3 -3
- ads/aqua/finetuning/finetuning.py +2 -3
- ads/aqua/model/constants.py +7 -7
- ads/aqua/model/entities.py +2 -3
- ads/aqua/model/enums.py +4 -5
- ads/aqua/model/model.py +46 -29
- ads/aqua/modeldeployment/deployment.py +6 -14
- ads/aqua/modeldeployment/entities.py +5 -3
- ads/aqua/server/__init__.py +4 -0
- ads/aqua/server/__main__.py +24 -0
- ads/aqua/server/app.py +47 -0
- ads/aqua/server/aqua_spec.yml +1291 -0
- ads/aqua/ui.py +5 -199
- ads/common/auth.py +50 -28
- ads/common/extended_enum.py +52 -44
- ads/common/utils.py +91 -11
- ads/config.py +3 -0
- ads/llm/__init__.py +12 -8
- ads/llm/langchain/plugins/embeddings/__init__.py +4 -0
- ads/llm/langchain/plugins/embeddings/oci_data_science_model_deployment_endpoint.py +184 -0
- ads/llm/langchain/plugins/llms/oci_data_science_model_deployment_endpoint.py +32 -23
- ads/model/artifact_downloader.py +6 -4
- ads/model/common/utils.py +15 -3
- ads/model/datascience_model.py +422 -71
- ads/model/generic_model.py +3 -3
- ads/model/model_metadata.py +70 -24
- ads/model/model_version_set.py +5 -3
- ads/model/service/oci_datascience_model.py +487 -17
- ads/opctl/anomaly_detection.py +11 -0
- ads/opctl/backend/marketplace/helm_helper.py +13 -14
- ads/opctl/cli.py +4 -5
- ads/opctl/cmds.py +28 -32
- ads/opctl/config/merger.py +8 -11
- ads/opctl/config/resolver.py +25 -30
- ads/opctl/forecast.py +11 -0
- ads/opctl/operator/cli.py +9 -9
- ads/opctl/operator/common/backend_factory.py +56 -60
- ads/opctl/operator/common/const.py +5 -5
- ads/opctl/operator/common/utils.py +16 -0
- ads/opctl/operator/lowcode/anomaly/const.py +8 -9
- ads/opctl/operator/lowcode/common/data.py +5 -2
- ads/opctl/operator/lowcode/common/transformations.py +2 -12
- ads/opctl/operator/lowcode/feature_store_marketplace/operator_utils.py +43 -48
- ads/opctl/operator/lowcode/forecast/__main__.py +5 -5
- ads/opctl/operator/lowcode/forecast/const.py +6 -6
- ads/opctl/operator/lowcode/forecast/model/arima.py +6 -3
- ads/opctl/operator/lowcode/forecast/model/automlx.py +61 -31
- ads/opctl/operator/lowcode/forecast/model/base_model.py +66 -40
- ads/opctl/operator/lowcode/forecast/model/forecast_datasets.py +79 -13
- ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +5 -2
- ads/opctl/operator/lowcode/forecast/model/prophet.py +28 -15
- ads/opctl/operator/lowcode/forecast/model_evaluator.py +13 -15
- ads/opctl/operator/lowcode/forecast/schema.yaml +1 -1
- ads/opctl/operator/lowcode/forecast/whatifserve/deployment_manager.py +7 -0
- ads/opctl/operator/lowcode/forecast/whatifserve/score.py +19 -11
- ads/opctl/operator/lowcode/pii/constant.py +6 -7
- ads/opctl/operator/lowcode/recommender/constant.py +12 -7
- ads/opctl/operator/runtime/marketplace_runtime.py +4 -10
- ads/opctl/operator/runtime/runtime.py +4 -6
- ads/pipeline/ads_pipeline_run.py +13 -25
- ads/pipeline/visualizer/graph_renderer.py +3 -4
- {oracle_ads-2.12.11.dist-info → oracle_ads-2.13.1.dist-info}/METADATA +18 -15
- {oracle_ads-2.12.11.dist-info → oracle_ads-2.13.1.dist-info}/RECORD +82 -74
- {oracle_ads-2.12.11.dist-info → oracle_ads-2.13.1.dist-info}/WHEEL +1 -1
- ads/aqua/config/evaluation/evaluation_service_model_config.py +0 -8
- {oracle_ads-2.12.11.dist-info → oracle_ads-2.13.1.dist-info}/entry_points.txt +0 -0
- {oracle_ads-2.12.11.dist-info → oracle_ads-2.13.1.dist-info/licenses}/LICENSE.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
|
3
|
-
# Copyright (c) 2023,
|
3
|
+
# Copyright (c) 2023, 2025 Oracle and/or its affiliates.
|
4
4
|
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
|
5
5
|
|
6
6
|
import logging
|
@@ -19,6 +19,7 @@ import report_creator as rc
|
|
19
19
|
from ads.common.decorator.runtime_dependency import runtime_dependency
|
20
20
|
from ads.common.object_storage_details import ObjectStorageDetails
|
21
21
|
from ads.opctl import logger
|
22
|
+
from ads.opctl.operator.lowcode.common.const import DataColumns
|
22
23
|
from ads.opctl.operator.lowcode.common.utils import (
|
23
24
|
datetime_to_seconds,
|
24
25
|
disable_print,
|
@@ -28,7 +29,6 @@ from ads.opctl.operator.lowcode.common.utils import (
|
|
28
29
|
seconds_to_datetime,
|
29
30
|
write_data,
|
30
31
|
)
|
31
|
-
from ads.opctl.operator.lowcode.common.const import DataColumns
|
32
32
|
from ads.opctl.operator.lowcode.forecast.model.forecast_datasets import TestData
|
33
33
|
from ads.opctl.operator.lowcode.forecast.utils import (
|
34
34
|
_build_metrics_df,
|
@@ -49,10 +49,9 @@ from ..const import (
|
|
49
49
|
SpeedAccuracyMode,
|
50
50
|
SupportedMetrics,
|
51
51
|
SupportedModels,
|
52
|
-
BACKTEST_REPORT_NAME,
|
53
52
|
)
|
54
53
|
from ..operator_config import ForecastOperatorConfig, ForecastOperatorSpec
|
55
|
-
from .forecast_datasets import ForecastDatasets
|
54
|
+
from .forecast_datasets import ForecastDatasets, ForecastResults
|
56
55
|
|
57
56
|
logging.getLogger("report_creator").setLevel(logging.WARNING)
|
58
57
|
|
@@ -121,27 +120,30 @@ class ForecastOperatorBaseModel(ABC):
|
|
121
120
|
|
122
121
|
# Generate metrics
|
123
122
|
summary_metrics = None
|
124
|
-
test_data =
|
123
|
+
test_data = self.datasets.test_data
|
125
124
|
self.eval_metrics = None
|
126
125
|
|
127
126
|
if self.spec.generate_report or self.spec.generate_metrics:
|
128
127
|
self.eval_metrics = self.generate_train_metrics()
|
129
128
|
if not self.target_cat_col:
|
130
|
-
self.eval_metrics.rename(
|
131
|
-
|
129
|
+
self.eval_metrics.rename(
|
130
|
+
{"Series 1": self.original_target_column}, axis=1, inplace=True
|
131
|
+
)
|
132
132
|
|
133
|
-
if self.
|
133
|
+
if self.datasets.test_data is not None:
|
134
134
|
try:
|
135
135
|
(
|
136
136
|
self.test_eval_metrics,
|
137
|
-
summary_metrics
|
138
|
-
test_data,
|
137
|
+
summary_metrics
|
139
138
|
) = self._test_evaluate_metrics(
|
140
139
|
elapsed_time=elapsed_time,
|
141
140
|
)
|
142
141
|
if not self.target_cat_col:
|
143
|
-
self.test_eval_metrics.rename(
|
144
|
-
|
142
|
+
self.test_eval_metrics.rename(
|
143
|
+
{"Series 1": self.original_target_column},
|
144
|
+
axis=1,
|
145
|
+
inplace=True,
|
146
|
+
)
|
145
147
|
except Exception:
|
146
148
|
logger.warn("Unable to generate Test Metrics.")
|
147
149
|
logger.debug(f"Full Traceback: {traceback.format_exc()}")
|
@@ -223,17 +225,23 @@ class ForecastOperatorBaseModel(ABC):
|
|
223
225
|
rc.Block(
|
224
226
|
first_10_title,
|
225
227
|
# series_subtext,
|
226
|
-
rc.Select(blocks=first_5_rows_blocks)
|
228
|
+
rc.Select(blocks=first_5_rows_blocks)
|
229
|
+
if self.target_cat_col
|
230
|
+
else first_5_rows_blocks[0],
|
227
231
|
),
|
228
232
|
rc.Block(
|
229
233
|
last_10_title,
|
230
234
|
# series_subtext,
|
231
|
-
rc.Select(blocks=last_5_rows_blocks)
|
235
|
+
rc.Select(blocks=last_5_rows_blocks)
|
236
|
+
if self.target_cat_col
|
237
|
+
else last_5_rows_blocks[0],
|
232
238
|
),
|
233
239
|
rc.Block(
|
234
240
|
summary_title,
|
235
241
|
# series_subtext,
|
236
|
-
rc.Select(blocks=data_summary_blocks)
|
242
|
+
rc.Select(blocks=data_summary_blocks)
|
243
|
+
if self.target_cat_col
|
244
|
+
else data_summary_blocks[0],
|
237
245
|
),
|
238
246
|
rc.Separator(),
|
239
247
|
)
|
@@ -308,7 +316,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
308
316
|
horizon=self.spec.horizon,
|
309
317
|
test_data=test_data,
|
310
318
|
ci_interval_width=self.spec.confidence_interval_width,
|
311
|
-
target_category_column=self.target_cat_col
|
319
|
+
target_category_column=self.target_cat_col,
|
312
320
|
)
|
313
321
|
if (
|
314
322
|
series_name is not None
|
@@ -341,17 +349,18 @@ class ForecastOperatorBaseModel(ABC):
|
|
341
349
|
)
|
342
350
|
|
343
351
|
# save the report and result CSV
|
344
|
-
self._save_report(
|
352
|
+
return self._save_report(
|
345
353
|
report_sections=report_sections,
|
346
354
|
result_df=result_df,
|
347
355
|
metrics_df=self.eval_metrics,
|
348
356
|
test_metrics_df=self.test_eval_metrics,
|
357
|
+
test_data=test_data,
|
349
358
|
)
|
350
359
|
|
351
360
|
def _test_evaluate_metrics(self, elapsed_time=0):
|
352
361
|
total_metrics = pd.DataFrame()
|
353
362
|
summary_metrics = pd.DataFrame()
|
354
|
-
data =
|
363
|
+
data = self.datasets.test_data
|
355
364
|
|
356
365
|
# Generate y_pred and y_true for each series
|
357
366
|
for s_id in self.forecast_output.list_series_ids():
|
@@ -388,7 +397,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
388
397
|
total_metrics = pd.concat([total_metrics, metrics_df], axis=1)
|
389
398
|
|
390
399
|
if total_metrics.empty:
|
391
|
-
return total_metrics, summary_metrics
|
400
|
+
return total_metrics, summary_metrics
|
392
401
|
|
393
402
|
summary_metrics = pd.DataFrame(
|
394
403
|
{
|
@@ -454,7 +463,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
454
463
|
]
|
455
464
|
summary_metrics = summary_metrics[new_column_order]
|
456
465
|
|
457
|
-
return total_metrics, summary_metrics
|
466
|
+
return total_metrics, summary_metrics
|
458
467
|
|
459
468
|
def _save_report(
|
460
469
|
self,
|
@@ -462,10 +471,12 @@ class ForecastOperatorBaseModel(ABC):
|
|
462
471
|
result_df: pd.DataFrame,
|
463
472
|
metrics_df: pd.DataFrame,
|
464
473
|
test_metrics_df: pd.DataFrame,
|
474
|
+
test_data: pd.DataFrame,
|
465
475
|
):
|
466
476
|
"""Saves resulting reports to the given folder."""
|
467
477
|
|
468
478
|
unique_output_dir = self.spec.output_directory.url
|
479
|
+
results = ForecastResults()
|
469
480
|
|
470
481
|
if ObjectStorageDetails.is_oci_path(unique_output_dir):
|
471
482
|
storage_options = default_signer()
|
@@ -491,13 +502,23 @@ class ForecastOperatorBaseModel(ABC):
|
|
491
502
|
f2.write(f1.read())
|
492
503
|
|
493
504
|
# forecast csv report
|
494
|
-
|
505
|
+
# todo: add test data into forecast.csv
|
506
|
+
# if self.spec.test_data is not None:
|
507
|
+
# test_data_dict = test_data.get_dict_by_series()
|
508
|
+
# for series_id, test_data_values in test_data_dict.items():
|
509
|
+
# result_df[DataColumns.Series] = test_data_values[]
|
510
|
+
result_df = (
|
511
|
+
result_df
|
512
|
+
if self.target_cat_col
|
513
|
+
else result_df.drop(DataColumns.Series, axis=1)
|
514
|
+
)
|
495
515
|
write_data(
|
496
516
|
data=result_df,
|
497
517
|
filename=os.path.join(unique_output_dir, self.spec.forecast_filename),
|
498
518
|
format="csv",
|
499
519
|
storage_options=storage_options,
|
500
520
|
)
|
521
|
+
results.set_forecast(result_df)
|
501
522
|
|
502
523
|
# metrics csv report
|
503
524
|
if self.spec.generate_metrics:
|
@@ -507,10 +528,11 @@ class ForecastOperatorBaseModel(ABC):
|
|
507
528
|
else "Series 1"
|
508
529
|
)
|
509
530
|
if metrics_df is not None:
|
531
|
+
metrics_df_formatted = metrics_df.reset_index().rename(
|
532
|
+
{"index": "metrics", "Series 1": metrics_col_name}, axis=1
|
533
|
+
)
|
510
534
|
write_data(
|
511
|
-
data=
|
512
|
-
{"index": "metrics", "Series 1": metrics_col_name}, axis=1
|
513
|
-
),
|
535
|
+
data=metrics_df_formatted,
|
514
536
|
filename=os.path.join(
|
515
537
|
unique_output_dir, self.spec.metrics_filename
|
516
538
|
),
|
@@ -518,18 +540,20 @@ class ForecastOperatorBaseModel(ABC):
|
|
518
540
|
storage_options=storage_options,
|
519
541
|
index=False,
|
520
542
|
)
|
543
|
+
results.set_metrics(metrics_df_formatted)
|
521
544
|
else:
|
522
545
|
logger.warn(
|
523
546
|
f"Attempted to generate the {self.spec.metrics_filename} file with the training metrics, however the training metrics could not be properly generated."
|
524
547
|
)
|
525
548
|
|
526
549
|
# test_metrics csv report
|
527
|
-
if self.
|
550
|
+
if self.datasets.test_data is not None:
|
528
551
|
if test_metrics_df is not None:
|
552
|
+
test_metrics_df_formatted = test_metrics_df.reset_index().rename(
|
553
|
+
{"index": "metrics", "Series 1": metrics_col_name}, axis=1
|
554
|
+
)
|
529
555
|
write_data(
|
530
|
-
data=
|
531
|
-
{"index": "metrics", "Series 1": metrics_col_name}, axis=1
|
532
|
-
),
|
556
|
+
data=test_metrics_df_formatted,
|
533
557
|
filename=os.path.join(
|
534
558
|
unique_output_dir, self.spec.test_metrics_filename
|
535
559
|
),
|
@@ -537,6 +561,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
537
561
|
storage_options=storage_options,
|
538
562
|
index=False,
|
539
563
|
)
|
564
|
+
results.set_test_metrics(test_metrics_df_formatted)
|
540
565
|
else:
|
541
566
|
logger.warn(
|
542
567
|
f"Attempted to generate the {self.spec.test_metrics_filename} file with the test metrics, however the test metrics could not be properly generated."
|
@@ -544,7 +569,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
544
569
|
# explanations csv reports
|
545
570
|
if self.spec.generate_explanations:
|
546
571
|
try:
|
547
|
-
if self.formatted_global_explanation
|
572
|
+
if not self.formatted_global_explanation.empty:
|
548
573
|
write_data(
|
549
574
|
data=self.formatted_global_explanation,
|
550
575
|
filename=os.path.join(
|
@@ -554,12 +579,13 @@ class ForecastOperatorBaseModel(ABC):
|
|
554
579
|
storage_options=storage_options,
|
555
580
|
index=True,
|
556
581
|
)
|
582
|
+
results.set_global_explanations(self.formatted_global_explanation)
|
557
583
|
else:
|
558
584
|
logger.warn(
|
559
585
|
f"Attempted to generate global explanations for the {self.spec.global_explanation_filename} file, but an issue occured in formatting the explanations."
|
560
586
|
)
|
561
587
|
|
562
|
-
if self.formatted_local_explanation
|
588
|
+
if not self.formatted_local_explanation.empty:
|
563
589
|
write_data(
|
564
590
|
data=self.formatted_local_explanation,
|
565
591
|
filename=os.path.join(
|
@@ -569,6 +595,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
569
595
|
storage_options=storage_options,
|
570
596
|
index=True,
|
571
597
|
)
|
598
|
+
results.set_local_explanations(self.formatted_local_explanation)
|
572
599
|
else:
|
573
600
|
logger.warn(
|
574
601
|
f"Attempted to generate local explanations for the {self.spec.local_explanation_filename} file, but an issue occured in formatting the explanations."
|
@@ -589,10 +616,12 @@ class ForecastOperatorBaseModel(ABC):
|
|
589
616
|
index=True,
|
590
617
|
indent=4,
|
591
618
|
)
|
619
|
+
results.set_model_parameters(self.model_parameters)
|
592
620
|
|
593
621
|
# model pickle
|
594
622
|
if self.spec.generate_model_pickle:
|
595
623
|
self._save_model(unique_output_dir, storage_options)
|
624
|
+
results.set_models(self.models)
|
596
625
|
|
597
626
|
logger.info(
|
598
627
|
f"The outputs have been successfully "
|
@@ -612,8 +641,10 @@ class ForecastOperatorBaseModel(ABC):
|
|
612
641
|
index=True,
|
613
642
|
indent=4,
|
614
643
|
)
|
644
|
+
results.set_errors_dict(self.errors_dict)
|
615
645
|
else:
|
616
646
|
logger.info("All modeling completed successfully.")
|
647
|
+
return results
|
617
648
|
|
618
649
|
def preprocess(self, df, series_id):
|
619
650
|
"""The method that needs to be implemented on the particular model level."""
|
@@ -667,7 +698,10 @@ class ForecastOperatorBaseModel(ABC):
|
|
667
698
|
)
|
668
699
|
|
669
700
|
def _validate_automlx_explanation_mode(self):
|
670
|
-
if
|
701
|
+
if (
|
702
|
+
self.spec.model != SupportedModels.AutoMLX
|
703
|
+
and self.spec.explanations_accuracy_mode == SpeedAccuracyMode.AUTOMLX
|
704
|
+
):
|
671
705
|
raise ValueError(
|
672
706
|
"AUTOMLX explanation accuracy mode is only supported for AutoMLX models. "
|
673
707
|
"Please select mode other than AUTOMLX from the available explanations_accuracy_mode options"
|
@@ -738,14 +772,6 @@ class ForecastOperatorBaseModel(ABC):
|
|
738
772
|
logger.warn(
|
739
773
|
"No explanations generated. Ensure that additional data has been provided."
|
740
774
|
)
|
741
|
-
elif (
|
742
|
-
self.spec.model == SupportedModels.AutoMLX
|
743
|
-
and self.spec.explanations_accuracy_mode
|
744
|
-
== SpeedAccuracyMode.AUTOMLX
|
745
|
-
):
|
746
|
-
logger.warning(
|
747
|
-
"Global explanations not available for AutoMLX models with inherent explainability"
|
748
|
-
)
|
749
775
|
else:
|
750
776
|
self.global_explanation[s_id] = dict(
|
751
777
|
zip(
|
@@ -794,7 +820,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
794
820
|
def get_explain_predict_fn(self, series_id, fcst_col_name="yhat"):
|
795
821
|
def _custom_predict(
|
796
822
|
data,
|
797
|
-
model=self.models[series_id],
|
823
|
+
model=self.models[series_id]["model"],
|
798
824
|
dt_column_name=self.datasets._datetime_column_name,
|
799
825
|
):
|
800
826
|
"""
|
@@ -1,8 +1,10 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
|
3
|
-
# Copyright (c) 2023,
|
3
|
+
# Copyright (c) 2023, 2025 Oracle and/or its affiliates.
|
4
4
|
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
|
5
5
|
|
6
|
+
from typing import Dict, List
|
7
|
+
|
6
8
|
import pandas as pd
|
7
9
|
|
8
10
|
from ads.opctl import logger
|
@@ -21,8 +23,8 @@ from ..operator_config import ForecastOperatorConfig
|
|
21
23
|
|
22
24
|
|
23
25
|
class HistoricalData(AbstractData):
|
24
|
-
def __init__(self, spec
|
25
|
-
super().__init__(spec=spec, name="historical_data")
|
26
|
+
def __init__(self, spec, historical_data = None):
|
27
|
+
super().__init__(spec=spec, name="historical_data", data=historical_data)
|
26
28
|
|
27
29
|
def _ingest_data(self, spec):
|
28
30
|
try:
|
@@ -50,8 +52,11 @@ class HistoricalData(AbstractData):
|
|
50
52
|
|
51
53
|
|
52
54
|
class AdditionalData(AbstractData):
|
53
|
-
def __init__(self, spec, historical_data):
|
54
|
-
if
|
55
|
+
def __init__(self, spec, historical_data, additional_data=None):
|
56
|
+
if additional_data is not None:
|
57
|
+
super().__init__(spec=spec, name="additional_data", data=additional_data)
|
58
|
+
self.additional_regressors = list(self.data.columns)
|
59
|
+
elif spec.additional_data is not None:
|
55
60
|
super().__init__(spec=spec, name="additional_data")
|
56
61
|
add_dates = self.data.index.get_level_values(0).unique().tolist()
|
57
62
|
add_dates.sort()
|
@@ -108,14 +113,15 @@ class AdditionalData(AbstractData):
|
|
108
113
|
|
109
114
|
|
110
115
|
class TestData(AbstractData):
|
111
|
-
def __init__(self, spec):
|
112
|
-
|
116
|
+
def __init__(self, spec, test_data):
|
117
|
+
if test_data is not None or spec.test_data is not None:
|
118
|
+
super().__init__(spec=spec, name="test_data", data=test_data)
|
113
119
|
self.dt_column_name = spec.datetime_column.name
|
114
120
|
self.target_name = spec.target_column
|
115
121
|
|
116
122
|
|
117
123
|
class ForecastDatasets:
|
118
|
-
def __init__(self, config: ForecastOperatorConfig):
|
124
|
+
def __init__(self, config: ForecastOperatorConfig, historical_data=None, additional_data=None, test_data=None):
|
119
125
|
"""Instantiates the DataIO instance.
|
120
126
|
|
121
127
|
Properties
|
@@ -125,11 +131,15 @@ class ForecastDatasets:
|
|
125
131
|
"""
|
126
132
|
self.historical_data: HistoricalData = None
|
127
133
|
self.additional_data: AdditionalData = None
|
128
|
-
|
129
134
|
self._horizon = config.spec.horizon
|
130
135
|
self._datetime_column_name = config.spec.datetime_column.name
|
131
136
|
self._target_col = config.spec.target_column
|
132
|
-
|
137
|
+
if historical_data is not None:
|
138
|
+
self.historical_data = HistoricalData(config.spec, historical_data)
|
139
|
+
self.additional_data = AdditionalData(config.spec, self.historical_data, additional_data)
|
140
|
+
else:
|
141
|
+
self._load_data(config.spec)
|
142
|
+
self.test_data = TestData(config.spec, test_data)
|
133
143
|
|
134
144
|
def _load_data(self, spec):
|
135
145
|
"""Loads forecasting input data."""
|
@@ -167,7 +177,7 @@ class ForecastDatasets:
|
|
167
177
|
self.historical_data.data,
|
168
178
|
self.additional_data.data,
|
169
179
|
],
|
170
|
-
axis=1
|
180
|
+
axis=1,
|
171
181
|
)
|
172
182
|
|
173
183
|
def get_data_by_series(self, include_horizon=True):
|
@@ -198,7 +208,7 @@ class ForecastDatasets:
|
|
198
208
|
return self.get_data_at_series(s_id)[-self._horizon :]
|
199
209
|
|
200
210
|
def has_artificial_series(self):
|
201
|
-
return self.historical_data.
|
211
|
+
return bool(self.historical_data.spec.target_category_columns)
|
202
212
|
|
203
213
|
def get_earliest_timestamp(self):
|
204
214
|
return self.historical_data.get_min_time()
|
@@ -249,7 +259,7 @@ class ForecastOutput:
|
|
249
259
|
target_column: str,
|
250
260
|
dt_column: str,
|
251
261
|
):
|
252
|
-
"""Forecast Output contains all
|
262
|
+
"""Forecast Output contains all the details required to generate the forecast.csv output file.
|
253
263
|
|
254
264
|
init
|
255
265
|
-------
|
@@ -416,3 +426,59 @@ class ForecastOutput:
|
|
416
426
|
for df in self.series_id_map.values():
|
417
427
|
output = pd.concat([output, df])
|
418
428
|
return output.reset_index(drop=True)
|
429
|
+
|
430
|
+
|
431
|
+
class ForecastResults:
|
432
|
+
"""
|
433
|
+
Forecast Results contains all outputs from the forecast run.
|
434
|
+
This class is returned to users who use the Forecast's `operate` method.
|
435
|
+
|
436
|
+
"""
|
437
|
+
|
438
|
+
def set_forecast(self, df: pd.DataFrame):
|
439
|
+
self.forecast = df
|
440
|
+
|
441
|
+
def get_forecast(self):
|
442
|
+
return getattr(self, "forecast", None)
|
443
|
+
|
444
|
+
def set_metrics(self, df: pd.DataFrame):
|
445
|
+
self.metrics = df
|
446
|
+
|
447
|
+
def get_metrics(self):
|
448
|
+
return getattr(self, "metrics", None)
|
449
|
+
|
450
|
+
def set_test_metrics(self, df: pd.DataFrame):
|
451
|
+
self.test_metrics = df
|
452
|
+
|
453
|
+
def get_test_metrics(self):
|
454
|
+
return getattr(self, "test_metrics", None)
|
455
|
+
|
456
|
+
def set_local_explanations(self, df: pd.DataFrame):
|
457
|
+
self.local_explanations = df
|
458
|
+
|
459
|
+
def get_local_explanations(self):
|
460
|
+
return getattr(self, "local_explanations", None)
|
461
|
+
|
462
|
+
def set_global_explanations(self, df: pd.DataFrame):
|
463
|
+
self.global_explanations = df
|
464
|
+
|
465
|
+
def get_global_explanations(self):
|
466
|
+
return getattr(self, "global_explanations", None)
|
467
|
+
|
468
|
+
def set_model_parameters(self, df: pd.DataFrame):
|
469
|
+
self.model_parameters = df
|
470
|
+
|
471
|
+
def get_model_parameters(self):
|
472
|
+
return getattr(self, "model_parameters", None)
|
473
|
+
|
474
|
+
def set_models(self, models: List):
|
475
|
+
self.models = models
|
476
|
+
|
477
|
+
def get_models(self):
|
478
|
+
return getattr(self, "models", None)
|
479
|
+
|
480
|
+
def set_errors_dict(self, errors_dict: Dict):
|
481
|
+
self.errors_dict = errors_dict
|
482
|
+
|
483
|
+
def get_errors_dict(self):
|
484
|
+
return getattr(self, "errors_dict", None)
|
@@ -172,8 +172,10 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
172
172
|
).values,
|
173
173
|
)
|
174
174
|
|
175
|
-
self.models[s_id] = model
|
176
175
|
self.trainers[s_id] = model.trainer
|
176
|
+
self.models[s_id] = {}
|
177
|
+
self.models[s_id]["model"] = model
|
178
|
+
self.models[s_id]["le"] = self.le[s_id]
|
177
179
|
|
178
180
|
self.model_parameters[s_id] = {
|
179
181
|
"framework": SupportedModels.NeuralProphet,
|
@@ -355,7 +357,8 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
355
357
|
|
356
358
|
sec5_text = rc.Heading("Neural Prophet Model Parameters", level=2)
|
357
359
|
model_states = []
|
358
|
-
for s_id,
|
360
|
+
for s_id, artifacts in self.models.items():
|
361
|
+
m = artifacts["model"]
|
359
362
|
model_states.append(
|
360
363
|
pd.Series(
|
361
364
|
m.state_dict(),
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
|
3
|
-
# Copyright (c) 2024 Oracle and/or its affiliates.
|
3
|
+
# Copyright (c) 2024, 2025 Oracle and/or its affiliates.
|
4
4
|
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
|
5
5
|
|
6
6
|
import logging
|
@@ -43,7 +43,11 @@ def _add_unit(num, unit):
|
|
43
43
|
def _fit_model(data, params, additional_regressors):
|
44
44
|
from prophet import Prophet
|
45
45
|
|
46
|
+
monthly_seasonality = params.pop("monthly_seasonality", False)
|
46
47
|
model = Prophet(**params)
|
48
|
+
if monthly_seasonality:
|
49
|
+
model.add_seasonality(name="monthly", period=30.5, fourier_order=5)
|
50
|
+
params["monthly_seasonality"] = monthly_seasonality
|
47
51
|
for add_reg in additional_regressors:
|
48
52
|
model.add_regressor(add_reg)
|
49
53
|
model.fit(data)
|
@@ -108,7 +112,10 @@ class ProphetOperatorModel(ForecastOperatorBaseModel):
|
|
108
112
|
upper_bound=self.get_horizon(forecast["yhat_upper"]).values,
|
109
113
|
lower_bound=self.get_horizon(forecast["yhat_lower"]).values,
|
110
114
|
)
|
111
|
-
|
115
|
+
|
116
|
+
self.models[series_id] = {}
|
117
|
+
self.models[series_id]["model"] = model
|
118
|
+
self.models[series_id]["le"] = self.le[series_id]
|
112
119
|
|
113
120
|
params = vars(model).copy()
|
114
121
|
for param in ["history", "history_dates", "stan_fit"]:
|
@@ -252,11 +259,11 @@ class ProphetOperatorModel(ForecastOperatorBaseModel):
|
|
252
259
|
all_sections = []
|
253
260
|
if len(series_ids) > 0:
|
254
261
|
sec1 = _select_plot_list(
|
255
|
-
lambda s_id: self.models[s_id].plot(
|
262
|
+
lambda s_id: self.models[s_id]["model"].plot(
|
256
263
|
self.outputs[s_id], include_legend=True
|
257
264
|
),
|
258
265
|
series_ids=series_ids,
|
259
|
-
target_category_column=self.target_cat_col
|
266
|
+
target_category_column=self.target_cat_col,
|
260
267
|
)
|
261
268
|
section_1 = rc.Block(
|
262
269
|
rc.Heading("Forecast Overview", level=2),
|
@@ -267,25 +274,25 @@ class ProphetOperatorModel(ForecastOperatorBaseModel):
|
|
267
274
|
)
|
268
275
|
|
269
276
|
sec2 = _select_plot_list(
|
270
|
-
lambda s_id: self.models[s_id].plot_components(self.outputs[s_id]),
|
277
|
+
lambda s_id: self.models[s_id]["model"].plot_components(self.outputs[s_id]),
|
271
278
|
series_ids=series_ids,
|
272
|
-
target_category_column=self.target_cat_col
|
279
|
+
target_category_column=self.target_cat_col,
|
273
280
|
)
|
274
281
|
section_2 = rc.Block(
|
275
282
|
rc.Heading("Forecast Broken Down by Trend Component", level=2), sec2
|
276
283
|
)
|
277
284
|
|
278
285
|
sec3_figs = {
|
279
|
-
s_id: self.models[s_id].plot(self.outputs[s_id]) for s_id in series_ids
|
286
|
+
s_id: self.models[s_id]["model"].plot(self.outputs[s_id]) for s_id in series_ids
|
280
287
|
}
|
281
288
|
for s_id in series_ids:
|
282
289
|
add_changepoints_to_plot(
|
283
|
-
sec3_figs[s_id].gca(), self.models[s_id], self.outputs[s_id]
|
290
|
+
sec3_figs[s_id].gca(), self.models[s_id]["model"], self.outputs[s_id]
|
284
291
|
)
|
285
292
|
sec3 = _select_plot_list(
|
286
293
|
lambda s_id: sec3_figs[s_id],
|
287
294
|
series_ids=series_ids,
|
288
|
-
target_category_column=self.target_cat_col
|
295
|
+
target_category_column=self.target_cat_col,
|
289
296
|
)
|
290
297
|
section_3 = rc.Block(rc.Heading("Forecast Changepoints", level=2), sec3)
|
291
298
|
|
@@ -294,12 +301,14 @@ class ProphetOperatorModel(ForecastOperatorBaseModel):
|
|
294
301
|
sec5_text = rc.Heading("Prophet Model Seasonality Components", level=2)
|
295
302
|
model_states = []
|
296
303
|
for s_id in series_ids:
|
297
|
-
m = self.models[s_id]
|
304
|
+
m = self.models[s_id]["model"]
|
298
305
|
model_states.append(
|
299
306
|
pd.Series(
|
300
307
|
m.seasonalities,
|
301
308
|
index=pd.Index(m.seasonalities.keys(), dtype="object"),
|
302
|
-
name=s_id
|
309
|
+
name=s_id
|
310
|
+
if self.target_cat_col
|
311
|
+
else self.original_target_column,
|
303
312
|
dtype="object",
|
304
313
|
)
|
305
314
|
)
|
@@ -330,11 +339,15 @@ class ProphetOperatorModel(ForecastOperatorBaseModel):
|
|
330
339
|
self.formatted_local_explanation = aggregate_local_explanations
|
331
340
|
|
332
341
|
if not self.target_cat_col:
|
333
|
-
self.formatted_global_explanation =
|
334
|
-
|
335
|
-
|
342
|
+
self.formatted_global_explanation = (
|
343
|
+
self.formatted_global_explanation.rename(
|
344
|
+
{"Series 1": self.original_target_column},
|
345
|
+
axis=1,
|
346
|
+
)
|
347
|
+
)
|
348
|
+
self.formatted_local_explanation.drop(
|
349
|
+
"Series", axis=1, inplace=True
|
336
350
|
)
|
337
|
-
self.formatted_local_explanation.drop("Series", axis=1, inplace=True)
|
338
351
|
|
339
352
|
# Create a markdown section for the global explainability
|
340
353
|
global_explanation_section = rc.Block(
|
@@ -91,19 +91,13 @@ class ModelEvaluator:
|
|
91
91
|
output_dir = operator_config.spec.output_directory.url
|
92
92
|
output_file_path = f'{output_dir}/back_testing/{model}/{backtest}'
|
93
93
|
Path(output_file_path).mkdir(parents=True, exist_ok=True)
|
94
|
-
historical_data_url = f'{output_file_path}/historical.csv'
|
95
|
-
additional_data_url = f'{output_file_path}/additional.csv'
|
96
|
-
test_data_url = f'{output_file_path}/test.csv'
|
97
|
-
historical_data.to_csv(historical_data_url, index=False)
|
98
|
-
additional_data.to_csv(additional_data_url, index=False)
|
99
|
-
test_data.to_csv(test_data_url, index=False)
|
100
94
|
backtest_op_config_draft = operator_config.to_dict()
|
101
95
|
backtest_spec = backtest_op_config_draft["spec"]
|
102
|
-
backtest_spec["
|
103
|
-
|
104
|
-
|
105
|
-
backtest_spec
|
106
|
-
backtest_spec["
|
96
|
+
backtest_spec["datetime_column"]["format"] = None
|
97
|
+
backtest_spec.pop("test_data")
|
98
|
+
backtest_spec.pop("additional_data")
|
99
|
+
backtest_spec.pop("historical_data")
|
100
|
+
backtest_spec["generate_report"] = False
|
107
101
|
backtest_spec["model"] = model
|
108
102
|
backtest_spec['model_kwargs'] = None
|
109
103
|
backtest_spec["output_directory"] = {"url": output_file_path}
|
@@ -118,19 +112,23 @@ class ModelEvaluator:
|
|
118
112
|
def run_all_models(self, datasets: ForecastDatasets, operator_config: ForecastOperatorConfig):
|
119
113
|
cut_offs, train_sets, additional_data, test_sets = self.generate_k_fold_data(datasets, operator_config)
|
120
114
|
metrics = {}
|
115
|
+
date_col = operator_config.spec.datetime_column.name
|
121
116
|
for model in self.models:
|
122
117
|
from .model.factory import ForecastOperatorModelFactory
|
123
118
|
metrics[model] = {}
|
124
119
|
for i in range(len(cut_offs)):
|
125
120
|
try:
|
126
|
-
backtest_historical_data = train_sets[i]
|
127
|
-
backtest_additional_data = additional_data[i]
|
128
|
-
backtest_test_data = test_sets[i]
|
121
|
+
backtest_historical_data = train_sets[i].set_index([date_col, DataColumns.Series])
|
122
|
+
backtest_additional_data = additional_data[i].set_index([date_col, DataColumns.Series])
|
123
|
+
backtest_test_data = test_sets[i].set_index([date_col, DataColumns.Series])
|
129
124
|
backtest_operator_config = self.create_operator_config(operator_config, i, model,
|
130
125
|
backtest_historical_data,
|
131
126
|
backtest_additional_data,
|
132
127
|
backtest_test_data)
|
133
|
-
datasets = ForecastDatasets(backtest_operator_config
|
128
|
+
datasets = ForecastDatasets(backtest_operator_config,
|
129
|
+
backtest_historical_data,
|
130
|
+
backtest_additional_data,
|
131
|
+
backtest_test_data)
|
134
132
|
ForecastOperatorModelFactory.get_model(
|
135
133
|
backtest_operator_config, datasets
|
136
134
|
).generate_report()
|