Files
geant4/examples/extended/parameterisations/Par04/training/model.py
T
2022-07-01 10:44:02 +02:00

135 lines
7.4 KiB
Python

"""
** model **
defines the VAE model class
"""
# Setup
import keras
from tensorflow.keras.layers import Input, Dense, Lambda, Layer, Multiply, Add, concatenate
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.models import Model
from tensorflow.keras import backend as K
from tensorflow.keras import metrics
# VAE model class
class VAE:
def __init__(self, **kwargs):
self.original_dim = kwargs.get('original_dim')
self.latent_dim = kwargs.get('latent_dim')
self.batch_size = kwargs.get('batch_size')
self.intermediate_dim1 = kwargs.get('intermediate_dim1')
self.intermediate_dim2 = kwargs.get('intermediate_dim2')
self.intermediate_dim3 = kwargs.get('intermediate_dim3')
self.intermediate_dim4 = kwargs.get('intermediate_dim4')
self.epsilon_std = kwargs.get('epsilon_std')
self.mu = kwargs.get('mu')
self.lr = kwargs.get('lr')
self.epochs = kwargs.get('epochs')
self.activ = kwargs.get('activ')
self.outActiv = kwargs.get('outActiv')
self.validation_split = kwargs.get('validation_split')
self.wReco = kwargs.get('wReco')
self.wkl = kwargs.get('wkl')
self.optimizer = kwargs.get('optimizer')
self.ki = kwargs.get('ki')
self.bi = kwargs.get('bi')
self.checkpoint_dir = kwargs.get('checkpoint_dir')
self.earlyStop = kwargs.get('earlyStop')
# KL divergence computation
class KLDivergenceLayer(Layer):
def __init__(self, *args, **kwargs):
self.is_placeholder = True
super(KLDivergenceLayer, self).__init__(*args, **kwargs)
def call(self, inputs):
mu, log_var = inputs
kl_batch = -self.wkl * K.sum(1 + log_var - K.square(mu) - K.exp(log_var), axis=-1)
self.add_loss(K.mean(kl_batch), inputs=inputs)
return inputs
# Build the encoder
xIn = Input((input_dim,))
eCond = Input(shape=(1,))
angleCond = Input(shape=(1,))
GeoCond = Input(shape=(2,))
mergedInput = concatenate([xIn, eCond, angleCond, GeoCond],)
h1 = Dense(self.intermediate_dim1, activation=self.activ,
kernel_initializer=self.ki, bias_initializer=self.bi)(mergedInput)
h1 = BatchNormalization()(h1)
h2 = Dense(self.intermediate_dim2, activation=self.activ,
kernel_initializer=self.ki, bias_initializer=self.bi)(h1)
h2 = BatchNormalization()(h2)
h3 = Dense(self.intermediate_dim3, activation=self.activ,
kernel_initializer=self.ki, bias_initializer=self.bi)(h2)
h3 = BatchNormalization()(h3)
h4 = Dense(self.intermediate_dim4, activation=self.activ,
kernel_initializer=self.ki, bias_initializer=self.bi)(h3)
h = BatchNormalization()(h4)
z_mu = Dense(self.latent_dim,)(h)
z_log_var = Dense(self.latent_dim,)(h)
# compute the KL divergence
z_mu, z_log_var = KLDivergenceLayer()([z_mu, z_log_var])
# Reparameterization trick
z_sigma = Lambda(lambda t: K.exp(.5*t))(z_log_var)
eps = Input(tensor=K.random_normal(shape=(K.shape(xIn)[0], self.latent_dim)))
z_eps = Multiply()([z_sigma, eps])
z = Add()([z_mu, z_eps])
zCond = concatenate([z,eCond,angleCond,GeoCond],)
# This defines the encoder which takes noise and input and outputs the latent variable z
self.encoder = Model(inputs=[xIn,eCond,angleCond,GeoCond,eps], outputs=zCond)
# Build the decoder / Generator
decoL4 = Dense(self.intermediate_dim4, input_dim=(self.latent_dim+4),
activation=self.activ, kernel_initializer=self.ki, bias_initializer=self.bi)
decoL4_BN = BatchNormalization()
decoL3 = Dense(self.intermediate_dim3, input_dim=self.intermediate_dim4,
activation=self.activ, kernel_initializer=self.ki, bias_initializer=self.bi)
decoL3_BN = BatchNormalization()
decoL2 = Dense(self.intermediate_dim2, input_dim=self.intermediate_dim3,
activation=self.activ, kernel_initializer=self.ki, bias_initializer=self.bi)
decoL2_BN = BatchNormalization()
decoL1 = Dense(self.intermediate_dim1, input_dim=self.intermediate_dim2,
activation=self.activ, kernel_initializer=self.ki, bias_initializer=self.bi)
decoL1_BN = BatchNormalization()
x_reco = Dense(self.original_dim, activation=self.outActiv)
zDecoInput = Input(shape=(latent_dim+4,))
x_recoDeco = x_reco((((decoL1_BN(decoL1(decoL2_BN(decoL2(decoL3_BN(decoL3(decoL4_BN(decoL4(zDecoInput))))))))))))
# This defines the decoder which takes an input of size latent dimension + condition size dimension and outputs the reconstructed input version
self.decoder = Model(inputs=[zDecoInput], outputs=[x_recoDeco])
# This defines the reconstruction loss of the VAE model
def reconstructionLoss(G4_Event, VAE_Event):
return K.mean(self.wReco*K.sum(metrics.binary_crossentropy(G4_Event, VAE_Event)))
# This defines the VAE model (encoder and decoder)
self.vae = Model(inputs=[xIn,eCond,angleCond,GeoCond,eps], outputs=[self.decoder(self.encoder([xIn, eCond,angleCond,GeoCond,eps]))])
self.vae.compile(optimizer=self.optimizer, loss=[reconstructionLoss] )
# Training function
def train(self, trainSet, eCond, angleCond, GeoCond):
# If the early stopping flag is on then stop the training when a monitored metric (validation) has stopped improving after (patience) number of epochs
if(self.earlyStop):
from tensorflow.keras.callbacks import EarlyStopping
cP = EarlyStopping(monitor='val_loss', min_delta=0.01, patience=5,verbose=1)
# If the early stopping flag is off then run the training for the number of epochs and save the model every (period) epochs
else:
cP = keras.callbacks.ModelCheckpoint('%s/VAE-{epoch:02d}.h5'%self.checkpoint_dir, monitor='val_loss',
verbose=0, save_best_only=False, save_weights_only=False, mode='auto',
period=100)
noise = np.random.normal(0,1, size = (trainSet.shape[0],latent_dim))
history = self.vae.fit([trainSet, eCond, angleCond, GeoCond,noise], [trainSet],
shuffle=True,
epochs=self.epochs,
verbose=1,
validation_split=self.validation_split,
batch_size=self.batch_size,
callbacks=[cP]
)
return history
# Encode function uses only the encoder to generate the latent representation of an input
def encode(self, dataSet):
return self.encoder.predict(dataSet, batch_size=self.batch_size)
# Generate function uses only the decoder to generate new showers using the z_sample which is a vector of 10D Gaussians in addition to
def generate(self, z_sample):
return self.decoder.predict([z_sample])
# Encode function
def predict(self, dataSet):
return self.vae.predict(dataSet, batch_size=self.batch_size)
# Encode function
def evaluate(self, dataSet):
return self.vae.evaluate(dataSet, batch_size=self.batch_size)