Skip to content

Commit 2fd4af8

Browse files
committed
iseflow v1.0.0
1 parent 4501d7a commit 2fd4af8

16 files changed

+87815
-1032
lines changed

checkpoint_AIS_temponly_regions

-98.1 KB
Binary file not shown.

ise/models/ISEFlow.py

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import os
1515
import pickle
1616
import warnings
17+
import copy
1718

1819
import numpy as np
1920
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_
12801281
)
12811282
self.trained = True
12821283

1283-
def forward(
1284-
self,
1285-
x,
1286-
smooth_projection=False,
1287-
):
1284+
def forward(self, x, smooth_projection=False, window_size=3):
12881285
"""
12891286
Performs a forward pass through the hybrid emulator.
12901287
12911288
Args:
12921289
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.
12971292
"""
12981293
self.eval()
12991294
x = to_tensor(x).to(self.device)
13001295
if not self.trained:
13011296
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)
13061300
prediction, epistemic = self.deep_ensemble(X_latent)
13071301
aleatoric = self.normalizing_flow.aleatoric(x, 100)
1302+
1303+
# Convert tensors to numpy arrays
13081304
prediction = prediction.detach().cpu().numpy()
13091305
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+
13101325
uncertainties = dict(
13111326
total=aleatoric + epistemic,
13121327
epistemic=epistemic,
13131328
aleatoric=aleatoric,
13141329
)
13151330

1316-
if smooth_projection:
1317-
stop = ""
1331+
13181332
return prediction, uncertainties
13191333

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+
"""
13211348
self.eval()
13221349
if output_scaler is None and self.scaler_path is None:
13231350
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):
13291354
self.scaler_path = output_scaler
13301355
with open(self.scaler_path, "rb") as f:
13311356
output_scaler = pickle.load(f)
13321357

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
13391367

1340-
bound_epistemic, bound_aleatoric = predictions + epi, predictions + ale
1368+
uncertainties = dict(
1369+
total=epistemic + aleatoric,
1370+
epistemic=epistemic,
1371+
aleatoric=aleatoric,
1372+
)
13411373

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
13421404
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+
13451408
epistemic = unscaled_bound_epistemic - unscaled_predictions
13461409
aleatoric = unscaled_bound_aleatoric - unscaled_predictions
13471410

ise/utils/functions.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -582,12 +582,13 @@ def get_X_y(
582582

583583
data['region'] = data.sector.map(sector_to_region)
584584
data['id'] = data['id'].apply(lambda x: "_".join(x.split('_')[:-1]))
585-
data = data.groupby(['region', 'id', 'year']).agg({
586-
'sle': 'mean',
585+
data = data.groupby(['region', 'id', 'year', 'Scenario']).agg({
586+
'sle': 'mean', # for GP, used mean instead of sum so that scales of errors are the same
587587
**{col: 'mean' for col in data.columns.difference(['sle']) if data[col].dtype == 'float64'}
588588
}, )
589589
data = data.drop(columns=['year']) # drop year so that there aren't two year columns after reset_index()
590590
data = data.reset_index()
591+
scenarios = data.Scenario
591592

592593
dropped_columns = [
593594
"id",
@@ -658,6 +659,9 @@ def get_X_y(
658659
raise ValueError(
659660
f"return_format must be in ['numpy', 'tensor', 'pandas'], received {return_format}"
660661
)
662+
663+
if regions:
664+
return X, y, scenarios
661665

662666
return X, y
663667

0 commit comments

Comments
 (0)