Here's a simple method for making a generative-adversarial network by using a binary classifier (discriminator) in the custom loss function provided to the phygnn model.
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.losses import binary_crossentropy
from rex import init_logger
from rex import Resource
from phygnn import PhysicsGuidedNeuralNetwork
init_logger('phygnn', log_level='INFO', log_file=None)
<Logger phygnn (INFO)>
The data includes 30-minute clearsky and all-sky GHI profiles from 9 locations for 1998-2019 inclusive.
with Resource('./nsrdb_data.h5') as res:
df = pd.DataFrame({'ghi': res['ghi'], 'clearsky_ghi': res['clearsky_ghi']})
df['ghi'] = np.roll(df['ghi'], -7).astype(np.float32)
df['clearsky_ghi'] = np.roll(df['clearsky_ghi'], -7).astype(np.float32)
df.describe()
ghi | clearsky_ghi | |
---|---|---|
count | 3.471120e+06 | 3.471120e+06 |
mean | 1.865992e+02 | 2.387738e+02 |
std | 2.741522e+02 | 3.133253e+02 |
min | 0.000000e+00 | 0.000000e+00 |
25% | 0.000000e+00 | 0.000000e+00 |
50% | 0.000000e+00 | 0.000000e+00 |
75% | 3.240000e+02 | 4.760000e+02 |
max | 1.100000e+03 | 1.100000e+03 |
df.iloc[:192, :].plot()
<AxesSubplot:>
The data will be stored 2D arrays where each row is one day of data containing 30-minute profiles for clearsky and all-sky GHI along with some physical metrics of the all-sky profile such as total cumulative GHI, maximum GHI, ramp rates, etc... Our goal is to use the physical metrics to condition the generator (CGAN) into producing realistic solar profiles with the desired physical properties.
n_days = int(len(df) / 48)
ghi = df['ghi'].values.reshape((n_days, 48))
cs_ghi = df['clearsky_ghi'].values.reshape((n_days, 48))
max_ghi = ghi.max(axis=1)
sum_ghi = ghi.sum(axis=1)
max_diff = np.nanmax(np.abs(np.diff(ghi, axis=1)), axis=1)
mean_diff = np.nanmean(np.abs(np.diff(ghi, axis=1)), axis=1)
max_ghi = np.expand_dims(max_ghi, axis=1)
sum_ghi = np.expand_dims(sum_ghi, axis=1)
max_diff = np.expand_dims(max_diff, axis=1)
mean_diff = np.expand_dims(mean_diff, axis=1)
real_data = np.hstack([cs_ghi, max_ghi, sum_ghi, max_diff, mean_diff, ghi])
# global variables for indexing
I0 = 48
I1 = 52
assert np.isnan(real_data).sum() == 0
real_data.shape
(72315, 100)
test_index = sorted(np.random.choice(n_days, 200))
train_index = list(set(range(n_days)) - set(test_index))
test_data = real_data[test_index, :]
real_data = real_data[train_index, :]
display(test_data.shape, real_data.shape)
(200, 100)
(72115, 100)
fig = plt.figure(figsize=(14, 5))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
for i in range(5):
ax1.plot(real_data[i, 0:48])
ax2.plot(real_data[i, I1:])
print('i: {} max: {} sum: {} max_diff: {:.2f} mean_diff: {:.2f} '
.format(i, real_data[i, 48], real_data[i, 49],
real_data[i, 50], real_data[i, 51]))
plt.legend(range(5))
i: 0 max: 436.0 sum: 5077.0 max_diff: 111.00 mean_diff: 22.30 i: 1 max: 411.0 sum: 4273.0 max_diff: 190.00 mean_diff: 25.79 i: 2 max: 390.0 sum: 4060.0 max_diff: 188.00 mean_diff: 30.38 i: 3 max: 548.0 sum: 5852.0 max_diff: 148.00 mean_diff: 23.32 i: 4 max: 574.0 sum: 6832.0 max_diff: 91.00 mean_diff: 24.43
<matplotlib.legend.Legend at 0x7f2fe3c63040>
g_features = real_data[:, 0:I1] # clearsky profiles
g_known = real_data[:, I1:] # all-sky profiles
# clearsky profiles + [max_ghi, sum_ghi, max_diff, mean_diff]
g_features[0, :]
array([ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 63. , 144. , 228. , 307. , 378. , 436. , 483. , 513. , 530. , 531. , 518. , 488. , 444. , 387. , 319. , 240. , 157. , 75. , 0. , 0. , 0. , 0. , 0. , 0. , 436. , 5077. , 111. , 22.297873], dtype=float32)
# all-sky profiles
g_known[0, :]
array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 63., 144., 228., 307., 378., 436., 376., 416., 344., 389., 365., 333., 336., 291., 241., 240., 129., 61., 0., 0., 0., 0., 0., 0.], dtype=float32)
The SolarGan class is a subclass of the PhysicsGuidedNeuralNetwork framework. The subclass includes the custom GAN loss function and modifies the prediction function to constrain the generated solar profiles using the clearsky profiles
class SolarGan(PhysicsGuidedNeuralNetwork):
"""Subclass for solar data generation."""
def predict(self, x, to_numpy=True, training=True):
"""Generate synthetic solar data and bound by the clearsky features (x)
Parameters
----------
x : np.ndarray
Feature data in a 2D array (clearsky features
+ physical conditioning features)
to_numpy : bool
Flag to convert output from tensor to numpy array
training : bool
Flag for predict() used in the training routine.
For the solar GAN, we always have training flag
True to use dropout during prediction
Returns
-------
y : tf.Tensor | np.ndarray
Predicted solar profiles in a 2D array (n_days x 48_hours)
made to be consistent with the clearsky input features
"""
# always have training flag True to use dropout during prediction
y = super().predict(x, to_numpy=to_numpy, training=True)
if isinstance(y, tf.Tensor):
y = tf.where(x[:, 0:48] == 0, x[:, 0:48], y)
y = tf.where(y > x[:, 0:48], x[:, 0:48], y)
y = tf.where(y < 0, 0, y)
elif isinstance(y, np.ndarray):
y = np.where(x[:, 0:48] == 0, x[:, 0:48], y)
y = np.where(y > x[:, 0:48], x[:, 0:48], y)
y = np.where(y < 0, 0, y)
return y
@staticmethod
def calc_features(y_gen):
"""Calculate conditioning features of the solar profiles
(total solar, ramp rates, etc...)
Parameters
----------
y_gen : tf.Tensor | np.ndarray
Solar data profiles of shape (n_days x 48_hours)
Returns
-------
out : tf.Tensor | list
Either a 2D tensor or a list of 1D numpy arrays:
gen_max, gen_sum, max_diff, mean_diff
"""
if isinstance(y_gen, tf.Tensor):
gen_max = tf.expand_dims(tf.reduce_max(y_gen, axis=1), axis=1)
gen_sum = tf.expand_dims(tf.reduce_sum(y_gen, axis=1), axis=1)
diff = tf.abs(tf.experimental.numpy.diff(y_gen, axis=1))
max_diff = tf.expand_dims(tf.reduce_max(diff, axis=1), axis=1)
mean_diff = tf.expand_dims(tf.reduce_mean(diff, axis=1), axis=1)
out = tf.concat([gen_max, gen_sum, max_diff, mean_diff], axis=1)
else:
gen_max = y_gen.max(axis=1)
gen_sum = y_gen.sum(axis=1)
max_diff = np.nanmax(np.abs(np.diff(y_gen)), axis=1)
mean_diff = np.nanmean(np.abs(np.diff(y_gen)), axis=1)
out = [gen_max, gen_sum, max_diff, mean_diff]
return out
@classmethod
def gan_loss(cls, model, y_true, y_gen, real_data, discriminator=None,
i0=48, i1=52, val_data_len=14423):
"""Guiding loss function for training a phygnn-GAN.
Parameters
----------
model : PhysicsGuidedNeuralNetwork
Instance of the phygnn model (generative model) at the current
point in training.
y_true : np.ndarray
Placeholder, not actually used in GAN formulation.
y_gen : tf.Tensor
Generated data from the prediction of the generative model
real_data : np.ndarray
Supplemental feature data. In this case, this is a 2D array of REAL
30min clearsky and all-sky GHI daily profiles with shape (n_days, 96)
where the first 48 columns are clearsky and the last 48 are all-sky
real_data = np.hstack((arr_ghi, arr_cs_ghi))
discriminator : tf.keras.Sequential
Discriminator (adversary) binary classification model.
i0 : int
Column indexer in real_data where the physical features start.
i1 : int
Column indexer in real_data where the all-sky profiles start.
val_data_len : int
Length of the validation dataset. Used as flag to not train the
discriminator.
Returns
-------
generator_loss : tf.Tensor
A 0D tensor loss value inversely proportional to the
discriminator's classification error.
"""
labels_gen = tf.zeros((len(y_gen), 1))
labels_real = tf.ones((len(real_data), 1))
d_features = tf.concat([real_data[:, i1:], y_gen], axis=0)
d_labels = tf.concat([labels_real, labels_gen], axis=0)
indices = tf.range(start=0, limit=len(d_features), dtype=tf.int32)
shuffled_indices = tf.random.shuffle(indices)
d_features = tf.gather(d_features, shuffled_indices)
d_labels = tf.gather(d_labels, shuffled_indices)
d_out = discriminator(d_features)
generator_loss = -1 * binary_crossentropy(tf.squeeze(d_labels), tf.squeeze(d_out))
binary_accuracy = tf.reduce_mean(tf.abs(d_labels - tf.round(d_out)))
# penalize total daily ghi, ramp rate stats
gen_features = cls.calc_features(y_gen)
max_ghi_loss = tf.reduce_mean(tf.math.squared_difference(gen_features[:, 0], real_data[:, i0])) * (0.01)**2
total_ghi_loss = tf.reduce_mean(tf.math.squared_difference(gen_features[:, 1], real_data[:, i0 + 1])) * (0.001)**2
max_ramp_loss = tf.reduce_mean(tf.math.squared_difference(gen_features[:, 2], real_data[:, i0 + 2])) * (0.01)**2
mean_ramp_loss = tf.reduce_mean(tf.math.squared_difference(gen_features[:, 3], real_data[:, i0 + 3])) * (0.1)**2
generator_loss += 0.1 * (max_ghi_loss + total_ghi_loss + max_ramp_loss + mean_ramp_loss)
if len(y_gen) != val_data_len:
discriminator.fit(d_features, d_labels,
batch_size=len(d_features),
epochs=1, verbose=0)
return generator_loss
SolarGan.seed(0)
discriminator = keras.Sequential(
[keras.Input(shape=(48)),
layers.Dense(64, activation="relu"),
layers.Dense(64, activation="relu"),
layers.Dense(1, activation="sigmoid"),
]
)
discriminator.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.0002),
loss=keras.losses.BinaryCrossentropy(),
metrics=[keras.metrics.BinaryAccuracy()],
)
hidden_layers = [{'units': 128, 'activation': 'relu', 'dropout': 0.5},
{'units': 128, 'activation': 'relu', 'dropout': 0.5},
{'units': 128, 'activation': 'relu', 'dropout': 0.5},
{'units': 128, 'activation': 'relu', 'dropout': 0.5},
]
generator = SolarGan(p_fun=SolarGan.gan_loss,
hidden_layers=hidden_layers,
loss_weights=(0.0, 1.0),
metric='mse',
learning_rate=0.0005,
n_features=52,
n_labels=48,
name='Generator')
p_kwargs = {'discriminator': discriminator,
'i0': I0, 'i1': I1,
'val_data_len': int(len(real_data) * 0.2)}
p_kwargs
{'discriminator': <tensorflow.python.keras.engine.sequential.Sequential at 0x7f2fe3378520>, 'i0': 48, 'i1': 52, 'val_data_len': 14423}
Here we train the GAN with the full GAN loss with discriminator term
generator.fit(g_features, g_known, real_data, n_batch=16, n_epoch=200, p_kwargs=p_kwargs)
INFO - 2021-02-11 14:12:43,459 [phygnn.py:783] : Epoch 0 train loss: 1.25e+00 val loss: 1.16e+00 for "Generator" INFO - 2021-02-11 14:12:52,923 [phygnn.py:783] : Epoch 1 train loss: 3.68e+00 val loss: 3.50e+00 for "Generator" INFO - 2021-02-11 14:12:59,250 [phygnn.py:783] : Epoch 2 train loss: 4.93e+00 val loss: 4.76e+00 for "Generator" INFO - 2021-02-11 14:13:03,385 [phygnn.py:783] : Epoch 3 train loss: 5.47e+00 val loss: 5.10e+00 for "Generator" INFO - 2021-02-11 14:13:07,890 [phygnn.py:783] : Epoch 4 train loss: 2.44e+00 val loss: 2.62e+00 for "Generator" INFO - 2021-02-11 14:13:12,403 [phygnn.py:783] : Epoch 5 train loss: 5.55e+00 val loss: 5.44e+00 for "Generator" INFO - 2021-02-11 14:13:17,117 [phygnn.py:783] : Epoch 6 train loss: 5.66e+00 val loss: 5.65e+00 for "Generator" INFO - 2021-02-11 14:13:22,116 [phygnn.py:783] : Epoch 7 train loss: 5.62e+00 val loss: 5.49e+00 for "Generator" INFO - 2021-02-11 14:13:35,297 [phygnn.py:783] : Epoch 8 train loss: 5.29e+00 val loss: 5.22e+00 for "Generator" INFO - 2021-02-11 14:13:44,308 [phygnn.py:783] : Epoch 9 train loss: 4.99e+00 val loss: 4.84e+00 for "Generator" INFO - 2021-02-11 14:13:52,875 [phygnn.py:783] : Epoch 10 train loss: 4.41e+00 val loss: 4.34e+00 for "Generator" INFO - 2021-02-11 14:14:01,719 [phygnn.py:783] : Epoch 11 train loss: 3.84e+00 val loss: 3.85e+00 for "Generator" INFO - 2021-02-11 14:14:14,661 [phygnn.py:783] : Epoch 12 train loss: 3.47e+00 val loss: 3.47e+00 for "Generator" INFO - 2021-02-11 14:14:24,065 [phygnn.py:783] : Epoch 13 train loss: 3.10e+00 val loss: 3.03e+00 for "Generator" INFO - 2021-02-11 14:14:33,117 [phygnn.py:783] : Epoch 14 train loss: 7.26e-01 val loss: 5.66e-01 for "Generator" INFO - 2021-02-11 14:14:38,014 [phygnn.py:783] : Epoch 15 train loss: -3.56e-01 val loss: -6.80e-01 for "Generator" INFO - 2021-02-11 14:14:42,795 [phygnn.py:783] : Epoch 16 train loss: -3.44e+00 val loss: -3.32e+00 for "Generator" INFO - 2021-02-11 14:14:48,405 [phygnn.py:783] : Epoch 17 train loss: -1.70e+00 val loss: -1.49e+00 for "Generator" INFO - 2021-02-11 14:14:56,614 [phygnn.py:783] : Epoch 18 train loss: -2.46e+00 val loss: -2.62e+00 for "Generator" INFO - 2021-02-11 14:15:03,587 [phygnn.py:783] : Epoch 19 train loss: -2.52e+00 val loss: -2.65e+00 for "Generator" INFO - 2021-02-11 14:15:10,997 [phygnn.py:783] : Epoch 20 train loss: -3.59e+00 val loss: -3.68e+00 for "Generator" INFO - 2021-02-11 14:15:18,201 [phygnn.py:783] : Epoch 21 train loss: -4.73e+00 val loss: -4.68e+00 for "Generator" INFO - 2021-02-11 14:15:27,781 [phygnn.py:783] : Epoch 22 train loss: -4.14e+00 val loss: -4.11e+00 for "Generator" INFO - 2021-02-11 14:15:35,219 [phygnn.py:783] : Epoch 23 train loss: -3.37e+00 val loss: -3.20e+00 for "Generator" INFO - 2021-02-11 14:15:42,957 [phygnn.py:783] : Epoch 24 train loss: -1.67e+00 val loss: -1.62e+00 for "Generator" INFO - 2021-02-11 14:15:52,538 [phygnn.py:783] : Epoch 25 train loss: -2.04e+00 val loss: -2.34e+00 for "Generator" INFO - 2021-02-11 14:16:00,905 [phygnn.py:783] : Epoch 26 train loss: -2.75e+00 val loss: -2.80e+00 for "Generator" INFO - 2021-02-11 14:16:08,316 [phygnn.py:783] : Epoch 27 train loss: -3.02e+00 val loss: -3.04e+00 for "Generator" INFO - 2021-02-11 14:16:13,523 [phygnn.py:783] : Epoch 28 train loss: -2.64e+00 val loss: -2.60e+00 for "Generator" INFO - 2021-02-11 14:16:21,199 [phygnn.py:783] : Epoch 29 train loss: -2.92e+00 val loss: -3.05e+00 for "Generator" INFO - 2021-02-11 14:16:28,874 [phygnn.py:783] : Epoch 30 train loss: -2.60e+00 val loss: -2.70e+00 for "Generator" INFO - 2021-02-11 14:16:36,674 [phygnn.py:783] : Epoch 31 train loss: -2.44e+00 val loss: -2.45e+00 for "Generator" INFO - 2021-02-11 14:16:40,854 [phygnn.py:783] : Epoch 32 train loss: -2.33e+00 val loss: -2.27e+00 for "Generator" INFO - 2021-02-11 14:16:50,160 [phygnn.py:783] : Epoch 33 train loss: -2.24e+00 val loss: -2.32e+00 for "Generator" INFO - 2021-02-11 14:17:01,600 [phygnn.py:783] : Epoch 34 train loss: -1.58e+00 val loss: -1.53e+00 for "Generator" INFO - 2021-02-11 14:17:08,674 [phygnn.py:783] : Epoch 35 train loss: -2.05e+00 val loss: -2.08e+00 for "Generator" INFO - 2021-02-11 14:17:17,016 [phygnn.py:783] : Epoch 36 train loss: -1.85e+00 val loss: -1.92e+00 for "Generator" INFO - 2021-02-11 14:17:24,327 [phygnn.py:783] : Epoch 37 train loss: -1.54e+00 val loss: -1.60e+00 for "Generator" INFO - 2021-02-11 14:17:32,669 [phygnn.py:783] : Epoch 38 train loss: -2.10e+00 val loss: -2.06e+00 for "Generator" INFO - 2021-02-11 14:17:41,481 [phygnn.py:783] : Epoch 39 train loss: -2.29e+00 val loss: -2.15e+00 for "Generator" INFO - 2021-02-11 14:17:49,583 [phygnn.py:783] : Epoch 40 train loss: -1.44e+00 val loss: -1.58e+00 for "Generator" INFO - 2021-02-11 14:17:59,723 [phygnn.py:783] : Epoch 41 train loss: -1.68e+00 val loss: -1.66e+00 for "Generator" INFO - 2021-02-11 14:18:08,832 [phygnn.py:783] : Epoch 42 train loss: -1.80e+00 val loss: -1.79e+00 for "Generator" INFO - 2021-02-11 14:18:16,377 [phygnn.py:783] : Epoch 43 train loss: -2.05e+00 val loss: -2.03e+00 for "Generator" INFO - 2021-02-11 14:18:23,208 [phygnn.py:783] : Epoch 44 train loss: -1.88e+00 val loss: -1.90e+00 for "Generator" INFO - 2021-02-11 14:18:27,516 [phygnn.py:783] : Epoch 45 train loss: -1.15e+00 val loss: -1.09e+00 for "Generator" INFO - 2021-02-11 14:18:35,698 [phygnn.py:783] : Epoch 46 train loss: -1.07e+00 val loss: -1.00e+00 for "Generator" INFO - 2021-02-11 14:18:43,270 [phygnn.py:783] : Epoch 47 train loss: -9.14e-01 val loss: -9.67e-01 for "Generator" INFO - 2021-02-11 14:18:54,582 [phygnn.py:783] : Epoch 48 train loss: -1.27e+00 val loss: -1.41e+00 for "Generator" INFO - 2021-02-11 14:19:29,834 [phygnn.py:783] : Epoch 49 train loss: -1.74e+00 val loss: -1.72e+00 for "Generator" INFO - 2021-02-11 14:19:52,878 [phygnn.py:783] : Epoch 50 train loss: -1.66e+00 val loss: -1.66e+00 for "Generator" INFO - 2021-02-11 14:20:15,531 [phygnn.py:783] : Epoch 51 train loss: -1.66e+00 val loss: -1.79e+00 for "Generator" INFO - 2021-02-11 14:20:27,914 [phygnn.py:783] : Epoch 52 train loss: -1.66e+00 val loss: -1.69e+00 for "Generator" INFO - 2021-02-11 14:20:39,150 [phygnn.py:783] : Epoch 53 train loss: -1.23e+00 val loss: -1.21e+00 for "Generator" INFO - 2021-02-11 14:20:48,560 [phygnn.py:783] : Epoch 54 train loss: -9.10e-01 val loss: -8.63e-01 for "Generator" INFO - 2021-02-11 14:20:57,386 [phygnn.py:783] : Epoch 55 train loss: -7.25e-01 val loss: -7.60e-01 for "Generator" INFO - 2021-02-11 14:21:06,286 [phygnn.py:783] : Epoch 56 train loss: -6.80e-01 val loss: -7.21e-01 for "Generator" INFO - 2021-02-11 14:21:14,828 [phygnn.py:783] : Epoch 57 train loss: -8.73e-01 val loss: -9.25e-01 for "Generator" INFO - 2021-02-11 14:21:23,635 [phygnn.py:783] : Epoch 58 train loss: -1.46e+00 val loss: -1.49e+00 for "Generator" INFO - 2021-02-11 14:21:34,081 [phygnn.py:783] : Epoch 59 train loss: -9.15e-01 val loss: -7.95e-01 for "Generator" INFO - 2021-02-11 14:21:45,592 [phygnn.py:783] : Epoch 60 train loss: -8.10e-01 val loss: -8.31e-01 for "Generator" INFO - 2021-02-11 14:21:57,256 [phygnn.py:783] : Epoch 61 train loss: -1.11e+00 val loss: -1.15e+00 for "Generator" INFO - 2021-02-11 14:22:06,673 [phygnn.py:783] : Epoch 62 train loss: -6.84e-01 val loss: -7.31e-01 for "Generator" INFO - 2021-02-11 14:22:16,352 [phygnn.py:783] : Epoch 63 train loss: -8.31e-01 val loss: -9.07e-01 for "Generator" INFO - 2021-02-11 14:22:25,455 [phygnn.py:783] : Epoch 64 train loss: -9.31e-01 val loss: -8.34e-01 for "Generator" INFO - 2021-02-11 14:22:43,633 [phygnn.py:783] : Epoch 65 train loss: -6.20e-01 val loss: -6.12e-01 for "Generator" INFO - 2021-02-11 14:23:06,404 [phygnn.py:783] : Epoch 66 train loss: -6.43e-01 val loss: -6.75e-01 for "Generator" INFO - 2021-02-11 14:23:20,553 [phygnn.py:783] : Epoch 67 train loss: -8.61e-01 val loss: -8.46e-01 for "Generator" INFO - 2021-02-11 14:23:29,631 [phygnn.py:783] : Epoch 68 train loss: -9.27e-01 val loss: -8.82e-01 for "Generator" INFO - 2021-02-11 14:23:43,630 [phygnn.py:783] : Epoch 69 train loss: -7.66e-01 val loss: -7.65e-01 for "Generator" INFO - 2021-02-11 14:24:03,793 [phygnn.py:783] : Epoch 70 train loss: -8.06e-01 val loss: -8.13e-01 for "Generator" INFO - 2021-02-11 14:24:18,444 [phygnn.py:783] : Epoch 71 train loss: -8.87e-01 val loss: -8.81e-01 for "Generator" INFO - 2021-02-11 14:24:29,484 [phygnn.py:783] : Epoch 72 train loss: -1.19e+00 val loss: -1.11e+00 for "Generator" INFO - 2021-02-11 14:24:40,562 [phygnn.py:783] : Epoch 73 train loss: -7.93e-01 val loss: -7.64e-01 for "Generator" INFO - 2021-02-11 14:24:49,938 [phygnn.py:783] : Epoch 74 train loss: -7.80e-01 val loss: -6.99e-01 for "Generator" INFO - 2021-02-11 14:24:59,284 [phygnn.py:783] : Epoch 75 train loss: -7.39e-01 val loss: -7.33e-01 for "Generator" INFO - 2021-02-11 14:25:08,495 [phygnn.py:783] : Epoch 76 train loss: -8.17e-01 val loss: -8.96e-01 for "Generator" INFO - 2021-02-11 14:25:18,004 [phygnn.py:783] : Epoch 77 train loss: -8.91e-01 val loss: -8.95e-01 for "Generator" INFO - 2021-02-11 14:25:26,944 [phygnn.py:783] : Epoch 78 train loss: -9.33e-01 val loss: -8.93e-01 for "Generator" INFO - 2021-02-11 14:25:37,532 [phygnn.py:783] : Epoch 79 train loss: -6.20e-01 val loss: -6.17e-01 for "Generator" INFO - 2021-02-11 14:25:50,632 [phygnn.py:783] : Epoch 80 train loss: -5.29e-01 val loss: -5.71e-01 for "Generator" INFO - 2021-02-11 14:26:03,912 [phygnn.py:783] : Epoch 81 train loss: -8.12e-01 val loss: -7.97e-01 for "Generator" INFO - 2021-02-11 14:26:12,446 [phygnn.py:783] : Epoch 82 train loss: -9.62e-01 val loss: -8.83e-01 for "Generator" INFO - 2021-02-11 14:26:21,153 [phygnn.py:783] : Epoch 83 train loss: -7.58e-01 val loss: -7.71e-01 for "Generator" INFO - 2021-02-11 14:26:29,902 [phygnn.py:783] : Epoch 84 train loss: -7.44e-01 val loss: -7.56e-01 for "Generator" INFO - 2021-02-11 14:26:38,816 [phygnn.py:783] : Epoch 85 train loss: -5.37e-01 val loss: -5.73e-01 for "Generator" INFO - 2021-02-11 14:26:47,862 [phygnn.py:783] : Epoch 86 train loss: -6.19e-01 val loss: -6.11e-01 for "Generator" INFO - 2021-02-11 14:26:56,958 [phygnn.py:783] : Epoch 87 train loss: -6.16e-01 val loss: -5.87e-01 for "Generator" INFO - 2021-02-11 14:27:06,388 [phygnn.py:783] : Epoch 88 train loss: -7.21e-01 val loss: -7.47e-01 for "Generator" INFO - 2021-02-11 14:27:15,079 [phygnn.py:783] : Epoch 89 train loss: -7.28e-01 val loss: -6.50e-01 for "Generator" INFO - 2021-02-11 14:27:21,039 [phygnn.py:783] : Epoch 90 train loss: -7.52e-01 val loss: -7.63e-01 for "Generator" INFO - 2021-02-11 14:27:31,704 [phygnn.py:783] : Epoch 91 train loss: -7.41e-01 val loss: -7.74e-01 for "Generator" INFO - 2021-02-11 14:27:39,644 [phygnn.py:783] : Epoch 92 train loss: -6.04e-01 val loss: -5.83e-01 for "Generator" INFO - 2021-02-11 14:27:47,750 [phygnn.py:783] : Epoch 93 train loss: -7.12e-01 val loss: -7.25e-01 for "Generator" INFO - 2021-02-11 14:27:56,843 [phygnn.py:783] : Epoch 94 train loss: -5.71e-01 val loss: -6.15e-01 for "Generator" INFO - 2021-02-11 14:28:10,010 [phygnn.py:783] : Epoch 95 train loss: -8.71e-01 val loss: -7.89e-01 for "Generator" INFO - 2021-02-11 14:28:18,777 [phygnn.py:783] : Epoch 96 train loss: -5.16e-01 val loss: -5.39e-01 for "Generator" INFO - 2021-02-11 14:28:26,091 [phygnn.py:783] : Epoch 97 train loss: -5.52e-01 val loss: -5.78e-01 for "Generator" INFO - 2021-02-11 14:28:33,517 [phygnn.py:783] : Epoch 98 train loss: -6.33e-01 val loss: -5.81e-01 for "Generator" INFO - 2021-02-11 14:28:40,803 [phygnn.py:783] : Epoch 99 train loss: -7.20e-01 val loss: -6.80e-01 for "Generator" INFO - 2021-02-11 14:28:48,410 [phygnn.py:783] : Epoch 100 train loss: -5.20e-01 val loss: -5.64e-01 for "Generator" INFO - 2021-02-11 14:28:55,813 [phygnn.py:783] : Epoch 101 train loss: -6.72e-01 val loss: -6.88e-01 for "Generator" INFO - 2021-02-11 14:29:04,289 [phygnn.py:783] : Epoch 102 train loss: -5.54e-01 val loss: -5.88e-01 for "Generator" INFO - 2021-02-11 14:29:11,666 [phygnn.py:783] : Epoch 103 train loss: -4.93e-01 val loss: -5.15e-01 for "Generator" INFO - 2021-02-11 14:29:19,042 [phygnn.py:783] : Epoch 104 train loss: -5.47e-01 val loss: -5.38e-01 for "Generator" INFO - 2021-02-11 14:29:26,680 [phygnn.py:783] : Epoch 105 train loss: -6.33e-01 val loss: -5.81e-01 for "Generator" INFO - 2021-02-11 14:29:34,235 [phygnn.py:783] : Epoch 106 train loss: -6.52e-01 val loss: -6.37e-01 for "Generator" INFO - 2021-02-11 14:29:41,435 [phygnn.py:783] : Epoch 107 train loss: -6.58e-01 val loss: -6.09e-01 for "Generator" INFO - 2021-02-11 14:29:49,376 [phygnn.py:783] : Epoch 108 train loss: -3.98e-01 val loss: -3.94e-01 for "Generator" INFO - 2021-02-11 14:29:57,941 [phygnn.py:783] : Epoch 109 train loss: -5.12e-01 val loss: -4.75e-01 for "Generator" INFO - 2021-02-11 14:30:05,586 [phygnn.py:783] : Epoch 110 train loss: -4.46e-01 val loss: -4.70e-01 for "Generator" INFO - 2021-02-11 14:30:13,063 [phygnn.py:783] : Epoch 111 train loss: -4.17e-01 val loss: -4.00e-01 for "Generator" INFO - 2021-02-11 14:30:19,521 [phygnn.py:783] : Epoch 112 train loss: -4.95e-01 val loss: -4.92e-01 for "Generator" INFO - 2021-02-11 14:30:26,966 [phygnn.py:783] : Epoch 113 train loss: -6.60e-01 val loss: -6.28e-01 for "Generator" INFO - 2021-02-11 14:30:41,191 [phygnn.py:783] : Epoch 114 train loss: -4.62e-01 val loss: -4.71e-01 for "Generator" INFO - 2021-02-11 14:30:47,510 [phygnn.py:783] : Epoch 115 train loss: -4.62e-01 val loss: -4.53e-01 for "Generator" INFO - 2021-02-11 14:30:54,205 [phygnn.py:783] : Epoch 116 train loss: -4.00e-01 val loss: -4.21e-01 for "Generator" INFO - 2021-02-11 14:31:00,476 [phygnn.py:783] : Epoch 117 train loss: -4.20e-01 val loss: -4.07e-01 for "Generator" INFO - 2021-02-11 14:31:07,712 [phygnn.py:783] : Epoch 118 train loss: -4.20e-01 val loss: -4.21e-01 for "Generator" INFO - 2021-02-11 14:31:15,023 [phygnn.py:783] : Epoch 119 train loss: -4.35e-01 val loss: -4.62e-01 for "Generator" INFO - 2021-02-11 14:31:23,410 [phygnn.py:783] : Epoch 120 train loss: -4.39e-01 val loss: -4.51e-01 for "Generator" INFO - 2021-02-11 14:31:31,308 [phygnn.py:783] : Epoch 121 train loss: -4.82e-01 val loss: -5.07e-01 for "Generator" INFO - 2021-02-11 14:31:39,971 [phygnn.py:783] : Epoch 122 train loss: -5.23e-01 val loss: -5.25e-01 for "Generator" INFO - 2021-02-11 14:31:48,554 [phygnn.py:783] : Epoch 123 train loss: -4.57e-01 val loss: -4.93e-01 for "Generator" INFO - 2021-02-11 14:32:00,291 [phygnn.py:783] : Epoch 124 train loss: -4.98e-01 val loss: -5.53e-01 for "Generator" INFO - 2021-02-11 14:32:11,417 [phygnn.py:783] : Epoch 125 train loss: -4.64e-01 val loss: -4.66e-01 for "Generator" INFO - 2021-02-11 14:32:20,061 [phygnn.py:783] : Epoch 126 train loss: -5.35e-01 val loss: -5.30e-01 for "Generator" INFO - 2021-02-11 14:32:27,830 [phygnn.py:783] : Epoch 127 train loss: -5.12e-01 val loss: -5.16e-01 for "Generator" INFO - 2021-02-11 14:32:35,307 [phygnn.py:783] : Epoch 128 train loss: -5.38e-01 val loss: -5.01e-01 for "Generator" INFO - 2021-02-11 14:32:42,260 [phygnn.py:783] : Epoch 129 train loss: -5.26e-01 val loss: -5.22e-01 for "Generator" INFO - 2021-02-11 14:32:50,616 [phygnn.py:783] : Epoch 130 train loss: -5.60e-01 val loss: -5.66e-01 for "Generator" INFO - 2021-02-11 14:32:58,289 [phygnn.py:783] : Epoch 131 train loss: -5.46e-01 val loss: -5.51e-01 for "Generator" INFO - 2021-02-11 14:33:05,937 [phygnn.py:783] : Epoch 132 train loss: -5.06e-01 val loss: -5.09e-01 for "Generator" INFO - 2021-02-11 14:33:13,646 [phygnn.py:783] : Epoch 133 train loss: -4.54e-01 val loss: -4.65e-01 for "Generator" INFO - 2021-02-11 14:33:21,385 [phygnn.py:783] : Epoch 134 train loss: -4.53e-01 val loss: -4.72e-01 for "Generator" INFO - 2021-02-11 14:33:28,923 [phygnn.py:783] : Epoch 135 train loss: -5.64e-01 val loss: -5.45e-01 for "Generator" INFO - 2021-02-11 14:33:36,196 [phygnn.py:783] : Epoch 136 train loss: -5.71e-01 val loss: -6.02e-01 for "Generator" INFO - 2021-02-11 14:33:43,105 [phygnn.py:783] : Epoch 137 train loss: -5.13e-01 val loss: -5.66e-01 for "Generator" INFO - 2021-02-11 14:33:58,625 [phygnn.py:783] : Epoch 138 train loss: -5.46e-01 val loss: -5.33e-01 for "Generator" INFO - 2021-02-11 14:34:14,401 [phygnn.py:783] : Epoch 139 train loss: -5.41e-01 val loss: -5.35e-01 for "Generator" INFO - 2021-02-11 14:34:26,874 [phygnn.py:783] : Epoch 140 train loss: -4.09e-01 val loss: -4.49e-01 for "Generator" INFO - 2021-02-11 14:34:38,687 [phygnn.py:783] : Epoch 141 train loss: -4.49e-01 val loss: -4.41e-01 for "Generator" INFO - 2021-02-11 14:34:49,689 [phygnn.py:783] : Epoch 142 train loss: -4.44e-01 val loss: -4.90e-01 for "Generator" INFO - 2021-02-11 14:34:54,436 [phygnn.py:783] : Epoch 143 train loss: -5.10e-01 val loss: -5.47e-01 for "Generator" INFO - 2021-02-11 14:35:04,490 [phygnn.py:783] : Epoch 144 train loss: -4.27e-01 val loss: -4.29e-01 for "Generator" INFO - 2021-02-11 14:35:14,127 [phygnn.py:783] : Epoch 145 train loss: -5.66e-01 val loss: -5.74e-01 for "Generator" INFO - 2021-02-11 14:35:20,170 [phygnn.py:783] : Epoch 146 train loss: -4.93e-01 val loss: -4.81e-01 for "Generator" INFO - 2021-02-11 14:35:28,682 [phygnn.py:783] : Epoch 147 train loss: -4.57e-01 val loss: -4.64e-01 for "Generator" INFO - 2021-02-11 14:35:36,689 [phygnn.py:783] : Epoch 148 train loss: -4.54e-01 val loss: -4.90e-01 for "Generator" INFO - 2021-02-11 14:35:45,829 [phygnn.py:783] : Epoch 149 train loss: -4.54e-01 val loss: -4.91e-01 for "Generator" INFO - 2021-02-11 14:35:54,905 [phygnn.py:783] : Epoch 150 train loss: -4.75e-01 val loss: -4.72e-01 for "Generator" INFO - 2021-02-11 14:36:03,843 [phygnn.py:783] : Epoch 151 train loss: -4.97e-01 val loss: -5.16e-01 for "Generator" INFO - 2021-02-11 14:36:12,853 [phygnn.py:783] : Epoch 152 train loss: -4.99e-01 val loss: -5.27e-01 for "Generator" INFO - 2021-02-11 14:36:21,166 [phygnn.py:783] : Epoch 153 train loss: -4.49e-01 val loss: -4.64e-01 for "Generator" INFO - 2021-02-11 14:36:29,773 [phygnn.py:783] : Epoch 154 train loss: -4.05e-01 val loss: -4.25e-01 for "Generator" INFO - 2021-02-11 14:36:38,749 [phygnn.py:783] : Epoch 155 train loss: -4.42e-01 val loss: -4.76e-01 for "Generator" INFO - 2021-02-11 14:36:48,327 [phygnn.py:783] : Epoch 156 train loss: -4.94e-01 val loss: -5.34e-01 for "Generator" INFO - 2021-02-11 14:36:57,263 [phygnn.py:783] : Epoch 157 train loss: -4.75e-01 val loss: -4.78e-01 for "Generator" INFO - 2021-02-11 14:37:07,244 [phygnn.py:783] : Epoch 158 train loss: -4.39e-01 val loss: -4.74e-01 for "Generator" INFO - 2021-02-11 14:37:16,151 [phygnn.py:783] : Epoch 159 train loss: -4.34e-01 val loss: -4.59e-01 for "Generator" INFO - 2021-02-11 14:37:25,096 [phygnn.py:783] : Epoch 160 train loss: -4.46e-01 val loss: -4.61e-01 for "Generator" INFO - 2021-02-11 14:37:33,619 [phygnn.py:783] : Epoch 161 train loss: -4.96e-01 val loss: -5.30e-01 for "Generator" INFO - 2021-02-11 14:37:42,549 [phygnn.py:783] : Epoch 162 train loss: -4.32e-01 val loss: -4.30e-01 for "Generator" INFO - 2021-02-11 14:37:51,091 [phygnn.py:783] : Epoch 163 train loss: -4.45e-01 val loss: -4.49e-01 for "Generator" INFO - 2021-02-11 14:37:59,630 [phygnn.py:783] : Epoch 164 train loss: -4.29e-01 val loss: -4.54e-01 for "Generator" INFO - 2021-02-11 14:38:08,301 [phygnn.py:783] : Epoch 165 train loss: -5.21e-01 val loss: -5.37e-01 for "Generator" INFO - 2021-02-11 14:38:17,150 [phygnn.py:783] : Epoch 166 train loss: -5.20e-01 val loss: -5.25e-01 for "Generator" INFO - 2021-02-11 14:38:25,883 [phygnn.py:783] : Epoch 167 train loss: -4.26e-01 val loss: -4.66e-01 for "Generator" INFO - 2021-02-11 14:38:33,818 [phygnn.py:783] : Epoch 168 train loss: -4.83e-01 val loss: -5.21e-01 for "Generator" INFO - 2021-02-11 14:38:41,908 [phygnn.py:783] : Epoch 169 train loss: -5.12e-01 val loss: -4.86e-01 for "Generator" INFO - 2021-02-11 14:38:49,270 [phygnn.py:783] : Epoch 170 train loss: -5.33e-01 val loss: -5.60e-01 for "Generator" INFO - 2021-02-11 14:38:59,687 [phygnn.py:783] : Epoch 171 train loss: -4.34e-01 val loss: -4.18e-01 for "Generator" INFO - 2021-02-11 14:39:08,258 [phygnn.py:783] : Epoch 172 train loss: -4.83e-01 val loss: -4.30e-01 for "Generator" INFO - 2021-02-11 14:39:17,035 [phygnn.py:783] : Epoch 173 train loss: -5.23e-01 val loss: -5.21e-01 for "Generator" INFO - 2021-02-11 14:39:25,818 [phygnn.py:783] : Epoch 174 train loss: -4.89e-01 val loss: -5.27e-01 for "Generator" INFO - 2021-02-11 14:39:34,521 [phygnn.py:783] : Epoch 175 train loss: -4.45e-01 val loss: -4.48e-01 for "Generator" INFO - 2021-02-11 14:39:46,995 [phygnn.py:783] : Epoch 176 train loss: -4.16e-01 val loss: -4.29e-01 for "Generator" INFO - 2021-02-11 14:39:53,134 [phygnn.py:783] : Epoch 177 train loss: -4.66e-01 val loss: -4.90e-01 for "Generator" INFO - 2021-02-11 14:40:03,716 [phygnn.py:783] : Epoch 178 train loss: -5.17e-01 val loss: -5.29e-01 for "Generator" INFO - 2021-02-11 14:40:10,820 [phygnn.py:783] : Epoch 179 train loss: -4.60e-01 val loss: -4.49e-01 for "Generator" INFO - 2021-02-11 14:40:16,100 [phygnn.py:783] : Epoch 180 train loss: -4.60e-01 val loss: -4.14e-01 for "Generator" INFO - 2021-02-11 14:40:21,600 [phygnn.py:783] : Epoch 181 train loss: -4.34e-01 val loss: -4.81e-01 for "Generator" INFO - 2021-02-11 14:40:31,214 [phygnn.py:783] : Epoch 182 train loss: -5.24e-01 val loss: -5.32e-01 for "Generator" INFO - 2021-02-11 14:40:40,187 [phygnn.py:783] : Epoch 183 train loss: -4.90e-01 val loss: -4.89e-01 for "Generator" INFO - 2021-02-11 14:40:49,402 [phygnn.py:783] : Epoch 184 train loss: -3.82e-01 val loss: -3.95e-01 for "Generator" INFO - 2021-02-11 14:40:57,366 [phygnn.py:783] : Epoch 185 train loss: -4.35e-01 val loss: -4.58e-01 for "Generator" INFO - 2021-02-11 14:41:10,183 [phygnn.py:783] : Epoch 186 train loss: -5.84e-01 val loss: -6.21e-01 for "Generator" INFO - 2021-02-11 14:41:20,330 [phygnn.py:783] : Epoch 187 train loss: -6.25e-01 val loss: -6.23e-01 for "Generator" INFO - 2021-02-11 14:41:29,066 [phygnn.py:783] : Epoch 188 train loss: -4.65e-01 val loss: -4.98e-01 for "Generator" INFO - 2021-02-11 14:41:36,005 [phygnn.py:783] : Epoch 189 train loss: -5.09e-01 val loss: -5.40e-01 for "Generator" INFO - 2021-02-11 14:41:46,251 [phygnn.py:783] : Epoch 190 train loss: -5.58e-01 val loss: -5.90e-01 for "Generator" INFO - 2021-02-11 14:41:54,694 [phygnn.py:783] : Epoch 191 train loss: -5.83e-01 val loss: -5.70e-01 for "Generator" INFO - 2021-02-11 14:42:03,235 [phygnn.py:783] : Epoch 192 train loss: -4.61e-01 val loss: -4.62e-01 for "Generator" INFO - 2021-02-11 14:42:11,877 [phygnn.py:783] : Epoch 193 train loss: -5.15e-01 val loss: -4.97e-01 for "Generator" INFO - 2021-02-11 14:42:20,260 [phygnn.py:783] : Epoch 194 train loss: -4.93e-01 val loss: -5.01e-01 for "Generator" INFO - 2021-02-11 14:42:28,869 [phygnn.py:783] : Epoch 195 train loss: -4.70e-01 val loss: -4.69e-01 for "Generator" INFO - 2021-02-11 14:42:38,942 [phygnn.py:783] : Epoch 196 train loss: -5.00e-01 val loss: -4.92e-01 for "Generator" INFO - 2021-02-11 14:42:47,315 [phygnn.py:783] : Epoch 197 train loss: -5.16e-01 val loss: -5.07e-01 for "Generator" INFO - 2021-02-11 14:42:54,983 [phygnn.py:783] : Epoch 198 train loss: -4.39e-01 val loss: -4.47e-01 for "Generator" INFO - 2021-02-11 14:43:03,685 [phygnn.py:783] : Epoch 199 train loss: -4.28e-01 val loss: -4.38e-01 for "Generator"
generator.history[['training_loss', 'validation_loss']].plot()
<AxesSubplot:xlabel='epoch'>
Here we see ensembles of synthetically generated profiles generated from the conditioning features of a single real day. The histograms compare the single real day's conditioning features to the those of the full training data set and the generated ensemble.
N = 1000
all_max, all_sum, all_max_diff, all_mean_diff = SolarGan.calc_features(real_data[:, I1:])
for I in range(0, len(test_data), 4):
test_features = np.tile(np.expand_dims(test_data[I, 0:I1], axis=1), N).T
generated = generator.predict(test_features)
gen_max, gen_sum, gen_max_diff, gen_mean_diff = SolarGan.calc_features(generated)
real_max, real_sum, real_max_diff, real_mean_diff = SolarGan.calc_features(np.expand_dims(test_data[I, I1:], axis=0))
fig = plt.figure(figsize=(16, 3))
ax1 = fig.add_subplot(141)
ax2 = fig.add_subplot(142)
ax3 = fig.add_subplot(143)
ax4 = fig.add_subplot(144)
for i in range(0, N):
a, = ax1.plot(generated[i, 10:], c='k', alpha=0.1, linewidth=0.2)
b, = ax1.plot(test_data[I, I1 + 10:], c='r')
ax1.legend([a, b], ['Gen Ensemble', 'Real Day'], loc=2)
ax1.set_xlabel('Hour')
ax1.set_ylabel('GHI (W/m2)')
counts = ax2.hist(all_sum, bins=50, density=True, alpha=0.5)[0]
counts = ax2.hist(gen_sum, bins=50, density=True, alpha=0.5)[0]
ax2.plot([real_sum, real_sum], [0, counts.max()], '--')
ax2.set_xlabel('Sum GHI')
ax2.set_yticklabels([])
counts = ax3.hist(all_max_diff, bins=50, density=True, alpha=0.5)[0]
counts = ax3.hist(gen_max_diff, bins=50, density=True, alpha=0.5)[0]
ax3.plot([real_max_diff, real_max_diff], [0, counts.max()], '--')
ax3.set_xlabel('Maximum 30-min Ramp')
ax3.set_yticklabels([])
counts = ax4.hist(all_mean_diff, bins=50, density=True, alpha=0.5)[0]
counts = ax4.hist(gen_mean_diff, bins=50, density=True, alpha=0.5)[0]
ax4.plot([real_mean_diff, real_mean_diff], [0, counts.max()], '--')
ax4.set_xlabel('Mean 30-min Ramp')
ax4.legend(['Real Day', 'All Observed', 'Gen Ensemble'])
ax4.set_yticklabels([])
plt.show()
plt.close()
Here we see single synthetically generated profiles generated from the conditioning features of a single real day.
features = test_data[:, :I1]
generated = generator.predict(features)
gen_max, gen_sum, gen_max_diff, gen_mean_diff = SolarGan.calc_features(generated)
for I in range(0, len(test_data), 4):
plt.plot(test_data[I, 0:48])
plt.plot(test_data[I, I1:])
plt.plot(generated[I, :])
legend_real = ('Real \n - ghi_sum: {:.0f} \n - max_ramp: {:.0f} \n - mean_ramp: {:.0f}'
.format(test_data[I, 49], test_data[I, 50], test_data[I, 51]))
legend_gen = ('Generated \n - ghi_sum: {:.0f} \n - max_ramp: {:.0f} \n - mean_ramp: {:.0f}'
.format(gen_sum[I], gen_max_diff[I], gen_mean_diff[I]))
plt.legend(['Clear', legend_real, legend_gen])
plt.show()
plt.close()
This histogram will be Zero if discriminator is perfect
classification = discriminator(generated).numpy().flatten()
print(classification.mean())
_ = plt.hist(classification, bins=100)
0.44374865
This histogram will be One if the discriminator is perfect
classification = discriminator(real_data[:, I1:]).numpy().flatten()
print(classification.mean())
_ = plt.hist(classification, bins=100)
0.48724818