Coverage for uqmodels/custom_UQModel.py: 15%

314 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-05 14:29 +0000

1################################################################################### 

2# UQModel : "Model than apply an modeling pipeline composed of an predictor(optional), UQestimator and KPI 

3# To write a custom UQModel, inherite from UQModel class an use supoer().__init__() 

4 

5# Class UQmodels : from an UQestimators : perform dadada 

6 

7import numpy as np 

8 

9import uqmodels.postprocessing.custom_UQKPI_Processor as custom_UQProc 

10import uqmodels.postprocessing.UQKPI_Processor as UQProc 

11import uqmodels.preprocessing.preprocessing as pre_pre 

12import uqmodels.preprocessing.structure as pre_struc 

13from uqmodels.modelization.DL_estimator import lstm_ed, transformer_ed 

14from uqmodels.modelization.DL_estimator.metalayers import mlp 

15from uqmodels.modelization.DL_estimator.neural_network_UQ import ( 

16 NN_UQ, 

17 get_params_dict, 

18 get_training_parameters, 

19) 

20from uqmodels.modelization.ML_estimator.random_forest_UQ import ( 

21 RandomForestRegressor, 

22 RF_UQEstimator, 

23) 

24from uqmodels.preprocessing.Custom_Preprocessor import Generic_Features_processor 

25from uqmodels.preprocessing.features_processing import ( 

26 compute_pca, 

27 fit_compute_lag_values, 

28 fit_pca, 

29) 

30from uqmodels.processing import Cache_manager 

31from uqmodels.UQModel import UQModel 

32from uqmodels.utils import apply_mask, coefficients_spreaded, cut 

33 

34# We can also create a more complexe UQmodel that handle several UQKPI_Processor to build at inference (UQMesure, Predictive interval and model unreliability score) and after observation (Anomaly score) 

35 

36# Specification of the UQestimator & Instanciation in a UQmodels wrapper that include post-processing 

37 

38 

39type_output = "MC_Dropout" 

40 

41factory_params = {"factory_lag_st": 0, "factory_lag_lt": 0} 

42 

43 

44class TADKIT_UQModel(UQModel): 

45 def __init__( 

46 self, 

47 y_shape, 

48 n_window=20, 

49 model="RF_UQ", 

50 model_params=None, 

51 epochs=30, 

52 reduc_coef=True, 

53 beta=0.001, 

54 random_state=None, 

55 cache_manager=Cache_manager(), 

56 ): 

57 

58 lag_values_params = { 

59 "lag": coefficients_spreaded(n_window), 

60 "deriv": [0, 1], 

61 "windows": [1, int(n_window / 4), n_window], 

62 } 

63 

64 list_params_features = [lag_values_params] 

65 list_fit_features = [fit_pca, None] 

66 list_compute_features = [fit_compute_lag_values] 

67 

68 targets_params = {"lag": [0]} 

69 list_params_targets = [targets_params] 

70 list_fit_targets = [None] 

71 list_compute_targets = [fit_compute_lag_values] 

72 

73 Formalizer = Generic_Features_processor( 

74 name="Generic_Features_processor", 

75 cache=None, 

76 list_params_features=list_params_features, 

77 list_fit_features=list_fit_features, 

78 list_compute_features=list_compute_features, 

79 list_params_targets=list_params_targets, 

80 list_fit_targets=list_fit_targets, 

81 list_compute_targets=list_compute_targets, 

82 concat_features=True, 

83 ) 

84 # Specification of the UQestimator 

85 X_, y_ = Formalizer.fit_transform(np.zeros((2 * n_window, y_shape))) 

86 X_shape = X_.shape[-1] 

87 

88 Anom_param = { 

89 "type_norm": "Nsigma_global", 

90 "em" "beta": beta, 

91 "filt": [0.05, 0.15, 0.8, 0, 0], 

92 "min_cut": 0.05, 

93 "max_cut": 0.95, 

94 "var_min": 0.0001, 

95 "q_var": 0.7, 

96 "q_var_e": 0.7, 

97 "k_var_e": 1, 

98 "q_Eratio": 1, 

99 "d": 2, 

100 "global_median_normalization": False, 

101 "reduc_filter": None, 

102 "roll": 0, 

103 "with_born": False, 

104 } 

105 # PostProcesseur Instanciation that compute an epistemics lvl score 

106 Anom_proc = UQProc.Anomscore_processor(KPI_parameters=Anom_param) 

107 

108 reduc_filter_pred = None 

109 if model == "RF_UQ": 

110 UQEstimator_initializer = RF_UQEstimator 

111 if model_params is None: 

112 estimator = RandomForestRegressor( 

113 min_samples_leaf=5, 

114 n_estimators=75, 

115 max_depth=16, 

116 ccp_alpha=0.00001, 

117 max_samples=0.7, 

118 max_features=0.75, 

119 random_state=random_state, 

120 ) 

