From 886afc677b3a0806da5a111785dd4175797b3b65 Mon Sep 17 00:00:00 2001 From: Luke de Oliveira Date: Sat, 18 Mar 2017 23:20:56 -0700 Subject: [PATCH] restructuring release --- models/lagan/generator.py | 62 ------- models/manifolds.py | 150 +++++++++++++++++ models/metrics.py | 111 +++++++++++++ models/{lagan => networks}/__init__.py | 0 models/networks/dcgan.py | 140 ++++++++++++++++ models/networks/fcn.py | 102 ++++++++++++ models/networks/hybrid.py | 156 ++++++++++++++++++ .../discriminator.py => networks/lagan.py} | 53 +++++- models/{lagan => networks}/ops.py | 0 models/train.py | 27 +-- 10 files changed, 722 insertions(+), 79 deletions(-) delete mode 100644 models/lagan/generator.py create mode 100644 models/manifolds.py create mode 100644 models/metrics.py rename models/{lagan => networks}/__init__.py (100%) create mode 100644 models/networks/dcgan.py create mode 100644 models/networks/fcn.py create mode 100644 models/networks/hybrid.py rename models/{lagan/discriminator.py => networks/lagan.py} (62%) rename models/{lagan => networks}/ops.py (100%) diff --git a/models/lagan/generator.py b/models/lagan/generator.py deleted file mode 100644 index b9cda30..0000000 --- a/models/lagan/generator.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -file: generators.py -description: generator submodel for [arXiv/1701.05927] -author: Luke de Oliveira (lukedeoliveira@lbl.gov) -""" -import keras.backend as K -from keras.layers import (Input, Dense, Reshape, Flatten, Embedding, merge, - Dropout, BatchNormalization, Activation) - -from keras.layers.advanced_activations import LeakyReLU - -from keras.layers.local import LocallyConnected2D -from keras.layers.convolutional import UpSampling2D, Conv2D, ZeroPadding2D - -from keras.models import Model, Sequential - -K.set_image_dim_ordering('tf') - - -def generator(latent_size, return_intermediate=False): - - loc = Sequential([ - # DCGAN-style project & reshape, - Dense(128 * 7 * 7, input_dim=latent_size), - Reshape((7, 7, 128)), - - # block 1: (None, 7, 7, 128) => (None, 14, 14, 64), - Conv2D(64, 5, 5, border_mode='same', init='he_uniform'), - LeakyReLU(), - BatchNormalization(), - UpSampling2D(size=(2, 2)), - - # block 2: (None, 14, 14, 64) => (None, 28, 28, 6), - ZeroPadding2D((2, 2)), - LocallyConnected2D(6, 5, 5, init='he_uniform'), - LeakyReLU(), - BatchNormalization(), - UpSampling2D(size=(2, 2)), - - # block 3: (None, 28, 28, 6) => (None, 25, 25, 1), - LocallyConnected2D(6, 3, 3, init='he_uniform'), - LeakyReLU(), - LocallyConnected2D(1, 2, 2, bias=False, init='glorot_normal'), - Activation('relu') - ]) - - # this is the z space commonly refered to in GAN papers - latent = Input(shape=(latent_size, )) - - # this will be our label - image_class = Input(shape=(1, ), dtype='int32') - emb = Flatten()(Embedding(2, latent_size, input_length=1, - init='glorot_normal')(image_class)) - - # hadamard product between z-space and a class conditional embedding - h = merge([latent, emb], mode='mul') - - fake_image = loc(h) - - return Model(input=[latent, image_class], output=fake_image) diff --git a/models/manifolds.py b/models/manifolds.py new file mode 100644 index 0000000..f4febfb --- /dev/null +++ b/models/manifolds.py @@ -0,0 +1,150 @@ +from joblib import Parallel, delayed +import numpy as np +import math + + +# form the grids you need to represent the eta, phi coordinates +grid = 0.5 * (np.linspace(-1.25, 1.25, 26)[:-1] + np.linspace(-1.25, 1.25, 26)[1:]) +eta = np.tile(grid, (25, 1)) +phi = np.tile(grid[::-1].reshape(-1, 1), (1, 25)) + + +def discrete_mass(jet_image): + ''' + Calculates the jet mass from a pixelated jet image + Args: + ----- + jet_image: numpy ndarray of dim (1, 25, 25) + Returns: + -------- + M: float, jet mass + ''' + Px = np.sum(jet_image * np.cos(phi), axis=(1, 2)) + Py = np.sum(jet_image * np.sin(phi), axis=(1, 2)) + Pz = np.sum(jet_image * np.sinh(eta), axis=(1, 2)) + E = np.sum(jet_image * np.cosh(eta), axis=(1, 2)) + PT2 = np.square(Px) + np.square(Py) + M2 = np.square(E) - (PT2 + np.square(Pz)) + M = np.sqrt(M2) + return M + + +def discrete_pt(jet_image): + ''' + Calculates the jet transverse momentum from a pixelated jet image + Args: + ----- + jet_image: numpy ndarray of dim (1, 25, 25) + Returns: + -------- + float, jet transverse momentum + ''' + Px = np.sum(jet_image * np.cos(phi), axis=(1, 2)) + Py = np.sum(jet_image * np.sin(phi), axis=(1, 2)) + return np.sqrt(np.square(Px) + np.square(Py)) + + +def dphi(phi1, phi2): + ''' + Calculates the difference between two angles avoiding |phi1 - phi2| > 180 degrees + ''' + import math + return math.acos(math.cos(abs(phi1 - phi2))) + + +def _tau1(jet_image): + ''' + Calculates the normalized tau1 from a pixelated jet image + Args: + ----- + jet_image: numpy ndarray of dim (1, 25, 25) + Returns: + -------- + float, normalized jet tau1 + ''' + # find coordinate of most energetic pixel, then use formula to compute tau1 + tau1_axis_eta = eta.ravel()[np.argmax(jet_image)] + tau1_axis_phi = phi.ravel()[np.argmax(jet_image)] + tau1 = np.sum(jet_image * + np.sqrt(np.square(tau1_axis_eta - eta) + + np.square([dphi(tau1_axis_phi, p) for p in phi.ravel()]).reshape(25, 25)) + ) + return tau1 / np.sum(jet_image) # normalize by the total intensity + + +def _tau2(jet_image): + ''' + Calculates the normalized tau2 from a pixelated jet image + Args: + ----- + jet_image: numpy ndarray of dim (1, 25, 25) + Returns: + -------- + float, normalized jet tau2 + Notes: + ------ + slow implementation + ''' + proto = np.array(zip(jet_image[jet_image != 0], + eta[jet_image != 0], + phi[jet_image != 0])) + while len(proto) > 2: + candidates = [ + ( + (i, j), + (min(pt1, pt2) ** 2) * ((eta1 - eta2) ** 2 + (phi1 - phi2) ** 2) + ) + for i, (pt1, eta1, phi1) in enumerate(proto) + for j, (pt2, eta2, phi2) in enumerate(proto) + if j > i + ] + index, value = zip(*candidates) + pix1, pix2 = index[np.argmin(value)] + if pix1 > pix2: + # swap + pix1, pix2 = pix2, pix1 + (pt1, eta1, phi1) = proto[pix1] + (pt2, eta2, phi2) = proto[pix2] + e1 = pt1 / np.cosh(eta1) + e2 = pt2 / np.cosh(eta2) + choice = e1 > e2 + eta_add = (eta1 if choice else eta2) + phi_add = (phi1 if choice else phi2) + pt_add = (e1 + e2) * np.cosh(eta_add) + proto[pix1] = (pt_add, eta_add, phi_add) + proto = np.delete(proto, pix2, axis=0).tolist() + + (_, eta1, phi1), (_, eta2, phi2) = proto + np.sqrt(np.square(eta - eta1) + np.square(phi - phi1)) + grid = np.array([ + np.sqrt(np.square(eta - eta1) + np.square(phi - phi1)), + np.sqrt(np.square(eta - eta2) + np.square(phi - phi2)) + ]).min(axis=0) + # normalize by the total intensity + return np.sum(jet_image * grid) / np.sum(jet_image) + + +def tau21(jet_image, nb_jobs=1, verbose=False): + ''' + Calculates the tau21 from a pixelated jet image using the functions above + Args: + ----- + jet_image: numpy ndarray of dim (1, 25, 25) + Returns: + -------- + float, jet tau21 + Notes: + ------ + slow implementation + ''' + if len(jet_image.shape) == 2: + tau1 = _tau1(jet_image) + if tau1 <= 0: + return 0 + else: + tau2 = _tau2(jet_image) + return tau2 / tau1 + + return np.array(Parallel(n_jobs=nb_jobs, verbose=verbose)( + delayed(tau21)(im) for im in jet_image + )) diff --git a/models/metrics.py b/models/metrics.py new file mode 100644 index 0000000..0beafa3 --- /dev/null +++ b/models/metrics.py @@ -0,0 +1,111 @@ +import numpy as np +from scipy.spatial.distance import cdist as distance +from pyemd import emd +from scipy.linalg import toeplitz + + +class AnnoyingError(Exception): + pass + + +def _calculate_emd_2D(D1, D2, bins=(40, 40)): + """ + Args: + ----- + D1, D2: two np arrays with potentially differing + numbers of rows, but two columns. The empirical + distributions you want a similarity over + bins: number of bins in each dim + """ + + try: + _, bx, by = np.histogram2d(*np.concatenate((D1, D2), axis=0).T, bins=bins) + except ValueError, e: + print '[ERROR] found here' + + raise AnnoyingError('Fuck this') + + H1, _, _ = np.histogram2d(*D1.T, bins=(bx, by)) + H2, _, _ = np.histogram2d(*D2.T, bins=(bx, by)) + + H1 /= H1.sum() + H2 /= H2.sum() + + _x, _y = np.indices(H1.shape) + + coords = np.array(zip(_x.ravel(), _y.ravel())) + + D = distance(coords, coords) + + return emd(H1.ravel(), H2.ravel(), D) + + +def _calculate_emd_1D(D1, D2, bins=40): + """ + Args: + ----- + D1, D2: two np arrays with potentially differing + numbers of rows, but two columns. The empirical + distributions you want a similarity over + bins: number of bins in each dim + """ + + D1 = D1[np.isnan(D1).sum(axis=-1) < 1] + D2 = D2[np.isnan(D2).sum(axis=-1) < 1] + + try: + _, bx = np.histogram(np.concatenate((D1, D2), axis=0), bins=bins) + except ValueError, e: + print '[ERROR] found here' + + raise AnnoyingError('Fuck this') + + H1, _ = np.histogram(D1, bins=bx, normed=True) + H2, _ = np.histogram(D2, bins=bx, normed=True) + + H1 /= H1.sum() + H2 /= H2.sum() + + # _x, _y = np.indices(H1.shape) + + D = toeplitz(range(len(H1))).astype(float) + + # print coords + + # D = distance(coords, coords) + + return emd(H1, H2, D) + + +def calculate_metric(D1, signal1, D2, signal2, bins=(40, 40)): + """ + Args: + ----- + D1: (nb_rows, 2) array of observations from first distribution + signal1: (nb_rows, ) array of 1 or 0, indicating class of + first distribution + D2: (nb_rows, 2) array of observations from second distribution + signal2: (nb_rows, ) array of 1 or 0, indicating class of + second distribution + + bins: number of bins in each dim + """ + + try: + if len(D1.shape) == 2: + sig_cond = _calculate_emd_2D(D1[signal1 == True], D2[ + signal2 == True], bins=bins) + bkg_cond = _calculate_emd_2D(D1[signal1 == False], D2[ + signal2 == False], bins=bins) + + else: + if not isinstance(bins, int): + bins = bins[0] + sig_cond = _calculate_emd_1D(D1[signal1 == True], D2[ + signal2 == True], bins=bins) + bkg_cond = _calculate_emd_1D(D1[signal1 == False], D2[ + signal2 == False], bins=bins) + + return max(sig_cond, bkg_cond) + except AnnoyingError, a: + return 999 diff --git a/models/lagan/__init__.py b/models/networks/__init__.py similarity index 100% rename from models/lagan/__init__.py rename to models/networks/__init__.py diff --git a/models/networks/dcgan.py b/models/networks/dcgan.py new file mode 100644 index 0000000..b95e4fa --- /dev/null +++ b/models/networks/dcgan.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +file: dcgan.py +description: dcgan implementation for [arXiv/1701.05927] +author: Luke de Oliveira (lukedeoliveira@lbl.gov) +""" + +import keras.backend as K +from keras.layers import (Input, Dense, Reshape, Flatten, Lambda, merge, + Dropout, BatchNormalization, Activation, Embedding) +from keras.layers.advanced_activations import LeakyReLU +from keras.layers.convolutional import (UpSampling2D, Conv2D, ZeroPadding2D, + AveragePooling2D) +from keras.layers.local import LocallyConnected2D + +from keras.models import Model, Sequential + +from .ops import minibatch_discriminator, minibatch_output_shape, Dense3D + + +K.set_image_dim_ordering('tf') + + +def discriminator(): + + image = Input(shape=(25, 25, 1)) + + # block 1: normal 5x5 conv, + # *NO* batchnorm (recommendation from [arXiv/1511.06434]) + x = Conv2D(32, 5, 5, border_mode='same')(image) + x = LeakyReLU()(x) + x = Dropout(0.2)(x) + + # block 2: 'same' bordered 5x5 locally connected block with batchnorm and + # 2x2 subsampling + x = Conv2D(64, 5, 5, border_mode='same', subsample=(2, 2))(x) + x = LeakyReLU()(x) + x = BatchNormalization()(x) + x = Dropout(0.2)(x) + + # block 2: 'same' bordered 5x5 locally connected block with batchnorm + x = Conv2D(128, 5, 5, border_mode='same')(x) + x = LeakyReLU()(x) + x = BatchNormalization()(x) + x = Dropout(0.2)(x) + + # block 3: 'same' bordered 3x3 locally connected block with batchnorm and + # 2x2 subsampling + x = Conv2D(256, 3, 3, border_mode='valid', subsample=(2, 2))(x) + x = LeakyReLU()(x) + x = BatchNormalization()(x) + x = Dropout(0.2)(x) + + x = AveragePooling2D((2, 2))(x) + + h = Flatten()(x) + + dnn = Model(image, h) + + image = Input(shape=(25, 25, 1)) + + dnn_out = dnn(image) + + # nb of features to obtain + nb_features = 20 + + # dim of kernel space + vspace_dim = 10 + + # creates the kernel space for the minibatch discrimination + K_x = Dense3D(nb_features, vspace_dim)(dnn_out) + + minibatch_featurizer = Lambda(minibatch_discriminator, + output_shape=minibatch_output_shape) + + # concat the minibatch features with the normal ones + features = merge([ + minibatch_featurizer(K_x), + dnn_out + ], mode='concat') + + # fake output tracks binary fake / not-fake, and the auxiliary requires + # reconstruction of latent features, in this case, labels + fake = Dense(1, activation='sigmoid', name='generation')(features) + aux = Dense(1, activation='sigmoid', name='auxiliary')(features) + + return Model(input=image, output=[fake, aux]) + + +def generator(latent_size, return_intermediate=False): + + loc = Sequential([ + # DCGAN-style project & reshape, + Dense(128 * 7 * 7, input_dim=latent_size), + Reshape((7, 7, 128)), + + # block 1: (None, 7, 7, 128) => (None, 14, 14, 64), + Deconv2D(64, 5, 5, (None, 14, 14, 64), + subsample=(2, 2), + border_mode='same', + init='he_uniform'), + LeakyReLU(), + BatchNormalization(), + # UpSampling2D(size=(2, 2)), + + # block 2: (None, 14, 14, 64) => (None, 28, 28, 6), + + Deconv2D(32, 5, 5, (None, 28, 28, 32), + subsample=(2, 2), + border_mode='same', + init='he_uniform'), + LeakyReLU(), + BatchNormalization(), + + # block 3: (None, 28, 28, 6) => (None, 25, 25, 1), + Conv2D(8, 3, 3, init='he_uniform', border_mode='valid'), + # LocallyConnected2D(6, 3, 3, init='he_uniform'), + LeakyReLU(), + + Conv2D(1, 2, 2, init='he_uniform', border_mode='valid'), + + # LocallyConnected2D(1, 2, 2, bias=False, init='glorot_normal'), + Activation('relu') + ]) + + # this is the z space commonly refered to in GAN papers + latent = Input(shape=(latent_size, )) + + # this will be our label + image_class = Input(shape=(1, ), dtype='int32') + emb = Flatten()(Embedding(2, latent_size, input_length=1, + init='glorot_normal')(image_class)) + + # hadamard product between z-space and a class conditional embedding + h = merge([latent, emb], mode='mul') + + fake_image = loc(h) + + return Model(input=[latent, image_class], output=fake_image) diff --git a/models/networks/fcn.py b/models/networks/fcn.py new file mode 100644 index 0000000..ea7226f --- /dev/null +++ b/models/networks/fcn.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +file: fcn.py +description: discrimination submodel for [arXiv/1701.05927] +author: Luke de Oliveira (lukedeoliveira@lbl.gov) +""" + +import keras.backend as K +from keras.layers import (Input, Dense, Reshape, Flatten, Lambda, merge, + Dropout, BatchNormalization, Activation, Embedding) +from keras.layers.advanced_activations import LeakyReLU +from keras.layers.convolutional import (UpSampling2D, Conv2D, ZeroPadding2D, + AveragePooling2D) +from keras.layers.local import LocallyConnected2D + +from keras.models import Model, Sequential + +from .ops import minibatch_discriminator, minibatch_output_shape, Dense3D + + +K.set_image_dim_ordering('tf') + + +def discriminator(): + + image = Input(shape=(25, 25, 1)) + + x = Flatten()(image) + x = Dense(1024)(x) + x = LeakyReLU()(x) + x = Dropout(0.2)(x) + + x = Dense(512)(x) + x = LeakyReLU()(x) + x = Dropout(0.2)(x) + + x = Dense(128)(x) + x = LeakyReLU()(x) + h = Dropout(0.2)(x) + + dnn = Model(image, h) + + image = Input(shape=(25, 25, 1)) + + dnn_out = dnn(image) + + # nb of features to obtain + nb_features = 20 + + # dim of kernel space + vspace_dim = 10 + + # creates the kernel space for the minibatch discrimination + K_x = Dense3D(nb_features, vspace_dim)(dnn_out) + + minibatch_featurizer = Lambda(minibatch_discriminator, + output_shape=minibatch_output_shape) + + # concat the minibatch features with the normal ones + features = merge([ + minibatch_featurizer(K_x), + dnn_out + ], mode='concat') + + # fake output tracks binary fake / not-fake, and the auxiliary requires + # reconstruction of latent features, in this case, labels + fake = Dense(1, activation='sigmoid', name='generation')(features) + aux = Dense(1, activation='sigmoid', name='auxiliary')(features) + + return Model(input=image, output=[fake, aux]) + + +def generator(latent_size, return_intermediate=False): + + loc = Sequential([ + # DCGAN-style project & reshape, + Dense(256, input_dim=latent_size), + LeakyReLU(), + Dense(512), + LeakyReLU(), + Dense(625), + LeakyReLU(), + Dense(625), + Activation('relu'), + Reshape((25, 25, 1)) + ]) + + # this is the z space commonly refered to in GAN papers + latent = Input(shape=(latent_size, )) + + # this will be our label + image_class = Input(shape=(1, ), dtype='int32') + emb = Flatten()(Embedding(2, latent_size, input_length=1, + init='glorot_normal')(image_class)) + + # hadamard product between z-space and a class conditional embedding + h = merge([latent, emb], mode='mul') + + fake_image = loc(h) + + return Model(input=[latent, image_class], output=fake_image) diff --git a/models/networks/hybrid.py b/models/networks/hybrid.py new file mode 100644 index 0000000..b9e93db --- /dev/null +++ b/models/networks/hybrid.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +file: hybrid.py +description: hybridgan implementation for [arXiv/1701.05927] +author: Luke de Oliveira (lukedeoliveira@lbl.gov) +""" + +import keras.backend as K +from keras.layers import (Input, Dense, Reshape, Flatten, Lambda, merge, + Dropout, BatchNormalization, Activation, Embedding) +from keras.layers.advanced_activations import LeakyReLU +from keras.layers.convolutional import (UpSampling2D, Conv2D, ZeroPadding2D, + AveragePooling2D) +from keras.layers.local import LocallyConnected2D + +from keras.models import Model, Sequential + +from .ops import minibatch_discriminator, minibatch_output_shape, Dense3D + + +K.set_image_dim_ordering('tf') + + +def discriminator(): + + image = Input(shape=(25, 25, 1)) + + # block 1: normal 5x5 conv, + # *NO* batchnorm (recommendation from [arXiv/1511.06434]) + x = Conv2D(32, 5, 5, border_mode='same')(image) + x = LeakyReLU()(x) + x = Dropout(0.2)(x) + + # block 2: 'same' bordered 5x5 locally connected block with batchnorm and + # 2x2 subsampling + x = Conv2D(64, 5, 5, border_mode='same', subsample=(2, 2))(x) + x = LeakyReLU()(x) + x = BatchNormalization()(x) + x = Dropout(0.2)(x) + + # block 2: 'same' bordered 5x5 locally connected block with batchnorm + x = Conv2D(128, 5, 5, border_mode='same')(x) + x = LeakyReLU()(x) + x = BatchNormalization()(x) + x = Dropout(0.2)(x) + + # block 3: 'same' bordered 3x3 locally connected block with batchnorm and + # 2x2 subsampling + x = Conv2D(256, 3, 3, border_mode='valid', subsample=(2, 2))(x) + x = LeakyReLU()(x) + x = BatchNormalization()(x) + x = Dropout(0.2)(x) + + x = AveragePooling2D((2, 2))(x) + + h = Flatten()(x) + + cnn = Model(image, h) + + image = Input(shape=(25, 25, 1)) + + x = Flatten()(image) + x = Dense(1024)(x) + x = LeakyReLU()(x) + x = Dropout(0.2)(x) + x = Dense(512)(x) + x = LeakyReLU()(x) + x = Dropout(0.2)(x) + x = Dense(128)(x) + x = LeakyReLU()(x) + h = Dropout(0.2)(x) + + dnn = Model(image, h) + + image = Input(shape=(25, 25, 1)) + + dnn_out = merge([dnn(image), cnn(image)], mode='concat', concat_axis=-1) + + # nb of features to obtain + nb_features = 20 + + # dim of kernel space + vspace_dim = 10 + + # creates the kernel space for the minibatch discrimination + K_x = Dense3D(nb_features, vspace_dim)(dnn_out) + + minibatch_featurizer = Lambda(minibatch_discriminator, + output_shape=minibatch_output_shape) + + # concat the minibatch features with the normal ones + features = merge([ + minibatch_featurizer(K_x), + dnn_out + ], mode='concat') + + # fake output tracks binary fake / not-fake, and the auxiliary requires + # reconstruction of latent features, in this case, labels + fake = Dense(1, activation='sigmoid', name='generation')(features) + aux = Dense(1, activation='sigmoid', name='auxiliary')(features) + + return Model(input=image, output=[fake, aux]) + + +def generator(latent_size, return_intermediate=False): + + loc = Sequential([ + # DCGAN-style project & reshape, + Dense(256, input_dim=latent_size), + LeakyReLU(), + Dense(512), + LeakyReLU(), + Dense(625), + LeakyReLU(), + Dense(625), + Activation('relu'), + Reshape((25, 25, 1)) + ]) + + spread = Sequential([ + # DCGAN-style project & reshape, + Dense(128 * 7 * 7, input_dim=latent_size), + Reshape((7, 7, 128)), + Deconv2D(64, 5, 5, (None, 14, 14, 64), + subsample=(2, 2), + border_mode='same', + init='he_uniform'), + LeakyReLU(), + BatchNormalization(), + Deconv2D(32, 5, 5, (None, 28, 28, 32), + subsample=(2, 2), + border_mode='same', + init='he_uniform'), + LeakyReLU(), + BatchNormalization(), + Conv2D(8, 3, 3, init='he_uniform', border_mode='valid'), + LeakyReLU(), + Conv2D(1, 2, 2, init='he_uniform', border_mode='valid'), + Activation('relu') + ]) + + # this is the z space commonly refered to in GAN papers + latent = Input(shape=(latent_size, )) + + # this will be our label + image_class = Input(shape=(1, ), dtype='int32') + emb = Flatten()(Embedding(2, latent_size, input_length=1, + init='glorot_normal')(image_class)) + + # hadamard product between z-space and a class conditional embedding + h = merge([latent, emb], mode='mul') + + fake_image = merge([loc(h), spread(h)], mode='ave') + + return Model(input=[latent, image_class], output=fake_image) diff --git a/models/lagan/discriminator.py b/models/networks/lagan.py similarity index 62% rename from models/lagan/discriminator.py rename to models/networks/lagan.py index ad66850..51348da 100644 --- a/models/lagan/discriminator.py +++ b/models/networks/lagan.py @@ -1,22 +1,24 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -file: discriminators.py -description: discrimination submodel for [arXiv/1701.05927] +file: lagan.py +description: submodels for [arXiv/1701.05927] author: Luke de Oliveira (lukedeoliveira@lbl.gov) """ import keras.backend as K from keras.layers import (Input, Dense, Reshape, Flatten, Lambda, merge, - Dropout, BatchNormalization) + Dropout, BatchNormalization, Activation, Embedding) from keras.layers.advanced_activations import LeakyReLU from keras.layers.convolutional import (UpSampling2D, Conv2D, ZeroPadding2D, AveragePooling2D) from keras.layers.local import LocallyConnected2D -from keras.models import Model + +from keras.models import Model, Sequential from .ops import minibatch_discriminator, minibatch_output_shape, Dense3D + K.set_image_dim_ordering('tf') @@ -86,3 +88,46 @@ def discriminator(): aux = Dense(1, activation='sigmoid', name='auxiliary')(features) return Model(input=image, output=[fake, aux]) + + +def generator(latent_size, return_intermediate=False): + + loc = Sequential([ + # DCGAN-style project & reshape, + Dense(128 * 7 * 7, input_dim=latent_size), + Reshape((7, 7, 128)), + + # block 1: (None, 7, 7, 128) => (None, 14, 14, 64), + Conv2D(64, 5, 5, border_mode='same', init='he_uniform'), + LeakyReLU(), + BatchNormalization(), + UpSampling2D(size=(2, 2)), + + # block 2: (None, 14, 14, 64) => (None, 28, 28, 6), + ZeroPadding2D((2, 2)), + LocallyConnected2D(6, 5, 5, init='he_uniform'), + LeakyReLU(), + BatchNormalization(), + UpSampling2D(size=(2, 2)), + + # block 3: (None, 28, 28, 6) => (None, 25, 25, 1), + LocallyConnected2D(6, 3, 3, init='he_uniform'), + LeakyReLU(), + LocallyConnected2D(1, 2, 2, bias=False, init='glorot_normal'), + Activation('relu') + ]) + + # this is the z space commonly refered to in GAN papers + latent = Input(shape=(latent_size, )) + + # this will be our label + image_class = Input(shape=(1, ), dtype='int32') + emb = Flatten()(Embedding(2, latent_size, input_length=1, + init='glorot_normal')(image_class)) + + # hadamard product between z-space and a class conditional embedding + h = merge([latent, emb], mode='mul') + + fake_image = loc(h) + + return Model(input=[latent, image_class], output=fake_image) diff --git a/models/lagan/ops.py b/models/networks/ops.py similarity index 100% rename from models/lagan/ops.py rename to models/networks/ops.py diff --git a/models/train.py b/models/train.py index cf28de2..35e1417 100644 --- a/models/train.py +++ b/models/train.py @@ -37,7 +37,9 @@ def get_parser(): 'Sensible defaults come from [arXiv/1511.06434]', formatter_class=argparse.ArgumentDefaultsHelpFormatter ) - + parser.add_argument('--model', '-m', action='store', type=str, + default='lagan', help='Model architecture to use.', + choices=['lagan', 'fcn', 'hybrid', 'dcgan']) parser.add_argument('--nb-epochs', action='store', type=int, default=50, help='Number of epochs to train for.') parser.add_argument('--batch-size', action='store', type=int, default=100, @@ -90,8 +92,10 @@ def get_parser(): from keras.utils.generic_utils import Progbar from sklearn.cross_validation import train_test_split - from lagan.generator import generator as build_generator - from lagan.discriminator import discriminator as build_discriminator + exec('from networks.{} import generator as build_generator, ' + 'discriminator as build_discriminator'.format(results.model)) + + print('[INFO] Building the {} model.'.format(results.model)) # batch, latent size, and whether or not to be verbose with a progress bar nb_epochs = results.nb_epochs @@ -105,7 +109,7 @@ def get_parser(): adam_beta_1 = results.adam_beta # build the discriminator - print('Building discriminator') + print('[INFO] Building discriminator') discriminator = build_discriminator() discriminator.compile( optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1), @@ -113,7 +117,7 @@ def get_parser(): ) # build the generator - print('Building generator') + print('[INFO] Building generator') generator = build_generator(latent_size) generator.compile( optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1), @@ -144,20 +148,20 @@ def get_parser(): # if we don't have the dataset, go fetch it from Zenodo, or re-find in the # Keras cache - print('Loading data') + print('[INFO] Loading data') if (datafile is None) or (not os.path.isfile(datafile)): from keras.utils.data_utils import get_file - print('File not found or not specified. Downloading from Zenodo. ' - '(Or, falling back to cache if present)') + print('[WARN] File not found or not specified. Downloading from ' + 'Zenodo. (Or, falling back to cache if present)') # Info for downloading the dataset from Zenodo MD5_HASH = 'f9b11c46b6a0ff928bec2eccf865ecf0' DATAFILE = 'jet-images_Mass60-100_pT250-300_R1.25_Pix25.hdf5' URL_TEMPLATE = 'https://zenodo.org/record/{record}/files/{filename}' - print('MD5 verification: {}'.format(MD5_HASH)) + print('[INFO] MD5 verification: {}'.format(MD5_HASH)) datafile = get_file( fname='lagan-jet-images.hdf5', @@ -179,7 +183,7 @@ def get_parser(): X, y = X[ix], y[ix] except IOError: - print('Failure to read as HDF5, falling back to numpy') + print('[WARN] Failure to read as HDF5, falling back to numpy') d = np.load(datafile, mmap_mode='r') ix = list(range(d.shape[0])) @@ -345,6 +349,3 @@ def get_parser(): overwrite=True) discriminator.save_weights('{0}{1:03d}.hdf5'.format(results.d_pfx, epoch), overwrite=True) - - pickle.dump({'train': train_history, 'test': test_history}, - open('acgan-history.pkl', 'wb'))