Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ omit =
*tests*
*__init__*
[report]
show_missing = True
exclude_lines =
pragma: no cover
if __name__ == .__main__.:
if __name__ == .__main__.:
@tf.function
2 changes: 1 addition & 1 deletion PanoramAI/GANorama.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def train(self, epochs, steps_for_update = None, make_checkpoints = False):

# Save the model every 15 epochs
if epoch % steps_for_update == 0:
if make_checkpoints:
if make_checkpoints: # pragma: no cover
checkpoint.save(file_prefix = checkpoint_prefix)
print(f"Time for epoch {epoch} "
f" is {time.time() - start:.4f} sec -- "
Expand Down
32 changes: 15 additions & 17 deletions PanoramAI/VAEorama.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,34 @@ def __init__(self, dataset,
latent_dim = 100):
super().__init__(dataset, BATCH_SIZE, test_size, latent_dim)

def _generate_random_vector(self, n_samples):
self.n_samples_to_generate = n_samples
return tf.random.normal(
shape=[n_samples, self.latent_dim])

def generate_samples(self, n_samples):
#if n_samples > self.n_samples_to_generate:
# print("Regenerating sample vector.")
return self.model.sample(self._generate_random_vector(n_samples))

self.n_samples_to_generate = n_samples
return self.model.sample(
tf.random.normal(shape=[n_samples, self.latent_dim]))
def create_model(self):
M, N = self.dimensions
self.model = _CVAE(M, N, self.latent_dim)
return

def log_normal_pdf(self, sample, mean, logvar, raxis=1):
log2pi = tf.math.log(2. * np.pi)
return tf.reduce_sum(
-.5 * ((sample - mean) ** 2. * tf.exp(-logvar) + logvar + log2pi),
axis=raxis)

@tf.function
def compute_loss(self, x):
mean, logvar = self.model.encode(x)
z = self.model.reparameterize(mean, logvar)
x_predicted = self.model.decode(z)
MSE = tf.losses.MSE(x, x_predicted)
logpx_z = -tf.reduce_sum(MSE)
logpz = self.log_normal_pdf(z, 0., 0.)
logqz_x = self.log_normal_pdf(z, mean, logvar)
#Log-normal distributions for z
#The prior has mean 0 with no variance
#The variational distribution has a mean and logvar
log2pi = tf.math.log(2. * np.pi)
logpz = tf.reduce_sum(
-0.5 * (z ** 2. + log2pi), axis=1)
logqz_x = tf.reduce_sum(
-.5 * ((z - mean) ** 2. * tf.exp(-logvar) + logvar + log2pi),
axis=1)
#logpz = self.log_normal_pdf(z, 0., 0.)
#logqz_x = self.log_normal_pdf(z, mean, logvar)
return -tf.reduce_mean(logpx_z + logpz - logqz_x)

@tf.function
Expand Down
13 changes: 8 additions & 5 deletions PanoramAI/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,26 @@ def reset_optimizer(self, opt = tfk.optimizers.Adam):
self.optimizer = opt(1e-4)
return

def save_model(self, epoch, loss, recon, kl, save_path = "./models/"):
def save_model(self, epoch, loss, recon, kl, save_path = "./saved_models/"):
"""Write logs and save the model"""
train_summary_writer = tf.summary.create_file_writer(summary_path)
train_summary_writer = tf.summary.create_file_writer(save_path)
with train_summary_writer.as_default():
tf.summary.scalar("Total Loss", loss, step=epoch)
tf.summary.scalar("KL Divergence", kl, step=epoch)
tf.summary.scalar("Reconstruction Loss", recon, step=epoch)

# save model
if loss < self.BEST_LOSS:
if loss < self.BEST_LOSS: # pragma: no cover
self.BEST_LOSS = loss
self.model.save(save_path+"BEST_MODEL")
self.model.save(save_path)
if self.model is not None:
self.model.save(save_path+"BEST_MODEL")
if self.model is not None: # pragma: no cover
self.model.save(save_path)

def create_model(self):
"""Create the generative model.

"""
self.model = None
pass

28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,30 @@ These can all be installed together using the `requirements.txt` file as shown a

## Usage

This section is still being finalized.
PanoramAI contains models to generate panoramic images. Models provided in this package are untrained. This section documents how to instantiate and train a model, and then how to generate new panoramic images.

First, import and create a model. As an example here, we consider a convolutional variational autoencoder (`VAEorama`).

```python
import PanoramAI

model = PanoramAI.VAEorama(dataset)
```

In this example, `dataset` is a `numpy.ndarray` containing the input panoramic images for training. It should have `N` RGB images in total, meaning its shape must be (`N`,`Height`,`Width`,`3`) where the height and width are in pixels.

Once created, we train the model for some number of epochs.
```python
epochs = 100
model.train(epochs)
```

Predictions can be made in batches
```python
#Create 10 sample panoramas
samples - model.generate_samples(10)
```

### Models

At present, PanoramAI includes two fully generative models: a DCGAN and a convolutional variational autoencoder (VAE). A conditional VAE is in development.
10 changes: 9 additions & 1 deletion tests/test_GANorama.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ def test_training():
BS = 25
data = np.random.randn(1000, M, N, 3)
G = PA.GANorama(data, latent_dim = LD, BATCH_SIZE = BS)
G.train(2)
G.train(1)

#Test the loss directly
for x in G.train_dataset:
DL = G.discriminator_loss(x, x)
GL = G.generator_loss(x)
npt.assert_equal(DL.dtype, np.float64)
npt.assert_equal(GL.dtype, np.float64)
break

def test_sample():
M, N = 16, 128
Expand Down
8 changes: 8 additions & 0 deletions tests/test_GENERICorama.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,13 @@ def test_GENERICorama():
data = np.random.randn(10, 4, 8, 3)
o = PA.GENERICorama(data)

def test_save_model():
data = np.random.randn(10, 4, 8, 3)
o = PA.GENERICorama(data)
o.save_model(0, 0, 0, 0)
import os
res = os.system("rm -rf saved_models")
npt.assert_equal(res, 0)

if __name__ == "__main__":
test_GENERICorama()
34 changes: 34 additions & 0 deletions tests/test_VAEorama.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,40 @@ def test_sample():
examples = V.generate_samples(1)[0]
assert examples.shape == (M, N, 3)

def test_encode_decode():
#Test shapes of the encode->decode process
M, N = 16, 128
LD = 100
BS = 25
data = np.random.randn(128, M, N, 3)
V = PA.VAEorama(data, latent_dim = LD, BATCH_SIZE = BS)

for x in V.train_dataset:
mean, logvar = V.model.encode(x)
npt.assert_equal(
mean.get_shape().as_list(), (V.BATCH_SIZE, V.latent_dim,))
npt.assert_equal(
logvar.get_shape().as_list(), (V.BATCH_SIZE, V.latent_dim,))
z = V.model.reparameterize(mean, logvar)
npt.assert_equal(z.shape, mean.shape)

xprime = V.model.decode(z)
npt.assert_equal(x.shape, xprime.shape)
break

def test_save_and_load():
import os
M, N = 16, 128
LD = 100
BS = 25
data = np.random.randn(128, M, N, 3)
V = PA.VAEorama(data, latent_dim = LD, BATCH_SIZE = BS)
V.save_model_weights("temp/")
V.load_model_weights("temp/")
res = os.system("rm -rf temp")
npt.assert_equal(res, 0)


if __name__ == "__main__":
#test_VAEorama_smoketest()
#test_attributes()
Expand Down