121 

122 UQEstimator_parameters = { 

123 "estimator": estimator, 

124 "var_min": 0.002, 

125 "type_UQ": "var_A&E", 

126 "rescale": True, 

127 "random_state": random_state, 

128 } 

129 

130 elif model == "MLP_MC_DROPOUT": 

131 type_output = "MC_Dropout" # 'Deep_ensemble' 

132 UQEstimator_initializer = NN_UQ 

133 mlp_params = get_params_dict(X_shape, y_shape, type_output=type_output) 

134 trainig_params = get_training_parameters( 

135 epochs=[epochs], b_s=[64], l_r=[0.005], type_output=type_output 

136 ) 

137 UQEstimator_initializer = NN_UQ 

138 UQEstimator_parameters = { 

139 "rescale": False, 

140 "model_initializer": mlp, 

141 "model_parameters": mlp_params, 

142 "training_parameters": trainig_params, 

143 "type_output": type_output, 

144 "random_state": random_state, 

145 } 

146 

147 elif model == "MLP_DEEP_ENSEMBLE": 

148 type_output = "Deep_ensemble" 

149 UQEstimator_initializer = NN_UQ 

150 mlp_params = get_params_dict(X_shape, y_shape, type_output=type_output) 

151 trainig_params = get_training_parameters( 

152 epochs=[epochs], b_s=[64], l_r=[0.005], type_output=type_output 

153 ) 

154 

155 UQEstimator_parameters = { 

156 "rescale": False, 

157 "model_initializer": mlp, 

158 "model_parameters": mlp_params, 

159 "training_parameters": trainig_params, 

160 "type_output": type_output, 

161 "random_state": random_state, 

162 } 

163 

164 UQ_proc = UQProc.UQKPI_Processor( 

165 KPI_parameters={ 

166 "pred_and_UQ": True, 

167 "reduc_filter": reduc_filter_pred, 

168 "roll": 0, 

169 "reduc_filter": reduc_filter_pred, 

170 } 

171 ) 

172 

173 # Instanciation of the UQmodel modeling pipeline 

174 UQModels = super().__init__( 

175 UQEstimator_initializer, 

176 UQEstimator_parameters, 

177 preprocessor=Formalizer, 

178 name="UQModels", 

179 predictor=None, 

180 list_predict_KPI_processors=[UQ_proc], 

181 list_score_KPI_processors=[Anom_proc], 

182 reduc_filter=reduc_filter_pred, 

183 cache_manager=cache_manager, 

184 random_state=random_state, 

185 ) 

186 return UQModels 

187 

188 

189class UQModel_KPI(UQModel): 

190 def __init__( 

191 self, 

192 UQEstimator_initializer, 

193 UQEstimator_params, 

194 # Necesite with_prediction or to provide a predictor. 

195 name="UQModel", 

196 predictor=None, 

197 preprocessor=None, 

198 list_alpha=[0.025, 0.16, 0.84, 0.975], 

199 list_percent_escore=[0.50, 0.80, 0.95, 0.98, 0.995, 1], 

200 reduc_filter_pred=None, 

201 reduc_filter_KPI=None, 

202 roll_KPI=1, 

203 anom_with_born=False, 

204 beta=0.01, 

205 var_min=0.001, 

206 cache_manager=Cache_manager(), 

207 q_Eratio=3, 

208 mode_epistemic_indicator="levels", 

209 random_state=None, 

210 ): 

211 

212 UQ_proc = UQProc.UQKPI_Processor( 

213 KPI_parameters={ 

214 "pred_and_UQ": True, 

215 "reduc_filter": reduc_filter_pred, 

216 "roll": 0, 

217 } 

218 ) 

219 

220 # PostProcesseur that compute Predictive intervals 

221 PIs_proc = UQProc.NormalPIs_processor( 

222 KPI_parameters={ 

223 "list_alpha": list_alpha, 

224 "reduc_filter": reduc_filter_pred, 

225 "roll": 0, 

226 } 

227 ) 

228 

229 # PostProcesseur that compute an epistemics lvl score 

230 Elvl_proc = UQProc.Epistemicscorelvl_processor( 

231 KPI_parameters={ 

232 "list_percent": list_percent_escore, 

233 "reduc_filter": reduc_filter_pred, 

234 "roll": 0, 

235 "mode": mode_epistemic_indicator, 

236 "q_Eratio": q_Eratio, 

237 "var_min": var_min, 

238 } 

239 ) 

240 

241 # PostProcesseur Instanciation that compute an epistemics lvl score 

