PVNet 3.0.22__tar.gz → 3.0.24__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.
Files changed (52) hide show
  1. {PVNet-3.0.22 → PVNet-3.0.24}/PKG-INFO +1 -1
  2. {PVNet-3.0.22 → PVNet-3.0.24}/PVNet.egg-info/PKG-INFO +1 -1
  3. PVNet-3.0.24/pvnet/__init__.py +2 -0
  4. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/base_model.py +74 -63
  5. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/unimodal_teacher.py +10 -1
  6. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/optimizers.py +0 -1
  7. PVNet-3.0.22/pvnet/__init__.py +0 -2
  8. {PVNet-3.0.22 → PVNet-3.0.24}/LICENSE +0 -0
  9. {PVNet-3.0.22 → PVNet-3.0.24}/MANIFEST.in +0 -0
  10. {PVNet-3.0.22 → PVNet-3.0.24}/PVNet.egg-info/SOURCES.txt +0 -0
  11. {PVNet-3.0.22 → PVNet-3.0.24}/PVNet.egg-info/dependency_links.txt +0 -0
  12. {PVNet-3.0.22 → PVNet-3.0.24}/PVNet.egg-info/requires.txt +0 -0
  13. {PVNet-3.0.22 → PVNet-3.0.24}/PVNet.egg-info/top_level.txt +0 -0
  14. {PVNet-3.0.22 → PVNet-3.0.24}/README.md +0 -0
  15. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/callbacks.py +0 -0
  16. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/data/__init__.py +0 -0
  17. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/data/base.py +0 -0
  18. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/data/datamodule.py +0 -0
  19. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/data/pv_site_datamodule.py +0 -0
  20. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/data/utils.py +0 -0
  21. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/data/wind_datamodule.py +0 -0
  22. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/__init__.py +0 -0
  23. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/baseline/__init__.py +0 -0
  24. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/baseline/last_value.py +0 -0
  25. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/baseline/single_value.py +0 -0
  26. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/ensemble.py +0 -0
  27. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/__init__.py +0 -0
  28. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/basic_blocks.py +0 -0
  29. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/encoders/__init__.py +0 -0
  30. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/encoders/basic_blocks.py +0 -0
  31. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/encoders/encoders2d.py +0 -0
  32. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/encoders/encoders3d.py +0 -0
  33. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/encoders/encodersRNN.py +0 -0
  34. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/linear_networks/__init__.py +0 -0
  35. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/linear_networks/basic_blocks.py +0 -0
  36. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/linear_networks/networks.py +0 -0
  37. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/multimodal.py +0 -0
  38. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/multimodal_base.py +0 -0
  39. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/site_encoders/__init__.py +0 -0
  40. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/site_encoders/basic_blocks.py +0 -0
  41. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/multimodal/site_encoders/encoders.py +0 -0
  42. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/models/utils.py +0 -0
  43. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/training.py +0 -0
  44. {PVNet-3.0.22 → PVNet-3.0.24}/pvnet/utils.py +0 -0
  45. {PVNet-3.0.22 → PVNet-3.0.24}/pyproject.toml +0 -0
  46. {PVNet-3.0.22 → PVNet-3.0.24}/requirements-dev.txt +0 -0
  47. {PVNet-3.0.22 → PVNet-3.0.24}/requirements.txt +0 -0
  48. {PVNet-3.0.22 → PVNet-3.0.24}/setup.cfg +0 -0
  49. {PVNet-3.0.22 → PVNet-3.0.24}/setup.py +0 -0
  50. {PVNet-3.0.22 → PVNet-3.0.24}/tests/__init__.py +0 -0
  51. {PVNet-3.0.22 → PVNet-3.0.24}/tests/conftest.py +0 -0
  52. {PVNet-3.0.22 → PVNet-3.0.24}/tests/test_end2end.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PVNet
3
- Version: 3.0.22
3
+ Version: 3.0.24
4
4
  Summary: PVNet
5
5
  Author: Peter Dudfield
6
6
  Author-email: info@openclimatefix.org
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PVNet
3
- Version: 3.0.22
3
+ Version: 3.0.24
4
4
  Summary: PVNet
5
5
  Author: Peter Dudfield
