PVNet 2.6.10__tar.gz → 2.6.11__tar.gz
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.
- {PVNet-2.6.10 → PVNet-2.6.11}/PKG-INFO +1 -1
- {PVNet-2.6.10 → PVNet-2.6.11}/PVNet.egg-info/PKG-INFO +1 -1
- {PVNet-2.6.10 → PVNet-2.6.11}/PVNet.egg-info/SOURCES.txt +2 -0
- PVNet-2.6.11/pvnet/__init__.py +2 -0
- PVNet-2.6.10/pvnet/data/datamodule.py → PVNet-2.6.11/pvnet/data/base.py +1 -1
- PVNet-2.6.11/pvnet/data/datamodule.py +92 -0
- PVNet-2.6.11/pvnet/data/pv_site_datamodule.py +98 -0
- PVNet-2.6.11/pvnet/data/wind_datamodule.py +99 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/base_model.py +92 -24
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/baseline/last_value.py +2 -2
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/baseline/single_value.py +1 -1
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/deep_supervision.py +7 -7
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/encoders/encoders3d.py +24 -7
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/multimodal.py +101 -17
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/nwp_weighting.py +1 -1
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/site_encoders/encoders.py +48 -34
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/weather_residual.py +8 -8
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/utils.py +13 -5
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/utils.py +21 -9
- {PVNet-2.6.10 → PVNet-2.6.11}/tests/conftest.py +1 -1
- PVNet-2.6.10/pvnet/__init__.py +0 -2
- PVNet-2.6.10/pvnet/data/wind_datamodule.py +0 -141
- {PVNet-2.6.10 → PVNet-2.6.11}/LICENSE +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/MANIFEST.in +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/PVNet.egg-info/dependency_links.txt +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/PVNet.egg-info/requires.txt +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/PVNet.egg-info/top_level.txt +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/README.md +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/callbacks.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/data/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/data/utils.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/baseline/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/basic_blocks.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/encoders/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/encoders/basic_blocks.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/encoders/encoders2d.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/encoders/encodersRNN.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/linear_networks/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/linear_networks/basic_blocks.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/linear_networks/networks.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/site_encoders/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/models/multimodal/site_encoders/basic_blocks.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/optimizers.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pvnet/training.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/pyproject.toml +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/requirements-dev.txt +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/requirements.txt +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/setup.cfg +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/setup.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/tests/__init__.py +0 -0
- {PVNet-2.6.10 → PVNet-2.6.11}/tests/test_end2end.py +0 -0
|
@@ -11,7 +11,7 @@ from torch.utils.data.datapipes.iter import FileLister
|
|
|
11
11
|
from pvnet.data.utils import batch_to_tensor
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class BaseDataModule(LightningDataModule):
|
|
15
15
|
"""Datamodule for training pvnet and using pvnet pipeline in `ocf_datapipes`."""
|
|
16
16
|
|
|
17
17
|
def __init__(
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
""" Data module for pytorch lightning """
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from ocf_datapipes.batch import stack_np_examples_into_batch
|
|
5
|
+
from ocf_datapipes.training.pvnet import pvnet_datapipe
|
|
6
|
+
from torch.utils.data.datapipes.iter import FileLister
|
|
7
|
+
|
|
8
|
+
from pvnet.data.base import BaseDataModule
|
|
9
|
+
from pvnet.data.utils import batch_to_tensor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DataModule(BaseDataModule):
|
|
13
|
+
"""Datamodule for training pvnet and using pvnet pipeline in `ocf_datapipes`."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
configuration=None,
|
|
18
|
+
batch_size=16,
|
|
19
|
+
num_workers=0,
|
|
20
|
+
prefetch_factor=None,
|
|
21
|
+
train_period=[None, None],
|
|
22
|
+
val_period=[None, None],
|
|
23
|
+
test_period=[None, None],
|
|
24
|
+
batch_dir=None,
|
|
25
|
+
):
|
|
26
|
+
"""Datamodule for training pvnet and using pvnet pipeline in `ocf_datapipes`.
|
|
27
|
+
|
|
28
|
+
Can also be used with pre-made batches if `batch_dir` is set.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
configuration: Path to datapipe configuration file.
|
|
33
|
+
batch_size: Batch size.
|
|
34
|
+
num_workers: Number of workers to use in multiprocess batch loading.
|
|
35
|
+
prefetch_factor: Number of data will be prefetched at the end of each worker process.
|
|
36
|
+
train_period: Date range filter for train dataloader.
|
|
37
|
+
val_period: Date range filter for val dataloader.
|
|
38
|
+
test_period: Date range filter for test dataloader.
|
|
39
|
+
batch_dir: Path to the directory of pre-saved batches. Cannot be used together with
|
|
40
|
+
`configuration` or 'train/val/test_period'.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(
|
|
44
|
+
configuration=configuration,
|
|
45
|
+
batch_size=batch_size,
|
|
46
|
+
num_workers=num_workers,
|
|
47
|
+
prefetch_factor=prefetch_factor,
|
|
48
|
+
train_period=train_period,
|
|
49
|
+
val_period=val_period,
|
|
50
|
+
test_period=test_period,
|
|
51
|
+
batch_dir=batch_dir,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def _get_datapipe(self, start_time, end_time):
|
|
55
|
+
data_pipeline = pvnet_datapipe(
|
|
56
|
+
self.configuration,
|
|
57
|
+
start_time=start_time,
|
|
58
|
+
end_time=end_time,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
data_pipeline = (
|
|
62
|
+
data_pipeline.batch(self.batch_size)
|
|
63
|
+
.map(stack_np_examples_into_batch)
|
|
64
|
+
.map(batch_to_tensor)
|
|
65
|
+
)
|
|
66
|
+
return data_pipeline
|
|
67
|
+
|
|
68
|
+
def _get_premade_batches_datapipe(self, subdir, shuffle=False):
|
|
69
|
+
data_pipeline = FileLister(f"{self.batch_dir}/{subdir}", masks="*.pt", recursive=False)
|
|
70
|
+
if shuffle:
|
|
71
|
+
data_pipeline = (
|
|
72
|
+
data_pipeline.shuffle(buffer_size=10_000)
|
|
73
|
+
.sharding_filter()
|
|
74
|
+
.map(torch.load)
|
|
75
|
+
# Split the batches and reshuffle them to be combined into new batches
|
|
76
|
+
.split_batches()
|
|
77
|
+
.shuffle(buffer_size=100 * self.batch_size)
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
data_pipeline = (
|
|
81
|
+
data_pipeline.sharding_filter().map(torch.load)
|
|
82
|
+
# Split the batches so we can use any batch-size
|
|
83
|
+
.split_batches()
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
data_pipeline = (
|
|
87
|
+
data_pipeline.batch(self.batch_size)
|
|
88
|
+
.map(stack_np_examples_into_batch)
|
|
89
|
+
.map(batch_to_tensor)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return data_pipeline
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
""" Data module for pytorch lightning """
|
|
2
|
+
import glob
|
|
3
|
+
|
|
4
|
+
from ocf_datapipes.batch import BatchKey, stack_np_examples_into_batch
|
|
5
|
+
from ocf_datapipes.training.pvnet_site import pvnet_site_netcdf_datapipe
|
|
6
|
+
|
|
7
|
+
from pvnet.data.base import BaseDataModule
|
|
8
|
+
from pvnet.data.utils import batch_to_tensor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PVSiteDataModule(BaseDataModule):
|
|
12
|
+
"""Datamodule for training pvnet site and using pvnet site pipeline in `ocf_datapipes`."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
configuration=None,
|
|
17
|
+
batch_size=16,
|
|
18
|
+
num_workers=0,
|
|
19
|
+
prefetch_factor=None,
|
|
20
|
+
train_period=[None, None],
|
|
21
|
+
val_period=[None, None],
|
|
22
|
+
test_period=[None, None],
|
|
23
|
+
batch_dir=None,
|
|
24
|
+
):
|
|
25
|
+
"""Datamodule for training pvnet stie and using pvnet site pipeline in `ocf_datapipes`.
|
|
26
|
+
|
|
27
|
+
Can also be used with pre-made batches if `batch_dir` is set.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
configuration: Path to datapipe configuration file.
|
|
32
|
+
batch_size: Batch size.
|
|
33
|
+
num_workers: Number of workers to use in multiprocess batch loading.
|
|
34
|
+
train_period: Date range filter for train dataloader.
|
|
35
|
+
val_period: Date range filter for val dataloader.
|
|
36
|
+
test_period: Date range filter for test dataloader.
|
|
37
|
+
prefetch_factor: Number of data will be prefetched at the end of each worker process.
|
|
38
|
+
batch_dir: Path to the directory of pre-saved batches. Cannot be used together with
|
|
39
|
+
'train/val/test_period'.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(
|
|
43
|
+
configuration=configuration,
|
|
44
|
+
batch_size=batch_size,
|
|
45
|
+
num_workers=num_workers,
|
|
46
|
+
prefetch_factor=prefetch_factor,
|
|
47
|
+
train_period=train_period,
|
|
48
|
+
val_period=val_period,
|
|
49
|
+
test_period=test_period,
|
|
50
|
+
batch_dir=batch_dir,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def _get_datapipe(self, start_time, end_time):
|
|
54
|
+
data_pipeline = pvnet_site_netcdf_datapipe(
|
|
55
|
+
keys=["pv", "nwp"],
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
data_pipeline = (
|
|
59
|
+
data_pipeline.batch(self.batch_size)
|
|
60
|
+
.map(stack_np_examples_into_batch)
|
|
61
|
+
.map(batch_to_tensor)
|
|
62
|
+
)
|
|
63
|
+
return data_pipeline
|
|
64
|
+
|
|
65
|
+
def _get_premade_batches_datapipe(self, subdir, shuffle=False):
|
|
66
|
+
filenames = list(glob.glob(f"{self.batch_dir}/{subdir}/*.nc"))
|
|
67
|
+
data_pipeline = pvnet_site_netcdf_datapipe(
|
|
68
|
+
keys=["pv", "nwp"],
|
|
69
|
+
filenames=filenames,
|
|
70
|
+
)
|
|
71
|
+
data_pipeline = (
|
|
72
|
+
data_pipeline.batch(self.batch_size)
|
|
73
|
+
.map(stack_np_examples_into_batch)
|
|
74
|
+
.map(batch_to_tensor)
|
|
75
|
+
)
|
|
76
|
+
if shuffle:
|
|
77
|
+
data_pipeline = (
|
|
78
|
+
data_pipeline.shuffle(buffer_size=100)
|
|
79
|
+
.sharding_filter()
|
|
80
|
+
# Split the batches and reshuffle them to be combined into new batches
|
|
81
|
+
.split_batches(splitting_key=BatchKey.pv)
|
|
82
|
+
.shuffle(buffer_size=100 * self.batch_size)
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
data_pipeline = (
|
|
86
|
+
data_pipeline.sharding_filter()
|
|
87
|
+
# Split the batches so we can use any batch-size
|
|
88
|
+
.split_batches(splitting_key=BatchKey.pv)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
data_pipeline = (
|
|
92
|
+
data_pipeline.batch(self.batch_size)
|
|
93
|
+
.map(stack_np_examples_into_batch)
|
|
94
|
+
.map(batch_to_tensor)
|
|
95
|
+
.set_length(int(len(filenames) / self.batch_size))
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return data_pipeline
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
""" Data module for pytorch lightning """
|
|
2
|
+
import glob
|
|
3
|
+
|
|
4
|
+
from ocf_datapipes.batch import BatchKey, stack_np_examples_into_batch
|
|
5
|
+
from ocf_datapipes.training.windnet import windnet_netcdf_datapipe
|
|
6
|
+
|
|
7
|
+
from pvnet.data.base import BaseDataModule
|
|
8
|
+
from pvnet.data.utils import batch_to_tensor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WindDataModule(BaseDataModule):
|
|
12
|
+
"""Datamodule for training windnet and using windnet pipeline in `ocf_datapipes`."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
configuration=None,
|
|
17
|
+
batch_size=16,
|
|
18
|
+
num_workers=0,
|
|
19
|
+
prefetch_factor=None,
|
|
20
|
+
train_period=[None, None],
|
|
21
|
+
val_period=[None, None],
|
|
22
|
+
test_period=[None, None],
|
|
23
|
+
batch_dir=None,
|
|
24
|
+
):
|
|
25
|
+
"""Datamodule for training windnet and using windnet pipeline in `ocf_datapipes`.
|
|
26
|
+
|
|
27
|
+
Can also be used with pre-made batches if `batch_dir` is set.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
configuration: Path to datapipe configuration file.
|
|
32
|
+
batch_size: Batch size.
|
|
33
|
+
train_period: Date range filter for train dataloader.
|
|
34
|
+
val_period: Date range filter for val dataloader.
|
|
35
|
+
test_period: Date range filter for test dataloader.
|
|
36
|
+
num_workers: Number of workers to use in multiprocess batch loading.
|
|
37
|
+
prefetch_factor: Number of data will be prefetched at the end of each worker process.
|
|
38
|
+
batch_dir: Path to the directory of pre-saved batches. Cannot be used together with
|
|
39
|
+
'train/val/test_period'.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(
|
|
43
|
+
configuration=configuration,
|
|
44
|
+
batch_size=batch_size,
|
|
45
|
+
num_workers=num_workers,
|
|
46
|
+
prefetch_factor=prefetch_factor,
|
|
47
|
+
train_period=train_period,
|
|
48
|
+
val_period=val_period,
|
|
49
|
+
test_period=test_period,
|
|
50
|
+
batch_dir=batch_dir,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def _get_datapipe(self, start_time, end_time):
|
|
54
|
+
data_pipeline = windnet_netcdf_datapipe(
|
|
55
|
+
self.configuration,
|
|
56
|
+
keys=["wind", "nwp"],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
data_pipeline = (
|
|
60
|
+
data_pipeline.batch(self.batch_size)
|
|
61
|
+
.map(stack_np_examples_into_batch)
|
|
62
|
+
.map(batch_to_tensor)
|
|
63
|
+
)
|
|
64
|
+
return data_pipeline
|
|
65
|
+
|
|
66
|
+
def _get_premade_batches_datapipe(self, subdir, shuffle=False):
|
|
67
|
+
filenames = list(glob.glob(f"{self.batch_dir}/{subdir}/*.nc"))
|
|
68
|
+
data_pipeline = windnet_netcdf_datapipe(
|
|
69
|
+
keys=["wind", "nwp"],
|
|
70
|
+
filenames=filenames,
|
|
71
|
+
)
|
|
72
|
+
data_pipeline = (
|
|
73
|
+
data_pipeline.batch(self.batch_size)
|
|
74
|
+
.map(stack_np_examples_into_batch)
|
|
75
|
+
.map(batch_to_tensor)
|
|
76
|
+
)
|
|
77
|
+
if shuffle:
|
|
78
|
+
data_pipeline = (
|
|
79
|
+
data_pipeline.shuffle(buffer_size=100)
|
|
80
|
+
.sharding_filter()
|
|
81
|
+
# Split the batches and reshuffle them to be combined into new batches
|
|
82
|
+
.split_batches(splitting_key=BatchKey.wind)
|
|
83
|
+
.shuffle(buffer_size=100 * self.batch_size)
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
data_pipeline = (
|
|
87
|
+
data_pipeline.sharding_filter()
|
|
88
|
+
# Split the batches so we can use any batch-size
|
|
89
|
+
.split_batches(splitting_key=BatchKey.wind)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
data_pipeline = (
|
|
93
|
+
data_pipeline.batch(self.batch_size)
|
|
94
|
+
.map(stack_np_examples_into_batch)
|
|
95
|
+
.map(batch_to_tensor)
|
|
96
|
+
.set_length(int(len(filenames) / self.batch_size))
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return data_pipeline
|
|
@@ -7,6 +7,7 @@ from typing import Dict, Optional, Union
|
|
|
7
7
|
|
|
8
8
|
import hydra
|
|
9
9
|
import lightning.pytorch as pl
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
10
11
|
import pandas as pd
|
|
11
12
|
import torch
|
|
12
13
|
import torch.nn.functional as F
|
|
@@ -19,7 +20,6 @@ from huggingface_hub.hf_api import HfApi
|
|
|
19
20
|
from huggingface_hub.utils._deprecation import _deprecate_positional_args
|
|
20
21
|
from ocf_datapipes.batch import BatchKey
|
|
21
22
|
from ocf_ml_metrics.evaluation.evaluation import evaluation
|
|
22
|
-
from ocf_ml_metrics.metrics.errors import common_metrics
|
|
23
23
|
|
|
24
24
|
from pvnet.models.utils import (
|
|
25
25
|
BatchAccumulator,
|
|
@@ -65,6 +65,11 @@ def make_clean_data_config(input_path, output_path, placeholder="PLACEHOLDER"):
|
|
|
65
65
|
d["pv_filename"] = f"{placeholder}.netcdf"
|
|
66
66
|
d["pv_metadata_filename"] = f"{placeholder}.csv"
|
|
67
67
|
|
|
68
|
+
if "sensor" in config["input_data"]:
|
|
69
|
+
# If not empty - i.e. if used
|
|
70
|
+
if config["input_data"][source][f"{source}_filename"] != "":
|
|
71
|
+
config["input_data"][source][f"{source}_filename"] = f"{placeholder}.nc"
|
|
72
|
+
|
|
68
73
|
with open(output_path, "w") as outfile:
|
|
69
74
|
yaml.dump(config, outfile, default_flow_style=False)
|
|
70
75
|
|
|
@@ -234,7 +239,9 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
234
239
|
forecast_minutes: int,
|
|
235
240
|
optimizer: AbstractOptimizer,
|
|
236
241
|
output_quantiles: Optional[list[float]] = None,
|
|
237
|
-
target_key:
|
|
242
|
+
target_key: str = "gsp",
|
|
243
|
+
interval_minutes: int = 30,
|
|
244
|
+
timestep_intervals_to_plot: Optional[list[int]] = None,
|
|
238
245
|
):
|
|
239
246
|
"""Abtstract base class for PVNet submodels.
|
|
240
247
|
|
|
@@ -245,11 +252,21 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
245
252
|
output_quantiles: A list of float (0.0, 1.0) quantiles to predict values for. If set to
|
|
246
253
|
None the output is a single value.
|
|
247
254
|
target_key: The key of the target variable in the batch
|
|
255
|
+
interval_minutes: The interval in minutes between each timestep in the data
|
|
256
|
+
timestep_intervals_to_plot: Intervals, in timesteps, to plot during training
|
|
248
257
|
"""
|
|
249
258
|
super().__init__()
|
|
250
259
|
|
|
251
260
|
self._optimizer = optimizer
|
|
252
|
-
self.
|
|
261
|
+
self._target_key_name = target_key
|
|
262
|
+
self._target_key = BatchKey[f"{target_key}"]
|
|
263
|
+
if timestep_intervals_to_plot is not None:
|
|
264
|
+
for interval in timestep_intervals_to_plot:
|
|
265
|
+
assert type(interval) in [list, tuple] and len(interval) == 2, ValueError(
|
|
266
|
+
f"timestep_intervals_to_plot must be a list of tuples or lists of length 2, "
|
|
267
|
+
f"but got {timestep_intervals_to_plot=}"
|
|
268
|
+
)
|
|
269
|
+
self.time_step_intervals_to_plot = timestep_intervals_to_plot
|
|
253
270
|
|
|
254
271
|
# Model must have lr to allow tuning
|
|
255
272
|
# This setting is only used when lr is tuned with callback
|
|
@@ -260,13 +277,13 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
260
277
|
self.output_quantiles = output_quantiles
|
|
261
278
|
|
|
262
279
|
# Number of timestemps for 30 minutely data
|
|
263
|
-
self.
|
|
264
|
-
self.
|
|
280
|
+
self.history_len = history_minutes // interval_minutes
|
|
281
|
+
self.forecast_len = forecast_minutes // interval_minutes
|
|
265
282
|
|
|
266
|
-
self.weighted_losses = WeightedLosses(forecast_length=self.
|
|
283
|
+
self.weighted_losses = WeightedLosses(forecast_length=self.forecast_len)
|
|
267
284
|
|
|
268
285
|
self._accumulated_metrics = MetricAccumulator()
|
|
269
|
-
self._accumulated_batches = BatchAccumulator()
|
|
286
|
+
self._accumulated_batches = BatchAccumulator(key_to_keep=self._target_key_name)
|
|
270
287
|
self._accumulated_y_hat = PredAccumulator()
|
|
271
288
|
|
|
272
289
|
@property
|
|
@@ -278,9 +295,9 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
278
295
|
def num_output_features(self):
|
|
279
296
|
"""Number of ouput features he model chould predict for"""
|
|
280
297
|
if self.use_quantile_regression:
|
|
281
|
-
out_features = self.
|
|
298
|
+
out_features = self.forecast_len * len(self.output_quantiles)
|
|
282
299
|
else:
|
|
283
|
-
out_features = self.
|
|
300
|
+
out_features = self.forecast_len
|
|
284
301
|
return out_features
|
|
285
302
|
|
|
286
303
|
def _quantiles_to_prediction(self, y_quantiles):
|
|
@@ -372,15 +389,12 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
372
389
|
|
|
373
390
|
# Take median value for remaining metric calculations
|
|
374
391
|
y_hat = self._quantiles_to_prediction(y_hat)
|
|
392
|
+
mse_each_step = torch.mean((y_hat - y) ** 2, dim=0)
|
|
393
|
+
mae_each_step = torch.mean(torch.abs(y_hat - y), dim=0)
|
|
375
394
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
)
|
|
379
|
-
mse_each_step = common_metrics_each_step["rmse"] ** 2
|
|
380
|
-
mae_each_step = common_metrics_each_step["mae"]
|
|
395
|
+
losses.update({f"MSE_horizon/step_{i:03}": m for i, m in enumerate(mse_each_step)})
|
|
396
|
+
losses.update({f"MAE_horizon/step_{i:03}": m for i, m in enumerate(mae_each_step)})
|
|
381
397
|
|
|
382
|
-
losses.update({f"MSE_horizon/step_{i:02}": m for i, m in enumerate(mse_each_step)})
|
|
383
|
-
losses.update({f"MAE_horizon/step_{i:02}": m for i, m in enumerate(mae_each_step)})
|
|
384
398
|
return losses
|
|
385
399
|
|
|
386
400
|
def _calculate_test_losses(self, y, y_hat):
|
|
@@ -421,13 +435,20 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
421
435
|
# We only create the figure every 8 log steps
|
|
422
436
|
# This was reduced as it was creating figures too often
|
|
423
437
|
if grad_batch_num % (8 * self.trainer.log_every_n_steps) == 0:
|
|
424
|
-
fig = plot_batch_forecasts(
|
|
438
|
+
fig = plot_batch_forecasts(
|
|
439
|
+
batch,
|
|
440
|
+
y_hat,
|
|
441
|
+
batch_idx,
|
|
442
|
+
quantiles=self.output_quantiles,
|
|
443
|
+
key_to_plot=self._target_key_name,
|
|
444
|
+
)
|
|
425
445
|
fig.savefig("latest_logged_train_batch.png")
|
|
446
|
+
plt.close(fig)
|
|
426
447
|
|
|
427
448
|
def training_step(self, batch, batch_idx):
|
|
428
449
|
"""Run training step"""
|
|
429
450
|
y_hat = self(batch)
|
|
430
|
-
y = batch[self._target_key][:, -self.
|
|
451
|
+
y = batch[self._target_key][:, -self.forecast_len :, 0]
|
|
431
452
|
|
|
432
453
|
losses = self._calculate_common_losses(y, y_hat)
|
|
433
454
|
losses = {f"{k}/train": v for k, v in losses.items()}
|
|
@@ -443,12 +464,36 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
443
464
|
def validation_step(self, batch: dict, batch_idx):
|
|
444
465
|
"""Run validation step"""
|
|
445
466
|
y_hat = self(batch)
|
|
446
|
-
|
|
467
|
+
# Sensor seems to be in batch, station, time order
|
|
468
|
+
y = batch[self._target_key][:, -self.forecast_len :, 0]
|
|
447
469
|
|
|
448
470
|
losses = self._calculate_common_losses(y, y_hat)
|
|
449
471
|
losses.update(self._calculate_val_losses(y, y_hat))
|
|
450
472
|
|
|
451
473
|
logged_losses = {f"{k}/val": v for k, v in losses.items()}
|
|
474
|
+
# Get the losses in the format of {VAL>_horizon/step_000: 0.1, VAL>_horizon/step_001: 0.2}
|
|
475
|
+
# for each step in the forecast horizon
|
|
476
|
+
# This is needed for the custom plot
|
|
477
|
+
# And needs to be in order of step
|
|
478
|
+
x_values = [
|
|
479
|
+
int(k.split("_")[-1].split("/")[0])
|
|
480
|
+
for k in logged_losses.keys()
|
|
481
|
+
if "MAE_horizon/step" in k
|
|
482
|
+
]
|
|
483
|
+
y_values = []
|
|
484
|
+
for x in x_values:
|
|
485
|
+
y_values.append(logged_losses[f"MAE_horizon/step_{x:03}/val"])
|
|
486
|
+
per_step_losses = [[x, y] for (x, y) in zip(x_values, y_values)]
|
|
487
|
+
# Check if WandBLogger is being used
|
|
488
|
+
if isinstance(self.logger, pl.loggers.WandbLogger):
|
|
489
|
+
table = wandb.Table(data=per_step_losses, columns=["timestep", "MAE"])
|
|
490
|
+
wandb.log(
|
|
491
|
+
{
|
|
492
|
+
"mae_vs_timestep": wandb.plot.line(
|
|
493
|
+
table, "timestep", "MAE", title="MAE vs Timestep"
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
)
|
|
452
497
|
|
|
453
498
|
self.log_dict(
|
|
454
499
|
logged_losses,
|
|
@@ -462,23 +507,46 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
462
507
|
# Store these temporarily under self
|
|
463
508
|
if not hasattr(self, "_val_y_hats"):
|
|
464
509
|
self._val_y_hats = PredAccumulator()
|
|
465
|
-
self._val_batches = BatchAccumulator()
|
|
510
|
+
self._val_batches = BatchAccumulator(key_to_keep=self._target_key_name)
|
|
466
511
|
|
|
467
512
|
self._val_y_hats.append(y_hat)
|
|
468
513
|
self._val_batches.append(batch)
|
|
469
|
-
|
|
470
514
|
# if batch had accumulated
|
|
471
515
|
if (batch_idx + 1) % self.trainer.accumulate_grad_batches == 0:
|
|
472
516
|
y_hat = self._val_y_hats.flush()
|
|
473
517
|
batch = self._val_batches.flush()
|
|
474
518
|
|
|
475
|
-
fig = plot_batch_forecasts(
|
|
519
|
+
fig = plot_batch_forecasts(
|
|
520
|
+
batch,
|
|
521
|
+
y_hat,
|
|
522
|
+
quantiles=self.output_quantiles,
|
|
523
|
+
key_to_plot=self._target_key_name,
|
|
524
|
+
)
|
|
476
525
|
|
|
477
526
|
self.logger.experiment.log(
|
|
478
527
|
{
|
|
479
|
-
f"val_forecast_samples/batch_idx_{accum_batch_num}": wandb.Image(fig),
|
|
528
|
+
f"val_forecast_samples/batch_idx_{accum_batch_num}_all": wandb.Image(fig),
|
|
480
529
|
}
|
|
481
530
|
)
|
|
531
|
+
plt.close(fig)
|
|
532
|
+
|
|
533
|
+
if self.time_step_intervals_to_plot is not None:
|
|
534
|
+
for interval in self.time_step_intervals_to_plot:
|
|
535
|
+
fig = plot_batch_forecasts(
|
|
536
|
+
batch,
|
|
537
|
+
y_hat,
|
|
538
|
+
quantiles=self.output_quantiles,
|
|
539
|
+
key_to_plot=self._target_key_name,
|
|
540
|
+
timesteps_to_plot=interval,
|
|
541
|
+
)
|
|
542
|
+
self.logger.experiment.log(
|
|
543
|
+
{
|
|
544
|
+
f"val_forecast_samples/batch_idx_{accum_batch_num}_"
|
|
545
|
+
f"timestep_{interval}": wandb.Image(fig),
|
|
546
|
+
}
|
|
547
|
+
)
|
|
548
|
+
plt.close(fig)
|
|
549
|
+
|
|
482
550
|
del self._val_y_hats
|
|
483
551
|
del self._val_batches
|
|
484
552
|
|
|
@@ -487,7 +555,7 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
|
|
|
487
555
|
def test_step(self, batch, batch_idx):
|
|
488
556
|
"""Run test step"""
|
|
489
557
|
y_hat = self(batch)
|
|
490
|
-
y = batch[self._target_key][:, -self.
|
|
558
|
+
y = batch[self._target_key][:, -self.forecast_len :, 0]
|
|
491
559
|
|
|
492
560
|
losses = self._calculate_common_losses(y, y_hat)
|
|
493
561
|
losses.update(self._calculate_val_losses(y, y_hat))
|
|
@@ -36,8 +36,8 @@ class Model(BaseModel):
|
|
|
36
36
|
|
|
37
37
|
# take the last value non forecaster value and the first in the pv yeild
|
|
38
38
|
# (this is the pv site we are preditcting for)
|
|
39
|
-
y_hat = gsp_yield[:, -self.
|
|
39
|
+
y_hat = gsp_yield[:, -self.forecast_len - 1, 0]
|
|
40
40
|
|
|
41
41
|
# expand the last valid forward n predict steps
|
|
42
|
-
out = y_hat.unsqueeze(1).repeat(1, self.
|
|
42
|
+
out = y_hat.unsqueeze(1).repeat(1, self.forecast_len)
|
|
43
43
|
return out
|
|
@@ -33,5 +33,5 @@ class Model(BaseModel):
|
|
|
33
33
|
def forward(self, x: dict):
|
|
34
34
|
"""Run model forward on dict batch of data"""
|
|
35
35
|
# Returns a single value at all steps
|
|
36
|
-
y_hat = torch.zeros_like(x[BatchKey.gsp][:, : self.
|
|
36
|
+
y_hat = torch.zeros_like(x[BatchKey.gsp][:, : self.forecast_len, 0]) + self._value
|
|
37
37
|
return y_hat
|
|
@@ -161,7 +161,7 @@ class Model(BaseModel):
|
|
|
161
161
|
if self.include_sun:
|
|
162
162
|
# the minus 12 is bit of hard coded smudge for pvnet
|
|
163
163
|
self.sun_fc1 = nn.Linear(
|
|
164
|
-
in_features=2 * (self.
|
|
164
|
+
in_features=2 * (self.forecast_len + self.history_len + 1),
|
|
165
165
|
out_features=16,
|
|
166
166
|
)
|
|
167
167
|
|
|
@@ -170,18 +170,18 @@ class Model(BaseModel):
|
|
|
170
170
|
num_cat_features += encoder_out_features
|
|
171
171
|
self.sat_output_network = output_network(
|
|
172
172
|
in_features=encoder_out_features,
|
|
173
|
-
out_features=self.
|
|
173
|
+
out_features=self.forecast_len,
|
|
174
174
|
**output_network_kwargs,
|
|
175
175
|
)
|
|
176
176
|
if include_nwp:
|
|
177
177
|
num_cat_features += encoder_out_features
|
|
178
178
|
self.nwp_output_network = output_network(
|
|
179
179
|
in_features=encoder_out_features,
|
|
180
|
-
out_features=self.
|
|
180
|
+
out_features=self.forecast_len,
|
|
181
181
|
**output_network_kwargs,
|
|
182
182
|
)
|
|
183
183
|
if include_gsp_yield_history:
|
|
184
|
-
num_cat_features += self.
|
|
184
|
+
num_cat_features += self.history_len
|
|
185
185
|
if embedding_dim:
|
|
186
186
|
num_cat_features += embedding_dim
|
|
187
187
|
if include_sun:
|
|
@@ -189,7 +189,7 @@ class Model(BaseModel):
|
|
|
189
189
|
|
|
190
190
|
self.output_network = output_network(
|
|
191
191
|
in_features=num_cat_features,
|
|
192
|
-
out_features=self.
|
|
192
|
+
out_features=self.forecast_len,
|
|
193
193
|
**output_network_kwargs,
|
|
194
194
|
)
|
|
195
195
|
|
|
@@ -226,7 +226,7 @@ class Model(BaseModel):
|
|
|
226
226
|
# *********************** GSP Data ************************************
|
|
227
227
|
# add gsp yield history
|
|
228
228
|
if self.include_gsp_yield_history:
|
|
229
|
-
gsp_history = x[BatchKey.gsp][:, : self.
|
|
229
|
+
gsp_history = x[BatchKey.gsp][:, : self.history_len].float()
|
|
230
230
|
gsp_history = gsp_history.reshape(gsp_history.shape[0], -1)
|
|
231
231
|
gsp_history = self.source_dropout_0d(gsp_history)
|
|
232
232
|
modes["gsp"] = gsp_history
|
|
@@ -266,7 +266,7 @@ class Model(BaseModel):
|
|
|
266
266
|
def training_step(self, batch, batch_idx):
|
|
267
267
|
"""Training step"""
|
|
268
268
|
y_hats = self.multi_mode_forward(batch)
|
|
269
|
-
y = batch[BatchKey.gsp][:, -self.
|
|
269
|
+
y = batch[BatchKey.gsp][:, -self.forecast_len :, 0]
|
|
270
270
|
|
|
271
271
|
losses = self._calculate_common_losses(y, y_hats["all"])
|
|
272
272
|
losses = {f"{k}/train": v for k, v in losses.items()}
|