242 Anom_proc = UQProc.Anomscore_processor( 

243 KPI_parameters={ 

244 "beta": beta, 

245 "d": 2, 

246 "var_min": var_min, 

247 "q_var": 1, 

248 "k_var_e": 1, 

249 "q_var_e": 1, 

250 "q_Eratio": 3, 

251 "filt": [0.05, 0.15, 0.8, 0, 0], 

252 "with_born": anom_with_born, 

253 "reduc_filter": reduc_filter_KPI, 

254 "roll": roll_KPI, 

255 "debug": False, 

256 } 

257 ) 

258 

259 list_predict_KPI_processors = [UQ_proc, PIs_proc, Elvl_proc] 

260 list_score_KPI_processors = [UQ_proc, Anom_proc, PIs_proc, Elvl_proc] 

261 

262 super().__init__( 

263 UQEstimator_initializer=UQEstimator_initializer, 

264 UQEstimator_params=UQEstimator_params, 

265 name=name, 

266 predictor=predictor, 

267 preprocessor=preprocessor, 

268 list_predict_KPI_processors=list_predict_KPI_processors, 

269 list_score_KPI_processors=list_score_KPI_processors, 

270 reduc_filter=reduc_filter_pred, 

271 cache_manager=cache_manager, 

272 list_alpha=list_alpha, 

273 list_percent_escore=list_percent_escore, 

274 reduc_filter_pred=reduc_filter_pred, 

275 reduc_filter_KPI=reduc_filter_KPI, 

276 roll_KPI=roll_KPI, 

277 anom_with_born=anom_with_born, 

278 beta=beta, 

279 var_min=var_min, 

280 q_Eratio=q_Eratio, 

281 mode_epistemic_indicator=mode_epistemic_indicator, 

282 random_state=random_state, 

283 ) 

284 

285 

286# Complex UQmodels link to EMS use case ################################################# 

287 

288 

289class MultiDEEPUQModel(UQModel): 

290 """UQModel class for UQestimators that perform multisource forecast with UQ.""" 

291 

292 def __init__( 

293 self, 

294 UQEstimator_initializer, 

295 UQEstimator_params, 

296 list_sources, 

297 cut_params=(0.001, 0.999), 

298 multiscale_anomscore_params=None, 

299 name="MultiDEEPUQModel", 

300 cache_manager=Cache_manager(), 

301 save_result=True, 

302 save_models=True, 

303 with_generator=True, 

304 reduc_filter=None, 

305 random_state=None, 

306 ): 

307 """Instanciation of UQModels for UQestimators that perform multisource forecast with UQ 

308 # No predict_KPIs_processor 

309 # Multiscale_Anomscore_processor as score_predict_KPIS_processor : 

310 # to product multidimensional anomaly score matrix 

311 

312 

313 Args: 

314 UQEstimator_initializer (obj_init): init method for instanciate an UQEstimator 

315 UQEstimator_params (dict_params): params for the init method 

316 list_sources (List_str): Name of source for storage purpose 

317 cut_params (tuple, optional): Lower and upper quantile to threshold data and remove outliers during 

318 learning phase. Defaults to (0.001, 0.999). 

319 multiscale_anomscore_params : params link to postprocessing.UQ_processor.Multiscale_Anomscore_processor 

320 -> to see defaults parameters. 

321 name (str, optional): Wrapper name . Defaults to 'MultiDEEPUQModel'. 

322 cache_manager (cache_manager, optional): cache_manager. Defaults to None. 

323 """ 

324 self.futur_horizon = ( 

325 UQEstimator_params["model_parameters"]["dim_horizon"] 

326 * UQEstimator_params["model_parameters"]["step"] 

327 ) 

328 if multiscale_anomscore_params is None: 

329 multiscale_anomscore_params = { 

330 "type_norm": "Nsigma_local", 

331 "q_var": 1, 

332 "d": 2, 

333 "beta": 0.001, 

334 "beta_source": 0.001, 

335 "beta_global": 0.001, 

336 "min_cut": 0.002, 

337 "max_cut": 0.998, 

338 "per_seuil": 0.999, 

339 "reduc_filter": np.ones(self.futur_horizon), 

340 "roll": 1, 

341 "dim_chan": 1, 

342 "type_fusion": "mahalanobis", 

343 "filt": [0.1, 0.2, 0.5, 0.2, 0.1], 

344 } 

345 

346 list_score_KPI_processors = [ 

347 custom_UQProc.Multiscale_Anomscore_processor( 

348 KPI_parameters=multiscale_anomscore_params 

349 ) 

350 ] 

351 

352 super().__init__( 

353 UQEstimator_initializer, 

354 UQEstimator_params, 

355 name, 

356 predictor=None, 

357 list_predict_KPI_processors=[], 

358 list_score_KPI_processors=list_score_KPI_processors, 

359 cache_manager=cache_manager, 

360 save_result=save_result, 

361 save_models=save_models, 

362 random_state=random_state, 

363 ) 