6
6
  Author-email: info@openclimatefix.org
@@ -0,0 +1,2 @@
1
+ """PVNet"""
2
+ __version__ = "3.0.24"
@@ -165,7 +165,7 @@ class PVNetModelHubMixin(PyTorchModelHubMixin):
165
165
  self,
166
166
  save_directory: Union[str, Path],
167
167
  config: dict,
168
- data_config: Union[str, Path],
168
+ data_config: Optional[Union[str, Path]],
169
169
  repo_id: Optional[str] = None,
170
170
  push_to_hub: bool = False,
171
171
  wandb_ids: Optional[Union[list[str], str]] = None,
@@ -206,7 +206,8 @@ class PVNetModelHubMixin(PyTorchModelHubMixin):
206
206
  (save_directory / CONFIG_NAME).write_text(json.dumps(config, indent=4))
207
207
 
208
208
  # Save cleaned datapipes configuration file
209
- make_clean_data_config(data_config, save_directory / DATA_CONFIG_NAME)
209
+ if data_config is not None:
210
+ make_clean_data_config(data_config, save_directory / DATA_CONFIG_NAME)
210
211
 
211
212
  # Creating and saving model card.
212
213
  card_data = ModelCardData(language="en", license="mit", library_name="pytorch")
@@ -301,6 +302,7 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
301
302
  self._accumulated_metrics = MetricAccumulator()
302
303
  self._accumulated_batches = BatchAccumulator(key_to_keep=self._target_key_name)
303
304
  self._accumulated_y_hat = PredAccumulator()
305
+ self._horizon_maes = MetricAccumulator()
304
306
 
305
307
  # Store whether the model should use quantile regression or simply predict the mean
306
308
  self.use_quantile_regression = self.output_quantiles is not None
@@ -389,6 +391,18 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
389
391
 
390
392
  return losses
391
393
 
394
+ def _step_mae_and_mse(self, y, y_hat, dict_key_root):
395
+ """Calculate the MSE and MAE at each forecast step"""
396
+ losses = {}
397
+
398
+ mse_each_step = torch.mean((y_hat - y) ** 2, dim=0)
399
+ mae_each_step = torch.mean(torch.abs(y_hat - y), dim=0)
400
+
401
+ losses.update({f"MSE_{dict_key_root}/step_{i:03}": m for i, m in enumerate(mse_each_step)})
402
+ losses.update({f"MAE_{dict_key_root}/step_{i:03}": m for i, m in enumerate(mae_each_step)})
403
+
404
+ return losses
405
+
392
406
  def _calculate_val_losses(self, y, y_hat):
393
407
  """Calculate additional validation losses"""
394
408
 
@@ -404,12 +418,17 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
404
418
 
405
419
  # Take median value for remaining metric calculations
406
420
  y_hat = self._quantiles_to_prediction(y_hat)
407
- mse_each_step = torch.mean((y_hat - y) ** 2, dim=0)
408
- mae_each_step = torch.mean(torch.abs(y_hat - y), dim=0)
409
421
 
410
- losses.update({f"MSE_horizon/step_{i:03}": m for i, m in enumerate(mse_each_step)})
411
- losses.update({f"MAE_horizon/step_{i:03}": m for i, m in enumerate(mae_each_step)})
422
+ # Log the loss at each time horizon
423
+ losses.update(self._step_mae_and_mse(y, y_hat, dict_key_root="horizon"))
424
+
425
+ # Log the persistance losses
426
+ y_persist = y[:, -1].unsqueeze(1).expand(-1, self.forecast_len)
427
+ losses["MAE_persistence/val"] = F.l1_loss(y_persist, y)
428
+ losses["MSE_persistence/val"] = F.mse_loss(y_persist, y)
412
429
 
430
+ # Log persistance loss at each time horizon
431
+ losses.update(self._step_mae_and_mse(y, y_persist, dict_key_root="persistence"))
413
432
  return losses
414
433
 
415
434
  def _calculate_test_losses(self, y, y_hat):
@@ -476,51 +495,36 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
476
495
  opt_target = losses["MAE/train"]
477
496
  return opt_target
478
497
 
