|
14 | 14 | import os |
15 | 15 | import pickle |
16 | 16 | import warnings |
| 17 | +import copy |
17 | 18 |
|
18 | 19 | import numpy as np |
19 | 20 | import pandas as pd |
@@ -1280,68 +1281,130 @@ def fit(self, X, y, X_val=None, y_val=None, early_stopping=None, epochs=100, nf_ |
1280 | 1281 | ) |
1281 | 1282 | self.trained = True |
1282 | 1283 |
|
1283 | | - def forward( |
1284 | | - self, |
1285 | | - x, |
1286 | | - smooth_projection=False, |
1287 | | - ): |
| 1284 | + def forward(self, x, smooth_projection=False, window_size=3): |
1288 | 1285 | """ |
1289 | 1286 | Performs a forward pass through the hybrid emulator. |
1290 | 1287 |
|
1291 | 1288 | Args: |
1292 | 1289 | x (array-like): The input data. |
1293 | | -
|
1294 | | - Returns: |
1295 | | - tuple: A tuple containing the prediction, epistemic uncertainty, and aleatoric uncertainty. |
1296 | | -
|
| 1290 | + smooth_projection (bool): Apply rolling window smoothing to predictions if True. |
| 1291 | + window_size (int): The size of the rolling window for smoothing. |
1297 | 1292 | """ |
1298 | 1293 | self.eval() |
1299 | 1294 | x = to_tensor(x).to(self.device) |
1300 | 1295 | if not self.trained: |
1301 | 1296 | warnings.warn("This model has not been trained. Predictions will not be accurate.") |
1302 | | - z = self.normalizing_flow.get_latent( |
1303 | | - x, |
1304 | | - ).detach() |
1305 | | - X_latent = torch.concatenate((x, z), axis=1) |
| 1297 | + |
| 1298 | + z = self.normalizing_flow.get_latent(x).detach() |
| 1299 | + X_latent = torch.cat((x, z), axis=1) |
1306 | 1300 | prediction, epistemic = self.deep_ensemble(X_latent) |
1307 | 1301 | aleatoric = self.normalizing_flow.aleatoric(x, 100) |
| 1302 | + |
| 1303 | + # Convert tensors to numpy arrays |
1308 | 1304 | prediction = prediction.detach().cpu().numpy() |
1309 | 1305 | epistemic = epistemic.detach().cpu().numpy() |
| 1306 | + aleatoric = aleatoric.detach().cpu().numpy() if isinstance(aleatoric, torch.Tensor) else aleatoric |
| 1307 | + |
| 1308 | + # Apply smoothing if the flag is set to True |
| 1309 | + if smooth_projection: |
| 1310 | + if len(x) > 86: |
| 1311 | + warnings.warn("The input data is longer than the projection length. The output may not be accurate.") |
| 1312 | + |
| 1313 | + prediction_series = pd.Series(prediction).rolling(window=window_size,).mean() |
| 1314 | + prediction_series.iloc[:window_size] = prediction[:window_size] |
| 1315 | + prediction = prediction_series.to_numpy() |
| 1316 | + |
| 1317 | + epistemic_series = pd.Series(epistemic).rolling(window=window_size,).mean() |
| 1318 | + epistemic_series.iloc[:window_size] = epistemic[:window_size] |
| 1319 | + epistemic = epistemic_series.to_numpy() |
| 1320 | + |
| 1321 | + aleatoric_series = pd.Series(aleatoric).rolling(window=window_size,).mean() |
| 1322 | + aleatoric_series.iloc[:window_size] = aleatoric[:window_size] |
| 1323 | + aleatoric = aleatoric_series.to_numpy() |
| 1324 | + |
1310 | 1325 | uncertainties = dict( |
1311 | 1326 | total=aleatoric + epistemic, |
1312 | 1327 | epistemic=epistemic, |
1313 | 1328 | aleatoric=aleatoric, |
1314 | 1329 | ) |
1315 | 1330 |
|
1316 | | - if smooth_projection: |
1317 | | - stop = "" |
| 1331 | + |
1318 | 1332 | return prediction, uncertainties |
1319 | 1333 |
|
1320 | | - def predict(self, x, output_scaler=None, smooth_projection=False): |
| 1334 | + |
| 1335 | + def predict(self, x, output_scaler=None, smooth_projection=False, window_size=5): |
| 1336 | + """ |
| 1337 | + Makes predictions and applies the inverse transform of the scaler if provided. |
| 1338 | +
|
| 1339 | + Args: |
| 1340 | + x (array-like): The input data. |
| 1341 | + output_scaler (StandardScaler): Scaler object used for output transformations. |
| 1342 | + smooth_projection (bool): Apply rolling window smoothing to predictions if True. |
| 1343 | + window_size (int): The size of the rolling window for smoothing. |
| 1344 | +
|
| 1345 | + Returns: |
| 1346 | + tuple: A tuple containing predictions and uncertainties (total, epistemic, aleatoric). |
| 1347 | + """ |
1321 | 1348 | self.eval() |
1322 | 1349 | if output_scaler is None and self.scaler_path is None: |
1323 | 1350 | warnings.warn("No scaler path provided, uncertainties are not in units of SLE.") |
1324 | | - return self.forward(x, smooth_projection=smooth_projection) |
1325 | | - if not isinstance(output_scaler, str): |
1326 | | - if 'fit' not in dir(output_scaler) or 'transform' not in dir(output_scaler): |
1327 | | - raise ValueError("output_scaler must be a Scaler object or a path to a Scaler object.") |
1328 | | - else: |
| 1351 | + return self.forward(x, smooth_projection=smooth_projection, window_size=window_size) |
| 1352 | + |
| 1353 | + if isinstance(output_scaler, str): |
1329 | 1354 | self.scaler_path = output_scaler |
1330 | 1355 | with open(self.scaler_path, "rb") as f: |
1331 | 1356 | output_scaler = pickle.load(f) |
1332 | 1357 |
|
1333 | | - import time |
1334 | | - start_time = time.time() |
1335 | | - predictions, uncertainties = self.forward(x, smooth_projection=smooth_projection) |
1336 | | - print('forward time:', time.time() - start_time) |
1337 | | - epi = uncertainties["epistemic"] |
1338 | | - ale = uncertainties["aleatoric"] |
| 1358 | + predictions, uncertainties = self.forward(x, smooth_projection=smooth_projection, window_size=window_size) |
| 1359 | + |
| 1360 | + # Apply inverse scaling to the predictions and uncertainties |
| 1361 | + unscaled_predictions = output_scaler.inverse_transform(predictions.reshape(-1, 1)) |
| 1362 | + unscaled_bound_epistemic = output_scaler.inverse_transform((predictions + uncertainties['epistemic']).reshape(-1, 1)) |
| 1363 | + unscaled_bound_aleatoric = output_scaler.inverse_transform((predictions + uncertainties['aleatoric']).reshape(-1, 1)) |
| 1364 | + |
| 1365 | + epistemic = unscaled_bound_epistemic - unscaled_predictions |
| 1366 | + aleatoric = unscaled_bound_aleatoric - unscaled_predictions |
1339 | 1367 |
|
1340 | | - bound_epistemic, bound_aleatoric = predictions + epi, predictions + ale |
| 1368 | + uncertainties = dict( |
| 1369 | + total=epistemic + aleatoric, |
| 1370 | + epistemic=epistemic, |
| 1371 | + aleatoric=aleatoric, |
| 1372 | + ) |
1341 | 1373 |
|
| 1374 | + return unscaled_predictions, uncertainties |
| 1375 | + |
| 1376 | + |
| 1377 | + |
| 1378 | + def predict(self, x, output_scaler=None, smooth_projection=False, window_size=5): |
| 1379 | + """ |
| 1380 | + Makes predictions and applies the inverse transform of the scaler if provided. |
| 1381 | +
|
| 1382 | + Args: |
| 1383 | + x (array-like): The input data. |
| 1384 | + output_scaler (StandardScaler): Scaler object used for output transformations. |
| 1385 | + smooth_projection (bool): Apply rolling window smoothing to predictions if True. |
| 1386 | + window_size (int): The size of the rolling window for smoothing. |
| 1387 | +
|
| 1388 | + Returns: |
| 1389 | + tuple: A tuple containing predictions and uncertainties (total, epistemic, aleatoric). |
| 1390 | + """ |
| 1391 | + self.eval() |
| 1392 | + if output_scaler is None and self.scaler_path is None: |
| 1393 | + warnings.warn("No scaler path provided, uncertainties are not in units of SLE.") |
| 1394 | + return self.forward(x, smooth_projection=smooth_projection, window_size=window_size) |
| 1395 | + |
| 1396 | + if isinstance(output_scaler, str): |
| 1397 | + self.scaler_path = output_scaler |
| 1398 | + with open(self.scaler_path, "rb") as f: |
| 1399 | + output_scaler = pickle.load(f) |
| 1400 | + |
| 1401 | + predictions, uncertainties = self.forward(x, smooth_projection=smooth_projection, window_size=window_size) |
| 1402 | + |
| 1403 | + # Apply inverse scaling to the predictions and uncertainties |
1342 | 1404 | unscaled_predictions = output_scaler.inverse_transform(predictions.reshape(-1, 1)) |
1343 | | - unscaled_bound_epistemic = output_scaler.inverse_transform(bound_epistemic.reshape(-1, 1)) |
1344 | | - unscaled_bound_aleatoric = output_scaler.inverse_transform(bound_aleatoric.reshape(-1, 1)) |
| 1405 | + unscaled_bound_epistemic = output_scaler.inverse_transform((predictions + uncertainties['epistemic']).reshape(-1, 1)) |
| 1406 | + unscaled_bound_aleatoric = output_scaler.inverse_transform((predictions + uncertainties['aleatoric']).reshape(-1, 1)) |
| 1407 | + |
1345 | 1408 | epistemic = unscaled_bound_epistemic - unscaled_predictions |
1346 | 1409 | aleatoric = unscaled_bound_aleatoric - unscaled_predictions |
1347 | 1410 |
|
|
0 commit comments