364 

365 if reduc_filter is None: 

366 self.reduc_filter = np.zeros(self.futur_horizon) == 1 

367 self.reduc_filter[0] = True 

368 else: 

369 self.reduc_filter = reduc_filter 

370 

371 self.with_generator = with_generator 

372 self.list_sources = list_sources 

373 self.list_chan = ["Mean", "Std", "Extremum", "Count"] 

374 self.cut_params = cut_params 

375 

376 def multi_score_reshape(self, y=None, pred=None, UQ=None, mask=None): 

377 """Reshape from (n_sample,n_sources*s_chan) to List of n_sources (n_sample,n_chan) elements 

378 

379 Args: 

380 y (np.array, optional): y to reshape or None. Defaults to None. 

381 pred (np.array, optional): pred to reshape or None. Defaults to None. 

382 UQ (np.array, optional): UQ to reshape or None. Defaults to None. 

383 return: 

384 list_y,list_pred,list_UQ 

385 """ 

386 n_sources = len(self.list_sources) 

387 n_chan = len(self.list_chan) 

388 

389 list_y = None 

390 if y is not None: 

391 size_data = len(y) 

392 list_y = [ 

393 np.squeeze(y[:, mask, n_chan * i : n_chan * (i + 1)]) 

394 for i in range(n_sources) 

395 ] 

396 

397 # Issues : Reshape DL 

398 list_pred = None 

399 if pred is not None: 

400 size_data = len(pred) 

401 list_pred = [ 

402 np.squeeze(pred[:, mask, n_chan * i : n_chan * (i + 1)]) 

403 for i in range(n_sources) 

404 ] 

405 

406 list_UQ = None 

407 if UQ is not None: 

408 # IF UQ measure is not composed 

409 if (len(UQ.shape) == 2) & (size_data == UQ.shape[1]): 

410 UQ = UQ[None] 

411 list_UQ = [ 

412 np.squeeze( 

413 np.array( 

414 [ 

415 UQ_chan[:, mask, n_chan * i : n_chan * (i + 1)] 

416 for UQ_chan in UQ 

417 ] 

418 ) 

419 ) 

420 for i in range(n_sources) 

421 ] 

422 

423 return (list_y, list_pred, list_UQ) 

424 

425 def fit( 

426 self, X, y, sample_weight=None, skip_UQEstimator=False, shuffle=True, **kwargs 

427 ): 

428 """Fit method that apply fit method of (predictor), UQEstimators, predict_KPI_processors, score_KPI_processors 

429 Args: 

430 X (np.array): Features 

431 y (np.array): Targets/observations 

432 sample_weight (np.array or None, optional): sample_weight. Defaults to None. 

433 """ 

434 

435 # Init deep learning model 

436 if not self.UQEstimator: 

437 self.UQEstimator = self.UQEstimator_initializer(**self.UQEstimator_params) 

438 

439 self.type_UQ = self.UQEstimator.type_UQ 

440 self.type_UQ_params = None 

441 if hasattr(self.UQEstimator, "type_UQ_params"): 

442 self.type_UQ_params = self.UQEstimator.type_UQ_params 

443 

444 set_ = len(y) 

445 train_deep = np.arange(set_) < (set_ * 4 / 6) 

446 np.invert(train_deep) 

447 

448 # Call to factory method of model to transform features. 

449 if not (self.with_generator) & hasattr(self.UQEstimator, "factory"): 

450 train_deep = np.arange(set_) < (set_ * 4 / 6) 

451 inputs, targets, _ = self.UQEstimator.factory( 

452 X, y, train_deep, self.cut_params 

453 ) 

454 

455 else: 

456 inputs, targets = X, y 

457 

458 # Call to fit method of model to perform multivarite fiting. 

459 if not skip_UQEstimator: 

460 self.UQEstimator.fit( 

461 inputs, 

462 targets, 

463 sample_weight=sample_weight, 

464 skip_format=True, 

465 generator=self.with_generator, 

466 shuffle=shuffle, 

467 ) 

468 self.is_fitted = True 

469 

470 if self.save_result: 

471 self.save() 

472 

473 # Then call to fit for the post-processing procedure 

474 pred, UQ = self.UQEstimator.predict( 

475 inputs, skip_format=True, generator=self.with_generator 

476 ) 

477 

478 _, y_bis, _ = self.UQEstimator.factory( 

479 None, y, np.arange(len(y)), self.cut_params 

480 ) 

481 

482 y, pred, UQ = self.multi_score_reshape( 

483 y_bis, pred, UQ, mask=(np.ones(self.futur_horizon) == 1) 

484 ) 

485 

486 self._fit_score_KPI_processors(UQ, pred, y, **kwargs) 

487 

488 def predict(self, X, name_save="output.p"): 

489 """Predict method that apply predictor and UQestimators predict method, then compute predict_KPIs by use 

490 transform methods of predict_KPI_Processors 

491 

492 Args: 

493 X (np.array): feature 

494 name_save (str, optional): file_name for (Predictor) and UQEstimator outputs save file. 

495 Defaults to "output.p". 

496 

497 Returns: 

498 pred, UQ : prediction and UQmeasure 

499 """ 

500 if not (self.with_generator) & hasattr(self.UQEstimator, "factory"): 

501 inputs, _, _ = self.UQEstimator.factory( 

502 X, None, np.arange(len(X)), self.cut_params 

503 ) 

504 

505 else: 

506 inputs, _ = X, None 

507 

508 pred, UQ = self.UQEstimator.predict( 

509 inputs, beta=0.1, skip_format=True, generator=self.with_generator 

510 ) 

511 

512 mask = np.zeros(self.futur_horizon) 

513 mask[0] = 1 

514 

515 _, list_pred, list_UQ = self.multi_score_reshape( 

516 None, pred=pred, UQ=UQ, mask=(mask == 1) 

517 ) 

518 

519 if self.save_result: 

520 # Save in a multi_folder by source ways model outputs 

521 for n, source in enumerate(self.list_sources): 

522 query = {"name": source + "_" + name_save} 

523 self.cache_manager.save(query, (list_pred[n], list_UQ[n])) 

524 return pred, UQ 

525 

526 def _recovers_KPI(self, mode, name_save="output.p", **kwargs): 

527 if mode == "output": 

528 if name_save is None: 

529 name_save = "output.p" 

530 query = {"name": name_save} 

531 loaded_data = self.cache_manager.load(query) 

532 

533 if mode == "score": 

534 if name_save is None: 

535 name_save = "dict_res_anom.p" 

536 query = {"name": name_save} 

537 loaded_data = self.cache_manager.load(query) 

538 return loaded_data 

539 

540 def score(self, X, y, **kwargs): 

541 """Score method that produce score KPI that transform y observation and (pred,UQ) 

542 UQestimator outputs into a KPI according to score_KPI_processors 

543 

544 Args: 

545 X (np.array): Features 

546 y (np.array): Targets/obserbations 

547 

548 Returns: 

549 list_KPI_output: (List_Pred,list_UQ),KPI_anom 

550 """ 

551 

552 pred, UQ = self.predict(X) 

553 _, y_bis, _ = self.UQEstimator.factory( 

554 None, y, np.arange(len(y)), self.cut_params 

555 ) 

556 

557 list_y, list_pred, list_UQ = self.multi_score_reshape( 

558 y=y_bis, pred=pred, UQ=UQ, mask=(np.ones(self.futur_horizon) == 1) 

559 ) 

560 

561 KPIs_anom = self._transform_score_KPI_processors( 

562 list_UQ, list_pred, list_y, **kwargs 

563 ) 

564 

565 S_anom_chan, S_anom_agg, S_anom_sensor, list_bot, list_top = KPIs_anom 

566 

567 # Turn Multi-pred_horizon into 

568 

569 list_y, list_pred, list_UQ = self.multi_score_reshape( 

570 y=y_bis, pred=pred, UQ=UQ, mask=self.reduc_filter 

571 ) 

572 dict_res_anom = { 

573 "name": self.name, 

574 "S_anom_chan": S_anom_chan, 

575 "S_anom_agg": S_anom_agg, 

576 "S_anom_sensor": S_anom_sensor, 

577 "list_y": list_y, 

578 "list_pred": list_pred, 

579 "list_UQ": list_UQ, 

580 "list_bot": list_bot, 

581 "list_top": list_top, 

582 } 

583 

584 if self.save_result: 

585 query = {"name": "dict_res_anom"} 

586 self.cache_manager.save(query, dict_res_anom) 

587 return (list_pred, list_UQ), KPIs_anom 

588 

589 

590# @To do refactoring as UQestimator: 

591 

592 

593class MultiUQModel(UQModel): 

594 """Instanciation of UQModels that apply an UQestimator for each sources and combine results for compute 

595 multidimensional anomaly score 

596 

597 No predict_KPIs_processor 

598 Multiscale_Anomscore_processor as score_predict_KPIS_processor : to product multidimensional anomaly score matrix 

599 """ 

600 

601 def __init__( 

602 self, 

603 UQEstimator_initializer, 

604 UQEstimator_params, 

605 tunning_params, 

606 list_sources, 

607 list_delta=None, 

608 target_delta=None, 

609 multiscale_anomscore_params=None, 

610 models_in_ram=True, 

611 cut_params=(0.001, 0.999), 

612 name="Multi_RFUQModels", 

613 cache_manager=Cache_manager(), 

614 save_models=True, 

615 save_result=True, 

616 random_state=None, 

617 ): 

618 """Initialization of MultiUQModel class 

619 

620 Args: 

621 UQEstimator_initializer (obj_init): init method for instanciate an UQEstimator 

622 UQEstimator_params (dict_params): params for the init method of the UQEstimator 

623 tunning_params (dict_grid_params): grid params for tuning UQEstiamtor using scikit 

624 list_sources (List_str): Name of source for storage purpose 

625 models_in_ram : specify if manipulate list of UQestimators in ram (heavy in ram), or store and load. 

626 cut_params (tuple, optional): Lower and upper quantile to threshold data and remove outliers during 

627 learning phase. Defaults to (0.001, 0.999). 

628 multiscale_anomscore_params : params link to postprocessing.UQ_processor.Multiscale_Anomscore_processor 

629 -> to see defaults parameters. 

630 name (str, optional): Wrapper name . Defaults to 'MultiDEEPUQModel'. 

631 cache_manager (cache_manager, optional): cache_manager. Defaults to None. 

632 """ 

633 if multiscale_anomscore_params is None: 

634 multiscale_anomscore_params = { 

635 "type_norm": "Nsigma_local", 

636 "q_var": 1, 

637 "d": 2, 

638 "beta": 0.001, 

639 "beta_source": 0.001, 

640 "beta_global": 0.001, 

641 "min_cut": 0.002, 

642 "max_cut": 0.998, 

643 "per_seuil": 0.999, 

644 "type_fusion": "mahalanobis", 

645 "filt": [0.1, 0.2, 0.5, 0.2, 0.1], 

646 } 

647 

648 list_score_KPI_processors = [ 

649 custom_UQProc.Multiscale_Anomscore_processor( 

650 KPI_parameters=multiscale_anomscore_params 

651 ) 

652 ] 

653 

654 super().__init__( 

655 UQEstimator_initializer, 

656 UQEstimator_params, 

657 name, 

658 list_score_KPI_processors=list_score_KPI_processors, 

659 cache_manager=cache_manager, 

660 save_models=save_models, 

661 save_result=save_result, 

662 random_state=random_state, 

663 ) 

664 self.tunning_params = tunning_params 

665 self.list_sources = list_sources 

666 self.list_delta = list_delta 

667 self.target_delta = target_delta 

668 self.UQEstimators_in_ram = models_in_ram 

669 self.cut_params = cut_params 

670 if self.UQEstimators_in_ram: 

671 self.list_UQEstimators = [] 

672 for _ in list_sources: 

673 self.list_UQEstimators.append([]) 

674 

675 def get_model(self, id_source): 

676 """Get model of an id_source 

677 

678 Args: 

679 model (UQEstimator): UQEstimator to set 

680 id_source (int): Id_source 

681 """ 

682 print("get model", id_source) 

683 if self.UQEstimators_in_ram: 

684 idx = self.list_sources.index(id_source) 

685 print("get model", idx) 

686 model = self.list_UQEstimators[idx] 

687 else: 

688 query = {"name": id_source + "_" + self.name} 

689 model = self.cache_manager.load(query) 

690 return model 

691 

692 def set_model(self, model, id_source): 

693 """Set model of an id_source 

694 

695 Args: 

696 model (UQEstimator): UQEstimator to set 

697 id_source (int): Id_source 

698 """ 

699 if self.UQEstimators_in_ram: 

700 idx = self.list_sources.index(id_source) 

701 self.list_UQEstimators[idx] = model 

702 else: 

703 query = {"name": id_source + "_" + self.name} 

704 self.cache_manager.save(query, model) 

705 del model 

706 

707 def _aux_fit( 

708 self, 

709 X, 

710 y, 

711 sample_weight=None, 

712 train=None, 

713 skip_UQEstimator=False, 

714 source=None, 

715 **kwargs 

716 ): 

717 """Fit method that apply fit method of (predictor) for source, the 

718 Args: 

719 X (np.array): Features 

720 y (np.array): Targets/observations 

721 sample_weight (np.array or None, optional): sample_weight. Defaults to None. 

722 """ 

723 

724 if not skip_UQEstimator: 

725 # Instanciate model 

726 model = self.UQEstimator_initializer(**self.UQEstimator_params) 

727 

728 # Store type_UQ 

729 self.type_UQ = model.type_UQ 

730 self.type_UQ_params = None 

731 if hasattr(model, "type_UQ_params"): 

732 self.type_UQ_params = model.type_UQ_params 

733 

734 if train is None: 

735 train = np.ones(len(X)) == 1 

736 # Run tunning if having gridsearch params 

737 if self.tunning_params is not None: 

738 model._tuning( 

739 X[train], y[train], n_esti=100, folds=4, params=self.tunning_params 

740 ) 