498
+ def _log_forecast_plot(self, batch, y_hat, accum_batch_num, timesteps_to_plot, plot_suffix):
499
+ """Log forecast plot to wandb"""
500
+ fig = plot_batch_forecasts(
501
+ batch,
502
+ y_hat,
503
+ quantiles=self.output_quantiles,
504
+ key_to_plot=self._target_key_name,
505
+ )
506
+
507
+ plot_name = f"val_forecast_samples/batch_idx_{accum_batch_num}_{plot_suffix}"
508
+
509
+ self.logger.experiment.log({plot_name: wandb.Image(fig)})
510
+ plt.close(fig)
511
+
479
512
  def validation_step(self, batch: dict, batch_idx):
480
513
  """Run validation step"""
481
514
  y_hat = self(batch)
482
515
  # Sensor seems to be in batch, station, time order
483
516
  y = batch[self._target_key][:, -self.forecast_len :, 0]
484
- persistence = batch[self._target_key][:, -self.forecast_len - 1, 0]
517
+
485
518
  # Expand persistence to be the same shape as y
486
- persistence = persistence.unsqueeze(1).expand(-1, self.forecast_len)
487
519
  losses = self._calculate_common_losses(y, y_hat)
488
520
  losses.update(self._calculate_val_losses(y, y_hat))
489
521
 
522
+ # Store these to make horizon accuracy plot
523
+ self._horizon_maes.append(
524
+ {i: losses[f"MAE_horizon/step_{i:03}"] for i in range(self.forecast_len)}
525
+ )
526
+
490
527
  logged_losses = {f"{k}/val": v for k, v in losses.items()}
491
- logged_losses["MAE_persistence/val"] = F.l1_loss(persistence, y)
492
- logged_losses["MSE_persistence/val"] = F.mse_loss(persistence, y)
493
- # Log for each timestep the persistence loss
494
- for i in range(self.forecast_len):
495
- logged_losses[f"MAE_persistence/step_{i:03}/val"] = F.l1_loss(
496
- persistence[:, i], y[:, i]
497
- )
498
- logged_losses[f"MSE_persistence/step_{i:03}/val"] = F.mse_loss(
499
- persistence[:, i], y[:, i]
500
- )
501
- # Get the losses in the format of {VAL>_horizon/step_000: 0.1, VAL>_horizon/step_001: 0.2}
502
- # for each step in the forecast horizon
503
- # This is needed for the custom plot
504
- # And needs to be in order of step
505
- x_values = [
506
- int(k.split("_")[-1].split("/")[0])
507
- for k in logged_losses.keys()
508
- if "MAE_horizon/step" in k
509
- ]
510
- y_values = []
511
- for x in x_values:
512
- y_values.append(logged_losses[f"MAE_horizon/step_{x:03}/val"])
513
- per_step_losses = [[x, y] for (x, y) in zip(x_values, y_values)]
514
- # Check if WandBLogger is being used
515
- if isinstance(self.logger, pl.loggers.WandbLogger):
516
- table = wandb.Table(data=per_step_losses, columns=["timestep", "MAE"])
517
- wandb.log(
518
- {
519
- "mae_vs_timestep": wandb.plot.line(
520
- table, "timestep", "MAE", title="MAE vs Timestep"
521
- )
522
- }
523
- )
524
528
 
