Skip to content

Commit 785a793

Browse files
option to ignore first part of forecast in the model (#202)
* option to ignore first part of forecast in the model * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix * correction for sun features * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix * init * add DA experiments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add images * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 93b6210 commit 785a793

File tree

8 files changed

+72
-5
lines changed

8 files changed

+72
-5
lines changed

experiments/india/006_da_only/bad.png

349 KB
Loading
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## DA forecasts only
2+
3+
The idea was to create a forecast for DA (day-ahead) only for Windnet.
4+
We hope this would bring down the DA MAE values.
5+
6+
We do this by not forecasting the first X hours.
7+
8+
Unfortunately, it doesnt not look like ignore X hours, make the DA forecast better.
9+
10+
## Experiments
11+
12+
1. Baseline - [here](https://wandb.ai/openclimatefix/india/runs/miszfep5)
13+
2. Ignore first 6 hours - [here](https://wandb.ai/openclimatefix/india/runs/uosk0qug)
14+
3. Ignore first 12 hours - [here](https://wandb.ai/openclimatefix/india/runs/s9cnn4ei)
15+
16+
## Results
17+
18+
| Timestep | all MAE % | 6 MAE % | 12 MAE % |
19+
| --- | --- |---------|---------|
20+
| 0-0 minutes | nan | nan | nan |
21+
| 15-15 minutes | nan | nan | nan |
22+
| 30-45 minutes | 0.065 | nan | nan |
23+
| 45-60 minutes | 0.066 | nan | nan |
24+
| 60-120 minutes | 0.063 | nan | nan |
25+
| 120-240 minutes | 0.063 | nan | nan |
26+
| 240-360 minutes | 0.064 | nan | nan |
27+
| 360-480 minutes | 0.065 | 0.068 | nan |
28+
| 480-720 minutes | 0.067 | 0.065 | nan |
29+
| 720-1440 minutes | 0.068 | 0.065 | 0.065 |
30+
| 1440-2880 minutes | 0.071 | 0.071 | 0.071 |
31+
32+
![](mae_steps.png "mae_steps")
33+
34+
Here's two examples from the 6 hour ignore model, one that forecated it well, one that didnt
35+
36+
![](bad.png "bad")
37+
![](good.png "good")
414 KB
Loading
119 KB
Loading

pvnet/models/base_model.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def save_pretrained(
245245

246246

247247
class BaseModel(pl.LightningModule, PVNetModelHubMixin):
248-
"""Abtstract base class for PVNet submodels"""
248+
"""Abstract base class for PVNet submodels"""
249249

250250
def __init__(
251251
self,
@@ -257,6 +257,7 @@ def __init__(
257257
interval_minutes: int = 30,
258258
timestep_intervals_to_plot: Optional[list[int]] = None,
259259
use_weighted_loss: bool = False,
260+
forecast_minutes_ignore: Optional[int] = 0,
260261
):
261262
"""Abtstract base class for PVNet submodels.
262263
@@ -270,6 +271,8 @@ def __init__(
270271
interval_minutes: The interval in minutes between each timestep in the data
271272
timestep_intervals_to_plot: Intervals, in timesteps, to plot during training
272273
use_weighted_loss: Whether to use a weighted loss function
274+
forecast_minutes_ignore: Number of forecast minutes to ignore when calculating losses.
275+
For example if set to 60, the model doesnt predict the first 60 minutes
273276
"""
274277
super().__init__()
275278

@@ -292,10 +295,12 @@ def __init__(
292295
self.forecast_minutes = forecast_minutes
293296
self.output_quantiles = output_quantiles
294297
self.interval_minutes = interval_minutes
298+
self.forecast_minutes_ignore = forecast_minutes_ignore
295299

296300
# Number of timestemps for 30 minutely data
297301
self.history_len = history_minutes // interval_minutes
298-
self.forecast_len = forecast_minutes // interval_minutes
302+
self.forecast_len = (forecast_minutes - forecast_minutes_ignore) // interval_minutes
303+
self.forecast_len_ignore = forecast_minutes_ignore // interval_minutes
299304

300305
self.weighted_losses = WeightedLosses(forecast_length=self.forecast_len)
301306

@@ -334,7 +339,7 @@ def _quantiles_to_prediction(self, y_quantiles):
334339
y_median = y_quantiles[..., idx]
335340
return y_median
336341

337-
def _calculate_qauntile_loss(self, y_quantiles, y):
342+
def _calculate_quantile_loss(self, y_quantiles, y):
338343
"""Calculate quantile loss.
339344
340345
Note:
@@ -366,7 +371,7 @@ def _calculate_common_losses(self, y, y_hat):
366371
losses = {}
367372

368373
if self.use_quantile_regression:
369-
losses["quantile_loss"] = self._calculate_qauntile_loss(y_hat, y)
374+
losses["quantile_loss"] = self._calculate_quantile_loss(y_hat, y)
370375
y_hat = self._quantiles_to_prediction(y_hat)
371376

372377
# calculate mse, mae

pvnet/models/multimodal/multimodal.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def __init__(
7171
timestep_intervals_to_plot: Optional[list[int]] = None,
7272
adapt_batches: Optional[bool] = False,
7373
use_weighted_loss: Optional[bool] = False,
74+
forecast_minutes_ignore: Optional[int] = 0,
7475
):
7576
"""Neural network which combines information from different sources.
7677
@@ -131,6 +132,8 @@ def __init__(
131132
the model to use. This allows us to overprepare batches and slice from them for the
132133
data we need for a model run.
133134
use_weighted_loss: Whether to use a weighted loss function
135+
forecast_minutes_ignore: Number of forecast minutes to ignore when calculating losses.
136+
For example if set to 60, the model doesnt predict the first 60 minutes
134137
"""
135138

136139
self.include_gsp_yield_history = include_gsp_yield_history
@@ -154,6 +157,7 @@ def __init__(
154157
interval_minutes=interval_minutes,
155158
timestep_intervals_to_plot=timestep_intervals_to_plot,
156159
use_weighted_loss=use_weighted_loss,
160+
forecast_minutes_ignore=forecast_minutes_ignore,
157161
)
158162

159163
# Number of features expected by the output_network
@@ -271,7 +275,8 @@ def __init__(
271275

272276
if self.include_sun:
273277
self.sun_fc1 = nn.Linear(
274-
in_features=2 * (self.forecast_len + self.history_len + 1),
278+
in_features=2
279+
* (self.forecast_len + self.forecast_len_ignore + self.history_len + 1),
275280
out_features=16,
276281
)
277282

tests/conftest.py

+9
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,12 @@ def multimodal_weighted_quantile_model(multimodal_model_kwargs):
303303
output_quantiles=[0.1, 0.5, 0.9], **multimodal_model_kwargs, use_weighted_loss=True
304304
)
305305
return model
306+
307+
308+
@pytest.fixture()
309+
def multimodal_quantile_model_ignore_minutes(multimodal_model_kwargs):
310+
"""Only forecsat second half of the 8 hours"""
311+
model = Model(
312+
output_quantiles=[0.1, 0.5, 0.9], **multimodal_model_kwargs, forecast_minutes_ignore=240
313+
)
314+
return model

tests/models/multimodal/test_multimodal.py

+11
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,14 @@ def test_weighted_quantile_model_backward(multimodal_weighted_quantile_model, sa
5151

5252
# Backwards on sum drives sum to zero
5353
y_quantiles.sum().backward()
54+
55+
56+
def test_weighted_quantile_model_forward(multimodal_quantile_model_ignore_minutes, sample_batch):
57+
y_quantiles = multimodal_quantile_model_ignore_minutes(sample_batch)
58+
59+
# check output is the correct shape
60+
# batch size=2, forecast_len=8, num_quantiles=3
61+
assert tuple(y_quantiles.shape) == (2, 8, 3), y_quantiles.shape
62+
63+
# Backwards on sum drives sum to zero
64+
y_quantiles.sum().backward()

0 commit comments

Comments
 (0)