741 

742 print("Fitting model source :", source) 

743 y = cut(y, self.cut_params[0], self.cut_params[1]) 

744 model.fit(X[train], y[train], sample_weight=sample_weight[train], **kwargs) 

745 

746 self.set_model(model, source) 

747 

748 def _recovers_all_results(self, mode): 

749 """auxiliar function aim to load stored output for each source_UQEstimators 

750 

751 Args: 

752 mode (str): specify 'output' to load output, score to load Anom kpi 

753 

754 Returns: 

755 results: List_pred,List_UQ if output, Anom-KPIs if 'score' 

756 """ 

757 

758 if mode == "output": 

759 # recover pred,output for each source_UQestimator 

760 

761 list_outputs = [ 

762 self.cache_manager.load({"name": source + "_output"}) 

763 for source in self.list_sources 

764 ] 

765 loaded_data = ( 

766 [output[0] for output in list_outputs], 

767 [output[1] for output in list_outputs], 

768 ) 

769 del list_outputs 

770 

771 if mode == "score": 

772 query = {"name": "dict_res_anom"} 

773 loaded_data = self.cache_manager.load(query) 

774 

775 return loaded_data 

776 

777 def fit(self, X, y, sample_weight=None, skip_UQEstimator=False, **kwargs): 

778 """Fit method that apply fit method of (predictor) for eachs source_UQEstimators, 

779 then fit the Anom_KPI_processors. 

780 

781 Args: 

782 X (np.array): Features 

783 y (np.array): Targets/observations 

784 sample_weight (np.array or None, optional): sample_weight. Defaults to None. 

785 """ 

786 for n, (X_, y_) in enumerate(zip(X, y)): 

787 source = self.list_sources[n] 

788 if sample_weight is not None: 

789 sample_weight_ = sample_weight[n] 

790 self._aux_fit( 

791 X_, 

792 y_, 

793 sample_weight_, 

794 train=None, 

795 skip_UQEstimator=skip_UQEstimator, 

796 source=source, 

797 **kwargs 

798 ) 

799 del X_, y_ 

800 self.is_fitted = True 

801 list_pred, list_UQ = self.predict(X, recovers_at_end=False, **kwargs) 

802 self._fit_score_KPI_processors(list_UQ, list_pred, y, **kwargs) 

803 if self.save_models: 

804 self.save() 

805 

806 def fit_with_generator( 

807 self, data_generator, skip_UQEstimator=False, shuffle=True, **kwargs 

808 ): 

809 """Specific fit method that handle data_generator 

810 

811 Args: 

812 data_generator (datagenerator): Iterative object that provide : X, y, sample_weight, x_split, context, 

813 objective, source (see data_generator) 

814 shuffle (bool, optional): use shuffle or not. Defaults to True. 

815 """ 

816 list_y = [] 

817 list_UQ = [] 

818 list_pred = [] 

819 for _n, dataset in enumerate(data_generator): 

820 # Recovers data from data_generator 

821 X, y, sample_weight, x_split, _context, _objective, source = dataset 

822 train = None 

823 if x_split is not None: 

824 train = x_split == 1 

825 X, y = apply_mask(X, train), apply_mask(y, train) 

826 if sample_weight is not None: 

827 sample_weight = sample_weight[train] 

828 # Called fit procedure for each dataset 

829 self._aux_fit( 

830 X, 

831 y, 

832 sample_weight=sample_weight, 

833 train=None, 

834 skip_UQEstimator=skip_UQEstimator, 

835 source=source, 

836 **kwargs 

837 ) 

838 pred, UQ = self._aux_predict(X, source, **kwargs) 

839 list_y.append(y) 

840 list_pred.append(pred) 

841 list_UQ.append(UQ) 

842 self._fit_score_KPI_processors(list_UQ, list_pred, list_y, **kwargs) 

843 

844 def _aux_predict(self, X, source, **kwargs): 

845 model = self.get_model(source) 

846 

847 pred, UQ = model.predict(X, **kwargs) 

848 del model 

849 

850 # Store results 

851 if self.save_result: 

852 query = {"name": source + "_output.p"} 

853 self.cache_manager.save(query, (pred, UQ)) 

854 

855 return pred, UQ 

856 

857 def predict(self, X, recovers_at_end=False, **kwargs): 

858 """Predict method that applies predictor and UQestimators predict method, then computes predict_KPIs 

859 by using transform methods of predict_KPI_Processors. 

860 

861 Args: 

862 X (np.array): feature 

863 name_save (str, optional): file_name for (Predictor) and UQEstimator outputs save file. 

864 Defaults to "output.p". 

865 

866 Returns: 

867 pred, UQ : prediction and UQmeasure 

868 """ 

869 list_pred = [] 