525
529
  self.log_dict(
526
530
  logged_losses,
@@ -530,7 +534,8 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
530
534
 
531
535
  accum_batch_num = batch_idx // self.trainer.accumulate_grad_batches
532
536
 
533
- if accum_batch_num in [0, 1]:
537
+ # Make plots only if using wandb logger
538
+ if isinstance(self.logger, pl.loggers.WandbLogger) and accum_batch_num in [0, 1]:
534
539
  # Store these temporarily under self
535
540
  if not hasattr(self, "_val_y_hats"):
536
541
  self._val_y_hats = PredAccumulator()
@@ -538,47 +543,53 @@ class BaseModel(pl.LightningModule, PVNetModelHubMixin):
538
543
 
539
544
  self._val_y_hats.append(y_hat)
540
545
  self._val_batches.append(batch)
541
- # if batch had accumulated
546
+
547
+ # if batch has accumulated
542
548
  if (batch_idx + 1) % self.trainer.accumulate_grad_batches == 0:
543
549
  y_hat = self._val_y_hats.flush()
544
550
  batch = self._val_batches.flush()
545
551
 
546
- fig = plot_batch_forecasts(
552
+ self._log_forecast_plot(
547
553
  batch,
548
554
  y_hat,
549
- quantiles=self.output_quantiles,
550
- key_to_plot=self._target_key_name,
555
+ accum_batch_num,
556
+ timesteps_to_plot=None,
557
+ plot_suffix="all",
551
558
  )
552
559
 
553
- self.logger.experiment.log(
554
- {
555
- f"val_forecast_samples/batch_idx_{accum_batch_num}_all": wandb.Image(fig),
556
- }
557
- )
558
- plt.close(fig)
559
-
560
560
  if self.time_step_intervals_to_plot is not None:
561
561
  for interval in self.time_step_intervals_to_plot:
562
- fig = plot_batch_forecasts(
562
+ self._log_forecast_plot(
563
563
  batch,
564
564
  y_hat,
565
- quantiles=self.output_quantiles,
566
- key_to_plot=self._target_key_name,
565
+ accum_batch_num,
567
566
  timesteps_to_plot=interval,
567
+ plot_suffix=f"timestep_{interval}",
568
568
  )
569
- self.logger.experiment.log(
570
- {
571
- f"val_forecast_samples/batch_idx_{accum_batch_num}_"
572
- f"timestep_{interval}": wandb.Image(fig),
573
- }
574
- )
575
- plt.close(fig)
576
569
 
577
570
  del self._val_y_hats
578
571
  del self._val_batches
579
572
 
580
573
  return logged_losses
581
574
 
575
+ def on_validation_epoch_end(self):
576
+ """Run on epoch end"""
577
+
578
+ horizon_maes_dict = self._horizon_maes.flush()
579
+
580
+ # Create the horizon accuracy curve
581
+ if isinstance(self.logger, pl.loggers.WandbLogger):
582
+ per_step_losses = [[i, horizon_maes_dict[i]] for i in range(self.forecast_len)]
583
+
584
+ table = wandb.Table(data=per_step_losses, columns=["horizon_step", "MAE"])
585
+ wandb.log(
586
+ {
587
+ "horizon_loss_curve": wandb.plot.line(
588
+ table, "horizon_step", "MAE", title="Horizon loss curve"
589
+ )
590
+ },
591
+ )
592
+
582
593
  def test_step(self, batch, batch_idx):
583
594
  """Run test step"""
584
595
  y_hat = self(batch)
@@ -369,7 +369,10 @@ class Model(MultimodalBaseModel):
369
369
  def convert_to_multimodal_model(self, config):
370
370
  """Convert the model into a multimodal model class whilst preserving weights"""
371
371
  config = config.copy()
372
- del config["cold_start"]
372
+
373
+ if "cold_start" in config:
374
+ del config["cold_start"]
375
+
373
376
  config["_target_"] = "pvnet.models.multimodal.multimodal.Model"
374
377
 
375
378
  sources = []
@@ -416,4 +419,10 @@ class Model(MultimodalBaseModel):
416
419
 
417
420
  multimodal_model.output_network.load_state_dict(self.output_network.state_dict())
418
421
 
422
+ if self.embedding_dim:
423
+ multimodal_model.embed.load_state_dict(self.embed.state_dict())
424
+
425
+ if self.include_sun:
426
+ multimodal_model.sun_fc1.load_state_dict(self.sun_fc1.state_dict())
427
+
419
428
  return multimodal_model, config
@@ -185,7 +185,6 @@ class AdamWReduceLROnPlateau(AbstractOptimizer):
185
185
  if not isinstance(self._lr, float):
186
186
  return self._call_multi(model)
187
187
  else:
188
- assert False
189
188
  default_lr = self._lr if model.lr is None else model.lr
190
189
  opt = torch.optim.AdamW(model.parameters(), lr=default_lr, **self.opt_kwargs)
191
190
  sch = torch.optim.lr_scheduler.ReduceLROnPlateau(
@@ -1,2 +0,0 @@
1
- """PVNet"""
2
- __version__ = "3.0.22"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes