oracle-ads 2.11.6__py3-none-any.whl → 2.11.7__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/catalog/model.py +3 -3
- ads/catalog/notebook.py +3 -3
- ads/catalog/project.py +2 -2
- ads/catalog/summary.py +2 -4
- ads/cli.py +2 -1
- ads/common/serializer.py +1 -1
- ads/data_labeling/metadata.py +2 -2
- ads/dataset/dataset.py +3 -5
- ads/dataset/factory.py +2 -3
- ads/dataset/label_encoder.py +1 -1
- ads/dataset/sampled_dataset.py +3 -5
- ads/jobs/ads_job.py +26 -2
- ads/jobs/builders/infrastructure/dsc_job.py +20 -7
- ads/model/model_artifact_boilerplate/artifact_introspection_test/model_artifact_validate.py +1 -1
- ads/opctl/operator/lowcode/anomaly/model/anomaly_dataset.py +8 -15
- ads/opctl/operator/lowcode/anomaly/model/automlx.py +2 -1
- ads/opctl/operator/lowcode/anomaly/model/base_model.py +2 -2
- ads/opctl/operator/lowcode/anomaly/operator_config.py +18 -1
- ads/opctl/operator/lowcode/anomaly/schema.yaml +16 -4
- ads/opctl/operator/lowcode/common/data.py +16 -2
- ads/opctl/operator/lowcode/common/transformations.py +48 -14
- ads/opctl/operator/lowcode/forecast/environment.yaml +1 -0
- ads/opctl/operator/lowcode/forecast/model/arima.py +21 -12
- ads/opctl/operator/lowcode/forecast/model/automlx.py +79 -72
- ads/opctl/operator/lowcode/forecast/model/autots.py +182 -164
- ads/opctl/operator/lowcode/forecast/model/base_model.py +59 -41
- ads/opctl/operator/lowcode/forecast/model/neuralprophet.py +47 -47
- ads/opctl/operator/lowcode/forecast/model/prophet.py +48 -48
- ads/opctl/operator/lowcode/forecast/operator_config.py +18 -2
- ads/opctl/operator/lowcode/forecast/schema.yaml +20 -4
- ads/opctl/operator/lowcode/forecast/utils.py +4 -0
- ads/pipeline/ads_pipeline_step.py +11 -12
- {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/METADATA +4 -3
- {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/RECORD +37 -37
- {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/LICENSE.txt +0 -0
- {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/WHEEL +0 -0
- {oracle_ads-2.11.6.dist-info → oracle_ads-2.11.7.dist-info}/entry_points.txt +0 -0
@@ -54,145 +54,154 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
|
|
54
54
|
target_column=self.original_target_column,
|
55
55
|
dt_column=self.spec.datetime_column.name,
|
56
56
|
)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
full_data_indexed
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if len(additional_regressors) > 1:
|
134
|
-
future_regressor = full_data_indexed.reset_index().pivot(
|
135
|
-
index=self.spec.datetime_column.name,
|
136
|
-
columns=ForecastOutputColumns.SERIES,
|
137
|
-
values=additional_regressors,
|
138
|
-
)
|
139
|
-
future_reg = future_regressor[: -self.spec.horizon]
|
140
|
-
regr_fcst = future_regressor[-self.spec.horizon :]
|
141
|
-
else:
|
142
|
-
future_reg = None
|
143
|
-
regr_fcst = None
|
144
|
-
|
145
|
-
if self.loaded_models is None:
|
146
|
-
model = model.fit(
|
147
|
-
df_train,
|
148
|
-
future_regressor=future_reg,
|
149
|
-
date_col=self.spec.datetime_column.name,
|
150
|
-
value_col=self.original_target_column,
|
151
|
-
id_col=ForecastOutputColumns.SERIES,
|
152
|
-
)
|
153
|
-
# Store the trained model and generate forecasts
|
154
|
-
self.models = copy.deepcopy(model)
|
155
|
-
else:
|
156
|
-
self.models = self.loaded_models
|
157
|
-
|
158
|
-
self.outputs = model.predict(future_regressor=regr_fcst)
|
159
|
-
logger.debug("===========Forecast Generated===========")
|
160
|
-
|
161
|
-
hist_df = model.back_forecast().forecast
|
162
|
-
|
163
|
-
params = vars(model).copy()
|
164
|
-
for param in [
|
165
|
-
"ens_copy",
|
166
|
-
"df_wide_numeric",
|
167
|
-
"future_regressor_train",
|
168
|
-
"initial_results",
|
169
|
-
"score_per_series",
|
170
|
-
"validation_results",
|
171
|
-
"validation_train_indexes",
|
172
|
-
"validation_test_indexes",
|
173
|
-
"validation_indexes",
|
174
|
-
"best_model",
|
175
|
-
]:
|
176
|
-
if param in params:
|
177
|
-
params.pop(param)
|
178
|
-
|
179
|
-
for s_id in self.datasets.list_series_ids():
|
180
|
-
self.forecast_output.init_series_output(
|
181
|
-
series_id=s_id, data_at_series=self.datasets.get_data_at_series(s_id)
|
182
|
-
)
|
183
|
-
|
184
|
-
self.forecast_output.populate_series_output(
|
185
|
-
series_id=s_id,
|
186
|
-
fit_val=hist_df[s_id].values,
|
187
|
-
forecast_val=self.outputs.forecast[s_id].values,
|
188
|
-
upper_bound=self.outputs.upper_forecast[s_id].values,
|
189
|
-
lower_bound=self.outputs.lower_forecast[s_id].values,
|
57
|
+
try:
|
58
|
+
model = self.loaded_models if self.loaded_models is not None else None
|
59
|
+
if model is None:
|
60
|
+
# Initialize the AutoTS model with specified parameters
|
61
|
+
model = AutoTS(
|
62
|
+
forecast_length=self.spec.horizon,
|
63
|
+
frequency=self.spec.model_kwargs.get(
|
64
|
+
"frequency", "infer"
|
65
|
+
), # TODO: Use datasets.get_datetime_frequency ?
|
66
|
+
prediction_interval=self.spec.confidence_interval_width,
|
67
|
+
max_generations=self.spec.model_kwargs.get(
|
68
|
+
"max_generations", AUTOTS_MAX_GENERATION
|
69
|
+
),
|
70
|
+
no_negatives=self.spec.model_kwargs.get("no_negatives", False),
|
71
|
+
constraint=self.spec.model_kwargs.get("constraint", None),
|
72
|
+
ensemble=self.spec.model_kwargs.get("ensemble", "auto"),
|
73
|
+
initial_template=self.spec.model_kwargs.get(
|
74
|
+
"initial_template", "General+Random"
|
75
|
+
),
|
76
|
+
random_seed=self.spec.model_kwargs.get("random_seed", 2022),
|
77
|
+
holiday_country=self.spec.model_kwargs.get("holiday_country", "US"),
|
78
|
+
subset=self.spec.model_kwargs.get("subset", None),
|
79
|
+
aggfunc=self.spec.model_kwargs.get("aggfunc", "first"),
|
80
|
+
na_tolerance=self.spec.model_kwargs.get("na_tolerance", 1),
|
81
|
+
drop_most_recent=self.spec.model_kwargs.get("drop_most_recent", 0),
|
82
|
+
drop_data_older_than_periods=self.spec.model_kwargs.get(
|
83
|
+
"drop_data_older_than_periods", None
|
84
|
+
),
|
85
|
+
model_list=self.spec.model_kwargs.get("model_list", "fast_parallel"),
|
86
|
+
transformer_list=self.spec.model_kwargs.get("transformer_list", "auto"),
|
87
|
+
transformer_max_depth=self.spec.model_kwargs.get(
|
88
|
+
"transformer_max_depth", 6
|
89
|
+
),
|
90
|
+
models_mode=self.spec.model_kwargs.get("models_mode", "random"),
|
91
|
+
num_validations=self.spec.model_kwargs.get("num_validations", "auto"),
|
92
|
+
models_to_validate=self.spec.model_kwargs.get(
|
93
|
+
"models_to_validate", AUTOTS_MODELS_TO_VALIDATE
|
94
|
+
),
|
95
|
+
max_per_model_class=self.spec.model_kwargs.get(
|
96
|
+
"max_per_model_class", None
|
97
|
+
),
|
98
|
+
validation_method=self.spec.model_kwargs.get(
|
99
|
+
"validation_method", "backwards"
|
100
|
+
),
|
101
|
+
min_allowed_train_percent=self.spec.model_kwargs.get(
|
102
|
+
"min_allowed_train_percent", 0.5
|
103
|
+
),
|
104
|
+
remove_leading_zeroes=self.spec.model_kwargs.get(
|
105
|
+
"remove_leading_zeroes", False
|
106
|
+
),
|
107
|
+
prefill_na=self.spec.model_kwargs.get("prefill_na", None),
|
108
|
+
introduce_na=self.spec.model_kwargs.get("introduce_na", None),
|
109
|
+
preclean=self.spec.model_kwargs.get("preclean", None),
|
110
|
+
model_interrupt=self.spec.model_kwargs.get("model_interrupt", True),
|
111
|
+
generation_timeout=self.spec.model_kwargs.get(
|
112
|
+
"generation_timeout", None
|
113
|
+
),
|
114
|
+
current_model_file=self.spec.model_kwargs.get(
|
115
|
+
"current_model_file", None
|
116
|
+
),
|
117
|
+
verbose=-1 if logger.level > 40 else 1,
|
118
|
+
n_jobs=self.spec.model_kwargs.get("n_jobs", -1),
|
119
|
+
)
|
120
|
+
|
121
|
+
full_data_indexed = self.datasets.get_data_multi_indexed()
|
122
|
+
|
123
|
+
dates = full_data_indexed.index.get_level_values(0).unique().tolist()
|
124
|
+
train_idx = dates[: -self.spec.horizon]
|
125
|
+
|
126
|
+
df_train = full_data_indexed[
|
127
|
+
full_data_indexed.index.get_level_values(0).isin(train_idx)
|
128
|
+
][[self.original_target_column]].reset_index()
|
129
|
+
|
130
|
+
# Future regressors need to be in wide format - (num_unique_dates x (num_unique_series x num_unique_cols))
|
131
|
+
additional_regressors = list(
|
132
|
+
set(full_data_indexed.columns) - {self.original_target_column}
|
190
133
|
)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
134
|
+
if len(additional_regressors) > 1:
|
135
|
+
future_regressor = full_data_indexed.reset_index().pivot(
|
136
|
+
index=self.spec.datetime_column.name,
|
137
|
+
columns=ForecastOutputColumns.SERIES,
|
138
|
+
values=additional_regressors,
|
139
|
+
)
|
140
|
+
future_reg = future_regressor[: -self.spec.horizon]
|
141
|
+
regr_fcst = future_regressor[-self.spec.horizon:]
|
142
|
+
else:
|
143
|
+
future_reg = None
|
144
|
+
regr_fcst = None
|
145
|
+
|
146
|
+
for s_id in self.datasets.list_series_ids():
|
147
|
+
self.forecast_output.init_series_output(
|
148
|
+
series_id=s_id, data_at_series=self.datasets.get_data_at_series(s_id)
|
149
|
+
)
|
150
|
+
|
151
|
+
if self.loaded_models is None:
|
152
|
+
model = model.fit(
|
153
|
+
df_train,
|
154
|
+
future_regressor=future_reg,
|
155
|
+
date_col=self.spec.datetime_column.name,
|
156
|
+
value_col=self.original_target_column,
|
157
|
+
id_col=ForecastOutputColumns.SERIES,
|
158
|
+
)
|
159
|
+
# Store the trained model and generate forecasts
|
160
|
+
self.models = copy.deepcopy(model)
|
161
|
+
else:
|
162
|
+
self.models = self.loaded_models
|
163
|
+
|
164
|
+
self.outputs = model.predict(future_regressor=regr_fcst)
|
165
|
+
logger.debug("===========Forecast Generated===========")
|
166
|
+
|
167
|
+
hist_df = model.back_forecast().forecast
|
168
|
+
|
169
|
+
params = vars(model).copy()
|
170
|
+
for param in [
|
171
|
+
"ens_copy",
|
172
|
+
"df_wide_numeric",
|
173
|
+
"future_regressor_train",
|
174
|
+
"initial_results",
|
175
|
+
"score_per_series",
|
176
|
+
"validation_results",
|
177
|
+
"validation_train_indexes",
|
178
|
+
"validation_test_indexes",
|
179
|
+
"validation_indexes",
|
180
|
+
"best_model",
|
181
|
+
]:
|
182
|
+
if param in params:
|
183
|
+
params.pop(param)
|
184
|
+
|
185
|
+
for s_id in self.datasets.list_series_ids():
|
186
|
+
self.forecast_output.populate_series_output(
|
187
|
+
series_id=s_id,
|
188
|
+
fit_val=hist_df[s_id].values,
|
189
|
+
forecast_val=self.outputs.forecast[s_id].values,
|
190
|
+
upper_bound=self.outputs.upper_forecast[s_id].values,
|
191
|
+
lower_bound=self.outputs.lower_forecast[s_id].values,
|
192
|
+
)
|
193
|
+
|
194
|
+
self.model_parameters[s_id] = {
|
195
|
+
"framework": SupportedModels.AutoTS,
|
196
|
+
**params,
|
197
|
+
}
|
198
|
+
|
199
|
+
except Exception as e:
|
200
|
+
for s_id in self.datasets.list_series_ids():
|
201
|
+
self.errors_dict[s_id] = {
|
202
|
+
"model_name": self.spec.model,
|
203
|
+
"error": str(e),
|
204
|
+
}
|
196
205
|
|
197
206
|
logger.debug("===========Done===========")
|
198
207
|
|
@@ -213,29 +222,30 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
|
|
213
222
|
- ci_col_names (list): A list of column names for confidence intervals.
|
214
223
|
"""
|
215
224
|
import datapane as dp
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
sec_1 = _select_plot_list(
|
223
|
-
lambda s_id: self.outputs.plot(self.models.df_wide_numeric, series=s_id),
|
224
|
-
self.datasets.list_series_ids(),
|
225
|
-
)
|
226
|
-
|
227
|
-
# Section 2: AutoTS Model Parameters
|
228
|
-
sec2_text = dp.Text(f"## AutoTS Model Parameters")
|
229
|
-
try:
|
230
|
-
sec2 = dp.Code(
|
231
|
-
code=yaml.dump(list(self.models.best_model.T.to_dict().values())[0]),
|
232
|
-
language="yaml",
|
225
|
+
all_sections = []
|
226
|
+
if self.models:
|
227
|
+
# Section 1: Forecast Overview
|
228
|
+
sec1_text = dp.Text(
|
229
|
+
"## Forecast Overview \n"
|
230
|
+
"These plots show your forecast in the context of historical data."
|
233
231
|
)
|
232
|
+
sec_1 = _select_plot_list(
|
233
|
+
lambda s_id: self.outputs.plot(self.models.df_wide_numeric, series=s_id),
|
234
|
+
self.datasets.list_series_ids(),
|
235
|
+
)
|
236
|
+
|
237
|
+
# Section 2: AutoTS Model Parameters
|
238
|
+
sec2_text = dp.Text(f"## AutoTS Model Parameters")
|
239
|
+
try:
|
240
|
+
sec2 = dp.Code(
|
241
|
+
code=yaml.dump(list(self.models.best_model.T.to_dict().values())[0]),
|
242
|
+
language="yaml",
|
243
|
+
)
|
234
244
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
245
|
+
except KeyError as ke:
|
246
|
+
logger.warn(f"Issue generating Model Parameters Table Section. Skipping")
|
247
|
+
sec2 = dp.Text(f"Error generating model parameters.")
|
248
|
+
all_sections = [sec1_text, sec_1, sec2_text, sec2]
|
239
249
|
|
240
250
|
if self.spec.generate_explanations:
|
241
251
|
logger.warn(f"Explanations not yet supported for the AutoTS Module")
|
@@ -266,9 +276,17 @@ class AutoTSOperatorModel(ForecastOperatorBaseModel):
|
|
266
276
|
r2 -4.60E-06
|
267
277
|
Explained Variance 0.002177087
|
268
278
|
"""
|
269
|
-
|
270
|
-
|
271
|
-
self.models.
|
272
|
-
|
273
|
-
|
279
|
+
df = pd.DataFrame()
|
280
|
+
try:
|
281
|
+
mapes = pd.DataFrame(self.models.best_model_per_series_mape()).T
|
282
|
+
scores = pd.DataFrame(
|
283
|
+
self.models.best_model_per_series_score(), columns=["AutoTS Score"]
|
284
|
+
).T
|
285
|
+
df = pd.concat([mapes, scores])
|
286
|
+
except Exception as e:
|
287
|
+
logger.debug(
|
288
|
+
f"Failed to generate training metrics"
|
289
|
+
)
|
290
|
+
logger.debug(f"Received Error Statement: {e}")
|
291
|
+
|
274
292
|
return df
|
@@ -88,7 +88,7 @@ class ForecastOperatorBaseModel(ABC):
|
|
88
88
|
self.formatted_local_explanation = None
|
89
89
|
|
90
90
|
self.forecast_col_name = "yhat"
|
91
|
-
self.perform_tuning = self.spec.tuning != None
|
91
|
+
self.perform_tuning = (self.spec.tuning != None) and (self.spec.tuning.n_trials != None)
|
92
92
|
|
93
93
|
def generate_report(self):
|
94
94
|
"""Generates the forecasting report."""
|
@@ -270,17 +270,20 @@ class ForecastOperatorBaseModel(ABC):
|
|
270
270
|
sec9 = dp.DataTable(self.eval_metrics)
|
271
271
|
train_metrics_sections = [sec9_text, sec9]
|
272
272
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
273
|
+
|
274
|
+
forecast_plots = []
|
275
|
+
if len(self.forecast_output.list_series_ids()) > 0:
|
276
|
+
forecast_text = dp.Text(f"## Forecasted Data Overlaying Historical")
|
277
|
+
forecast_sec = get_forecast_plots(
|
278
|
+
self.forecast_output,
|
279
|
+
horizon=self.spec.horizon,
|
280
|
+
test_data=test_data,
|
281
|
+
ci_interval_width=self.spec.confidence_interval_width,
|
282
|
+
)
|
283
|
+
if series_name is not None and len(self.datasets.list_series_ids()) > 1:
|
284
|
+
forecast_plots = [forecast_text, series_subtext, forecast_sec]
|
285
|
+
else:
|
286
|
+
forecast_plots = [forecast_text, forecast_sec]
|
284
287
|
|
285
288
|
yaml_appendix_title = dp.Text(f"## Reference: YAML File")
|
286
289
|
yaml_appendix = dp.Code(code=self.config.to_yaml(), language="yaml")
|
@@ -557,13 +560,14 @@ class ForecastOperatorBaseModel(ABC):
|
|
557
560
|
)
|
558
561
|
if self.errors_dict:
|
559
562
|
write_data(
|
560
|
-
data=pd.DataFrame(self.errors_dict
|
563
|
+
data=pd.DataFrame.from_dict(self.errors_dict),
|
561
564
|
filename=os.path.join(
|
562
565
|
unique_output_dir, self.spec.errors_dict_filename
|
563
566
|
),
|
564
|
-
format="
|
567
|
+
format="json",
|
565
568
|
storage_options=storage_options,
|
566
569
|
index=True,
|
570
|
+
indent=4,
|
567
571
|
)
|
568
572
|
else:
|
569
573
|
logger.info(f"All modeling completed successfully.")
|
@@ -650,38 +654,47 @@ class ForecastOperatorBaseModel(ABC):
|
|
650
654
|
for s_id, data_i in self.datasets.get_data_by_series(
|
651
655
|
include_horizon=False
|
652
656
|
).items():
|
653
|
-
|
657
|
+
if s_id in self.models:
|
654
658
|
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
kernel_explnr = PermutationExplainer(
|
663
|
-
model=explain_predict_fn, masker=data_trimmed
|
664
|
-
)
|
665
|
-
kernel_explnr_vals = kernel_explnr.shap_values(data_trimmed)
|
659
|
+
explain_predict_fn = self.get_explain_predict_fn(series_id=s_id)
|
660
|
+
data_trimmed = data_i.tail(max(int(len(data_i) * ratio), 5)).reset_index(
|
661
|
+
drop=True
|
662
|
+
)
|
663
|
+
data_trimmed[datetime_col_name] = data_trimmed[datetime_col_name].apply(
|
664
|
+
lambda x: x.timestamp()
|
665
|
+
)
|
666
666
|
|
667
|
-
|
668
|
-
global_ex_time = global_ex_time + exp_end_time - exp_start_time
|
667
|
+
# Explainer fails when boolean columns are passed
|
669
668
|
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
local_ex_time = local_ex_time + time.time() - exp_end_time
|
669
|
+
_, data_trimmed_encoded = _label_encode_dataframe(
|
670
|
+
data_trimmed, no_encode={datetime_col_name, self.original_target_column}
|
671
|
+
)
|
674
672
|
|
675
|
-
|
676
|
-
|
677
|
-
f"No explanations generated. Ensure that additional data has been provided."
|
673
|
+
kernel_explnr = PermutationExplainer(
|
674
|
+
model=explain_predict_fn, masker=data_trimmed_encoded
|
678
675
|
)
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
676
|
+
kernel_explnr_vals = kernel_explnr.shap_values(data_trimmed_encoded)
|
677
|
+
exp_end_time = time.time()
|
678
|
+
global_ex_time = global_ex_time + exp_end_time - exp_start_time
|
679
|
+
self.local_explainer(
|
680
|
+
kernel_explnr, series_id=s_id, datetime_col_name=datetime_col_name
|
681
|
+
)
|
682
|
+
local_ex_time = local_ex_time + time.time() - exp_end_time
|
683
|
+
|
684
|
+
if not len(kernel_explnr_vals):
|
685
|
+
logger.warn(
|
686
|
+
f"No explanations generated. Ensure that additional data has been provided."
|
687
|
+
)
|
688
|
+
else:
|
689
|
+
self.global_explanation[s_id] = dict(
|
690
|
+
zip(
|
691
|
+
data_trimmed.columns[1:],
|
692
|
+
np.average(np.absolute(kernel_explnr_vals[:, 1:]), axis=0),
|
693
|
+
)
|
684
694
|
)
|
695
|
+
else:
|
696
|
+
logger.warn(
|
697
|
+
f"Skipping explanations for {s_id}, as forecast was not generated."
|
685
698
|
)
|
686
699
|
|
687
700
|
logger.info(
|
@@ -700,9 +713,14 @@ class ForecastOperatorBaseModel(ABC):
|
|
700
713
|
kernel_explainer: The kernel explainer object to use for generating explanations.
|
701
714
|
"""
|
702
715
|
data = self.datasets.get_horizon_at_series(s_id=series_id)
|
703
|
-
|
716
|
+
# columns that were dropped in train_model in arima, should be dropped here as well
|
704
717
|
data[datetime_col_name] = datetime_to_seconds(data[datetime_col_name])
|
705
718
|
data = data.reset_index(drop=True)
|
719
|
+
|
720
|
+
# Explainer fails when boolean columns are passed
|
721
|
+
_, data = _label_encode_dataframe(
|
722
|
+
data, no_encode={datetime_col_name, self.original_target_column}
|
723
|
+
)
|
706
724
|
# Generate local SHAP values using the kernel explainer
|
707
725
|
local_kernel_explnr_vals = kernel_explainer.shap_values(data)
|
708
726
|
|
@@ -120,11 +120,11 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
120
120
|
data = self.preprocess(df, s_id)
|
121
121
|
data_i = self.drop_horizon(data)
|
122
122
|
|
123
|
-
if self.loaded_models is not None:
|
123
|
+
if self.loaded_models is not None and s_id in self.loaded_models:
|
124
124
|
model = self.loaded_models[s_id]
|
125
125
|
accepted_regressors_config = model.config_regressors or dict()
|
126
126
|
self.accepted_regressors[s_id] = list(accepted_regressors_config.keys())
|
127
|
-
if self.loaded_trainers is not None:
|
127
|
+
if self.loaded_trainers is not None and s_id in self.loaded_trainers:
|
128
128
|
model.trainer = self.loaded_trainers[s_id]
|
129
129
|
else:
|
130
130
|
if self.perform_tuning:
|
@@ -310,56 +310,56 @@ class NeuralProphetOperatorModel(ForecastOperatorBaseModel):
|
|
310
310
|
|
311
311
|
def _generate_report(self):
|
312
312
|
import datapane as dp
|
313
|
-
|
313
|
+
series_ids = self.models.keys()
|
314
314
|
all_sections = []
|
315
|
+
if len(series_ids) > 0:
|
316
|
+
try:
|
317
|
+
sec1_text = dp.Text(
|
318
|
+
"## Forecast Overview \nThese plots show your "
|
319
|
+
"forecast in the context of historical data."
|
320
|
+
)
|
321
|
+
sec1 = _select_plot_list(
|
322
|
+
lambda s_id: self.models[s_id].plot(self.outputs[s_id]),
|
323
|
+
series_ids=series_ids,
|
324
|
+
)
|
325
|
+
all_sections = all_sections + [sec1_text, sec1]
|
326
|
+
except Exception as e:
|
327
|
+
logger.debug(f"Failed to plot with exception: {e.args}")
|
315
328
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
all_sections = all_sections + [sec1_text, sec1]
|
326
|
-
except Exception as e:
|
327
|
-
logger.debug(f"Failed to plot with exception: {e.args}")
|
328
|
-
|
329
|
-
try:
|
330
|
-
sec2_text = dp.Text(f"## Forecast Broken Down by Trend Component")
|
331
|
-
sec2 = _select_plot_list(
|
332
|
-
lambda s_id: self.models[s_id].plot_components(self.outputs[s_id]),
|
333
|
-
series_ids=self.datasets.list_series_ids(),
|
334
|
-
)
|
335
|
-
all_sections = all_sections + [sec2_text, sec2]
|
336
|
-
except Exception as e:
|
337
|
-
logger.debug(f"Failed to plot with exception: {e.args}")
|
329
|
+
try:
|
330
|
+
sec2_text = dp.Text(f"## Forecast Broken Down by Trend Component")
|
331
|
+
sec2 = _select_plot_list(
|
332
|
+
lambda s_id: self.models[s_id].plot_components(self.outputs[s_id]),
|
333
|
+
series_ids=series_ids,
|
334
|
+
)
|
335
|
+
all_sections = all_sections + [sec2_text, sec2]
|
336
|
+
except Exception as e:
|
337
|
+
logger.debug(f"Failed to plot with exception: {e.args}")
|
338
338
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
)
|
345
|
-
all_sections = all_sections + [sec3_text, sec3]
|
346
|
-
except Exception as e:
|
347
|
-
logger.debug(f"Failed to plot with exception: {e.args}")
|
348
|
-
|
349
|
-
sec5_text = dp.Text(f"## Neural Prophet Model Parameters")
|
350
|
-
model_states = []
|
351
|
-
for i, (s_id, m) in enumerate(self.models.items()):
|
352
|
-
model_states.append(
|
353
|
-
pd.Series(
|
354
|
-
m.state_dict(),
|
355
|
-
index=m.state_dict().keys(),
|
356
|
-
name=s_id,
|
339
|
+
try:
|
340
|
+
sec3_text = dp.Text(f"## Forecast Parameter Plots")
|
341
|
+
sec3 = _select_plot_list(
|
342
|
+
lambda s_id: self.models[s_id].plot_parameters(),
|
343
|
+
series_ids=series_ids,
|
357
344
|
)
|
358
|
-
|
359
|
-
|
360
|
-
|
345
|
+
all_sections = all_sections + [sec3_text, sec3]
|
346
|
+
except Exception as e:
|
347
|
+
logger.debug(f"Failed to plot with exception: {e.args}")
|
348
|
+
|
349
|
+
sec5_text = dp.Text(f"## Neural Prophet Model Parameters")
|
350
|
+
model_states = []
|
351
|
+
for i, (s_id, m) in enumerate(self.models.items()):
|
352
|
+
model_states.append(
|
353
|
+
pd.Series(
|
354
|
+
m.state_dict(),
|
355
|
+
index=m.state_dict().keys(),
|
356
|
+
name=s_id,
|
357
|
+
)
|
358
|
+
)
|
359
|
+
all_model_states = pd.concat(model_states, axis=1)
|
360
|
+
sec5 = dp.DataTable(all_model_states)
|
361
361
|
|
362
|
-
|
362
|
+
all_sections = all_sections + [sec5_text, sec5]
|
363
363
|
|
364
364
|
if self.spec.generate_explanations:
|
365
365
|
try:
|