870 list_UQ = [] 

871 for n, X_ in enumerate(X): 

872 source = self.list_sources[n] 

873 pred, UQ = self._aux_predict(X_, source, **kwargs) 

874 if not recovers_at_end: 

875 list_pred.append(pred) 

876 list_UQ.append(UQ) 

877 del pred, UQ, X_ 

878 

879 if recovers_at_end: 

880 pred, UQ = self._recovers_at_end("output") 

881 

882 return (pred, UQ) 

883 

884 def predict_with_generator(self, data_generator, recovers_at_end=True, **kwargs): 

885 list_pred = [] 

886 list_UQ = [] 

887 for _n, (X, _, _, _, _, _, source) in enumerate(data_generator): 

888 # Recovers data from data_generator 

889 output = self._aux_predict(X, source, **kwargs) 

890 if not (recovers_at_end): 

891 list_pred.append(output[0]) 

892 list_UQ.append(output[1]) 

893 del output, X 

894 

895 if recovers_at_end: 

896 list_pred, list_UQ = self._recovers_all_results("output") 

897 return (list_pred, list_UQ) 

898 

899 def _aux_score(self, list_y, list_pred, list_UQ, **kwargs): 

900 if (self.list_delta is not None) & (self.target_delta is not None): 

901 for n in np.arange(len(list_y)): 

902 if not self.list_delta[n] == self.target_delta: 

903 current_delta = pre_struc.check_delta(self.list_delta[n]) 

904 target_delta = pre_struc.check_delta(self.target_delta) 

905 ratio = current_delta / target_delta 

906 

907 # Scale différent entre capteurs 

908 x = np.arange(0, list_y[n]) 

909 x_new = np.arange(0, len(list_y[n]), ratio) 

910 

911 list_y[n] = pre_pre.interpolate( 

912 x=x, y=list_y[n], xnew=x_new, type_interpolation="previous" 

913 )[1] 

914 

915 list_pred[n] = pre_pre.interpolate( 

916 x=x, y=list_pred[n], xnew=x_new, type_interpolation="previous" 

917 )[1] 

918 

919 UQ_temp = [ 

920 pre_pre.interpolate( 

921 x=x, y=UQ_part, xnew=x_new, type_interpolation="previous" 

922 )[1] 

923 for UQ_part in list_UQ 

924 ] 

925 if len(UQ_temp) == 1: 

926 UQ_temp = UQ_temp[0] 

927 

928 list_UQ[n] = UQ_temp 

929 

930 KPIs_anom = self._transform_score_KPI_processors( 

931 list_UQ, list_pred, list_y, **kwargs 

932 ) 

933 S_anom_chan, S_anom_agg, S_anom_sensor, list_bot, list_top = KPIs_anom 

934 

935 dict_res_anom = { 

936 "name": self.name, 

937 "list_y": list_y, 

938 "list_pred": list_pred, 

939 "list_UQ": list_UQ, 

940 "S_anom_chan": S_anom_chan, 

941 "S_anom_agg": S_anom_agg, 

942 "S_anom_sensor": S_anom_sensor, 

943 "list_bot": list_bot, 

944 "list_top": list_top, 

945 } 

946 

947 if self.cache_manager is not None: 

948 query = {"name": "dict_res_anom"} 

949 self.cache_manager.save(query, dict_res_anom) 

950 

951 return (S_anom_chan, S_anom_agg, S_anom_sensor) 

952 

953 def score(self, X, y, **kwargs): 

954 """Score method that produces score KPI by transforming y observation and (pred,UQ) UQestimator outputs 

955 into a KPI according to score_KPI_processors. 

956 

957 Args: 

958 X (np.array): Features 

959 y (np.array): Targets/obserbations 

960 

961 Returns: 

962 list_KPI_output: list_KPI_output or KPI_ouput if len(list)==1 

963 """ 

964 list_pred, list_UQ = self.predict(X) 

965 KPI_anom = self._aux_score(y, list_pred, list_UQ) 

966 return (list_pred, list_UQ), KPI_anom 

967 

968 def score_with_generator(self, data_generator, recovers_at_end=True, **kwargs): 

969 list_y = [] 

970 list_pred = [] 

971 list_UQ = [] 

972 for _n, (X, y, _, _, _, _, source) in enumerate(data_generator): 

973 list_y.append(y) 

974 output = self._aux_predict(X, source, **kwargs) 

975 if not recovers_at_end: 

976 list_pred.append(output[1]) 

977 list_UQ.append(output[2]) 

978 del output, X, y 

979 

980 if recovers_at_end: 

981 list_pred, list_UQ = self._recovers_all_results("output") 

982 

983 KPI_anom = self._aux_score(list_y, list_pred, list_UQ) 

984 return (list_pred, list_UQ), KPI_anom