From 839e93fd179e3a3ac0e7ed13ca949dc907ed183f Mon Sep 17 00:00:00 2001 From: Warvito Date: Mon, 13 Apr 2020 00:31:05 +0100 Subject: [PATCH] Add gated pixelcnn new notebooks, remove old code --- .../Receptive_fields.ipynb | 0 WIP/4 - Gated_PixelCNN/gated_pixelCNN.py | 314 ++- WIP/4 - Gated_PixelCNN/gated_pixelcnn.ipynb | 744 +++++-- .../gated_pixelcnn_cropped.ipynb | 629 ------ .../3d_gated_pixelcnn_conv.ipynb | 1892 ----------------- WIP/6-gated_pixelcnn_cropped/Untitled0.ipynb | 608 ------ .../comparing_2d_3d.py | 0 .../comparing_masked_conv_cropped.py | 205 -- .../comparing_masked_cropped.ipynb | 430 ---- .../gated_pixelCNN.py | 275 +++ WIP/pixelCNN.py | 0 WIP/train_gatedpixelcnn2.py | 284 --- WIP/train_gatedpixelcnn2_cropped.py | 523 ----- 13 files changed, 1170 insertions(+), 4734 deletions(-) rename WIP/{3 - => 3 -PixelCNNs blind spot in the receptive field}/Receptive_fields.ipynb (100%) delete mode 100644 WIP/4 - Gated_PixelCNN/gated_pixelcnn_cropped.ipynb delete mode 100644 WIP/6-gated_pixelcnn_cropped/3d_gated_pixelcnn_conv.ipynb delete mode 100644 WIP/6-gated_pixelcnn_cropped/Untitled0.ipynb delete mode 100644 WIP/6-gated_pixelcnn_cropped/comparing_2d_3d.py delete mode 100644 WIP/6-gated_pixelcnn_cropped/comparing_masked_conv_cropped.py delete mode 100644 WIP/6-gated_pixelcnn_cropped/comparing_masked_cropped.ipynb create mode 100644 WIP/6-gated_pixelcnn_cropped/gated_pixelCNN.py delete mode 100644 WIP/pixelCNN.py delete mode 100644 WIP/train_gatedpixelcnn2.py delete mode 100644 WIP/train_gatedpixelcnn2_cropped.py diff --git a/WIP/3 -/Receptive_fields.ipynb b/WIP/3 -PixelCNNs blind spot in the receptive field/Receptive_fields.ipynb similarity index 100% rename from WIP/3 -/Receptive_fields.ipynb rename to WIP/3 -PixelCNNs blind spot in the receptive field/Receptive_fields.ipynb diff --git a/WIP/4 - Gated_PixelCNN/gated_pixelCNN.py b/WIP/4 - Gated_PixelCNN/gated_pixelCNN.py index 5a381fb..dd0a87c 100644 --- a/WIP/4 - Gated_PixelCNN/gated_pixelCNN.py +++ b/WIP/4 - Gated_PixelCNN/gated_pixelCNN.py @@ -1,8 +1,318 @@ +import tensorflow as tf + +gpu_devices = tf.config.experimental.list_physical_devices('GPU') +for device in gpu_devices: tf.config.experimental.set_memory_growth(device, True) + import random as rn -import time import matplotlib import matplotlib.pyplot as plt import numpy as np import tensorflow as tf -from tensorflow import keras \ No newline at end of file +from tensorflow.keras.utils import Progbar +from tensorflow import keras +from tensorflow.keras import initializers +from tensorflow import nn + +# Defining random seeds +random_seed = 42 +tf.random.set_seed(random_seed) +np.random.seed(random_seed) +rn.seed(random_seed) + +# Loading data +(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() + +height = 28 +width = 28 +n_channel = 1 + +x_train = x_train.astype('float32') / 255. +x_test = x_test.astype('float32') / 255. + +x_train = x_train.reshape(x_train.shape[0], height, width, n_channel) +x_test = x_test.reshape(x_test.shape[0], height, width, n_channel) + + +def quantise(images, q_levels): + """Quantise image into q levels""" + return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32') + + +# Quantise the input data in q levels +q_levels = 2 +x_train_quantised = quantise(x_train, q_levels) +x_test_quantised = quantise(x_test, q_levels) + +# Creating input stream using tf.data API +batch_size = 192 +train_buf = 10000 + +train_dataset = tf.data.Dataset.from_tensor_slices((x_train_quantised / (q_levels - 1), + x_train_quantised.astype('int32'))) +train_dataset = train_dataset.shuffle(buffer_size=train_buf) +train_dataset = train_dataset.batch(batch_size) + +test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1), + x_test_quantised.astype('int32'))) +test_dataset = test_dataset.batch(batch_size) + + +class MaskedConv2D(keras.layers.Layer): + """Convolutional layers with masks for Gated PixelCNN. + + Masked convolutional layers used to implement Vertical and Horizontal + stacks of the Gated PixelCNN. + + Note: This implementation is different from the normal PixelCNN. + + Arguments: + mask_type: one of `"V"`, `"A"` or `"B".` + filters: Integer, the dimensionality of the output space + (i.e. the number of output filters in the convolution). + kernel_size: An integer or tuple/list of 2 integers, specifying the + height and width of the 2D convolution window. + Can be a single integer to specify the same value for + all spatial dimensions. + strides: An integer or tuple/list of 2 integers, + specifying the strides of the convolution along the height and width. + Can be a single integer to specify the same value for + all spatial dimensions. + Specifying any stride value != 1 is incompatible with specifying + any `dilation_rate` value != 1. + padding: one of `"valid"` or `"same"` (case-insensitive). + kernel_initializer: Initializer for the `kernel` weights matrix. + bias_initializer: Initializer for the bias vector. + """ + + def __init__(self, + mask_type, + filters, + kernel_size, + strides=1, + padding='same', + kernel_initializer='glorot_uniform', + bias_initializer='zeros'): + super(MaskedConv2D, self).__init__() + + assert mask_type in {'A', 'B', 'V'} + self.mask_type = mask_type + + self.filters = filters + + if isinstance(kernel_size, int): + kernel_size = (kernel_size, kernel_size) + self.kernel_size = kernel_size + + self.strides = strides + self.padding = padding.upper() + self.kernel_initializer = initializers.get(kernel_initializer) + self.bias_initializer = initializers.get(bias_initializer) + + def build(self, input_shape): + kernel_h, kernel_w = self.kernel_size + + self.kernel = self.add_weight('kernel', + shape=(kernel_h, + kernel_w, + int(input_shape[-1]), + self.filters), + initializer=self.kernel_initializer, + trainable=True) + + self.bias = self.add_weight('bias', + shape=(self.filters,), + initializer=self.bias_initializer, + trainable=True) + + mask = np.ones(self.kernel.shape, dtype=np.float32) + + if kernel_h % 2 != 0: + center_h = kernel_h // 2 + else: + center_h = (kernel_h - 1) // 2 + + if kernel_w % 2 != 0: + center_w = kernel_w // 2 + else: + center_w = (kernel_w - 1) // 2 + + if self.mask_type == 'V': + mask[center_h + 1:, :, :, :] = 0. + else: + mask[:center_h, :, :] = 0. + mask[center_h, center_w + (self.mask_type == 'B'):, :, :] = 0. + mask[center_h + 1:, :, :] = 0. + + self.mask = tf.constant(mask, dtype=tf.float32, name='mask') + + def call(self, input): + masked_kernel = tf.math.multiply(self.mask, self.kernel) + x = nn.conv2d(input, + masked_kernel, + strides=[1, self.strides, self.strides, 1], + padding=self.padding) + x = nn.bias_add(x, self.bias) + return x + + +class GatedBlock(tf.keras.Model): + """ Gated block of the Gated PixelCNN.""" + + def __init__(self, mask_type, filters, kernel_size): + super(GatedBlock, self).__init__(name='') + + self.mask_type = mask_type + self.vertical_conv = MaskedConv2D(mask_type='V', + filters=2 * filters, + kernel_size=kernel_size) + + self.horizontal_conv = MaskedConv2D(mask_type=mask_type, + filters=2 * filters, + kernel_size=kernel_size) + + self.padding = keras.layers.ZeroPadding2D(padding=((1, 0), 0)) + self.cropping = keras.layers.Cropping2D(cropping=((0, 1), 0)) + + self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1) + + self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1) + + def _gate(self, x): + tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1) + return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation) + + def call(self, input_tensor): + v = input_tensor[0] + h = input_tensor[1] + + vertical_preactivation = self.vertical_conv(v) # NxN + + # Shifting feature map down to ensure causality + v_to_h = self.padding(vertical_preactivation) + v_to_h = self.cropping(v_to_h) + v_to_h = self.v_to_h_conv(v_to_h) # 1x1 + + horizontal_preactivation = self.horizontal_conv(h) # 1xN + + v_out = self._gate(vertical_preactivation) + + horizontal_preactivation = horizontal_preactivation + v_to_h + h_activated = self._gate(horizontal_preactivation) + h_activated = self.horizontal_output(h_activated) + + if self.mask_type == 'A': + h_out = h_activated + elif self.mask_type == 'B': + h_out = h + h_activated + + return v_out, h_out + + +# Create Gated PixelCNN model +inputs = keras.layers.Input(shape=(height, width, n_channel)) +v, h = GatedBlock(mask_type='A', filters=64, kernel_size=3)([inputs, inputs]) + +for i in range(7): + v, h = GatedBlock(mask_type='B', filters=64, kernel_size=3)([v, h]) + +x = keras.layers.Activation(activation='relu')(h) +x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x) + +x = keras.layers.Activation(activation='relu')(x) +x = keras.layers.Conv2D(filters=q_levels, kernel_size=1, strides=1)(x) + +gated_pixelcnn = tf.keras.Model(inputs=inputs, outputs=x) + +# Prepare optimizer and loss function +lr_decay = 0.999995 +learning_rate = 1e-3 +optimizer = keras.optimizers.Adam(lr=learning_rate) + +compute_loss = keras.losses.CategoricalCrossentropy(from_logits=True) + + +@tf.function +def train_step(batch_x, batch_y): + with tf.GradientTape() as ae_tape: + logits = gated_pixelcnn(batch_x, training=True) + + loss = compute_loss(tf.squeeze(tf.one_hot(batch_y, q_levels)), logits) + + gradients = ae_tape.gradient(loss, gated_pixelcnn.trainable_variables) + gradients, _ = tf.clip_by_global_norm(gradients, 1.0) + optimizer.apply_gradients(zip(gradients, gated_pixelcnn.trainable_variables)) + + return loss + + +# Training loop +n_epochs = 50 +n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size)) +for epoch in range(n_epochs): + progbar = Progbar(n_iter) + print('Epoch {:}/{:}'.format(epoch + 1, n_epochs)) + + for i_iter, (batch_x, batch_y) in enumerate(train_dataset): + optimizer.lr = optimizer.lr * lr_decay + loss = train_step(batch_x, batch_y) + + progbar.add(1, values=[('loss', loss)]) + +# Test set performance +test_loss = [] +for batch_x, batch_y in test_dataset: + logits = gated_pixelcnn(batch_x, training=False) + + # Calculate cross-entropy (= negative log-likelihood) + loss = compute_loss(tf.one_hot(batch_y, q_levels), logits) + + test_loss.append(loss) +print('nll : {:} nats'.format(np.array(test_loss).mean())) +print('bits/dim : {:}'.format(np.array(test_loss).mean() / np.log(2))) + +# Generating new images +samples = np.zeros((100, height, width, n_channel), dtype='float32') +for i in range(height): + for j in range(width): + logits = gated_pixelcnn(samples) + next_sample = tf.random.categorical(logits[:, i, j, :], 1) + samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0] + +fig = plt.figure(figsize=(10, 10)) +for i in range(100): + ax = fig.add_subplot(10, 10, i + 1) + ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) + plt.xticks(np.array([])) + plt.yticks(np.array([])) +plt.show() + +# Filling occluded images +occlude_start_row = 14 +num_generated_images = 10 +samples = np.copy(x_test_quantised[0:num_generated_images, :, :, :]) +samples = samples / (q_levels - 1) +samples[:, occlude_start_row:, :, :] = 0 + +fig = plt.figure(figsize=(10, 10)) + +for i in range(10): + ax = fig.add_subplot(1, 10, i + 1) + ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) + plt.xticks(np.array([])) + plt.yticks(np.array([])) + +for i in range(occlude_start_row, height): + for j in range(width): + logits = gated_pixelcnn(samples) + next_sample = tf.random.categorical(logits[:, i, j, :], 1) + samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0] + +fig = plt.figure(figsize=(10, 10)) + +for i in range(10): + ax = fig.add_subplot(1, 10, i + 1) + ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) + plt.xticks(np.array([])) + plt.yticks(np.array([])) +plt.show() diff --git a/WIP/4 - Gated_PixelCNN/gated_pixelcnn.ipynb b/WIP/4 - Gated_PixelCNN/gated_pixelcnn.ipynb index 1114ac6..0d9cf3b 100644 --- a/WIP/4 - Gated_PixelCNN/gated_pixelcnn.ipynb +++ b/WIP/4 - Gated_PixelCNN/gated_pixelcnn.ipynb @@ -1,21 +1,70 @@ { + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "gated_pixelcnn.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "mOIJQZ47p7BM", + "colab_type": "text", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# Fixing the blind spot — Gated PixelCNN\n", + "\n", + "...." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "64KBFmbBqWAq", + "colab_type": "text", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Gated PixelCNN\n", + "\n", + "..." + ] + }, { "cell_type": "code", - "execution_count": 1, "metadata": { - "colab": {}, "colab_type": "code", - "id": "k1uZnxh4Xz9Z" + "id": "k1uZnxh4Xz9Z", + "colab": {} }, - "outputs": [], "source": [ - "import tensorflow as tf\n", - "gpu_devices = tf.config.experimental.list_physical_devices('GPU')\n", - "for device in gpu_devices: tf.config.experimental.set_memory_growth(device, True)\n", - "\n", "import random as rn\n", - "import time\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", @@ -25,34 +74,47 @@ "from tensorflow import keras\n", "from tensorflow.keras import initializers\n", "from tensorflow import nn" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Yfn90OUeqo6i", + "colab_type": "text", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "Then, we define the random seeds to have reproducible results and we load the MNIST dataset to train the Gated PixelCNN.\n" ] }, { "cell_type": "code", - "execution_count": 2, "metadata": { - "colab": {}, "colab_type": "code", - "id": "NN6vJl7eVnZ4" + "id": "NN6vJl7eVnZ4", + "colab": {} }, - "outputs": [], "source": [ "# Defining random seeds\n", "random_seed = 42\n", "tf.random.set_seed(random_seed)\n", "np.random.seed(random_seed)\n", "rn.seed(random_seed)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 3, "metadata": { - "colab": {}, "colab_type": "code", - "id": "8BnkhgCjVpJu" + "id": "8BnkhgCjVpJu", + "colab": {} }, - "outputs": [], "source": [ "# Loading data\n", "(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()\n", @@ -66,48 +128,74 @@ "\n", "x_train = x_train.reshape(x_train.shape[0], height, width, n_channel)\n", "x_test = x_test.reshape(x_test.shape[0], height, width, n_channel)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xAzRiBLIq-im", + "colab_type": "text", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "In this example, to make the probability distribution of a single pixel easier to be defined, we decide to quantitise the number of possible values that a pixel could have. Originally, in the MNIST dataset the pixels are represented by a uint8 variable, beeing able assume values between [0, 255]. In this example, we restrict the image to have only 2 different values ([0, 1]).\n" ] }, { "cell_type": "code", - "execution_count": 4, "metadata": { - "colab": {}, "colab_type": "code", - "id": "3ne-qY7JVZaB" + "id": "3ne-qY7JVZaB", + "colab": {} }, - "outputs": [], "source": [ "def quantise(images, q_levels):\n", " \"\"\"Quantise image into q levels\"\"\"\n", " return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32')" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 5, "metadata": { - "colab": {}, "colab_type": "code", - "id": "3QVhnMymVrzc" + "id": "3QVhnMymVrzc", + "colab": {} }, - "outputs": [], "source": [ "# Quantise the input data in q levels\n", "q_levels = 2\n", "x_train_quantised = quantise(x_train, q_levels)\n", "x_test_quantised = quantise(x_test, q_levels)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HY8G-0FIqzLf", + "colab_type": "text", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "Using the tensorflow.Data API, we defined the input data streams for our model during the training and the evaluation. In these dataset, we define the inputs as the images with 2 levels normalized to be between [0, 1] and the target values are the categoricals pixels values between [0, 1].\n" ] }, { "cell_type": "code", - "execution_count": 6, "metadata": { - "colab": {}, "colab_type": "code", - "id": "ZObIXqzNGwmo" + "id": "ZObIXqzNGwmo", + "colab": {} }, - "outputs": [], "source": [ "# Creating input stream using tf.data API\n", "batch_size = 192\n", @@ -121,17 +209,30 @@ "test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1),\n", " x_test_quantised.astype('int32')))\n", "test_dataset = test_dataset.batch(batch_size)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "myQw2mhwrH8Y", + "colab_type": "text", + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "##Masked Convolutions\n" ] }, { "cell_type": "code", - "execution_count": 7, "metadata": { - "colab": {}, "colab_type": "code", - "id": "75VTDkK8VZLA" + "id": "75VTDkK8VZLA", + "colab": {} }, - "outputs": [], "source": [ "class MaskedConv2D(keras.layers.Layer):\n", " \"\"\"Convolutional layers with masks for Gated PixelCNN.\n", @@ -229,17 +330,290 @@ " padding=self.padding)\n", " x = nn.bias_add(x, self.bias)\n", " return x" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Gated Residual blocks\n", + "\n", + "..." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "class GatedBlock(tf.keras.Model):\n", + " \"\"\" Gated block of the Gated PixelCNN.\"\"\"\n", + "\n", + " def __init__(self, mask_type, filters, kernel_size):\n", + " super(GatedBlock, self).__init__(name='')\n", + "\n", + " self.mask_type = mask_type\n", + " self.vertical_conv = MaskedConv2D(mask_type='V',\n", + " filters=2 * filters,\n", + " kernel_size=kernel_size)\n", + " \n", + " self.horizontal_conv = MaskedConv2D(mask_type=mask_type,\n", + " filters=2 * filters,\n", + " kernel_size=kernel_size)\n", + "\n", + " self.padding = keras.layers.ZeroPadding2D(padding=((1,0),0))\n", + " self.cropping = keras.layers.Cropping2D(cropping=((0, 1), 0))\n", + "\n", + " self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1)\n", + "\n", + " self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1)\n", + "\n", + " def _gate(self, x):\n", + " tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1)\n", + " return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation)\n", + "\n", + " def call(self, input_tensor):\n", + " v = input_tensor[0]\n", + " h = input_tensor[1]\n", + "\n", + " vertical_preactivation = self.vertical_conv(v) # NxN\n", + "\n", + " # Shifting feature map down to ensure causality\n", + " v_to_h = self.padding(vertical_preactivation)\n", + " v_to_h = self.cropping(v_to_h)\n", + " v_to_h = self.v_to_h_conv(v_to_h) # 1x1\n", + "\n", + " horizontal_preactivation = self.horizontal_conv(h) # 1xN\n", + " \n", + " v_out = self._gate(vertical_preactivation)\n", + "\n", + " horizontal_preactivation = horizontal_preactivation + v_to_h\n", + " h_activated = self._gate(horizontal_preactivation)\n", + " h_activated = self.horizontal_output(h_activated)\n", + "\n", + " if self.mask_type == 'A':\n", + " h_out = h_activated\n", + " elif self.mask_type == 'B':\n", + " h_out = h + h_activated\n", + "\n", + " return v_out, h_out" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## PixelCNN architecture" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Create Gated PixelCNN model\n", + "inputs = keras.layers.Input(shape=(height, width, n_channel))\n", + "v, h = GatedBlock(mask_type='A', filters=64, kernel_size=3)([inputs, inputs])\n", + "\n", + "for i in range(7):\n", + " v, h = GatedBlock(mask_type='B', filters=64, kernel_size=3)([v, h])\n", + "\n", + "x = keras.layers.Activation(activation='relu')(h)\n", + "x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x)\n", + "\n", + "x = keras.layers.Activation(activation='relu')(x)\n", + "x = keras.layers.Conv2D(filters=q_levels, kernel_size=1, strides=1)(x)\n", + "\n", + "gated_pixelcnn = tf.keras.Model(inputs=inputs, outputs=x)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "In this implementation we use a simple Adam optimizer with learning rate decay to train the neural network. The loss function is defined by the cross-entropy (that in this case is equivalent to minimizing the negative log-likelihood of the training data)." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Prepare optimizer and loss function\n", + "lr_decay = 0.999995\n", + "learning_rate = 1e-3\n", + "optimizer = keras.optimizers.Adam(lr=learning_rate)\n", + "\n", + "compute_loss = keras.losses.CategoricalCrossentropy(from_logits=True)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "The training step is defined by the forward propagation through the model. Then, the gradients are calculated, clipped to be between [-1, 1], and applied to upgrade the PixelCNN parameters.\n" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "@tf.function\n", + "def train_step(batch_x, batch_y):\n", + " with tf.GradientTape() as ae_tape:\n", + " logits = gated_pixelcnn(batch_x, training=True)\n", + "\n", + " loss = compute_loss(tf.squeeze(tf.one_hot(batch_y, q_levels)), logits)\n", + "\n", + " gradients = ae_tape.gradient(loss, gated_pixelcnn.trainable_variables)\n", + " gradients, _ = tf.clip_by_global_norm(gradients, 1.0)\n", + " optimizer.apply_gradients(zip(gradients, gated_pixelcnn.trainable_variables))\n", + "\n", + " return loss" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "In this implementation, we defined the training loop with 100 epochs.\n" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Training loop\n", + "n_epochs = 50\n", + "n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size))\n", + "for epoch in range(n_epochs):\n", + " progbar = Progbar(n_iter)\n", + " print('Epoch {:}/{:}'.format(epoch + 1, n_epochs))\n", + "\n", + " for i_iter, (batch_x, batch_y) in enumerate(train_dataset):\n", + " optimizer.lr = optimizer.lr * lr_decay\n", + " loss = train_step(batch_x, batch_y)\n", + "\n", + " progbar.add(1, values=[('loss', loss)])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "To evaluate the performance of the model, we measured its negative log-likelihood (NLL) in the test set.\n" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# Test set performance\n", + "test_loss = []\n", + "for batch_x, batch_y in test_dataset:\n", + " logits = gated_pixelcnn(batch_x, training=False)\n", + "\n", + " # Calculate cross-entropy (= negative log-likelihood)\n", + " loss = compute_loss(tf.one_hot(batch_y, q_levels), logits)\n", + "\n", + " test_loss.append(loss)\n", + "print('nll : {:} nats'.format(np.array(test_loss).mean()))\n", + "print('bits/dim : {:}'.format(np.array(test_loss).mean() / np.log(2)))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Finally, we sampled some images from the trained model. First, we sampled from scratch, then we completed images partially occluded." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Try0OmL4re9Y", + "colab_type": "text" + }, + "source": [ + "# Generating new images\n", + "samples = np.zeros((100, height, width, n_channel), dtype='float32')\n", + "for i in range(height):\n", + " for j in range(width):\n", + " logits = gated_pixelcnn(samples)\n", + " next_sample = tf.random.categorical(logits[:, i, j, :], 1)\n", + " samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0]\n", + "\n", + "fig = plt.figure(figsize=(10, 10))\n", + "for i in range(100):\n", + " ax = fig.add_subplot(10, 10, i + 1)\n", + " ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary)\n", + " plt.xticks(np.array([]))\n", + " plt.yticks(np.array([]))\n", + "plt.show()" ] }, { "cell_type": "code", - "execution_count": 8, "metadata": { - "colab": {}, "colab_type": "code", - "id": "PTUN4s52Nu3w" + "id": "PTUN4s52Nu3w", + "colab": {} }, - "outputs": [], "source": [ "class GatedBlock(tf.keras.Model):\n", " \"\"\" Gated block of the Gated PixelCNN.\"\"\"\n", @@ -292,17 +666,27 @@ " h_out = h + h_activated\n", "\n", " return v_out, h_out" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tU91uHX6rm4K", + "colab_type": "text" + }, + "source": [ + "## PixelCNN architecture" ] }, { "cell_type": "code", - "execution_count": 9, "metadata": { - "colab": {}, "colab_type": "code", - "id": "WB57YufrVxn2" + "id": "WB57YufrVxn2", + "colab": {} }, - "outputs": [], "source": [ "# Create Gated PixelCNN model\n", "inputs = keras.layers.Input(shape=(height, width, n_channel))\n", @@ -318,17 +702,27 @@ "x = keras.layers.Conv2D(filters=q_levels, kernel_size=1, strides=1)(x)\n", "\n", "gated_pixelcnn = tf.keras.Model(inputs=inputs, outputs=x)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "O9luweDbrseV", + "colab_type": "text" + }, + "source": [ + "In this implementation we use a simple Adam optimizer with learning rate decay to train the neural network. The loss function is defined by the cross-entropy (that in this case is equivalent to minimizing the negative log-likelihood of the training data)." ] }, { "cell_type": "code", - "execution_count": 10, "metadata": { - "colab": {}, "colab_type": "code", - "id": "_LnzHUaqV77d" + "id": "_LnzHUaqV77d", + "colab": {} }, - "outputs": [], "source": [ "# Prepare optimizer and loss function\n", "lr_decay = 0.999995\n", @@ -336,17 +730,27 @@ "optimizer = keras.optimizers.Adam(lr=learning_rate)\n", "\n", "compute_loss = keras.losses.CategoricalCrossentropy(from_logits=True)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jRYvmf_7ryxU", + "colab_type": "text" + }, + "source": [ + "The training step is defined by the forward propagation through the model. Then, the gradients are calculated, clipped to be between [-1, 1], and applied to upgrade the PixelCNN parameters.\n" ] }, { "cell_type": "code", - "execution_count": 11, "metadata": { - "colab": {}, "colab_type": "code", - "id": "CsAgEKVzLCJD" + "id": "CsAgEKVzLCJD", + "colab": {} }, - "outputs": [], "source": [ "@tf.function\n", "def train_step(batch_x, batch_y):\n", @@ -360,23 +764,48 @@ " optimizer.apply_gradients(zip(gradients, gated_pixelcnn.trainable_variables))\n", "\n", " return loss" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pCFbAGwar33O", + "colab_type": "text" + }, + "source": [ + "In this implementation, we defined the training loop with 100 epochs.\n" ] }, { "cell_type": "code", - "execution_count": 12, "metadata": { + "colab_type": "code", + "id": "NoEPrfwQNM-s", + "outputId": "52dae9be-4f84-4b91-c5ba-e2369259478e", "colab": { "base_uri": "https://localhost:8080/", "height": 591 - }, - "colab_type": "code", - "id": "NoEPrfwQNM-s", - "outputId": "52dae9be-4f84-4b91-c5ba-e2369259478e" + } }, + "source": [ + "# Training loop\n", + "n_epochs = 50\n", + "n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size))\n", + "for epoch in range(n_epochs):\n", + " progbar = Progbar(n_iter)\n", + " print('Epoch {:}/{:}'.format(epoch + 1, n_epochs))\n", + "\n", + " for i_iter, (batch_x, batch_y) in enumerate(train_dataset):\n", + " optimizer.lr = optimizer.lr * lr_decay\n", + " loss = train_step(batch_x, batch_y)\n", + "\n", + " progbar.add(1, values=[('loss', loss)])" + ], + "execution_count": 0, "outputs": [ { - "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/50\n", @@ -479,46 +908,32 @@ "313/313 [==============================] - 103s 328ms/step - loss: 0.0762\n", "Epoch 50/50\n", "313/313 [==============================] - 103s 328ms/step - loss: 0.0761\n" - ] + ], + "name": "stdout" } - ], + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qG2CjL6xsCVb", + "colab_type": "text" + }, "source": [ - "# Training loop\n", - "n_epochs = 50\n", - "n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size))\n", - "for epoch in range(n_epochs):\n", - " progbar = Progbar(n_iter)\n", - " print('Epoch {:}/{:}'.format(epoch + 1, n_epochs))\n", - "\n", - " for i_iter, (batch_x, batch_y) in enumerate(train_dataset):\n", - " optimizer.lr = optimizer.lr * lr_decay\n", - " loss = train_step(batch_x, batch_y)\n", - "\n", - " progbar.add(1, values=[('loss', loss)])" + "To evaluate the performance of the model, we measured its negative log-likelihood (NLL) in the test set.\n" ] }, { "cell_type": "code", - "execution_count": 13, "metadata": { + "colab_type": "code", + "id": "ue0vZbitSNmz", + "outputId": "b4be6fad-6f16-4380-8dbd-2e01bc3e1af1", "colab": { "base_uri": "https://localhost:8080/", "height": 52 - }, - "colab_type": "code", - "id": "ue0vZbitSNmz", - "outputId": "b4be6fad-6f16-4380-8dbd-2e01bc3e1af1" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "nll : 0.08091852068901062 nats\n", - "bits/dim : 0.11674074851410661\n" - ] } - ], + }, "source": [ "# Test set performance\n", "test_loss = []\n", @@ -531,32 +946,40 @@ " test_loss.append(loss)\n", "print('nll : {:} nats'.format(np.array(test_loss).mean()))\n", "print('bits/dim : {:}'.format(np.array(test_loss).mean() / np.log(2)))" + ], + "execution_count": 0, + "outputs": [ + { + "output_type": "stream", + "text": [ + "nll : 0.08091852068901062 nats\n", + "bits/dim : 0.11674074851410661\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GNVtP_GVsGZY", + "colab_type": "text" + }, + "source": [ + "Finally, we sampled some images from the trained model. First, we sampled from scratch, then we completed images partially occluded." ] }, { "cell_type": "code", - "execution_count": 14, "metadata": { + "colab_type": "code", + "id": "-Ia9VXYySkuW", + "outputId": "924010f4-0dcb-4a82-bd1d-75c6091cadc6", "colab": { "base_uri": "https://localhost:8080/", "height": 581 - }, - "colab_type": "code", - "id": "-Ia9VXYySkuW", - "outputId": "924010f4-0dcb-4a82-bd1d-75c6091cadc6" - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } - ], + }, "source": [ "# Generating new images\n", "samples = np.zeros((100, height, width, n_channel), dtype='float32')\n", @@ -573,42 +996,34 @@ " plt.xticks(np.array([]))\n", " plt.yticks(np.array([]))\n", "plt.show()" + ], + "execution_count": 0, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } ] }, { "cell_type": "code", - "execution_count": 15, "metadata": { + "colab_type": "code", + "id": "KBuWx-FtSouR", + "outputId": "85fb1988-aeea-4695-f5e3-d5a57d28bed6", "colab": { "base_uri": "https://localhost:8080/", "height": 153 - }, - "colab_type": "code", - "id": "KBuWx-FtSouR", - "outputId": "85fb1988-aeea-4695-f5e3-d5a57d28bed6" - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAABECAYAAABu1lQcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAADy0lEQVR4nO3cQVLkOBAF0PTEHIFeTx2C+58A7kCvhztoFkwHBOFy2WW7UCrfW3Y4OpSWbL5Shqm1FgAAVfz10wMAAHgk4QcAKEX4AQBKEX4AgFKEHwCgFOEHAChF+AEAShF+AIBShB8AoJS/t1z89PTULpfLSUM519vbW7y/v09L12SuLyLi9fX1vbX2a+mazDWumcMINfbOs/ghc43W6afRa8xcX8T1Z3FT+LlcLvHy8nLcqB7o+fn55jWZ64uImKbp961rMte4Zg4j1Ng7z+KHzDVap59GrzFzfRHXn0XHXgBAKcIPAFDKpmMvtpum+ePU1tqDRwIAROj8AADF6Pwc5FqH59b1FTpAo9Q6N8fZawLGNE2T99MCnR8AoBSdnwNs7fpAT0bpzO2V8T58f/dkGjvb3XvC8If18Un4OcnSIvu6IDO+cNcaKRSOVMtRvt6TzOv3+9xmeCbPWI9Z5jPLOPfyzjmXYy8AoJRTOz8VPxDdWt/o9yO7W7uvDF2Ca/buLFtrdqc/4Oj3apajEWttbI/OCzo/AEApp3R+lhL6kem9lx3K2nFU3bn0Mk9b+LCwhuzd6UxjvdeanydZ78O9489a7x9r369L1+29B6eEn7Uf++6VfeGPLGvQO2rcPf+NjaOCWtY5zuqMD317/nh4y/rq+Xm75lZ92epZ0uO7wrEXAFDKw3/VfW+a7Xmnck321jrzvs5hjzubORmfnzNkma+IXGP9SVlOAqrM59o65+br+1zO/V97u306PwBAKf7I4cmqpPwR7NmpzO1QsuxEtxp1TY82T3Myd6GXOgRz/9ZbXZnv/dHu+cD76PeOzg8AUEqazk+2bxXO/BW9nmWbp7XW1tJrB+iseRlpjjPZ8ht72Tt1o66xUes6osO19K3PUboPP9kf3K9GXewjM2efRnoWM1nzg+Deuel1ffe2edjLs/Ohp7/z59gLACil+85P1rSfddx7Za575OOgI8bTW017ZKwl45jvtfWYuXdZxnmEpVp7ug86PwBAKcIPAFCK8AMAlCL8AAClCD8AQCnCDwBQivADAJQi/AAApQg/AEApwg8AUIrwAwCUIvwAAKUIPwBAKcIPAFCK8AMAlCL8AAClCD8AQCnCDwBQivADAJQi/AAApQg/AEApwg8AUIrwAwCUMrXW1l88Tf9GxO/zhnOqf1prv5YuSF5fxPg13qwvQo0JjL5OI8av0Tr93+g1Jq8v4kqNm8IPAEB2jr0AgFKEHwCgFOEHAChF+AEAShF+AIBShB8AoBThBwAoRfgBAEoRfgCAUv4Dzge+SeXbn58AAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAABECAYAAABu1lQcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAFDUlEQVR4nO3dQXLbOBAAQHArT3DOq0f4/y+w/+Cc4z9wD16XXCqKAkVCwmC6jylFhRFAeDAAyWme5wIAkMU/z24AAMAjSX4AgFQkPwBAKpIfACAVyQ8AkIrkBwBIRfIDAKQi+QEAUpH8AACp/Nry4ZeXl/l0OjVqSlsfHx/l8/NzWvtM5PhKKeX9/f1znuffa5+JHGNNH5Yixt65Fr9EjtE4PRs9xsjxlXL9WtyU/JxOp/L29nZcqx7o9fX15mcix1dKKdM0/bn1mcgx1vRhKWLsnWvxS+QYjdOz0WOMHF8p169F214AQCqSHwAglU3bXmw3TcvbqfM8P7glAEApKj8AQDIqPwe5VuG59fkMFaBRYl3q4+gxAWOapsn8tELlBwBIReXnAFurPtCTUSpze0X8HS7nnkhtZ7t7dxi+GR9nkp9G1gbZzwEZccKtNVJSOFIsR/n5m0Qev5d9G+GabDEeo/RnlHbuZc5py7YXAJBK08pPxgOiW+Mb/feI7tbqK0KV4Jq9K8t5nq1On+DoeTXK1oixNrZH5wsqPwBAKk0qP2sZ+pHZey8rlNp2ZF259NJPWzhYmEP06nSktt6r5u9J1N/h3vZHjfdb7fy69rm9v0GT5Kf2sO9e0Qf+yKImeke1u+dnbByVqEXt46haHPTt+fDwlvHV8/V2za34osWzpse5wrYXAJDKw29135vN9rxSuSZ6aZ1lP/uwx5XNkojXTwtR+quUWG19pig7AVn6szbOpf667Mul79pb7VP5AQBS8ZDDxrJk+SPYs1JZWqFEWYluNeqYHq2flkSuQq9VCJb+rbe4Iv/2R7vngPfR847KDwCQSpjKT7SzCi1v0etZtH6qVRtLrxWgVv0yUh9HsuWOveiVulHH2KhxHVHhWjvrc5Tuk5/oF+5Pow72kemzs5GuxUhq/hDc2ze9ju/eFg97uXa+9PScP9teAEAq3Vd+LkXO/rPr/cBfT21pKfPTqCOvwGsP/K79v8jxE1dPFZ9vKj8AQCrdVn5GWp1GbvseVpkxrFXkMhzcjxzHvW3vPealW5tHOPuTQYvDyi36vNvkJyp/8G8zeT3e1ruBMiQ9mYw0L0WMpcVzanp35HZri3e32fYCAFLpsvIT+Vkxj3g+QTQ99mGrMdb7oe4ltVWh3uO4xTUZqw9Hn0sjvon+XjVxPrqfVX4AgFS6rPyMIEtGf2n01do12eIljmxjs9e5N+vc2CuVHwAglS4rP71m7oxpzy20o94VNdJZn2/Z7rgZpQ8v236tDyPG6Pb95+ky+SG+3i/mteeI7P1e+mXrIb7o11ivLz9+lmddi7a9AIBUVH5I68gqwCgrtpGerM5Z5soCz3HrSMCWebfFuFX5AQBSUfkhvSwP+bsl6zmYrW9Gj+JnZTNi+0c36vvLaq6nHuYayQ+siDwJ7TF63F7cCsepvQPv3qTHu70AAHZS+QHSUhXhmY666eJZ7xTcWjnt6VETKj8AQCoqP1TZmqkvZf0R33ieib4A9qqd+7d+x9FUfgCAVFR+2KzmBH9tpj/CrZ0Az9BjNb2H8zw1JD+sqnnOzREvBPUsEoBYIj8l37YXAJDKtCXbmqbpbynlT7vmNPXvPM+/1z4QPL5Sxo/xZnyliDGA0cdpKePHaJz+b/QYg8dXypUYNyU/AADR2fYCAFKR/AAAqUh+AIBUJD8AQCqSHwAgFckPAJCK5AcASEXyAwCkIvkBAFL5D4QYq3XSuptvAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } - ], + }, "source": [ "# Filling occluded images\n", "occlude_start_row = 14\n", @@ -639,40 +1054,47 @@ " plt.xticks(np.array([]))\n", " plt.yticks(np.array([]))\n", "plt.show()" + ], + "execution_count": 0, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAABECAYAAABu1lQcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAADy0lEQVR4nO3cQVLkOBAF0PTEHIFeTx2C+58A7kCvhztoFkwHBOFy2WW7UCrfW3Y4OpSWbL5Shqm1FgAAVfz10wMAAHgk4QcAKEX4AQBKEX4AgFKEHwCgFOEHAChF+AEAShF+AIBShB8AoJS/t1z89PTULpfLSUM519vbW7y/v09L12SuLyLi9fX1vbX2a+mazDWumcMINfbOs/ghc43W6afRa8xcX8T1Z3FT+LlcLvHy8nLcqB7o+fn55jWZ64uImKbp961rMte4Zg4j1Ng7z+KHzDVap59GrzFzfRHXn0XHXgBAKcIPAFDKpmMvtpum+ePU1tqDRwIAROj8AADF6Pwc5FqH59b1FTpAo9Q6N8fZawLGNE2T99MCnR8AoBSdnwNs7fpAT0bpzO2V8T58f/dkGjvb3XvC8If18Un4OcnSIvu6IDO+cNcaKRSOVMtRvt6TzOv3+9xmeCbPWI9Z5jPLOPfyzjmXYy8AoJRTOz8VPxDdWt/o9yO7W7uvDF2Ca/buLFtrdqc/4Oj3apajEWttbI/OCzo/AEApp3R+lhL6kem9lx3K2nFU3bn0Mk9b+LCwhuzd6UxjvdeanydZ78O9489a7x9r369L1+29B6eEn7Uf++6VfeGPLGvQO2rcPf+NjaOCWtY5zuqMD317/nh4y/rq+Xm75lZ92epZ0uO7wrEXAFDKw3/VfW+a7Xmnck321jrzvs5hjzubORmfnzNkma+IXGP9SVlOAqrM59o65+br+1zO/V97u306PwBAKf7I4cmqpPwR7NmpzO1QsuxEtxp1TY82T3Myd6GXOgRz/9ZbXZnv/dHu+cD76PeOzg8AUEqazk+2bxXO/BW9nmWbp7XW1tJrB+iseRlpjjPZ8ht72Tt1o66xUes6osO19K3PUboPP9kf3K9GXewjM2efRnoWM1nzg+Deuel1ffe2edjLs/Ohp7/z59gLACil+85P1rSfddx7Za575OOgI8bTW017ZKwl45jvtfWYuXdZxnmEpVp7ug86PwBAKcIPAFCK8AMAlCL8AAClCD8AQCnCDwBQivADAJQi/AAApQg/AEApwg8AUIrwAwCUIvwAAKUIPwBAKcIPAFCK8AMAlCL8AAClCD8AQCnCDwBQivADAJQi/AAApQg/AEApwg8AUIrwAwCUMrXW1l88Tf9GxO/zhnOqf1prv5YuSF5fxPg13qwvQo0JjL5OI8av0Tr93+g1Jq8v4kqNm8IPAEB2jr0AgFKEHwCgFOEHAChF+AEAShF+AIBShB8AoBThBwAoRfgBAEoRfgCAUv4Dzge+SeXbn58AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + }, + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAABECAYAAABu1lQcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAFDUlEQVR4nO3dQXLbOBAAQHArT3DOq0f4/y+w/+Cc4z9wD16XXCqKAkVCwmC6jylFhRFAeDAAyWme5wIAkMU/z24AAMAjSX4AgFQkPwBAKpIfACAVyQ8AkIrkBwBIRfIDAKQi+QEAUpH8AACp/Nry4ZeXl/l0OjVqSlsfHx/l8/NzWvtM5PhKKeX9/f1znuffa5+JHGNNH5Yixt65Fr9EjtE4PRs9xsjxlXL9WtyU/JxOp/L29nZcqx7o9fX15mcix1dKKdM0/bn1mcgx1vRhKWLsnWvxS+QYjdOz0WOMHF8p169F214AQCqSHwAglU3bXmw3TcvbqfM8P7glAEApKj8AQDIqPwe5VuG59fkMFaBRYl3q4+gxAWOapsn8tELlBwBIReXnAFurPtCTUSpze0X8HS7nnkhtZ7t7dxi+GR9nkp9G1gbZzwEZccKtNVJSOFIsR/n5m0Qev5d9G+GabDEeo/RnlHbuZc5py7YXAJBK08pPxgOiW+Mb/feI7tbqK0KV4Jq9K8t5nq1On+DoeTXK1oixNrZH5wsqPwBAKk0qP2sZ+pHZey8rlNp2ZF259NJPWzhYmEP06nSktt6r5u9J1N/h3vZHjfdb7fy69rm9v0GT5Kf2sO9e0Qf+yKImeke1u+dnbByVqEXt46haHPTt+fDwlvHV8/V2za34osWzpse5wrYXAJDKw29135vN9rxSuSZ6aZ1lP/uwx5XNkojXTwtR+quUWG19pig7AVn6szbOpf667Mul79pb7VP5AQBS8ZDDxrJk+SPYs1JZWqFEWYluNeqYHq2flkSuQq9VCJb+rbe4Iv/2R7vngPfR847KDwCQSpjKT7SzCi1v0etZtH6qVRtLrxWgVv0yUh9HsuWOveiVulHH2KhxHVHhWjvrc5Tuk5/oF+5Pow72kemzs5GuxUhq/hDc2ze9ju/eFg97uXa+9PScP9teAEAq3Vd+LkXO/rPr/cBfT21pKfPTqCOvwGsP/K79v8jxE1dPFZ9vKj8AQCrdVn5GWp1GbvseVpkxrFXkMhzcjxzHvW3vPealW5tHOPuTQYvDyi36vNvkJyp/8G8zeT3e1ruBMiQ9mYw0L0WMpcVzanp35HZri3e32fYCAFLpsvIT+Vkxj3g+QTQ99mGrMdb7oe4ltVWh3uO4xTUZqw9Hn0sjvon+XjVxPrqfVX4AgFS6rPyMIEtGf2n01do12eIljmxjs9e5N+vc2CuVHwAglS4rP71m7oxpzy20o94VNdJZn2/Z7rgZpQ8v236tDyPG6Pb95+ky+SG+3i/mteeI7P1e+mXrIb7o11ivLz9+lmddi7a9AIBUVH5I68gqwCgrtpGerM5Z5soCz3HrSMCWebfFuFX5AQBSUfkhvSwP+bsl6zmYrW9Gj+JnZTNi+0c36vvLaq6nHuYayQ+siDwJ7TF63F7cCsepvQPv3qTHu70AAHZS+QHSUhXhmY666eJZ7xTcWjnt6VETKj8AQCoqP1TZmqkvZf0R33ieib4A9qqd+7d+x9FUfgCAVFR+2KzmBH9tpj/CrZ0Az9BjNb2H8zw1JD+sqnnOzREvBPUsEoBYIj8l37YXAJDKtCXbmqbpbynlT7vmNPXvPM+/1z4QPL5Sxo/xZnyliDGA0cdpKePHaJz+b/QYg8dXypUYNyU/AADR2fYCAFKR/AAAqUh+AIBUJD8AQCqSHwAgFckPAJCK5AcASEXyAwCkIvkBAFL5D4QYq3XSuptvAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "gated_pixelcnn.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 + "metadata": { + "id": "msguDUiFpsnT", + "colab_type": "code", + "colab": {} }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.9" + "source": [ + "" + ], + "execution_count": 0, + "outputs": [] } - }, - "nbformat": 4, - "nbformat_minor": 1 -} + ] +} \ No newline at end of file diff --git a/WIP/4 - Gated_PixelCNN/gated_pixelcnn_cropped.ipynb b/WIP/4 - Gated_PixelCNN/gated_pixelcnn_cropped.ipynb deleted file mode 100644 index 68f9e2c..0000000 --- a/WIP/4 - Gated_PixelCNN/gated_pixelcnn_cropped.ipynb +++ /dev/null @@ -1,629 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "gated_pixelcnn.ipynb", - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "accelerator": "GPU" - }, - "cells": [ - { - "cell_type": "code", - "metadata": { - "id": "VMF0k8kyGjQ7", - "colab_type": "code", - "outputId": "2db847f1-27e9-4fb1-fb73-8ac1306d3ea9", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 402 - } - }, - "source": [ - "! pip3 install tensorflow-gpu==2.0.0-rc1" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Requirement already satisfied: tensorflow-gpu==2.0.0-rc1 in /usr/local/lib/python3.6/dist-packages (2.0.0rc1)\n", - "Requirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.1.0)\n", - "Requirement already satisfied: numpy<2.0,>=1.16.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.16.5)\n", - "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.12.0)\n", - "Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (0.8.0)\n", - "Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.15.0)\n", - "Requirement already satisfied: tb-nightly<1.15.0a20190807,>=1.15.0a20190806 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.15.0a20190806)\n", - "Requirement already satisfied: protobuf>=3.6.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (3.7.1)\n", - "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (0.33.6)\n", - "Requirement already satisfied: gast>=0.2.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (0.2.2)\n", - "Requirement already satisfied: absl-py>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (0.8.0)\n", - "Requirement already satisfied: wrapt>=1.11.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.11.2)\n", - "Requirement already satisfied: keras-applications>=1.0.8 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.0.8)\n", - "Requirement already satisfied: opt-einsum>=2.3.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (3.0.1)\n", - "Requirement already satisfied: tf-estimator-nightly<1.14.0.dev2019080602,>=1.14.0.dev2019080601 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.14.0.dev2019080601)\n", - "Requirement already satisfied: google-pasta>=0.1.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (0.1.7)\n", - "Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow-gpu==2.0.0-rc1) (1.1.0)\n", - "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tb-nightly<1.15.0a20190807,>=1.15.0a20190806->tensorflow-gpu==2.0.0-rc1) (3.1.1)\n", - "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.6/dist-packages (from tb-nightly<1.15.0a20190807,>=1.15.0a20190806->tensorflow-gpu==2.0.0-rc1) (41.2.0)\n", - "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tb-nightly<1.15.0a20190807,>=1.15.0a20190806->tensorflow-gpu==2.0.0-rc1) (0.15.6)\n", - "Requirement already satisfied: h5py in /usr/local/lib/python3.6/dist-packages (from keras-applications>=1.0.8->tensorflow-gpu==2.0.0-rc1) (2.8.0)\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "k1uZnxh4Xz9Z", - "colab_type": "code", - "colab": {} - }, - "source": [ - "import random as rn\n", - "import time\n", - "\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import tensorflow as tf\n", - "from tensorflow import keras" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "NN6vJl7eVnZ4", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Defining random seeds\n", - "random_seed = 42\n", - "tf.random.set_seed(random_seed)\n", - "np.random.seed(random_seed)\n", - "rn.seed(random_seed)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "qMnNh9emVfI-", - "colab_type": "code", - "colab": {} - }, - "source": [ - "def sample_from(distribution):\n", - " \"\"\"Sample random values from distribution\"\"\"\n", - " batch_size, bins = distribution.shape\n", - " return np.array([np.random.choice(bins, p=distr) for distr in distribution])\n", - "\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "8BnkhgCjVpJu", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Loading data\n", - "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", - "\n", - "height = 28\n", - "width = 28\n", - "n_channel = 1\n", - "\n", - "x_train = x_train.astype('float32') / 255.\n", - "x_test = x_test.astype('float32') / 255.\n", - "\n", - "x_train = x_train.reshape(x_train.shape[0], height, width, 1)\n", - "x_test = x_test.reshape(x_test.shape[0], height, width, 1)\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "3ne-qY7JVZaB", - "colab_type": "code", - "colab": {} - }, - "source": [ - "def quantise(images, q_levels):\n", - " \"\"\"Quantise image into q levels\"\"\"\n", - " return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32')" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "3QVhnMymVrzc", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Quantise the input data in q levels\n", - "q_levels = 16\n", - "x_train_quantised = quantise(x_train, q_levels)\n", - "x_test_quantised = quantise(x_test, q_levels)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "ZObIXqzNGwmo", - "colab_type": "code", - "outputId": "ee8aef2d-20a3-44b6-8fee-774b0be0e600", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 89 - } - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Creating input stream using tf.data API\n", - "batch_size = 128\n", - "train_buf = 60000\n", - "\n", - "train_dataset = tf.data.Dataset.from_tensor_slices((x_train_quantised / (q_levels - 1),\n", - " x_train_quantised.astype('int32')))\n", - "train_dataset = train_dataset.shuffle(buffer_size=train_buf)\n", - "train_dataset = train_dataset.batch(batch_size)\n", - "\n", - "test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1),\n", - " x_test_quantised.astype('int32')))\n", - "test_dataset = test_dataset.batch(batch_size)\n" - ], - "execution_count": 8, - "outputs": [ - { - "output_type": "stream", - "text": [ - "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow_core/python/data/util/random_seed.py:58: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Use tf.where in 2.0, which has the same broadcast rule as np.where\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "PTUN4s52Nu3w", - "colab_type": "code", - "colab": {} - }, - "source": [ - "\n", - "class GatedBlock(tf.keras.Model):\n", - " \"\"\"\"\"\"\n", - "\n", - " def __init__(self, mask_type, filters, kernel_size):\n", - " super(GatedBlock, self).__init__(name='')\n", - "\n", - " self.mask_type = mask_type\n", - " self.vertical_padding = keras.layers.ZeroPadding2D(padding=((kernel_size//2+1, 0),\n", - " (kernel_size//2, kernel_size//2)))\n", - "\n", - " self.vertical_conv = keras.layers.Conv2D(filters=2 * filters,\n", - " kernel_size=[kernel_size//2+1, kernel_size],\n", - " strides=1,\n", - " padding='valid')\n", - "\n", - " self.vertical_cropping = keras.layers.Cropping2D(cropping=((0, 1), (0, 0)))\n", - "\n", - " self.horizontal_padding = keras.layers.ZeroPadding2D(padding=((0, 0), (kernel_size // 2+1, 0)))\n", - " self.horizontal_conv = keras.layers.Conv2D(filters=2 * filters,\n", - " kernel_size=[1, kernel_size // 2 + 1],\n", - " strides=1,\n", - " padding='valid')\n", - " if mask_type == 'B':\n", - " self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (1, 0)))\n", - " elif mask_type == 'A':\n", - " self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (0, 1)))\n", - "\n", - " self.vertical_to_horizontal_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1)\n", - "\n", - " self.horizontal_output_conv = keras.layers.Conv2D(filters=filters, kernel_size=1)\n", - "\n", - " def _gate(self, x):\n", - " tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1)\n", - " return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation)\n", - "\n", - " def call(self, input_tensor):\n", - " v, h = tf.split(input_tensor, 2, axis=-1)\n", - "\n", - " vertical_preactivation = self.vertical_padding(v)\n", - " vertical_preactivation = self.vertical_conv(vertical_preactivation)\n", - " vertical_preactivation = self.vertical_cropping(vertical_preactivation)\n", - "\n", - " horizontal_preactivation = self.horizontal_padding(h)\n", - " horizontal_preactivation = self.horizontal_conv(horizontal_preactivation)\n", - " horizontal_preactivation = self.horizontal_cropping(horizontal_preactivation)\n", - "\n", - " v_to_h = self.vertical_to_horizontal_conv(vertical_preactivation) # 1x1\n", - " vertical_output = self._gate(vertical_preactivation)\n", - "\n", - " horizontal_preactivation = horizontal_preactivation + v_to_h\n", - " horizontal_activated = self._gate(horizontal_preactivation)\n", - "\n", - " if self.mask_type =='B':\n", - " horizontal_activated = self.horizontal_output_conv(horizontal_activated)\n", - " horizontal_activated = h + horizontal_activated\n", - "\n", - " output = tf.concat((vertical_output, horizontal_activated), axis=-1)\n", - " return output\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "WB57YufrVxn2", - "colab_type": "code", - "colab": {} - }, - "source": [ - "inputs = keras.layers.Input(shape=(height, width, n_channel))\n", - "x = keras.layers.Concatenate()([inputs, inputs])\n", - "x = GatedBlock(mask_type='A', filters=64, kernel_size=3)(x)\n", - "\n", - "for i in range(5):\n", - " x = GatedBlock(mask_type='B', filters=64, kernel_size=3)(x)\n", - "\n", - "v, h = tf.split(x, 2, axis=-1)\n", - "\n", - "x = keras.layers.Activation(activation='relu')(h)\n", - "x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x)\n", - "\n", - "x = keras.layers.Activation(activation='relu')(x)\n", - "x = keras.layers.Conv2D(filters=n_channel * q_levels, kernel_size=1, strides=1)(x) # shape [N,H,W,DC]\n", - "\n", - "pixelcnn = tf.keras.Model(inputs=inputs, outputs=x)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "_LnzHUaqV77d", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Prepare optimizer and loss function\n", - "lr_decay = 0.9995\n", - "learning_rate = 1e-3\n", - "optimizer = tf.keras.optimizers.Adam(lr=learning_rate)\n", - "\n", - "compute_loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True)\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "CsAgEKVzLCJD", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "@tf.function\n", - "def train_step(batch_x, batch_y):\n", - " with tf.GradientTape() as ae_tape:\n", - " logits = pixelcnn(batch_x, training=True)\n", - "\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) # shape [N,H,W,DC] -> [N,H,W,D,C]\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) # shape [N,H,W,D,C] -> [N,H,W,C,D]\n", - "\n", - " loss = compute_loss(tf.one_hot(batch_y, q_levels), logits)\n", - "\n", - " gradients = ae_tape.gradient(loss, pixelcnn.trainable_variables)\n", - " gradients, _ = tf.clip_by_global_norm(gradients, 1.0)\n", - " optimizer.apply_gradients(zip(gradients, pixelcnn.trainable_variables))\n", - "\n", - " return loss" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "NoEPrfwQNM-s", - "colab_type": "code", - "outputId": "3a27de5a-a2b8-4504-a217-7a60a96521b3", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 1000 - } - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Training loop\n", - "n_epochs = 30\n", - "n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size))\n", - "for epoch in range(n_epochs):\n", - " start_epoch = time.time()\n", - " for i_iter, (batch_x, batch_y) in enumerate(train_dataset):\n", - " start = time.time()\n", - " optimizer.lr = optimizer.lr * lr_decay\n", - " loss = train_step(batch_x, batch_y)\n", - " iter_time = time.time() - start\n", - " if i_iter % 100 == 0:\n", - " print('EPOCH {:3d}: ITER {:4d}/{:4d} TIME: {:.2f} LOSS: {:.4f}'.format(epoch,\n", - " i_iter, n_iter,\n", - " iter_time,\n", - " loss))\n", - " epoch_time = time.time() - start_epoch\n", - " print('EPOCH {:3d}: TIME: {:.2f} ETA: {:.2f}'.format(epoch,\n", - " epoch_time,\n", - " epoch_time * (n_epochs - epoch)))\n" - ], - "execution_count": 13, - "outputs": [ - { - "output_type": "stream", - "text": [ - "EPOCH 0: ITER 0/ 469 TIME: 4.83 LOSS: 2.7778\n", - "EPOCH 0: ITER 100/ 469 TIME: 0.41 LOSS: 0.3986\n", - "EPOCH 0: ITER 200/ 469 TIME: 0.41 LOSS: 0.3808\n", - "EPOCH 0: ITER 300/ 469 TIME: 0.41 LOSS: 0.3527\n", - "EPOCH 0: ITER 400/ 469 TIME: 0.41 LOSS: 0.3625\n", - "EPOCH 0: TIME: 197.38 ETA: 5921.27\n", - "EPOCH 1: ITER 0/ 469 TIME: 0.02 LOSS: 0.3733\n", - "EPOCH 1: ITER 100/ 469 TIME: 0.41 LOSS: 0.3496\n", - "EPOCH 1: ITER 200/ 469 TIME: 0.40 LOSS: 0.3498\n", - "EPOCH 1: ITER 300/ 469 TIME: 0.40 LOSS: 0.3362\n", - "EPOCH 1: ITER 400/ 469 TIME: 0.40 LOSS: 0.3300\n", - "EPOCH 1: TIME: 190.52 ETA: 5525.11\n", - "EPOCH 2: ITER 0/ 469 TIME: 0.01 LOSS: 0.3497\n", - "EPOCH 2: ITER 100/ 469 TIME: 0.41 LOSS: 0.3441\n", - "EPOCH 2: ITER 200/ 469 TIME: 0.40 LOSS: 0.3345\n", - "EPOCH 2: ITER 300/ 469 TIME: 0.40 LOSS: 0.3484\n", - "EPOCH 2: ITER 400/ 469 TIME: 0.40 LOSS: 0.3241\n", - "EPOCH 2: TIME: 190.45 ETA: 5332.49\n", - "EPOCH 3: ITER 0/ 469 TIME: 0.01 LOSS: 0.3294\n", - "EPOCH 3: ITER 100/ 469 TIME: 0.40 LOSS: 0.3457\n", - "EPOCH 3: ITER 200/ 469 TIME: 0.41 LOSS: 0.3347\n", - "EPOCH 3: ITER 300/ 469 TIME: 0.40 LOSS: 0.3306\n", - "EPOCH 3: ITER 400/ 469 TIME: 0.40 LOSS: 0.3417\n", - "EPOCH 3: TIME: 190.49 ETA: 5143.34\n", - "EPOCH 4: ITER 0/ 469 TIME: 0.01 LOSS: 0.3366\n", - "EPOCH 4: ITER 100/ 469 TIME: 0.40 LOSS: 0.3228\n", - "EPOCH 4: ITER 200/ 469 TIME: 0.40 LOSS: 0.3279\n", - "EPOCH 4: ITER 300/ 469 TIME: 0.40 LOSS: 0.3281\n", - "EPOCH 4: ITER 400/ 469 TIME: 0.40 LOSS: 0.3442\n", - "EPOCH 4: TIME: 190.57 ETA: 4954.81\n", - "EPOCH 5: ITER 0/ 469 TIME: 0.01 LOSS: 0.3235\n", - "EPOCH 5: ITER 100/ 469 TIME: 0.40 LOSS: 0.3363\n", - "EPOCH 5: ITER 200/ 469 TIME: 0.41 LOSS: 0.3215\n", - "EPOCH 5: ITER 300/ 469 TIME: 0.40 LOSS: 0.3229\n", - "EPOCH 5: ITER 400/ 469 TIME: 0.40 LOSS: 0.3183\n", - "EPOCH 5: TIME: 190.37 ETA: 4759.15\n", - "EPOCH 6: ITER 0/ 469 TIME: 0.01 LOSS: 0.3294\n", - "EPOCH 6: ITER 100/ 469 TIME: 0.40 LOSS: 0.3311\n", - "EPOCH 6: ITER 200/ 469 TIME: 0.40 LOSS: 0.3227\n", - "EPOCH 6: ITER 300/ 469 TIME: 0.41 LOSS: 0.3238\n", - "EPOCH 6: ITER 400/ 469 TIME: 0.40 LOSS: 0.3166\n", - "EPOCH 6: TIME: 190.58 ETA: 4574.03\n", - "EPOCH 7: ITER 0/ 469 TIME: 0.02 LOSS: 0.3314\n", - "EPOCH 7: ITER 100/ 469 TIME: 0.40 LOSS: 0.3219\n", - "EPOCH 7: ITER 200/ 469 TIME: 0.41 LOSS: 0.3232\n", - "EPOCH 7: ITER 300/ 469 TIME: 0.41 LOSS: 0.3220\n", - "EPOCH 7: ITER 400/ 469 TIME: 0.40 LOSS: 0.3325\n", - "EPOCH 7: TIME: 190.59 ETA: 4383.51\n", - "EPOCH 8: ITER 0/ 469 TIME: 0.01 LOSS: 0.3213\n", - "EPOCH 8: ITER 100/ 469 TIME: 0.40 LOSS: 0.3197\n", - "EPOCH 8: ITER 200/ 469 TIME: 0.40 LOSS: 0.3274\n", - "EPOCH 8: ITER 300/ 469 TIME: 0.41 LOSS: 0.3274\n", - "EPOCH 8: ITER 400/ 469 TIME: 0.40 LOSS: 0.3133\n", - "EPOCH 8: TIME: 190.44 ETA: 4189.69\n", - "EPOCH 9: ITER 0/ 469 TIME: 0.01 LOSS: 0.3269\n", - "EPOCH 9: ITER 100/ 469 TIME: 0.41 LOSS: 0.3230\n", - "EPOCH 9: ITER 200/ 469 TIME: 0.40 LOSS: 0.3252\n", - "EPOCH 9: ITER 300/ 469 TIME: 0.40 LOSS: 0.3283\n", - "EPOCH 9: ITER 400/ 469 TIME: 0.40 LOSS: 0.3346\n", - "EPOCH 9: TIME: 190.22 ETA: 3994.71\n", - "EPOCH 10: ITER 0/ 469 TIME: 0.01 LOSS: 0.3210\n", - "EPOCH 10: ITER 100/ 469 TIME: 0.40 LOSS: 0.3250\n", - "EPOCH 10: ITER 200/ 469 TIME: 0.40 LOSS: 0.3373\n", - "EPOCH 10: ITER 300/ 469 TIME: 0.40 LOSS: 0.3306\n", - "EPOCH 10: ITER 400/ 469 TIME: 0.40 LOSS: 0.3253\n", - "EPOCH 10: TIME: 189.99 ETA: 3799.84\n", - "EPOCH 11: ITER 0/ 469 TIME: 0.01 LOSS: 0.3255\n", - "EPOCH 11: ITER 100/ 469 TIME: 0.40 LOSS: 0.3241\n", - "EPOCH 11: ITER 200/ 469 TIME: 0.40 LOSS: 0.3210\n" - ], - "name": "stdout" - }, - { - "output_type": "error", - "ename": "KeyboardInterrupt", - "evalue": "ignored", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi_iter\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mbatch_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_y\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrain_dataset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mstart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlr\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlr_decay\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 8\u001b[0m \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtrain_step\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatch_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_y\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0miter_time\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mstart\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/ops/variables.py\u001b[0m in \u001b[0;36m_run_op\u001b[0;34m(a, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1077\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_run_op\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1078\u001b[0m \u001b[0;31m# pylint: disable=protected-access\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1079\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mtensor_oper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1080\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1081\u001b[0m \u001b[0mfunctools\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_run_op\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtensor_oper\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/ops/math_ops.py\u001b[0m in \u001b[0;36mbinary_op_wrapper\u001b[0;34m(x, y)\u001b[0m\n\u001b[1;32m 910\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 911\u001b[0m \u001b[0;32mraise\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 912\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 913\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 914\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mbinary_op_wrapper_sparse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msp_x\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/ops/math_ops.py\u001b[0m in \u001b[0;36m_mul_dispatch\u001b[0;34m(x, y, name)\u001b[0m\n\u001b[1;32m 1204\u001b[0m \u001b[0mis_tensor_y\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTensor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1205\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mis_tensor_y\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1206\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mgen_math_ops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmul\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1207\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1208\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msparse_tensor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSparseTensor\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Case: Dense * Sparse.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/tensorflow_core/python/ops/gen_math_ops.py\u001b[0m in \u001b[0;36mmul\u001b[0;34m(x, y, name)\u001b[0m\n\u001b[1;32m 6683\u001b[0m _result = _pywrap_tensorflow.TFE_Py_FastPathExecute(\n\u001b[1;32m 6684\u001b[0m \u001b[0m_ctx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_context_handle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_ctx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_thread_local_data\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdevice_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"Mul\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 6685\u001b[0;31m name, _ctx._post_execution_callbacks, x, y)\n\u001b[0m\u001b[1;32m 6686\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0m_result\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6687\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0m_core\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_FallbackException\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mKeyboardInterrupt\u001b[0m: " - ] - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "ue0vZbitSNmz", - "colab_type": "code", - "outputId": "e0241cec-20ca-49e9-da6d-fd50f7c8d52d", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 52 - } - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Test\n", - "test_loss = []\n", - "for batch_x, batch_y in test_dataset:\n", - " logits = pixelcnn(batch_x, training=False)\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel])\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3])\n", - "\n", - " # Calculate cross-entropy (= negative log-likelihood)\n", - " loss = compute_loss(tf.one_hot(batch_y, q_levels), logits)\n", - "\n", - " test_loss.append(loss)\n", - "print('nll : {:} nats'.format(np.array(test_loss).mean()))\n", - "print('bits/dim : {:}'.format(np.array(test_loss).mean() / (height * width)))\n", - "\n" - ], - "execution_count": 14, - "outputs": [ - { - "output_type": "stream", - "text": [ - "nll : 0.32299938797950745 nats\n", - "bits/dim : 0.000411989015279984\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "-Ia9VXYySkuW", - "colab_type": "code", - "outputId": "0dd257ee-3198-4aea-96fb-c65f60100fec", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 526 - } - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Generating new images\n", - "samples = (np.random.rand(100, height, width, n_channel) * 0.01).astype('float32')\n", - "for i in range(height):\n", - " for j in range(width):\n", - " logits = pixelcnn(samples)\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel])\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3])\n", - " probs = tf.nn.softmax(logits)\n", - " next_sample = probs[:, i, j, 0, :]\n", - " samples[:, i, j, 0] = sample_from(next_sample.numpy()) / (q_levels - 1)\n", - "\n", - "fig = plt.figure(figsize=(10, 10))\n", - "for x in range(1, 10):\n", - " for y in range(1, 10):\n", - " ax = fig.add_subplot(10, 10, 10 * y + x)\n", - " ax.matshow(samples[10 * y + x, :, :, 0], cmap=matplotlib.cm.binary)\n", - " plt.xticks(np.array([]))\n", - " plt.yticks(np.array([]))\n", - "plt.show()\n" - ], - "execution_count": 15, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\nh4Mbp+jLKIq8qbgo8uFxWqeQAq2gFLGSH+fzeWUeTRtYyg3u89S8Q1+hH9Fneq2xFAp81uV8tbbr\nXkpbuRU0EmqSZZkbrBiUf//+LWXMBkHgfq4XWwzI4kKzWCzckbU+VUODnHuJDIkBj8mIBCTfQklY\nJKyQwTnQpmJFN18MBJH6surLy0vtg5KAT2NU5DiZEdzd3bl/wyCqsxgPBgM3Z/XcLdZY6DLxzWpv\nFcUkWX0AHZ5JkiSdlW/G3617wJGeq8W+0rtwYBTq630q73+NMYTvwDNcr9eNGO8MNxBCCCHE5O7t\n7a3+xXd3/xWR/zR3O43x77e3t3+du6jH7RO5/TbWap8I2+g5HKf/wDZ6Ddv4DxcZCYQQQgj5PjDc\nQAghhBATGgmEEEIIMaGRQAghhBATGgmEEEIIMaGRQAghhBATGgmEEEIIMaGRQAghhBCTi8oy//z5\n8+3Xr18N3Upz/P79W/78+XN37rq+tk9EJE3TP3UKY/S1jXX7UIRt9Jlbn4vsw2PYRn+p28aLjIRf\nv365mv59AocVnaOv7RMRubu7q1Xxq69trNuHImyjz9z6XGQfHsM2+kvdNjLcQAghhBATGgmEEEII\nMaGRQAghhBATGgmEEEIIMbkocZGQKtI0FZGPhJgwDEVEJMsyd00QBCIislwuW7679tntdvL4+Hj0\nWRiGstlsOrqjd9BPURTJ09OTiIisVisR+R79Mh6PRURkv9+LiMhsNhOR97ZPp9PO7otcjziORURc\nf7JfP0+jRsJ8Ppc8z0XkY2Ku12sZDoef+j68bHa7nVvokFX68vLirttutyLyMflJO2hjQORjomp2\nu52IiGw2G9eHnx0PvoIxXzQQRKTTLGj0j5XVvF6vRaS/RkIcx5IkiYh8jLHBYOCMH7wk4jh2xgHA\n7yVJwrXjRkD/+bC2YJ3DejgcDt18qwvmLsbqdrs9eucVgYN2DYeE4QZCCCGEmDSqJDw9PcnhcDj6\n7Pn52VlWlwIPYbFYmD8fjUaf+t42gUWovWx8Bg/0+fm59NxExEnDkOx9Io7jk/1isd/vXTs+Ox6a\nAH2QZZlEUSQiH9a7yLt3KvLhjcNDDcPQea36+iI/fvy4/k3XYLFYmMoOsFSPPoBnbnlMh8PB/bwu\n8/lcREQmk4mIvD83eH0Y32EYeuGhktP41D9YD/T8u1RJwNjDO/Ac+FvL5dKp+J+FSgIhhBBCTFpP\n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\nQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [] - } - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "KBuWx-FtSouR", - "colab_type": "code", - "outputId": "4021ec1a-8191-41eb-a446-d2b616ea570e", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 526 - } - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Filling occluded images\n", - "occlude_start_row = 14\n", - "num_generated_images = 100\n", - "samples = np.copy(x_test_quantised[0:num_generated_images, :, :, :])\n", - "samples = samples / (q_levels - 1)\n", - "samples[:, occlude_start_row:, :, :] = 0\n", - "\n", - "for i in range(occlude_start_row, height):\n", - " for j in range(width):\n", - " logits = pixelcnn(samples)\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel])\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3])\n", - " probs = tf.nn.softmax(logits)\n", - " next_sample = probs[:, i, j, 0, :]\n", - " samples[:, i, j, 0] = sample_from(next_sample.numpy()) / (q_levels - 1)\n", - "\n", - "fig = plt.figure(figsize=(10, 10))\n", - "for x in range(1, 10):\n", - " for y in range(1, 10):\n", - " ax = fig.add_subplot(10, 10, 10 * y + x)\n", - " ax.matshow(samples[10 * y + x, :, :, 0], cmap=matplotlib.cm.binary)\n", - " plt.xticks(np.array([]))\n", - " plt.yticks(np.array([]))\n", - "plt.show()\n" - ], - "execution_count": 16, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n1wmLy/1xZxIAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [] - } - } - ] - } - ] -} \ No newline at end of file diff --git a/WIP/6-gated_pixelcnn_cropped/3d_gated_pixelcnn_conv.ipynb b/WIP/6-gated_pixelcnn_cropped/3d_gated_pixelcnn_conv.ipynb deleted file mode 100644 index 8df9dee..0000000 --- a/WIP/6-gated_pixelcnn_cropped/3d_gated_pixelcnn_conv.ipynb +++ /dev/null @@ -1,1892 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "3d_gated_pixelcnn_conv.ipynb", - "provenance": [], - "collapsed_sections": [], - "toc_visible": true - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "accelerator": "GPU" - }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "HgFGN07idT26", - "colab_type": "text" - }, - "source": [ - "# Converting masked-based implementation to cropping-based implementation" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "ObS7YqtCbC33", - "colab_type": "code", - "colab": {} - }, - "source": [ - "import tensorflow as tf\n", - "import tensorflow.keras as keras\n", - "import numpy as np\n", - "import math" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "iCMR2mKLbt_l", - "colab_type": "code", - "colab": {} - }, - "source": [ - "test_ones_2d = np.ones([1, 5, 5, 1], dtype='float32')\n", - "test_ones_3d = np.ones([1, 5, 5, 5, 1], dtype='float32')" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "NycU0IQZb1X1", - "colab_type": "code", - "colab": {} - }, - "source": [ - "def print_3d(matrix_3d):\n", - " for i in range(matrix_3d.shape[0]):\n", - " print(f'Depth {i}')\n", - " print(matrix_3d[i,...])" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "AeH21Zkzcrt5", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 538 - }, - "outputId": "456c0c0f-9701-40c2-d545-c8091d50a59a" - }, - "source": [ - "print_3d(test_ones_3d.squeeze())" - ], - "execution_count": 146, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Depth 0\n", - "[[1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]]\n", - "Depth 1\n", - "[[1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]]\n", - "Depth 2\n", - "[[1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]]\n", - "Depth 3\n", - "[[1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]]\n", - "Depth 4\n", - "[[1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "xqrqiDnbfoqW", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 104 - }, - "outputId": "88e2939e-78e1-4692-abbb-6569282d48e8" - }, - "source": [ - "print(test_ones_2d[0,:,:,0].squeeze())" - ], - "execution_count": 5, - "outputs": [ - { - "output_type": "stream", - "text": [ - "[[1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]\n", - " [1. 1. 1. 1. 1.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1UcUkEj0d7wh", - "colab_type": "text" - }, - "source": [ - "## Creating 2D masked solution to check results with cropped solution later" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "83mZFyondaAT", - "colab_type": "code", - "colab": {} - }, - "source": [ - "class MaskedConv2D(tf.keras.layers.Layer):\n", - " def __init__(self,\n", - " mask_type,\n", - " filters,\n", - " kernel_size,\n", - " strides=1,\n", - " padding='same',\n", - " kernel_initializer='glorot_uniform',\n", - " bias_initializer='zeros'):\n", - " super(MaskedConv2D, self).__init__()\n", - "\n", - " assert mask_type in {'A', 'B', 'V'}\n", - " self.mask_type = mask_type\n", - "\n", - " self.filters = filters\n", - "\n", - " if isinstance(kernel_size, int):\n", - " kernel_size = (kernel_size, kernel_size)\n", - " self.kernel_size = kernel_size\n", - "\n", - " self.strides = strides\n", - " self.padding = padding.upper()\n", - " self.kernel_initializer = tf.keras.initializers.get(kernel_initializer)\n", - " self.bias_initializer = tf.keras.initializers.get(bias_initializer)\n", - "\n", - " def build(self, input_shape):\n", - " kernel_h, kernel_w = self.kernel_size\n", - "\n", - " self.kernel = self.add_weight(\"kernel\",\n", - " shape=(kernel_h,\n", - " kernel_w,\n", - " int(input_shape[-1]),\n", - " self.filters),\n", - " initializer=self.kernel_initializer,\n", - " trainable=True)\n", - "\n", - " self.bias = self.add_weight(\"bias\",\n", - " shape=(self.filters,),\n", - " initializer=self.bias_initializer,\n", - " trainable=True)\n", - "\n", - " mask = np.ones(self.kernel.shape, dtype=np.float32)\n", - "\n", - " if kernel_h % 2 != 0: \n", - " center_h = kernel_h // 2\n", - " else:\n", - " center_h = (kernel_h - 1) // 2\n", - "\n", - " if kernel_w % 2 != 0: \n", - " center_w = kernel_w // 2\n", - " else:\n", - " center_w = (kernel_w - 1) // 2\n", - "\n", - "\n", - "\n", - " if self.mask_type == 'V':\n", - " mask[center_h + 1:, :, :, :] = 0.\n", - " else:\n", - " mask[:center_h, :, :] = 0.\n", - " mask[center_h, center_w + (self.mask_type == 'B'):, :, :] = 0.\n", - " mask[center_h + 1:, :, :] = 0.\n", - "\n", - " self.mask = tf.constant(mask, dtype=tf.float32, name='mask')\n", - "\n", - " def call(self, input):\n", - " masked_kernel = tf.math.multiply(self.mask, self.kernel)\n", - " x = tf.nn.conv2d(input, masked_kernel, strides=[1, self.strides, self.strides, 1], padding=self.padding)\n", - " x = tf.nn.bias_add(x, self.bias)\n", - " return x" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nMXqIeSUkdcV", - "colab_type": "text" - }, - "source": [ - "### Tests with kernel_size 3" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "M62GZQe8ixvy", - "colab_type": "text" - }, - "source": [ - "#### Vertical stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "kjUrpEtIg7p9", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 208 - }, - "outputId": "3f769c93-2f50-452d-ea81-503488f3c7c1" - }, - "source": [ - "mask_type = 'V'\n", - "kernel_size=(3, 3)\n", - "\n", - "padding = keras.layers.ZeroPadding2D(padding=((1,0),0))\n", - "\n", - "conv = MaskedConv2D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping2D(cropping=((0, 1), 0))\n", - "\n", - "\n", - "x = padding(test_ones_2d)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "print('MASK')\n", - "print(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())" - ], - "execution_count": 111, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "[[1. 1. 1.]\n", - " [1. 1. 1.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iHY6UE2_p5oc", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack A" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "4_q_IunZkFmj", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 208 - }, - "outputId": "f9a62e80-29f4-4c9d-b511-620a3eb76037" - }, - "source": [ - "mask_type = 'A'\n", - "kernel_size=(3, 3)\n", - "\n", - "conv = MaskedConv2D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_2d)\n", - "\n", - "print('MASK')\n", - "print(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())" - ], - "execution_count": 113, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "[[0. 0. 0.]\n", - " [1. 0. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jMuS-vgWqAWK", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack B" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "5yeB5h2tkSs_", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 208 - }, - "outputId": "64e2dfcf-ba0f-4b8b-b92d-8b5504340a4a" - }, - "source": [ - "mask_type = 'B'\n", - "kernel_size=(3, 3)\n", - "\n", - "conv = MaskedConv2D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_2d)\n", - "\n", - "print('MASK')\n", - "print(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())" - ], - "execution_count": 115, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "[[0. 0. 0.]\n", - " [1. 1. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1NxkQ3U1knbE", - "colab_type": "text" - }, - "source": [ - "### Tests with kernel_size 4" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WNykK-WpqMlu", - "colab_type": "text" - }, - "source": [ - "#### Vertical stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "p3DTiFYCk57Y", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 225 - }, - "outputId": "f2ec68a3-95e6-4113-f0ba-9c37c7c63589" - }, - "source": [ - "mask_type = 'V'\n", - "kernel_size=(4, 4)\n", - "\n", - "padding = keras.layers.ZeroPadding2D(padding=((1,0),0))\n", - "\n", - "conv = MaskedConv2D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping2D(cropping=((0, 1), 0))\n", - "\n", - "\n", - "x = padding(test_ones_2d)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "print('MASK')\n", - "print(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())" - ], - "execution_count": 119, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "[[1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "[[0. 0. 0. 0. 0.]\n", - " [3. 4. 4. 3. 2.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "E5jUGK3_qbT8", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack A" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "J5HSUo7Rk5xO", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 225 - }, - "outputId": "bd56a4a9-985e-4746-c110-02f6a37bf964" - }, - "source": [ - "mask_type = 'A'\n", - "kernel_size=(4, 4)\n", - "\n", - "conv = MaskedConv2D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_2d)\n", - "\n", - "print('MASK')\n", - "print(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())" - ], - "execution_count": 120, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "[[0. 0. 0. 0.]\n", - " [1. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KqORK7mLqvPP", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack B" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "_2V51aerk5l1", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 225 - }, - "outputId": "5a6e6421-a0f9-4665-f453-b9fb0b80c325" - }, - "source": [ - "mask_type = 'B'\n", - "kernel_size=(4, 4)\n", - "\n", - "conv = MaskedConv2D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_2d)\n", - "\n", - "print('MASK')\n", - "print(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())" - ], - "execution_count": 121, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "[[0. 0. 0. 0.]\n", - " [1. 1. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "4XtSteZ4pbOd", - "colab_type": "text" - }, - "source": [ - "## Creating 3D masked solution to check results with cropped solution later" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "r6XeT4-cpdoA", - "colab_type": "code", - "colab": {} - }, - "source": [ - "class MaskedConv3D(tf.keras.layers.Layer):\n", - " def __init__(self,\n", - " mask_type,\n", - " filters,\n", - " kernel_size,\n", - " strides=1,\n", - " padding='same',\n", - " kernel_initializer='glorot_uniform',\n", - " bias_initializer='zeros'):\n", - " super(MaskedConv3D, self).__init__()\n", - "\n", - " assert mask_type in {'A', 'B', 'D', 'V'}\n", - " self.mask_type = mask_type\n", - "\n", - " self.filters = filters\n", - "\n", - " if isinstance(kernel_size, int):\n", - " kernel_size = (kernel_size, kernel_size, kernel_size)\n", - " self.kernel_size = kernel_size\n", - "\n", - " self.strides = strides\n", - " self.padding = padding.upper()\n", - " self.kernel_initializer = tf.keras.initializers.get(kernel_initializer)\n", - " self.bias_initializer = tf.keras.initializers.get(bias_initializer)\n", - "\n", - " def build(self, input_shape):\n", - " kernel_d, kernel_h, kernel_w = self.kernel_size\n", - "\n", - " self.kernel = self.add_weight(\"kernel\",\n", - " shape=(kernel_d,\n", - " kernel_h,\n", - " kernel_w,\n", - " int(input_shape[-1]),\n", - " self.filters),\n", - " initializer=self.kernel_initializer,\n", - " trainable=True)\n", - "\n", - " self.bias = self.add_weight(\"bias\",\n", - " shape=(self.filters,),\n", - " initializer=self.bias_initializer,\n", - " trainable=True)\n", - "\n", - " mask = np.ones(self.kernel.shape, dtype=np.float32)\n", - "\n", - "\n", - " if kernel_d % 2 != 0: \n", - " center_d = kernel_d // 2\n", - " else:\n", - " center_d = (kernel_d - 1) // 2\n", - "\n", - " if kernel_h % 2 != 0: \n", - " center_h = kernel_h // 2\n", - " else:\n", - " center_h = (kernel_h - 1) // 2\n", - "\n", - " if kernel_w % 2 != 0: \n", - " center_w = kernel_w // 2\n", - " else:\n", - " center_w = (kernel_w - 1) // 2\n", - "\n", - "\n", - " if self.mask_type == 'D':\n", - " mask[center_d+1:, :, :, :, :] = 0.\n", - " elif self.mask_type == 'V':\n", - " mask[:center_d, :, :, :, :] = 0.\n", - " mask[center_d, center_h+1:, :, :, :] = 0.\n", - " mask[center_d + 1:, :, :, :, :] = 0.\n", - " else:\n", - " mask[:center_d, :, :, :, :] = 0.\n", - " mask[:, :center_h, :, :, :] = 0.\n", - " mask[center_d, center_h, center_w + (self.mask_type == 'B'):, :, :] = 0.\n", - " mask[:, center_h + 1:, :, :, :] = 0.\n", - " mask[center_d + 1:, :, :, :, :] = 0.\n", - "\n", - " self.mask = tf.constant(mask, dtype=tf.float32, name='mask')\n", - "\n", - " def call(self, input):\n", - " masked_kernel = tf.math.multiply(self.mask, self.kernel)\n", - " x = tf.nn.conv3d(input, masked_kernel, strides=[1, self.strides, self.strides, self.strides, 1], padding=self.padding)\n", - " x = tf.nn.bias_add(x, self.bias)\n", - " return x" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TJSNB3m4Apfi", - "colab_type": "text" - }, - "source": [ - "### Tests with kernel_size 3" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "-NIs7IZdrVnu", - "colab_type": "text" - }, - "source": [ - "#### Depth stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "DuEGynZxsChh", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 799 - }, - "outputId": "f98454bb-09ed-416f-8d20-af28b813881a" - }, - "source": [ - "mask_type = 'D'\n", - "kernel_size=(3, 3, 3)\n", - "\n", - "padding = keras.layers.ZeroPadding3D(padding=((1,0),0,0))\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping3D(cropping=((0, 1), 0, 0))\n", - "\n", - "\n", - "x = padding(test_ones_3d)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())\n" - ], - "execution_count": 154, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[1. 1. 1.]\n", - " [1. 1. 1.]\n", - " [1. 1. 1.]]\n", - "Depth 1\n", - "[[1. 1. 1.]\n", - " [1. 1. 1.]\n", - " [1. 1. 1.]]\n", - "Depth 2\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]]\n", - "Depth 1\n", - "[[4. 6. 6. 6. 4.]\n", - " [6. 9. 9. 9. 6.]\n", - " [6. 9. 9. 9. 6.]\n", - " [6. 9. 9. 9. 6.]\n", - " [4. 6. 6. 6. 4.]]\n", - "Depth 2\n", - "[[ 8. 12. 12. 12. 8.]\n", - " [12. 18. 18. 18. 12.]\n", - " [12. 18. 18. 18. 12.]\n", - " [12. 18. 18. 18. 12.]\n", - " [ 8. 12. 12. 12. 8.]]\n", - "Depth 3\n", - "[[ 8. 12. 12. 12. 8.]\n", - " [12. 18. 18. 18. 12.]\n", - " [12. 18. 18. 18. 12.]\n", - " [12. 18. 18. 18. 12.]\n", - " [ 8. 12. 12. 12. 8.]]\n", - "Depth 4\n", - "[[ 8. 12. 12. 12. 8.]\n", - " [12. 18. 18. 18. 12.]\n", - " [12. 18. 18. 18. 12.]\n", - " [12. 18. 18. 18. 12.]\n", - " [ 8. 12. 12. 12. 8.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "u4K8-zY0timI", - "colab_type": "text" - }, - "source": [ - "#### Vertical stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "dCgUj-q5rwIt", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 799 - }, - "outputId": "b836ef8d-4cbf-40f2-f086-4a83dd7a0ddf" - }, - "source": [ - "mask_type = 'V'\n", - "kernel_size=(3, 3, 3)\n", - "\n", - "padding = keras.layers.ZeroPadding3D(padding=(0,(1,0),0))\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping3D(cropping=(0,(0, 1), 0))\n", - "\n", - "\n", - "x = padding(test_ones_3d)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())\n" - ], - "execution_count": 157, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 1\n", - "[[1. 1. 1.]\n", - " [1. 1. 1.]\n", - " [0. 0. 0.]]\n", - "Depth 2\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n", - "Depth 1\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n", - "Depth 2\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n", - "Depth 3\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n", - "Depth 4\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "oSlof4GvuaLu", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack A" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "0N8LWI67_vFv", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 799 - }, - "outputId": "cfea8fc3-f6af-41d6-98aa-5453eb3612ca" - }, - "source": [ - "mask_type = 'A'\n", - "kernel_size=(3, 3, 3)\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_3d)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())" - ], - "execution_count": 168, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 1\n", - "[[0. 0. 0.]\n", - " [1. 0. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 2\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 1\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 2\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 3\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 4\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ixWP3Ey7vs-H", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack B" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "yD6VvotZABid", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 799 - }, - "outputId": "50244671-766c-41e2-8999-1e4f34844622" - }, - "source": [ - "mask_type = 'B'\n", - "kernel_size=(3, 3, 3)\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_3d)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())" - ], - "execution_count": 169, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 1\n", - "[[0. 0. 0.]\n", - " [1. 1. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 2\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 1\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 2\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 3\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 4\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8AlzSu6GAvNj", - "colab_type": "text" - }, - "source": [ - "### Tests with kernel_size 4" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_ZFtCkYhvwGW", - "colab_type": "text" - }, - "source": [ - "#### Depth stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "AMp8F--cAwDs", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 937 - }, - "outputId": "e78c3adf-e32c-44fd-e0de-3a7db406bbd9" - }, - "source": [ - "mask_type = 'D'\n", - "kernel_size=(4, 4, 4)\n", - "\n", - "padding = keras.layers.ZeroPadding3D(padding=((1,0),0,0))\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping3D(cropping=((0, 1), 0, 0))\n", - "\n", - "\n", - "x = padding(test_ones_3d)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())\n" - ], - "execution_count": 171, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]]\n", - "Depth 1\n", - "[[1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]]\n", - "Depth 2\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 3\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]\n", - " [0. 0. 0. 0. 0.]]\n", - "Depth 1\n", - "[[ 9. 12. 12. 9. 6.]\n", - " [12. 16. 16. 12. 8.]\n", - " [12. 16. 16. 12. 8.]\n", - " [ 9. 12. 12. 9. 6.]\n", - " [ 6. 8. 8. 6. 4.]]\n", - "Depth 2\n", - "[[18. 24. 24. 18. 12.]\n", - " [24. 32. 32. 24. 16.]\n", - " [24. 32. 32. 24. 16.]\n", - " [18. 24. 24. 18. 12.]\n", - " [12. 16. 16. 12. 8.]]\n", - "Depth 3\n", - "[[18. 24. 24. 18. 12.]\n", - " [24. 32. 32. 24. 16.]\n", - " [24. 32. 32. 24. 16.]\n", - " [18. 24. 24. 18. 12.]\n", - " [12. 16. 16. 12. 8.]]\n", - "Depth 4\n", - "[[18. 24. 24. 18. 12.]\n", - " [24. 32. 32. 24. 16.]\n", - " [24. 32. 32. 24. 16.]\n", - " [18. 24. 24. 18. 12.]\n", - " [12. 16. 16. 12. 8.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3iXH2zkbv-qY", - "colab_type": "text" - }, - "source": [ - "#### Vertical stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "7A8KTeJXA0f6", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 937 - }, - "outputId": "3f0d6424-8783-42b5-f705-1e47592a40eb" - }, - "source": [ - "mask_type = 'V'\n", - "kernel_size=(4, 4, 4)\n", - "\n", - "padding = keras.layers.ZeroPadding3D(padding=(0,(1,0),0))\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping3D(cropping=(0,(0, 1), 0))\n", - "\n", - "\n", - "x = padding(test_ones_3d)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())" - ], - "execution_count": 172, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 1\n", - "[[1. 1. 1. 1.]\n", - " [1. 1. 1. 1.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 2\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 3\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[0. 0. 0. 0. 0.]\n", - " [3. 4. 4. 3. 2.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]]\n", - "Depth 1\n", - "[[0. 0. 0. 0. 0.]\n", - " [3. 4. 4. 3. 2.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]]\n", - "Depth 2\n", - "[[0. 0. 0. 0. 0.]\n", - " [3. 4. 4. 3. 2.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]]\n", - "Depth 3\n", - "[[0. 0. 0. 0. 0.]\n", - " [3. 4. 4. 3. 2.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]]\n", - "Depth 4\n", - "[[0. 0. 0. 0. 0.]\n", - " [3. 4. 4. 3. 2.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]\n", - " [6. 8. 8. 6. 4.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ANQnjmvPwI3K", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack A" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "RGSe6z0mA0WU", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 937 - }, - "outputId": "298206a2-bd57-469f-f497-84ab21a970b5" - }, - "source": [ - "mask_type = 'A'\n", - "kernel_size=(4, 4, 4)\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_3d)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())" - ], - "execution_count": 173, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 1\n", - "[[0. 0. 0. 0.]\n", - " [1. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 2\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "Depth 3\n", - "[[0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]\n", - " [0. 0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 1\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 2\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 3\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n", - "Depth 4\n", - "[[0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]\n", - " [0. 1. 1. 1. 1.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "KJzAJ40EwTL3", - "colab_type": "text" - }, - "source": [ - "#### Horizontal stack B" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "9jCW_EzVA0JB", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 799 - }, - "outputId": "fa304a57-4168-4bfd-d722-1503a2bc54e1" - }, - "source": [ - "mask_type = 'B'\n", - "kernel_size=(3, 3, 3)\n", - "\n", - "conv = MaskedConv3D(mask_type=mask_type,\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " padding='same',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "result = conv(test_ones_3d)\n", - "\n", - "print('MASK')\n", - "print_3d(conv.mask.numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print_3d(result.numpy().squeeze())" - ], - "execution_count": 174, - "outputs": [ - { - "output_type": "stream", - "text": [ - "MASK\n", - "Depth 0\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 1\n", - "[[0. 0. 0.]\n", - " [1. 1. 0.]\n", - " [0. 0. 0.]]\n", - "Depth 2\n", - "[[0. 0. 0.]\n", - " [0. 0. 0.]\n", - " [0. 0. 0.]]\n", - "\n", - "OUTPUT\n", - "Depth 0\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 1\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 2\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 3\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n", - "Depth 4\n", - "[[1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]\n", - " [1. 2. 2. 2. 2.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kZmui789Br2B", - "colab_type": "text" - }, - "source": [ - "## Creating 2D cropped solution" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "PxBNsvzhB1ec", - "colab_type": "code", - "colab": {} - }, - "source": [ - "class VerticalCroppedConv2d(tf.keras.Model):\n", - " def __init__(self,\n", - " filters,\n", - " kernel_size,\n", - " kernel_initializer, \n", - " bias_initializer):\n", - " super(VerticalCroppedConv2d, self).__init__(name='')\n", - "\n", - " if isinstance(kernel_size, int):\n", - " kernel_size = (kernel_size, kernel_size)\n", - "\n", - " kernel_h, kernel_w = kernel_size\n", - "\n", - " self.padding = keras.layers.ZeroPadding2D(padding=((kernel_h-1, 0),(int((kernel_w-1)/2),int((kernel_w-1)/2))))\n", - "\n", - " self.conv = keras.layers.Conv2D(filters=filters,\n", - " kernel_size=kernel_size,\n", - " strides=1,\n", - " padding='valid',\n", - " kernel_initializer=kernel_initializer, \n", - " bias_initializer=bias_initializer)\n", - "\n", - " def call(self, input_value):\n", - "\n", - " x = self.padding(input_value)\n", - " x = self.conv(x)\n", - " out = self.cropping(x)\n", - "\n", - " return out\n", - "\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RfFuwKlrP2JU", - "colab_type": "text" - }, - "source": [ - "Example step by step" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "fH3I0lfoPdcH", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 332 - }, - "outputId": "49b7ffca-0ec0-46d7-837c-8e0f51dd5205" - }, - "source": [ - "kernel_h = 2\n", - "kernel_w = 3\n", - "\n", - "kernel_size = (kernel_h, kernel_w)\n", - "\n", - "padding = keras.layers.ZeroPadding2D(padding=((kernel_h-1, 0),(int((kernel_w-1)/2),int((kernel_w-1)/2))))\n", - "\n", - "res = padding(test_ones_2d)\n", - "print(res.numpy().squeeze())\n", - "\n", - "conv = keras.layers.Conv2D(filters=1,\n", - " kernel_size=kernel_size,\n", - " strides=1,\n", - " padding='valid',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "res2 = conv(res)\n", - "print(res2.numpy().squeeze())\n", - "\n", - "\n", - "\n" - ], - "execution_count": 67, - "outputs": [ - { - "output_type": "stream", - "text": [ - "WARNING:tensorflow:Layer zero_padding2d_15 is casting an input tensor from dtype float64 to the layer's dtype of float32, which is new behavior in TensorFlow 2. The layer has dtype float32 because it's dtype defaults to floatx.\n", - "\n", - "If you intended to run this layer in float32, you can safely ignore this warning. If in doubt, this warning is likely only an issue if you are porting a TensorFlow 1.X model to TensorFlow 2.\n", - "\n", - "To change all layers to have dtype float64 by default, call `tf.keras.backend.set_floatx('float64')`. To change just this layer, pass dtype='float64' to the layer constructor. If you are the author of this layer, you can disable autocasting by passing autocast=False to the base Layer constructor.\n", - "\n", - "[[0. 0. 0. 0. 0. 0. 0.]\n", - " [0. 1. 1. 1. 1. 1. 0.]\n", - " [0. 1. 1. 1. 1. 1. 0.]\n", - " [0. 1. 1. 1. 1. 1. 0.]\n", - " [0. 1. 1. 1. 1. 1. 0.]\n", - " [0. 1. 1. 1. 1. 1. 0.]]\n", - "[[2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "TFexYspQWEpo", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 69 - }, - "outputId": "e97bd930-188e-4831-8025-d379ea385190" - }, - "source": [ - "conv.weights[0].numpy().squeeze()" - ], - "execution_count": 64, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "array([[1., 1., 1.],\n", - " [1., 1., 1.],\n", - " [1., 1., 1.]], dtype=float32)" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 64 - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "AfyRyUmTNYZ8", - "colab_type": "code", - "colab": {} - }, - "source": [ - "def build_test_croppedv_stack_2d(input_shape=(5, 5, 1), kernel_size=3):\n", - " inputs = tf.keras.layers.Input(shape=input_shape)\n", - " \n", - " x = VerticalCroppedConv2d(\n", - " filters=1,\n", - " kernel_size=kernel_size, \n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')(inputs)\n", - "\n", - " stack = tf.keras.Model(inputs=inputs, outputs=x)\n", - " stack.compile(optimizer='adam', loss='mse')\n", - " return stack" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Nskk-zJwN3Em", - "colab_type": "text" - }, - "source": [ - "###Tests with kernel_size 3" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DdcDFcMbxwpZ", - "colab_type": "text" - }, - "source": [ - "#### Vertical stack" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "jc06sHDoNzx8", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 191 - }, - "outputId": "49313976-a256-48c4-b198-0e4bf5a66bc9" - }, - "source": [ - "kernel_size=(2, 3)\n", - "kernel_h, kernel_w = kernel_size\n", - "\n", - "\n", - "\n", - "padding1 = keras.layers.ZeroPadding2D(padding=((1,0),0))\n", - "\n", - "padding2 = keras.layers.ZeroPadding2D(padding=((kernel_h-1, 0),(int((kernel_w-1)/2),int((kernel_w-1)/2))))\n", - "conv = keras.layers.Conv2D(filters=1,\n", - " kernel_size=kernel_size,\n", - " strides=1,\n", - " padding='valid',\n", - " kernel_initializer='ones', \n", - " bias_initializer='zeros')\n", - "\n", - "cropping = keras.layers.Cropping2D(cropping=((0, 1), 0))\n", - "\n", - "\n", - "x = padding1(test_ones_2d)\n", - "x = padding2(x)\n", - "x = conv(x)\n", - "result = cropping(x)\n", - "\n", - "\n", - "print('KERNEL')\n", - "print(conv.weights[0].numpy().squeeze())\n", - "print('')\n", - "print('OUTPUT')\n", - "print(result.numpy().squeeze())\n", - "\n" - ], - "execution_count": 182, - "outputs": [ - { - "output_type": "stream", - "text": [ - "KERNEL\n", - "[[1. 1. 1.]\n", - " [1. 1. 1.]]\n", - "\n", - "OUTPUT\n", - "[[0. 0. 0. 0. 0.]\n", - " [2. 3. 3. 3. 2.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]\n", - " [4. 6. 6. 6. 4.]]\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "5jLEZhYtOZgi", - "colab_type": "code", - "colab": {} - }, - "source": [ - "" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "swYJ4XMofUWv", - "colab_type": "text" - }, - "source": [ - "REFERENCES\n", - "\n", - "https://wiki.math.uwaterloo.ca/statwiki/index.php?title=STAT946F17/Conditional_Image_Generation_with_PixelCNN_Decoders#Gated_PixelCNN\n", - "\n", - "https://www.slideshare.net/suga93/conditional-image-generation-with-pixelcnn-decoders\n", - "\n", - "https://www.youtube.com/watch?v=1BURwCCYNEI" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "jTI9ts7i7Wch", - "colab_type": "code", - "colab": {} - }, - "source": [ - "" - ], - "execution_count": 0, - "outputs": [] - } - ] -} \ No newline at end of file diff --git a/WIP/6-gated_pixelcnn_cropped/Untitled0.ipynb b/WIP/6-gated_pixelcnn_cropped/Untitled0.ipynb deleted file mode 100644 index 991332f..0000000 --- a/WIP/6-gated_pixelcnn_cropped/Untitled0.ipynb +++ /dev/null @@ -1,608 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "Untitled0.ipynb", - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "accelerator": "GPU" - }, - "cells": [ - { - "cell_type": "code", - "metadata": { - "id": "Gec7SKNZsn7O", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "outputId": "0bc907e4-2e8e-446e-c1b6-7d43b1977f10" - }, - "source": [ - "%tensorflow_version 2.x" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "text": [ - "TensorFlow 2.x selected.\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "XbyxhYk_s5Da", - "colab_type": "code", - "colab": {} - }, - "source": [ - "import random as rn\n", - "import time\n", - "\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import tensorflow as tf\n", - "from tensorflow import keras\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "D7LxleiUs-b4", - "colab_type": "code", - "colab": {} - }, - "source": [ - "class GatedBlock(tf.keras.Model):\n", - " \"\"\"\"\"\"\n", - "\n", - " def __init__(self, mask_type, filters, kernel_size):\n", - " super(GatedBlock, self).__init__(name='')\n", - "\n", - " self.mask_type = mask_type\n", - " self.vertical_padding = keras.layers.ZeroPadding2D(padding=((kernel_size//2+1, 0),\n", - " (kernel_size//2, kernel_size//2)))\n", - "\n", - " self.vertical_conv = keras.layers.Conv2D(filters=2 * filters,\n", - " kernel_size=[kernel_size//2+1, kernel_size],\n", - " strides=1,\n", - " padding='valid')\n", - "\n", - " self.vertical_cropping = keras.layers.Cropping2D(cropping=((0, 1), (0, 0)))\n", - "\n", - " self.horizontal_padding = keras.layers.ZeroPadding2D(padding=((0, 0), (kernel_size // 2+1, 0)))\n", - " self.horizontal_conv = keras.layers.Conv2D(filters=2 * filters,\n", - " kernel_size=[1, kernel_size // 2 + 1],\n", - " strides=1,\n", - " padding='valid')\n", - " if mask_type == 'B':\n", - " self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (1, 0)))\n", - " elif mask_type == 'A':\n", - " self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (0, 1)))\n", - "\n", - " self.vertical_to_horizontal_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1)\n", - "\n", - " self.horizontal_output_conv = keras.layers.Conv2D(filters=filters, kernel_size=1)\n", - "\n", - " def _gate(self, x):\n", - " tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1)\n", - " return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation)\n", - "\n", - " def call(self, input_tensor):\n", - " v, h = tf.split(input_tensor, 2, axis=-1)\n", - "\n", - " vertical_preactivation = self.vertical_padding(v)\n", - " vertical_preactivation = self.vertical_conv(vertical_preactivation)\n", - " vertical_preactivation = self.vertical_cropping(vertical_preactivation)\n", - "\n", - " horizontal_preactivation = self.horizontal_padding(h)\n", - " horizontal_preactivation = self.horizontal_conv(horizontal_preactivation)\n", - " horizontal_preactivation = self.horizontal_cropping(horizontal_preactivation)\n", - "\n", - " v_to_h = self.vertical_to_horizontal_conv(vertical_preactivation) # 1x1\n", - " vertical_output = self._gate(vertical_preactivation)\n", - "\n", - " horizontal_preactivation = horizontal_preactivation + v_to_h\n", - " horizontal_activated = self._gate(horizontal_preactivation)\n", - "\n", - " if self.mask_type =='B':\n", - " horizontal_activated = self.horizontal_output_conv(horizontal_activated)\n", - " horizontal_activated = h + horizontal_activated\n", - "\n", - " output = tf.concat((vertical_output, horizontal_activated), axis=-1)\n", - " return output\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "mHODj3mttFeI", - "colab_type": "code", - "colab": {} - }, - "source": [ - "def quantise(images, q_levels):\n", - " \"\"\"Quantise image into q levels\"\"\"\n", - " return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32')\n", - "\n", - "\n", - "def sample_from(distribution):\n", - " \"\"\"Sample random values from distribution\"\"\"\n", - " batch_size, bins = distribution.shape\n", - " return np.array([np.random.choice(bins, p=distr) for distr in distribution])\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "4c-PIdLtsqkH", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Defining random seeds\n", - "random_seed = 42\n", - "tf.random.set_seed(random_seed)\n", - "np.random.seed(random_seed)\n", - "rn.seed(random_seed)\n", - "\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Loading data\n", - "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", - "\n", - "height = 28\n", - "width = 28\n", - "n_channel = 1\n", - "\n", - "x_train = x_train.astype('float32') / 255.\n", - "x_test = x_test.astype('float32') / 255.\n", - "\n", - "x_train = x_train.reshape(x_train.shape[0], height, width, 1)\n", - "x_test = x_test.reshape(x_test.shape[0], height, width, 1)\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Quantise the input data in q levels\n", - "q_levels = 16\n", - "x_train_quantised = quantise(x_train, q_levels)\n", - "x_test_quantised = quantise(x_test, q_levels)\n", - "\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Creating input stream using tf.data API\n", - "batch_size = 128\n", - "train_buf = 60000\n", - "\n", - "train_dataset = tf.data.Dataset.from_tensor_slices((x_train_quantised / (q_levels - 1),\n", - " x_train_quantised.astype('int32')))\n", - "train_dataset = train_dataset.shuffle(buffer_size=train_buf)\n", - "train_dataset = train_dataset.batch(batch_size)\n", - "\n", - "test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1),\n", - " x_test_quantised.astype('int32')))\n", - "test_dataset = test_dataset.batch(batch_size)\n", - "\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Create PixelCNN model\n", - "# https://github.com/RishabGoel/PixelCNN/blob/master/pixel_cnn.py\n", - "# https://github.com/jonathanventura/pixelcnn/blob/master/pixelcnn.py\n", - "\n", - "inputs = keras.layers.Input(shape=(height, width, n_channel))\n", - "x = keras.layers.Concatenate()([inputs, inputs])\n", - "x = GatedBlock(mask_type='A', filters=64, kernel_size=7)(x)\n", - "\n", - "for i in range(5):\n", - " x = GatedBlock(mask_type='B', filters=64, kernel_size=3)(x)\n", - "\n", - "v, h = tf.split(x, 2, axis=-1)\n", - "\n", - "x = keras.layers.Activation(activation='relu')(h)\n", - "x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x)\n", - "\n", - "x = keras.layers.Activation(activation='relu')(x)\n", - "x = keras.layers.Conv2D(filters=n_channel * q_levels, kernel_size=1, strides=1)(x) # shape [N,H,W,DC]\n", - "\n", - "pixelcnn = tf.keras.Model(inputs=inputs, outputs=x)\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Prepare optimizer and loss function\n", - "lr_decay = 0.9995\n", - "learning_rate = 1e-3\n", - "optimizer = tf.keras.optimizers.Adam(lr=learning_rate)\n", - "\n", - "compute_loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True)\n", - "\n", - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "@tf.function\n", - "def train_step(batch_x, batch_y):\n", - " with tf.GradientTape() as ae_tape:\n", - " logits = pixelcnn(batch_x, training=True)\n", - "\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) # shape [N,H,W,DC] -> [N,H,W,D,C]\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) # shape [N,H,W,D,C] -> [N,H,W,C,D]\n", - "\n", - " loss = compute_loss(tf.one_hot(batch_y, q_levels), logits)\n", - "\n", - " gradients = ae_tape.gradient(loss, pixelcnn.trainable_variables)\n", - " gradients, _ = tf.clip_by_global_norm(gradients, 1.0)\n", - " optimizer.apply_gradients(zip(gradients, pixelcnn.trainable_variables))\n", - "\n", - " return loss\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "herds1uKtNEm", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 1000 - }, - "outputId": "69bcc63c-b51c-4015-929f-5f3876f726ad" - }, - "source": [ - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Training loop\n", - "n_epochs = 30\n", - "n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size))\n", - "for epoch in range(n_epochs):\n", - " start_epoch = time.time()\n", - " for i_iter, (batch_x, batch_y) in enumerate(train_dataset):\n", - " start = time.time()\n", - " optimizer.lr = optimizer.lr * lr_decay\n", - " loss = train_step(batch_x, batch_y)\n", - " iter_time = time.time() - start\n", - " if i_iter % 100 == 0:\n", - " print('EPOCH {:3d}: ITER {:4d}/{:4d} TIME: {:.2f} LOSS: {:.4f}'.format(epoch,\n", - " i_iter, n_iter,\n", - " iter_time,\n", - " loss))\n", - " epoch_time = time.time() - start_epoch\n", - " print('EPOCH {:3d}: TIME: {:.2f} ETA: {:.2f}'.format(epoch,\n", - " epoch_time,\n", - " epoch_time * (n_epochs - epoch)))\n" - ], - "execution_count": 9, - "outputs": [ - { - "output_type": "stream", - "text": [ - "EPOCH 0: ITER 0/ 469 TIME: 0.01 LOSS: 0.3240\n", - "EPOCH 0: ITER 100/ 469 TIME: 0.11 LOSS: 0.3262\n", - "EPOCH 0: ITER 200/ 469 TIME: 0.11 LOSS: 0.3175\n", - "EPOCH 0: ITER 300/ 469 TIME: 0.11 LOSS: 0.3184\n", - "EPOCH 0: ITER 400/ 469 TIME: 0.11 LOSS: 0.3124\n", - "EPOCH 0: TIME: 51.02 ETA: 1530.49\n", - "EPOCH 1: ITER 0/ 469 TIME: 0.01 LOSS: 0.3265\n", - "EPOCH 1: ITER 100/ 469 TIME: 0.11 LOSS: 0.3174\n", - "EPOCH 1: ITER 200/ 469 TIME: 0.10 LOSS: 0.3187\n", - "EPOCH 1: ITER 300/ 469 TIME: 0.10 LOSS: 0.3164\n", - "EPOCH 1: ITER 400/ 469 TIME: 0.10 LOSS: 0.3276\n", - "EPOCH 1: TIME: 51.00 ETA: 1479.12\n", - "EPOCH 2: ITER 0/ 469 TIME: 0.01 LOSS: 0.3171\n", - "EPOCH 2: ITER 100/ 469 TIME: 0.11 LOSS: 0.3159\n", - "EPOCH 2: ITER 200/ 469 TIME: 0.11 LOSS: 0.3218\n", - "EPOCH 2: ITER 300/ 469 TIME: 0.11 LOSS: 0.3229\n", - "EPOCH 2: ITER 400/ 469 TIME: 0.11 LOSS: 0.3082\n", - "EPOCH 2: TIME: 51.01 ETA: 1428.30\n", - "EPOCH 3: ITER 0/ 469 TIME: 0.01 LOSS: 0.3214\n", - "EPOCH 3: ITER 100/ 469 TIME: 0.11 LOSS: 0.3177\n", - "EPOCH 3: ITER 200/ 469 TIME: 0.11 LOSS: 0.3200\n", - "EPOCH 3: ITER 300/ 469 TIME: 0.11 LOSS: 0.3229\n", - "EPOCH 3: ITER 400/ 469 TIME: 0.11 LOSS: 0.3300\n", - "EPOCH 3: TIME: 50.95 ETA: 1375.54\n", - "EPOCH 4: ITER 0/ 469 TIME: 0.01 LOSS: 0.3155\n", - "EPOCH 4: ITER 100/ 469 TIME: 0.11 LOSS: 0.3201\n", - "EPOCH 4: ITER 200/ 469 TIME: 0.11 LOSS: 0.3317\n", - "EPOCH 4: ITER 300/ 469 TIME: 0.11 LOSS: 0.3265\n", - "EPOCH 4: ITER 400/ 469 TIME: 0.11 LOSS: 0.3205\n", - "EPOCH 4: TIME: 50.94 ETA: 1324.56\n", - "EPOCH 5: ITER 0/ 469 TIME: 0.01 LOSS: 0.3208\n", - "EPOCH 5: ITER 100/ 469 TIME: 0.10 LOSS: 0.3193\n", - "EPOCH 5: ITER 200/ 469 TIME: 0.11 LOSS: 0.3169\n", - "EPOCH 5: ITER 300/ 469 TIME: 0.10 LOSS: 0.3224\n", - "EPOCH 5: ITER 400/ 469 TIME: 0.10 LOSS: 0.3176\n", - "EPOCH 5: TIME: 50.97 ETA: 1274.21\n", - "EPOCH 6: ITER 0/ 469 TIME: 0.01 LOSS: 0.3195\n", - "EPOCH 6: ITER 100/ 469 TIME: 0.11 LOSS: 0.3146\n", - "EPOCH 6: ITER 200/ 469 TIME: 0.14 LOSS: 0.3219\n", - "EPOCH 6: ITER 300/ 469 TIME: 0.11 LOSS: 0.3237\n", - "EPOCH 6: ITER 400/ 469 TIME: 0.11 LOSS: 0.3242\n", - "EPOCH 6: TIME: 50.94 ETA: 1222.59\n", - "EPOCH 7: ITER 0/ 469 TIME: 0.01 LOSS: 0.3133\n", - "EPOCH 7: ITER 100/ 469 TIME: 0.11 LOSS: 0.3190\n", - "EPOCH 7: ITER 200/ 469 TIME: 0.11 LOSS: 0.3214\n", - "EPOCH 7: ITER 300/ 469 TIME: 0.11 LOSS: 0.3226\n", - "EPOCH 7: ITER 400/ 469 TIME: 0.10 LOSS: 0.3194\n", - "EPOCH 7: TIME: 50.97 ETA: 1172.23\n", - "EPOCH 8: ITER 0/ 469 TIME: 0.01 LOSS: 0.3223\n", - "EPOCH 8: ITER 100/ 469 TIME: 0.11 LOSS: 0.3205\n", - "EPOCH 8: ITER 200/ 469 TIME: 0.10 LOSS: 0.3267\n", - "EPOCH 8: ITER 300/ 469 TIME: 0.10 LOSS: 0.3142\n", - "EPOCH 8: ITER 400/ 469 TIME: 0.10 LOSS: 0.3152\n", - "EPOCH 8: TIME: 50.98 ETA: 1121.64\n", - "EPOCH 9: ITER 0/ 469 TIME: 0.01 LOSS: 0.3278\n", - "EPOCH 9: ITER 100/ 469 TIME: 0.11 LOSS: 0.3178\n", - "EPOCH 9: ITER 200/ 469 TIME: 0.11 LOSS: 0.3293\n", - "EPOCH 9: ITER 300/ 469 TIME: 0.11 LOSS: 0.3246\n", - "EPOCH 9: ITER 400/ 469 TIME: 0.11 LOSS: 0.3303\n", - "EPOCH 9: TIME: 51.00 ETA: 1071.06\n", - "EPOCH 10: ITER 0/ 469 TIME: 0.01 LOSS: 0.3195\n", - "EPOCH 10: ITER 100/ 469 TIME: 0.11 LOSS: 0.3284\n", - "EPOCH 10: ITER 200/ 469 TIME: 0.11 LOSS: 0.3258\n", - "EPOCH 10: ITER 300/ 469 TIME: 0.11 LOSS: 0.3191\n", - "EPOCH 10: ITER 400/ 469 TIME: 0.11 LOSS: 0.3286\n", - "EPOCH 10: TIME: 51.01 ETA: 1020.14\n", - "EPOCH 11: ITER 0/ 469 TIME: 0.01 LOSS: 0.3163\n", - "EPOCH 11: ITER 100/ 469 TIME: 0.11 LOSS: 0.3242\n", - "EPOCH 11: ITER 200/ 469 TIME: 0.11 LOSS: 0.3076\n", - "EPOCH 11: ITER 300/ 469 TIME: 0.11 LOSS: 0.3153\n", - "EPOCH 11: ITER 400/ 469 TIME: 0.11 LOSS: 0.3086\n", - "EPOCH 11: TIME: 51.00 ETA: 968.99\n", - "EPOCH 12: ITER 0/ 469 TIME: 0.01 LOSS: 0.3139\n", - "EPOCH 12: ITER 100/ 469 TIME: 0.11 LOSS: 0.3162\n", - "EPOCH 12: ITER 200/ 469 TIME: 0.11 LOSS: 0.3195\n", - "EPOCH 12: ITER 300/ 469 TIME: 0.10 LOSS: 0.3153\n", - "EPOCH 12: ITER 400/ 469 TIME: 0.11 LOSS: 0.3115\n", - "EPOCH 12: TIME: 51.01 ETA: 918.14\n", - "EPOCH 13: ITER 0/ 469 TIME: 0.01 LOSS: 0.3253\n", - "EPOCH 13: ITER 100/ 469 TIME: 0.11 LOSS: 0.3151\n", - "EPOCH 13: ITER 200/ 469 TIME: 0.10 LOSS: 0.3140\n", - "EPOCH 13: ITER 300/ 469 TIME: 0.11 LOSS: 0.3111\n", - "EPOCH 13: ITER 400/ 469 TIME: 0.10 LOSS: 0.3216\n", - "EPOCH 13: TIME: 51.02 ETA: 867.28\n", - "EPOCH 14: ITER 0/ 469 TIME: 0.01 LOSS: 0.3162\n", - "EPOCH 14: ITER 100/ 469 TIME: 0.11 LOSS: 0.3164\n", - "EPOCH 14: ITER 200/ 469 TIME: 0.10 LOSS: 0.3270\n", - "EPOCH 14: ITER 300/ 469 TIME: 0.11 LOSS: 0.3141\n", - "EPOCH 14: ITER 400/ 469 TIME: 0.11 LOSS: 0.3164\n", - "EPOCH 14: TIME: 51.07 ETA: 817.17\n", - "EPOCH 15: ITER 0/ 469 TIME: 0.01 LOSS: 0.3180\n", - "EPOCH 15: ITER 100/ 469 TIME: 0.11 LOSS: 0.3251\n", - "EPOCH 15: ITER 200/ 469 TIME: 0.11 LOSS: 0.3278\n", - "EPOCH 15: ITER 300/ 469 TIME: 0.11 LOSS: 0.3175\n", - "EPOCH 15: ITER 400/ 469 TIME: 0.11 LOSS: 0.3213\n", - "EPOCH 15: TIME: 51.25 ETA: 768.75\n", - "EPOCH 16: ITER 0/ 469 TIME: 0.01 LOSS: 0.3070\n", - "EPOCH 16: ITER 100/ 469 TIME: 0.11 LOSS: 0.3194\n", - "EPOCH 16: ITER 200/ 469 TIME: 0.11 LOSS: 0.3118\n", - "EPOCH 16: ITER 300/ 469 TIME: 0.11 LOSS: 0.3136\n", - "EPOCH 16: ITER 400/ 469 TIME: 0.11 LOSS: 0.3121\n", - "EPOCH 16: TIME: 51.22 ETA: 717.14\n", - "EPOCH 17: ITER 0/ 469 TIME: 0.01 LOSS: 0.3239\n", - "EPOCH 17: ITER 100/ 469 TIME: 0.11 LOSS: 0.3155\n", - "EPOCH 17: ITER 200/ 469 TIME: 0.11 LOSS: 0.3057\n", - "EPOCH 17: ITER 300/ 469 TIME: 0.10 LOSS: 0.3146\n", - "EPOCH 17: ITER 400/ 469 TIME: 0.11 LOSS: 0.3139\n", - "EPOCH 17: TIME: 51.20 ETA: 665.59\n", - "EPOCH 18: ITER 0/ 469 TIME: 0.01 LOSS: 0.3160\n", - "EPOCH 18: ITER 100/ 469 TIME: 0.11 LOSS: 0.3063\n", - "EPOCH 18: ITER 200/ 469 TIME: 0.11 LOSS: 0.3098\n", - "EPOCH 18: ITER 300/ 469 TIME: 0.11 LOSS: 0.3204\n", - "EPOCH 18: ITER 400/ 469 TIME: 0.11 LOSS: 0.3202\n", - "EPOCH 18: TIME: 50.99 ETA: 611.92\n", - "EPOCH 19: ITER 0/ 469 TIME: 0.01 LOSS: 0.3130\n", - "EPOCH 19: ITER 100/ 469 TIME: 0.11 LOSS: 0.3228\n", - "EPOCH 19: ITER 200/ 469 TIME: 0.11 LOSS: 0.3268\n", - "EPOCH 19: ITER 300/ 469 TIME: 0.10 LOSS: 0.3068\n", - "EPOCH 19: ITER 400/ 469 TIME: 0.11 LOSS: 0.3097\n", - "EPOCH 19: TIME: 50.95 ETA: 560.42\n", - "EPOCH 20: ITER 0/ 469 TIME: 0.01 LOSS: 0.3242\n", - "EPOCH 20: ITER 100/ 469 TIME: 0.10 LOSS: 0.3196\n", - "EPOCH 20: ITER 200/ 469 TIME: 0.10 LOSS: 0.3152\n", - "EPOCH 20: ITER 300/ 469 TIME: 0.11 LOSS: 0.3205\n", - "EPOCH 20: ITER 400/ 469 TIME: 0.10 LOSS: 0.3073\n", - "EPOCH 20: TIME: 50.94 ETA: 509.42\n", - "EPOCH 21: ITER 0/ 469 TIME: 0.01 LOSS: 0.3120\n", - "EPOCH 21: ITER 100/ 469 TIME: 0.10 LOSS: 0.3202\n", - "EPOCH 21: ITER 200/ 469 TIME: 0.11 LOSS: 0.3224\n", - "EPOCH 21: ITER 300/ 469 TIME: 0.11 LOSS: 0.3167\n", - "EPOCH 21: ITER 400/ 469 TIME: 0.10 LOSS: 0.3194\n", - "EPOCH 21: TIME: 50.96 ETA: 458.63\n", - "EPOCH 22: ITER 0/ 469 TIME: 0.01 LOSS: 0.3062\n", - "EPOCH 22: ITER 100/ 469 TIME: 0.10 LOSS: 0.3055\n", - "EPOCH 22: ITER 200/ 469 TIME: 0.11 LOSS: 0.3249\n", - "EPOCH 22: ITER 300/ 469 TIME: 0.11 LOSS: 0.3066\n", - "EPOCH 22: ITER 400/ 469 TIME: 0.10 LOSS: 0.3236\n", - "EPOCH 22: TIME: 51.03 ETA: 408.20\n", - "EPOCH 23: ITER 0/ 469 TIME: 0.01 LOSS: 0.3137\n", - "EPOCH 23: ITER 100/ 469 TIME: 0.11 LOSS: 0.3166\n", - "EPOCH 23: ITER 200/ 469 TIME: 0.11 LOSS: 0.3180\n", - "EPOCH 23: ITER 300/ 469 TIME: 0.11 LOSS: 0.3183\n", - "EPOCH 23: ITER 400/ 469 TIME: 0.11 LOSS: 0.3142\n", - "EPOCH 23: TIME: 51.15 ETA: 358.08\n", - "EPOCH 24: ITER 0/ 469 TIME: 0.01 LOSS: 0.3168\n", - "EPOCH 24: ITER 100/ 469 TIME: 0.11 LOSS: 0.3074\n", - "EPOCH 24: ITER 200/ 469 TIME: 0.11 LOSS: 0.3051\n", - "EPOCH 24: ITER 300/ 469 TIME: 0.10 LOSS: 0.3088\n", - "EPOCH 24: ITER 400/ 469 TIME: 0.11 LOSS: 0.3267\n", - "EPOCH 24: TIME: 51.03 ETA: 306.18\n", - "EPOCH 25: ITER 0/ 469 TIME: 0.01 LOSS: 0.3038\n", - "EPOCH 25: ITER 100/ 469 TIME: 0.10 LOSS: 0.3071\n", - "EPOCH 25: ITER 200/ 469 TIME: 0.11 LOSS: 0.3139\n", - "EPOCH 25: ITER 300/ 469 TIME: 0.11 LOSS: 0.3269\n", - "EPOCH 25: ITER 400/ 469 TIME: 0.11 LOSS: 0.3075\n", - "EPOCH 25: TIME: 51.08 ETA: 255.41\n", - "EPOCH 26: ITER 0/ 469 TIME: 0.01 LOSS: 0.3170\n", - "EPOCH 26: ITER 100/ 469 TIME: 0.11 LOSS: 0.3177\n", - "EPOCH 26: ITER 200/ 469 TIME: 0.11 LOSS: 0.3157\n", - "EPOCH 26: ITER 300/ 469 TIME: 0.11 LOSS: 0.3106\n", - "EPOCH 26: ITER 400/ 469 TIME: 0.11 LOSS: 0.3149\n", - "EPOCH 26: TIME: 51.02 ETA: 204.10\n", - "EPOCH 27: ITER 0/ 469 TIME: 0.01 LOSS: 0.3232\n", - "EPOCH 27: ITER 100/ 469 TIME: 0.11 LOSS: 0.3200\n", - "EPOCH 27: ITER 200/ 469 TIME: 0.11 LOSS: 0.3296\n", - "EPOCH 27: ITER 300/ 469 TIME: 0.11 LOSS: 0.3119\n", - "EPOCH 27: ITER 400/ 469 TIME: 0.10 LOSS: 0.3153\n", - "EPOCH 27: TIME: 51.01 ETA: 153.03\n", - "EPOCH 28: ITER 0/ 469 TIME: 0.01 LOSS: 0.3119\n", - "EPOCH 28: ITER 100/ 469 TIME: 0.10 LOSS: 0.3281\n", - "EPOCH 28: ITER 200/ 469 TIME: 0.11 LOSS: 0.3093\n", - "EPOCH 28: ITER 300/ 469 TIME: 0.11 LOSS: 0.3144\n", - "EPOCH 28: ITER 400/ 469 TIME: 0.10 LOSS: 0.3157\n", - "EPOCH 28: TIME: 50.99 ETA: 101.98\n", - "EPOCH 29: ITER 0/ 469 TIME: 0.01 LOSS: 0.3192\n", - "EPOCH 29: ITER 100/ 469 TIME: 0.10 LOSS: 0.3189\n", - "EPOCH 29: ITER 200/ 469 TIME: 0.11 LOSS: 0.3208\n", - "EPOCH 29: ITER 300/ 469 TIME: 0.10 LOSS: 0.3102\n", - "EPOCH 29: ITER 400/ 469 TIME: 0.11 LOSS: 0.3085\n", - "EPOCH 29: TIME: 51.02 ETA: 51.02\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "VHC4r35_tUHQ", - "colab_type": "code", - "colab": {} - }, - "source": [ - "# --------------------------------------------------------------------------------------------------------------\n", - "# Generating new images\n", - "samples = np.zeros((100, height, width, n_channel), dtype='float32')\n", - "for i in range(28):\n", - " for j in range(28):\n", - " logits = pixelcnn(samples)\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel])\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3])\n", - " probs = tf.nn.softmax(logits)\n", - " next_sample = probs[:, i, j, 0, :]\n", - " samples[:, i, j, 0] = sample_from(next_sample.numpy()) / (q_levels - 1)\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "RfcidCZ8tWNL", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 581 - }, - "outputId": "5f9fca83-0644-402b-a57a-852f55ce4b88" - }, - "source": [ - "\n", - "fig = plt.figure(figsize=(10, 10))\n", - "for i in range(100):\n", - " ax = fig.add_subplot(10, 10, i+1)\n", - " ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary)\n", - " plt.xticks(np.array([]))\n", - " plt.yticks(np.array([]))\n", - "plt.show()\n", - "\n" - ], - "execution_count": 11, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [] - } - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "QDbeF6MAtXOM", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 581 - }, - "outputId": "323bf1b0-aa14-4085-9deb-54bb7e69a0ed" - }, - "source": [ - "\n", - "# --------------------------------------------------------------------------------------------------------------\n", - "# Filling occluded images\n", - "occlude_start_row = 14\n", - "num_generated_images = 100\n", - "samples = np.copy(x_test_quantised[0:num_generated_images, :, :, :])\n", - "samples = samples / (q_levels - 1)\n", - "samples[:, occlude_start_row:, :, :] = 0\n", - "\n", - "for i in range(occlude_start_row, height):\n", - " for j in range(width):\n", - " logits = pixelcnn(samples)\n", - " logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel])\n", - " logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3])\n", - " probs = tf.nn.softmax(logits)\n", - " next_sample = probs[:, i, j, 0, :]\n", - " samples[:, i, j, 0] = sample_from(next_sample.numpy()) / (q_levels - 1)\n", - "\n", - "fig = plt.figure(figsize=(10, 10))\n", - "for i in range(100):\n", - " ax = fig.add_subplot(10, 10, i+1)\n", - " ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary)\n", - " plt.xticks(np.array([]))\n", - " plt.yticks(np.array([]))\n", - "plt.show()\n" - ], - "execution_count": 12, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\nQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "tags": [] - } - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "G6NCXfjF1UT9", - "colab_type": "code", - "colab": {} - }, - "source": [ - "" - ], - "execution_count": 0, - "outputs": [] - } - ] -} \ No newline at end of file diff --git a/WIP/6-gated_pixelcnn_cropped/comparing_2d_3d.py b/WIP/6-gated_pixelcnn_cropped/comparing_2d_3d.py deleted file mode 100644 index e69de29..0000000 diff --git a/WIP/6-gated_pixelcnn_cropped/comparing_masked_conv_cropped.py b/WIP/6-gated_pixelcnn_cropped/comparing_masked_conv_cropped.py deleted file mode 100644 index dedbc30..0000000 --- a/WIP/6-gated_pixelcnn_cropped/comparing_masked_conv_cropped.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Script to compare masked vs cropped conv2d""" -import random as rn -import time - -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import tensorflow as tf -from tensorflow import keras - -random_seed = 42 -tf.random.set_seed(random_seed) -np.random.seed(random_seed) -rn.seed(random_seed) - -# --------------------------------------------------------------------------------- -class MaskedConv2D(tf.keras.layers.Layer): - """Convolutional layers with masks for autoregressive models - - Convolutional layers with simple implementation to have masks type A and B. - """ - - def __init__(self, - mask_type, - filters, - kernel_size, - strides=1, - padding='same', - kernel_initializer='glorot_uniform', - bias_initializer='zeros'): - super(MaskedConv2D, self).__init__() - - assert mask_type in {'A', 'B', 'V'} - self.mask_type = mask_type - - self.filters = filters - - if isinstance(kernel_size, int): - kernel_size = (kernel_size, kernel_size) - self.kernel_size = kernel_size - - self.strides = strides - self.padding = padding.upper() - self.kernel_initializer = keras.initializers.get(kernel_initializer) - self.bias_initializer = keras.initializers.get(bias_initializer) - - def build(self, input_shape): - kernel_h, kernel_w = self.kernel_size - - self.kernel = self.add_weight("kernel", - shape=(kernel_h, - kernel_w, - int(input_shape[-1]), - self.filters), - initializer=self.kernel_initializer, - trainable=True) - - self.bias = self.add_weight("bias", - shape=(self.filters,), - initializer=self.bias_initializer, - trainable=True) - - mask = np.ones(self.kernel.shape, dtype=np.float32) - if self.mask_type == 'V': - mask[kernel_h // 2:, :, :, :] = 0. - else: - mask[kernel_h // 2, kernel_w // 2 + (self.mask_type == 'B'):, :, :] = 0. - mask[kernel_h // 2 + 1:, :, :] = 0. - - self.mask = tf.constant(mask, dtype=tf.float32, name='mask') - - def call(self, input): - masked_kernel = tf.math.multiply(self.mask, self.kernel) - x = tf.nn.conv2d(input, masked_kernel, strides=[1, self.strides, self.strides, 1], padding=self.padding) - x = tf.nn.bias_add(x, self.bias) - return x - -class MaskedGatedBlock(tf.keras.Model): - """""" - def __init__(self, mask_type, filters, kernel_size): - super(MaskedGatedBlock, self).__init__(name='') - - self.mask_type = mask_type - self.vertical_conv = MaskedConv2D(mask_type='V', filters=2 * filters, kernel_size=kernel_size, kernel_initializer='ones') - self.horizontal_conv = MaskedConv2D(mask_type=mask_type, filters=2 * filters, kernel_size=(1, kernel_size), kernel_initializer='ones') - self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1, kernel_initializer='ones') - - self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1, kernel_initializer='ones') - - def _gate(self, x): - tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1) - return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation) - - def call(self, input_tensor): - v = input_tensor[0] - h = input_tensor[1] - - horizontal_preactivation = self.horizontal_conv(h) # 1xN - vertical_preactivation = self.vertical_conv(v) # NxN - v_to_h = self.v_to_h_conv(vertical_preactivation) # 1x1 - v_out = self._gate(vertical_preactivation) - - horizontal_preactivation = horizontal_preactivation + v_to_h - h_activated = self._gate(horizontal_preactivation) - h_activated = self.horizontal_output(h_activated) - - if self.mask_type == 'A': - h_out = h_activated - elif self.mask_type == 'B': - h_out = h + h_activated - - return v_out, h_out - -# --------------------------------------------------------------------------------- -class DownShiftedConv2d(tf.keras.Model): - """""" - def __init__(self, filters, kernel_size): - super(DownShiftedConv2d, self).__init__(name='') - self.vertical_padding = keras.layers.ZeroPadding2D(padding=((kernel_size//2+1, 0), - (kernel_size//2, kernel_size//2))) - - self.vertical_conv = keras.layers.Conv2D(filters=filters, - kernel_size=[kernel_size//2+1, kernel_size], - strides=1, - padding='valid', kernel_initializer='ones') - - self.vertical_cropping = keras.layers.Cropping2D(cropping=((0, 1), (0, 0))) - - - def call(self, input_tensor): - vertical_preactivation = self.vertical_padding(input_tensor) - vertical_preactivation = self.vertical_conv(vertical_preactivation) - output = self.vertical_cropping(vertical_preactivation) - - return output - - -class DownRightShiftedConv2d(tf.keras.Model): - """""" - def __init__(self, mask_type, filters, kernel_size): - super(DownRightShiftedConv2d, self).__init__(name='') - self.mask_type = mask_type - - self.horizontal_padding = keras.layers.ZeroPadding2D(padding=((0, 0), (kernel_size // 2+1, 0))) - self.horizontal_conv = keras.layers.Conv2D(filters=filters, - kernel_size=[1, kernel_size // 2 + 1], - strides=1, - padding='valid', kernel_initializer='ones') - if mask_type == 'B': - self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (1, 0))) - elif mask_type == 'A': - self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (0, 1))) - - - def call(self, input_tensor): - - horizontal_preactivation = self.horizontal_padding(input_tensor) - horizontal_preactivation = self.horizontal_conv(horizontal_preactivation) - output = self.horizontal_cropping(horizontal_preactivation) - - return output - - - -class CroppedGatedBlock(tf.keras.Model): - """""" - def __init__(self, mask_type, filters, kernel_size): - super(CroppedGatedBlock, self).__init__(name='') - - self.mask_type = mask_type - self.vertical_stream = DownShiftedConv2d(2*filters, kernel_size) - self.horizontal_stream = DownRightShiftedConv2d(mask_type, 2*filters, kernel_size) - - self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1, kernel_initializer='ones') - - self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1, kernel_initializer='ones') - - - def _gate(self, x): - tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1) - return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation) - - def call(self, input_tensor): - v = input_tensor[0] - h = input_tensor[1] - - vertical_preactivation = self.vertical_stream(v) - horizontal_preactivation = self.horizontal_stream(h) - v_to_h = self.v_to_h_conv(vertical_preactivation) # 1x1 - v_out = self._gate(vertical_preactivation) - - horizontal_preactivation = horizontal_preactivation + v_to_h - h_activated = self._gate(horizontal_preactivation) - h_activated = self.horizontal_output(h_activated) - - if self.mask_type == 'A': - h_out = h_activated - elif self.mask_type == 'B': - h_out = h + h_activated - - return v_out, h_out - -# --------------------------------------------------------------------------------- -CroppedGatedBlock('B', 1, 3) -MaskedGatedBlock('B', 1, 3) \ No newline at end of file diff --git a/WIP/6-gated_pixelcnn_cropped/comparing_masked_cropped.ipynb b/WIP/6-gated_pixelcnn_cropped/comparing_masked_cropped.ipynb deleted file mode 100644 index f62e43b..0000000 --- a/WIP/6-gated_pixelcnn_cropped/comparing_masked_cropped.ipynb +++ /dev/null @@ -1,430 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "comparing_masked_cropped.ipynb", - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "accelerator": "GPU" - }, - "cells": [ - { - "cell_type": "code", - "metadata": { - "id": "tyRPVstBbYVq", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "outputId": "e8dd8833-0552-4cb9-8727-aa6a8327bf3c" - }, - "source": [ - "%tensorflow_version 2.x" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "text": [ - "TensorFlow 2.x selected.\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "cHHD2OaFbeLX", - "colab_type": "code", - "colab": {} - }, - "source": [ - "\"\"\"Script to compare masked vs cropped conv2d\"\"\"\n", - "import random as rn\n", - "import time\n", - "\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import tensorflow as tf\n", - "from tensorflow import keras\n", - "\n", - "random_seed = 42\n", - "tf.random.set_seed(random_seed)\n", - "np.random.seed(random_seed)\n", - "rn.seed(random_seed)\n", - "\n", - "# ---------------------------------------------------------------------------------\n", - "class MaskedConv2D(tf.keras.layers.Layer):\n", - " \"\"\"Convolutional layers with masks for autoregressive models\n", - "\n", - " Convolutional layers with simple implementation to have masks type A and B.\n", - " \"\"\"\n", - "\n", - " def __init__(self,\n", - " mask_type,\n", - " filters,\n", - " kernel_size,\n", - " strides=1,\n", - " padding='same',\n", - " kernel_initializer='glorot_uniform',\n", - " bias_initializer='zeros'):\n", - " super(MaskedConv2D, self).__init__()\n", - "\n", - " assert mask_type in {'A', 'B', 'V'}\n", - " self.mask_type = mask_type\n", - "\n", - " self.filters = filters\n", - "\n", - " if isinstance(kernel_size, int):\n", - " kernel_size = (kernel_size, kernel_size)\n", - " self.kernel_size = kernel_size\n", - "\n", - " self.strides = strides\n", - " self.padding = padding.upper()\n", - " self.kernel_initializer = keras.initializers.get(kernel_initializer)\n", - " self.bias_initializer = keras.initializers.get(bias_initializer)\n", - "\n", - " def build(self, input_shape):\n", - " kernel_h, kernel_w = self.kernel_size\n", - "\n", - " self.kernel = self.add_weight(\"kernel\",\n", - " shape=(kernel_h,\n", - " kernel_w,\n", - " int(input_shape[-1]),\n", - " self.filters),\n", - " initializer=self.kernel_initializer,\n", - " trainable=True)\n", - "\n", - " self.bias = self.add_weight(\"bias\",\n", - " shape=(self.filters,),\n", - " initializer=self.bias_initializer,\n", - " trainable=True)\n", - "\n", - " mask = np.ones(self.kernel.shape, dtype=np.float32)\n", - " if self.mask_type == 'V':\n", - " mask[kernel_h // 2:, :, :, :] = 0.\n", - " else:\n", - " mask[kernel_h // 2, kernel_w // 2 + (self.mask_type == 'B'):, :, :] = 0.\n", - " mask[kernel_h // 2 + 1:, :, :] = 0.\n", - "\n", - " self.mask = tf.constant(mask, dtype=tf.float32, name='mask')\n", - "\n", - " def call(self, input):\n", - " masked_kernel = tf.math.multiply(self.mask, self.kernel)\n", - " x = tf.nn.conv2d(input, masked_kernel, strides=[1, self.strides, self.strides, 1], padding=self.padding)\n", - " x = tf.nn.bias_add(x, self.bias)\n", - " return x\n", - "\n", - "class MaskedGatedBlock(tf.keras.Model):\n", - " \"\"\"\"\"\"\n", - " def __init__(self, mask_type, filters, kernel_size):\n", - " super(MaskedGatedBlock, self).__init__(name='')\n", - "\n", - " self.mask_type = mask_type\n", - " self.vertical_conv = MaskedConv2D(mask_type='V', filters=2 * filters, kernel_size=kernel_size, kernel_initializer='ones')\n", - " self.horizontal_conv = MaskedConv2D(mask_type=mask_type, filters=2 * filters, kernel_size=(1, kernel_size), kernel_initializer='ones')\n", - " self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1, kernel_initializer='ones')\n", - "\n", - " self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1, kernel_initializer='ones')\n", - "\n", - " def _gate(self, x):\n", - " tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1)\n", - " return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation)\n", - "\n", - " def call(self, input_tensor):\n", - " v = input_tensor[0]\n", - " h = input_tensor[1]\n", - "\n", - " horizontal_preactivation = self.horizontal_conv(h) # 1xN\n", - " vertical_preactivation = self.vertical_conv(v) # NxN\n", - " v_to_h = self.v_to_h_conv(vertical_preactivation) # 1x1\n", - " v_out = self._gate(vertical_preactivation)\n", - "\n", - " horizontal_preactivation = horizontal_preactivation + v_to_h\n", - " h_activated = self._gate(horizontal_preactivation)\n", - " h_activated = self.horizontal_output(h_activated)\n", - "\n", - " if self.mask_type == 'A':\n", - " h_out = h_activated\n", - " elif self.mask_type == 'B':\n", - " h_out = h + h_activated\n", - "\n", - " return v_out, h_out\n", - "\n", - "# ---------------------------------------------------------------------------------\n", - "class DownShiftedConv2d(tf.keras.Model):\n", - " \"\"\"\"\"\"\n", - " def __init__(self, filters, kernel_size):\n", - " super(DownShiftedConv2d, self).__init__(name='')\n", - " self.vertical_padding = keras.layers.ZeroPadding2D(padding=((kernel_size//2+1, 0),\n", - " (kernel_size//2, kernel_size//2)))\n", - "\n", - " self.vertical_conv = keras.layers.Conv2D(filters=filters,\n", - " kernel_size=[kernel_size//2+1, kernel_size],\n", - " strides=1,\n", - " padding='valid', kernel_initializer='ones')\n", - "\n", - " self.vertical_cropping = keras.layers.Cropping2D(cropping=((0, 1), (0, 0)))\n", - "\n", - "\n", - " def call(self, input_tensor):\n", - " vertical_preactivation = self.vertical_padding(input_tensor)\n", - " vertical_preactivation = self.vertical_conv(vertical_preactivation)\n", - " output = self.vertical_cropping(vertical_preactivation)\n", - "\n", - " return output\n", - "\n", - "\n", - "class DownRightShiftedConv2d(tf.keras.Model):\n", - " \"\"\"\"\"\"\n", - " def __init__(self, mask_type, filters, kernel_size):\n", - " super(DownRightShiftedConv2d, self).__init__(name='')\n", - " self.mask_type = mask_type\n", - "\n", - " self.horizontal_padding = keras.layers.ZeroPadding2D(padding=((0, 0), (kernel_size // 2+1, 0)))\n", - " self.horizontal_conv = keras.layers.Conv2D(filters=filters,\n", - " kernel_size=[1, kernel_size // 2 + 1],\n", - " strides=1,\n", - " padding='valid', kernel_initializer='ones')\n", - " if mask_type == 'B':\n", - " self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (1, 0)))\n", - " elif mask_type == 'A':\n", - " self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (0, 1)))\n", - "\n", - "\n", - " def call(self, input_tensor):\n", - "\n", - " horizontal_preactivation = self.horizontal_padding(input_tensor)\n", - " horizontal_preactivation = self.horizontal_conv(horizontal_preactivation)\n", - " output = self.horizontal_cropping(horizontal_preactivation)\n", - "\n", - " return output\n", - "\n", - "\n", - "\n", - "class CroppedGatedBlock(tf.keras.Model):\n", - " \"\"\"\"\"\"\n", - " def __init__(self, mask_type, filters, kernel_size):\n", - " super(CroppedGatedBlock, self).__init__(name='')\n", - "\n", - " self.mask_type = mask_type\n", - " self.vertical_stream = DownShiftedConv2d(2*filters, kernel_size)\n", - " self.horizontal_stream = DownRightShiftedConv2d(mask_type, 2*filters, kernel_size)\n", - "\n", - " self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1, kernel_initializer='ones')\n", - "\n", - " self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1, kernel_initializer='ones')\n", - "\n", - "\n", - " def _gate(self, x):\n", - " tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1)\n", - " return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation)\n", - "\n", - " def call(self, input_tensor):\n", - " v = input_tensor[0]\n", - " h = input_tensor[1]\n", - "\n", - " vertical_preactivation = self.vertical_stream(v)\n", - " horizontal_preactivation = self.horizontal_stream(h)\n", - " v_to_h = self.v_to_h_conv(vertical_preactivation) # 1x1\n", - " v_out = self._gate(vertical_preactivation)\n", - "\n", - " horizontal_preactivation = horizontal_preactivation + v_to_h\n", - " h_activated = self._gate(horizontal_preactivation)\n", - " h_activated = self.horizontal_output(h_activated)\n", - "\n", - " if self.mask_type == 'A':\n", - " h_out = h_activated\n", - " elif self.mask_type == 'B':\n", - " h_out = h + h_activated\n", - "\n", - " return v_out, h_out\n", - " " - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "PrsQQCZSbnep", - "colab_type": "code", - "colab": {} - }, - "source": [ - "A1 = CroppedGatedBlock('B', 1, 3)\n", - "A2 = MaskedGatedBlock('B', 1, 3)" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "mvPKMjXCbumD", - "colab_type": "code", - "colab": {} - }, - "source": [ - "B = np.ones((1,3,3,1), dtype='float32')" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "6pEZ8RLI2ky2", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 434 - }, - "outputId": "ec5784d7-26a9-4dfb-c046-da7738815e67" - }, - "source": [ - "A2([B,B])\n" - ], - "execution_count": 31, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "(,\n", - " )" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 31 - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "4gVusCXvyMKO", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 434 - }, - "outputId": "747956a2-99c4-4c73-bf74-f55bc6be6cc8" - }, - "source": [ - "A1([B,B])" - ], - "execution_count": 32, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "(,\n", - " )" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 32 - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "4H6chDKcBESu", - "colab_type": "code", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 225 - }, - "outputId": "ad74038c-8940-4cfe-e6de-fae4c849b39e" - }, - "source": [ - "A1.horizontal_stream(B)" - ], - "execution_count": 33, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 33 - } - ] - } - ] -} \ No newline at end of file diff --git a/WIP/6-gated_pixelcnn_cropped/gated_pixelCNN.py b/WIP/6-gated_pixelcnn_cropped/gated_pixelCNN.py new file mode 100644 index 0000000..56e3e3d --- /dev/null +++ b/WIP/6-gated_pixelcnn_cropped/gated_pixelCNN.py @@ -0,0 +1,275 @@ +import tensorflow as tf + +gpu_devices = tf.config.experimental.list_physical_devices('GPU') +for device in gpu_devices: tf.config.experimental.set_memory_growth(device, True) + +import random as rn + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import tensorflow as tf +from tensorflow.keras.utils import Progbar +from tensorflow import keras + + +# Defining random seeds +random_seed = 42 +tf.random.set_seed(random_seed) +np.random.seed(random_seed) +rn.seed(random_seed) + +# Loading data +(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() + +height = 28 +width = 28 +n_channel = 1 + +x_train = x_train.astype('float32') / 255. +x_test = x_test.astype('float32') / 255. + +x_train = x_train.reshape(x_train.shape[0], height, width, n_channel) +x_test = x_test.reshape(x_test.shape[0], height, width, n_channel) + +def quantise(images, q_levels): + """Quantise image into q levels""" + return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32') + +# Quantise the input data in q levels +q_levels = 2 +x_train_quantised = quantise(x_train, q_levels) +x_test_quantised = quantise(x_test, q_levels) + +# Creating input stream using tf.data API +batch_size = 192 +train_buf = 10000 + +train_dataset = tf.data.Dataset.from_tensor_slices((x_train_quantised / (q_levels - 1), + x_train_quantised.astype('int32'))) +train_dataset = train_dataset.shuffle(buffer_size=train_buf) +train_dataset = train_dataset.batch(batch_size) + +test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1), + x_test_quantised.astype('int32'))) +test_dataset = test_dataset.batch(batch_size) + + +class VerticalConv2D(keras.layers.Conv2D): + """https://github.com/JesseFarebro/PixelCNNPP/blob/master/layers/VerticalConv2D.py""" + + def __init__(self, + filters, + kernel_size, + **kwargs): + if not isinstance(kernel_size, tuple): + kernel_size = (kernel_size // 2 + 1, kernel_size) + + super(VerticalConv2D, self).__init__(filters, kernel_size, **kwargs) + + self.pad = tf.keras.layers.ZeroPadding2D( + ( + (kernel_size[0] - 1, 0), # Top, Bottom + (kernel_size[1] // 2, kernel_size[1] // 2), # Left, Right + ) + ) + + def call(self, inputs): + inputs = self.pad(inputs) + output = super(VerticalConv2D, self).call(inputs) + + return output + + +class HorizontalConv2D(keras.layers.Conv2D): + def __init__(self, + filters, + kernel_size, + **kwargs): + if not isinstance(kernel_size, tuple): + kernel_size = (kernel_size // 2 + 1,) * 2 + + super(HorizontalConv2D, self).__init__(filters, kernel_size, **kwargs) + self.pad = tf.keras.layers.ZeroPadding2D( + ( + (kernel_size[0] - 1, 0), # (Top, Bottom) + (kernel_size[1] - 1, 0), # (Left, Right) + ) + ) + + def call(self, inputs): + inputs = self.pad(inputs) + outputs = super(HorizontalConv2D, self).call(inputs) + + return outputs + +class GatedBlock(tf.keras.Model): + """ Gated block of the Gated PixelCNN.""" + + def __init__(self, + mask_type, + filters, + kernel_size): + super(GatedBlock, self).__init__(name='') + + self.mask_type = mask_type + self.vertical_conv = VerticalConv2D(filters=2 * filters, + kernel_size=kernel_size) + + if mask_type == 'A': + self.horizontal_conv = keras.layers.Conv2D(filters=2 * filters, + kernel_size=1) + + else: + self.horizontal_conv = HorizontalConv2D(filters=2 * filters, + kernel_size=kernel_size) + + self.padding_A = keras.layers.ZeroPadding2D(padding=(0, (1, 0))) + self.cropping_A = keras.layers.Cropping2D(cropping=(0, (0, 1))) + + self.padding = keras.layers.ZeroPadding2D(padding=((1, 0), 0)) + self.cropping = keras.layers.Cropping2D(cropping=((0, 1), 0)) + + self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1) + + self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1) + + def _gate(self, x): + tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1) + return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation) + + def call(self, input_tensor): + v = input_tensor[0] + h = input_tensor[1] + + vertical_preactivation = self.vertical_conv(v) # NxN + + # Shifting feature map down to ensure causality + v_to_h = self.padding(vertical_preactivation) + v_to_h = self.cropping(v_to_h) + v_to_h = self.v_to_h_conv(v_to_h) # 1x1 + + horizontal_preactivation = self.horizontal_conv(h) # 1xN + if self.mask_type == 'A': + horizontal_preactivation = self.padding_A(horizontal_preactivation) + horizontal_preactivation = self.cropping_A(horizontal_preactivation) + + v_out = self._gate(vertical_preactivation) + + horizontal_preactivation = horizontal_preactivation + v_to_h + h_activated = self._gate(horizontal_preactivation) + h_activated = self.horizontal_output(h_activated) + + if self.mask_type == 'A': + h_out = h_activated + elif self.mask_type == 'B': + h_out = h + h_activated + + return v_out, h_out + + +# Create Gated PixelCNN model +inputs = keras.layers.Input(shape=(height, width, n_channel)) +v, h = GatedBlock(mask_type='A', filters=64, kernel_size=3)([inputs, inputs]) + +for i in range(7): + v, h = GatedBlock(mask_type='B', filters=64, kernel_size=3)([v, h]) + +x = keras.layers.Activation(activation='relu')(h) +x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x) + +x = keras.layers.Activation(activation='relu')(x) +x = keras.layers.Conv2D(filters=q_levels, kernel_size=1, strides=1)(x) + +gated_pixelcnn = tf.keras.Model(inputs=inputs, outputs=x) + +# Prepare optimizer and loss function +lr_decay = 0.999995 +learning_rate = 1e-3 +optimizer = keras.optimizers.Adam(lr=learning_rate) + +compute_loss = keras.losses.CategoricalCrossentropy(from_logits=True) + +@tf.function +def train_step(batch_x, batch_y): + with tf.GradientTape() as ae_tape: + logits = gated_pixelcnn(batch_x, training=True) + + loss = compute_loss(tf.squeeze(tf.one_hot(batch_y, q_levels)), logits) + + gradients = ae_tape.gradient(loss, gated_pixelcnn.trainable_variables) + gradients, _ = tf.clip_by_global_norm(gradients, 1.0) + optimizer.apply_gradients(zip(gradients, gated_pixelcnn.trainable_variables)) + + return loss + +# Training loop +n_epochs = 50 +n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size)) +for epoch in range(n_epochs): + progbar = Progbar(n_iter) + print('Epoch {:}/{:}'.format(epoch + 1, n_epochs)) + + for i_iter, (batch_x, batch_y) in enumerate(train_dataset): + optimizer.lr = optimizer.lr * lr_decay + loss = train_step(batch_x, batch_y) + + progbar.add(1, values=[('loss', loss)]) + +# Test set performance +test_loss = [] +for batch_x, batch_y in test_dataset: + logits = gated_pixelcnn(batch_x, training=False) + + # Calculate cross-entropy (= negative log-likelihood) + loss = compute_loss(tf.squeeze(tf.one_hot(batch_y, q_levels)), logits) + + test_loss.append(loss) +print('nll : {:} nats'.format(np.array(test_loss).mean())) +print('bits/dim : {:}'.format(np.array(test_loss).mean() / np.log(2))) + +# Generating new images +samples = np.zeros((100, height, width, n_channel), dtype='float32') +for i in range(height): + for j in range(width): + logits = gated_pixelcnn(samples) + next_sample = tf.random.categorical(logits[:, i, j, :], 1) + samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0] + +fig = plt.figure(figsize=(10, 10)) +for i in range(100): + ax = fig.add_subplot(10, 10, i + 1) + ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) + plt.xticks(np.array([])) + plt.yticks(np.array([])) +plt.show() + +# Filling occluded images +occlude_start_row = 14 +num_generated_images = 10 +samples = np.copy(x_test_quantised[0:num_generated_images, :, :, :]) +samples = samples / (q_levels - 1) +samples[:, occlude_start_row:, :, :] = 0 + +fig = plt.figure(figsize=(10, 10)) + +for i in range(10): + ax = fig.add_subplot(1, 10, i + 1) + ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) + plt.xticks(np.array([])) + plt.yticks(np.array([])) + +for i in range(occlude_start_row, height): + for j in range(width): + logits = gated_pixelcnn(samples) + next_sample = tf.random.categorical(logits[:, i, j, :], 1) + samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0] + +fig = plt.figure(figsize=(10, 10)) + +for i in range(10): + ax = fig.add_subplot(1, 10, i + 1) + ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) + plt.xticks(np.array([])) + plt.yticks(np.array([])) +plt.show() diff --git a/WIP/pixelCNN.py b/WIP/pixelCNN.py deleted file mode 100644 index e69de29..0000000 diff --git a/WIP/train_gatedpixelcnn2.py b/WIP/train_gatedpixelcnn2.py deleted file mode 100644 index fc4b242..0000000 --- a/WIP/train_gatedpixelcnn2.py +++ /dev/null @@ -1,284 +0,0 @@ -""" -""" -import random as rn -import time - -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import tensorflow as tf -from tensorflow import keras - - -class MaskedConv2D(tf.keras.layers.Layer): - """Convolutional layers with masks for autoregressive models - - Convolutional layers with simple implementation to have masks type A and B. - """ - - def __init__(self, - mask_type, - filters, - kernel_size, - strides=1, - padding='same', - kernel_initializer='glorot_uniform', - bias_initializer='zeros'): - super(MaskedConv2D, self).__init__() - - assert mask_type in {'A', 'B', 'V'} - self.mask_type = mask_type - - self.filters = filters - - if isinstance(kernel_size, int): - kernel_size = (kernel_size, kernel_size) - self.kernel_size = kernel_size - - self.strides = strides - self.padding = padding.upper() - self.kernel_initializer = keras.initializers.get(kernel_initializer) - self.bias_initializer = keras.initializers.get(bias_initializer) - - def build(self, input_shape): - kernel_h, kernel_w = self.kernel_size - - self.kernel = self.add_weight("kernel", - shape=(kernel_h, - kernel_w, - int(input_shape[-1]), - self.filters), - initializer=self.kernel_initializer, - trainable=True) - - self.bias = self.add_weight("bias", - shape=(self.filters,), - initializer=self.bias_initializer, - trainable=True) - - mask = np.ones(self.kernel.shape, dtype=np.float32) - if self.mask_type == 'V': - mask[kernel_h // 2:, :, :, :] = 0. - else: - mask[kernel_h // 2, kernel_w // 2 + (self.mask_type == 'B'):, :, :] = 0. - mask[kernel_h // 2 + 1:, :, :] = 0. - - self.mask = tf.constant(mask, dtype=tf.float32, name='mask') - - def call(self, input): - masked_kernel = tf.math.multiply(self.mask, self.kernel) - x = tf.nn.conv2d(input, masked_kernel, strides=[1, self.strides, self.strides, 1], padding=self.padding) - x = tf.nn.bias_add(x, self.bias) - return x - - -class GatedBlock(tf.keras.Model): - """""" - - def __init__(self, mask_type, filters, kernel_size): - super(GatedBlock, self).__init__(name='') - - self.mask_type = mask_type - self.vertical_conv = MaskedConv2D(mask_type='V', filters=2 * filters, kernel_size=kernel_size) - self.horizontal_conv = MaskedConv2D(mask_type=mask_type, filters=2 * filters, kernel_size=(1, kernel_size)) - self.v_to_h_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1) - - self.horizontal_output = keras.layers.Conv2D(filters=filters, kernel_size=1) - - def _gate(self, x): - tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1) - return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation) - - def call(self, input_tensor): - v = input_tensor[0] - h = input_tensor[1] - - horizontal_preactivation = self.horizontal_conv(h) # 1xN - vertical_preactivation = self.vertical_conv(v) # NxN - v_to_h = self.v_to_h_conv(vertical_preactivation) # 1x1 - v_out = self._gate(vertical_preactivation) - - horizontal_preactivation = horizontal_preactivation + v_to_h - h_activated = self._gate(horizontal_preactivation) - h_activated = self.horizontal_output(h_activated) - - if self.mask_type == 'A': - h_out = h_activated - elif self.mask_type == 'B': - h_out = h + h_activated - - return v_out, h_out - - - -def quantise(images, q_levels): - """Quantise image into q levels""" - return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32') - - -# def main(): -# -------------------------------------------------------------------------------------------------------------- -# Defining random seeds -random_seed = 42 -tf.random.set_seed(random_seed) -np.random.seed(random_seed) -rn.seed(random_seed) - -# -------------------------------------------------------------------------------------------------------------- -# Loading data -(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() - -height = 28 -width = 28 -n_channel = 1 - -x_train = x_train.astype('float32') / 255. -x_test = x_test.astype('float32') / 255. - -x_train = x_train.reshape(x_train.shape[0], height, width, n_channel) -x_test = x_test.reshape(x_test.shape[0], height, width, n_channel) - -# -------------------------------------------------------------------------------------------------------------- -# Quantise the input data in q levels -q_levels = 256 -x_train_quantised = quantise(x_train, q_levels) -x_test_quantised = quantise(x_test, q_levels) - -# -------------------------------------------------------------------------------------------------------------- -# Creating input stream using tf.data API -batch_size = 128 -train_buf = 60000 - -train_dataset = tf.data.Dataset.from_tensor_slices((x_train_quantised / (q_levels - 1), - x_train_quantised.astype('int32'))) -train_dataset = train_dataset.shuffle(buffer_size=train_buf) -train_dataset = train_dataset.batch(batch_size) - -test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1), - x_test_quantised.astype('int32'))) -test_dataset = test_dataset.batch(batch_size) - -# -------------------------------------------------------------------------------------------------------------- -# Create PixelCNN model -# https://github.com/RishabGoel/PixelCNN/blob/master/pixel_cnn.py -# https://github.com/jonathanventura/pixelcnn/blob/master/pixelcnn.py - -inputs = keras.layers.Input(shape=(height, width, n_channel)) -v, h = GatedBlock(mask_type='A', filters=64, kernel_size=3)([inputs, inputs]) - -for i in range(7): - v, h = GatedBlock(mask_type='B', filters=64, kernel_size=3)([v, h]) - -x = keras.layers.Activation(activation='relu')(h) -x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x) - -x = keras.layers.Activation(activation='relu')(x) -x = keras.layers.Conv2D(filters=n_channel * q_levels, kernel_size=1, strides=1)(x) - -pixelcnn = tf.keras.Model(inputs=inputs, outputs=x) - -# -------------------------------------------------------------------------------------------------------------- -# Prepare optimizer and loss function -lr_decay = 0.9995 -learning_rate = 1e-3 -optimizer = tf.keras.optimizers.Adam(lr=learning_rate) - -compute_loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True) - - -# -------------------------------------------------------------------------------------------------------------- -@tf.function -def train_step(batch_x, batch_y): - with tf.GradientTape() as ae_tape: - logits = pixelcnn(batch_x, training=True) - - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - - loss = compute_loss(tf.one_hot(batch_y, q_levels), logits) - - gradients = ae_tape.gradient(loss, pixelcnn.trainable_variables) - gradients, _ = tf.clip_by_global_norm(gradients, 1.0) - optimizer.apply_gradients(zip(gradients, pixelcnn.trainable_variables)) - - return loss - - -# -------------------------------------------------------------------------------------------------------------- -# Training loop -n_epochs = 30 -n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size)) -for epoch in range(n_epochs): - start_epoch = time.time() - for i_iter, (batch_x, batch_y) in enumerate(train_dataset): - start = time.time() - optimizer.lr = optimizer.lr * lr_decay - loss = train_step(batch_x, batch_y) - iter_time = time.time() - start - if i_iter % 100 == 0: - print('EPOCH {:3d}: ITER {:4d}/{:4d} TIME: {:.2f} LOSS: {:.4f}'.format(epoch, - i_iter, n_iter, - iter_time, - loss)) - epoch_time = time.time() - start_epoch - print('EPOCH {:3d}: TIME: {:.2f} ETA: {:.2f}'.format(epoch, - epoch_time, - epoch_time * (n_epochs - epoch))) - -# -------------------------------------------------------------------------------------------------------------- -# Test -test_loss = [] -for batch_x, batch_y in test_dataset: - logits = pixelcnn(batch_x, training=False) - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - - # Calculate cross-entropy (= negative log-likelihood) - loss = compute_loss(tf.one_hot(batch_y, q_levels), logits) - - test_loss.append(loss) -print('nll : {:} nats'.format(np.array(test_loss).mean())) -print('bits/dim : {:}'.format(np.array(test_loss).mean() / (height * width))) - -# -------------------------------------------------------------------------------------------------------------- -# Generating new images -samples = np.zeros((100, height, width, n_channel), dtype='float32') -for i in range(height): - for j in range(width): - logits = pixelcnn(samples) - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - next_sample = tf.random.categorical(logits[:, i, j, 0, :], 1) - samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0] - -fig = plt.figure(figsize=(10, 10)) -for i in range(100): - ax = fig.add_subplot(10, 10, i + 1) - ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) - plt.xticks(np.array([])) - plt.yticks(np.array([])) -plt.show() - -# -------------------------------------------------------------------------------------------------------------- -# Filling occluded images -occlude_start_row = 14 -num_generated_images = 100 -samples = np.copy(x_test_quantised[0:num_generated_images, :, :, :]) -samples = samples / (q_levels - 1) -samples[:, occlude_start_row:, :, :] = 0 - -for i in range(occlude_start_row, height): - for j in range(width): - logits = pixelcnn(samples) - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - next_sample = tf.random.categorical(logits[:, i, j, 0, :], 1) - samples[:, i, j, 0] = (next_sample.numpy() / (q_levels - 1))[:, 0] - -fig = plt.figure(figsize=(10, 10)) -for i in range(100): - ax = fig.add_subplot(10, 10, i + 1) - ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) - plt.xticks(np.array([])) - plt.yticks(np.array([])) -plt.show() \ No newline at end of file diff --git a/WIP/train_gatedpixelcnn2_cropped.py b/WIP/train_gatedpixelcnn2_cropped.py deleted file mode 100644 index a0b60d2..0000000 --- a/WIP/train_gatedpixelcnn2_cropped.py +++ /dev/null @@ -1,523 +0,0 @@ -""" -# https://github.com/suga93/pixelcnn_keras/blob/master/core/layers.py -""" -import random as rn -import time - -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import tensorflow as tf -from tensorflow import keras - - -class GatedBlock(tf.keras.Model): - """""" - - def __init__(self, mask_type, filters, kernel_size): - super(GatedBlock, self).__init__(name='') - - self.mask_type = mask_type - self.vertical_padding = keras.layers.ZeroPadding2D(padding=((kernel_size//2+1, 0), - (kernel_size//2, kernel_size//2))) - - self.vertical_conv = keras.layers.Conv2D(filters=2 * filters, - kernel_size=[kernel_size//2+1, kernel_size], - strides=1, - padding='valid') - - self.vertical_cropping = keras.layers.Cropping2D(cropping=((0, 1), (0, 0))) - - self.horizontal_padding = keras.layers.ZeroPadding2D(padding=((0, 0), (kernel_size // 2+1, 0))) - self.horizontal_conv = keras.layers.Conv2D(filters=2 * filters, - kernel_size=[1, kernel_size // 2 + 1], - strides=1, - padding='valid') - if mask_type == 'B': - self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (1, 0))) - elif mask_type == 'A': - self.horizontal_cropping = keras.layers.Cropping2D(cropping=((0, 0), (0, 1))) - - self.vertical_to_horizontal_conv = keras.layers.Conv2D(filters=2 * filters, kernel_size=1) - - self.horizontal_output_conv = keras.layers.Conv2D(filters=filters, kernel_size=1) - - def _gate(self, x): - tanh_preactivation, sigmoid_preactivation = tf.split(x, 2, axis=-1) - return tf.nn.tanh(tanh_preactivation) * tf.nn.sigmoid(sigmoid_preactivation) - - def call(self, input_tensor): - v, h = tf.split(input_tensor, 2, axis=-1) - - vertical_preactivation = self.vertical_padding(v) - vertical_preactivation = self.vertical_conv(vertical_preactivation) - vertical_preactivation = self.vertical_cropping(vertical_preactivation) - - horizontal_preactivation = self.horizontal_padding(h) - horizontal_preactivation = self.horizontal_conv(horizontal_preactivation) - horizontal_preactivation = self.horizontal_cropping(horizontal_preactivation) - - v_to_h = self.vertical_to_horizontal_conv(vertical_preactivation) # 1x1 - vertical_output = self._gate(vertical_preactivation) - - horizontal_preactivation = horizontal_preactivation + v_to_h - horizontal_activated = self._gate(horizontal_preactivation) - - if self.mask_type =='B': - horizontal_activated = self.horizontal_output_conv(horizontal_activated) - horizontal_activated = h + horizontal_activated - - output = tf.concat((vertical_output, horizontal_activated), axis=-1) - return output - -def quantise(images, q_levels): - """Quantise image into q levels""" - return (np.digitize(images, np.arange(q_levels) / q_levels) - 1).astype('float32') - - -def sample_from(distribution): - """Sample random values from distribution""" - batch_size, bins = distribution.shape - return np.array([np.random.choice(bins, p=distr) for distr in distribution]) - - -# def main(): -# -------------------------------------------------------------------------------------------------------------- -# Defining random seeds -random_seed = 42 -tf.random.set_seed(random_seed) -np.random.seed(random_seed) -rn.seed(random_seed) - - -# -------------------------------------------------------------------------------------------------------------- -# Loading data -(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() - -height = 28 -width = 28 -n_channel = 1 - -x_train = x_train.astype('float32') / 255. -x_test = x_test.astype('float32') / 255. - -x_train = x_train.reshape(x_train.shape[0], height, width, 1) -x_test = x_test.reshape(x_test.shape[0], height, width, 1) - -# -------------------------------------------------------------------------------------------------------------- -# Quantise the input data in q levels -q_levels = 16 -x_train_quantised = quantise(x_train, q_levels) -x_test_quantised = quantise(x_test, q_levels) - - -# -------------------------------------------------------------------------------------------------------------- -# Creating input stream using tf.data API -batch_size = 128 -train_buf = 60000 - -train_dataset = tf.data.Dataset.from_tensor_slices((x_train_quantised / (q_levels - 1), - x_train_quantised.astype('int32'))) -train_dataset = train_dataset.shuffle(buffer_size=train_buf) -train_dataset = train_dataset.batch(batch_size) - -test_dataset = tf.data.Dataset.from_tensor_slices((x_test_quantised / (q_levels - 1), - x_test_quantised.astype('int32'))) -test_dataset = test_dataset.batch(batch_size) - - -# -------------------------------------------------------------------------------------------------------------- -# Create PixelCNN model -# https://github.com/RishabGoel/PixelCNN/blob/master/pixel_cnn.py -# https://github.com/jonathanventura/pixelcnn/blob/master/pixelcnn.py - -inputs = keras.layers.Input(shape=(height, width, n_channel)) -x = keras.layers.Concatenate()([inputs, inputs]) -x = GatedBlock(mask_type='A', filters=64, kernel_size=7)(x) - -for i in range(5): - x = GatedBlock(mask_type='B', filters=64, kernel_size=3)(x) - -v, h = tf.split(x, 2, axis=-1) - -x = keras.layers.Activation(activation='relu')(h) -x = keras.layers.Conv2D(filters=128, kernel_size=1, strides=1)(x) - -x = keras.layers.Activation(activation='relu')(x) -x = keras.layers.Conv2D(filters=n_channel * q_levels, kernel_size=1, strides=1)(x) # shape [N,H,W,DC] - -pixelcnn = tf.keras.Model(inputs=inputs, outputs=x) - -# -------------------------------------------------------------------------------------------------------------- -# Prepare optimizer and loss function -lr_decay = 0.9995 -learning_rate = 1e-3 -optimizer = tf.keras.optimizers.Adam(lr=learning_rate) - -compute_loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True) - - -# -------------------------------------------------------------------------------------------------------------- -@tf.function -def train_step(batch_x, batch_y): - with tf.GradientTape() as ae_tape: - logits = pixelcnn(batch_x, training=True) - - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) # shape [N,H,W,DC] -> [N,H,W,D,C] - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) # shape [N,H,W,D,C] -> [N,H,W,C,D] - - loss = compute_loss(tf.one_hot(batch_y, q_levels), logits) - - gradients = ae_tape.gradient(loss, pixelcnn.trainable_variables) - gradients, _ = tf.clip_by_global_norm(gradients, 1.0) - optimizer.apply_gradients(zip(gradients, pixelcnn.trainable_variables)) - - return loss - -# -------------------------------------------------------------------------------------------------------------- -# Training loop -n_epochs = 30 -n_iter = int(np.ceil(x_train_quantised.shape[0] / batch_size)) -for epoch in range(n_epochs): - start_epoch = time.time() - for i_iter, (batch_x, batch_y) in enumerate(train_dataset): - start = time.time() - optimizer.lr = optimizer.lr * lr_decay - loss = train_step(batch_x, batch_y) - iter_time = time.time() - start - if i_iter % 100 == 0: - print('EPOCH {:3d}: ITER {:4d}/{:4d} TIME: {:.2f} LOSS: {:.4f}'.format(epoch, - i_iter, n_iter, - iter_time, - loss)) - epoch_time = time.time() - start_epoch - print('EPOCH {:3d}: TIME: {:.2f} ETA: {:.2f}'.format(epoch, - epoch_time, - epoch_time * (n_epochs - epoch))) - -# -------------------------------------------------------------------------------------------------------------- -# Test -test_loss = [] -for batch_x, batch_y in test_dataset: - logits = pixelcnn(batch_x, training=False) - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - - # Calculate cross-entropy (= negative log-likelihood) - loss = compute_loss(tf.one_hot(batch_y, q_levels), logits) - - test_loss.append(loss) -print('nll : {:} nats'.format(np.array(test_loss).mean())) -print('bits/dim : {:}'.format(np.array(test_loss).mean() / (height * width))) - - -# -------------------------------------------------------------------------------------------------------------- -# Generating new images -samples = np.zeros((100, height, width, n_channel), dtype='float32') -for i in range(28): - for j in range(28): - logits = pixelcnn(samples) - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - probs = tf.nn.softmax(logits) - next_sample = probs[:, i, j, 0, :] - samples[:, i, j, 0] = sample_from(next_sample.numpy()) / (q_levels - 1) - -fig = plt.figure(figsize=(10, 10)) -for i in range(100): - ax = fig.add_subplot(10, 10, i+1) - ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) - plt.xticks(np.array([])) - plt.yticks(np.array([])) -plt.show() - - -# -------------------------------------------------------------------------------------------------------------- -# Filling occluded images -occlude_start_row = 14 -num_generated_images = 100 -samples = np.copy(x_test_quantised[0:num_generated_images, :, :, :]) -samples = samples / (q_levels - 1) -samples[:, occlude_start_row:, :, :] = 0 - -for i in range(occlude_start_row, height): - for j in range(width): - logits = pixelcnn(samples) - logits = tf.reshape(logits, [-1, height, width, q_levels, n_channel]) - logits = tf.transpose(logits, perm=[0, 1, 2, 4, 3]) - probs = tf.nn.softmax(logits) - next_sample = probs[:, i, j, 0, :] - samples[:, i, j, 0] = sample_from(next_sample.numpy()) / (q_levels - 1) - -fig = plt.figure(figsize=(10, 10)) -for i in range(100): - ax = fig.add_subplot(10, 10, i+1) - ax.matshow(samples[i, :, :, 0], cmap=matplotlib.cm.binary) - plt.xticks(np.array([])) - plt.yticks(np.array([])) -plt.show() - - -# --------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------- - -class CroppedConvolution(L.Convolution2D): -def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - -def __call__(self, x): - ret = super().__call__(x) - kh, kw = self.ksize - pad_h, pad_w = self.pad - h_crop = -(kh + 1) if pad_h == kh else None - w_crop = -(kw + 1) if pad_w == kw else None - - return ret[:, :, :h_crop, :w_crop] - - -def down_shift(x): -xs = int_shape(x) -return tf.concat([tf.zeros([xs[0], 1, xs[2], xs[3]]), x[:, :xs[1] - 1, :, :]], 1) - - -def right_shift(x): -xs = int_shape(x) -return tf.concat([tf.zeros([xs[0], xs[1], 1, xs[3]]), x[:, :, :xs[2] - 1, :]], 2) - - - -@add_arg_scope -def down_shifted_conv2d(x, num_filters, filter_size=[2, 3], stride=[1, 1], **kwargs): -x = tf.pad(x, [[0, 0], [filter_size[0] - 1, 0], [int((filter_size[1] - 1) / 2), int((filter_size[1] - 1) / 2)], - [0, 0]]) -return conv2d(x, num_filters, filter_size=filter_size, pad='VALID', stride=stride, **kwargs) - - -@add_arg_scope -def down_shifted_deconv2d(x, num_filters, filter_size=[2, 3], stride=[1, 1], **kwargs): -x = deconv2d(x, num_filters, filter_size=filter_size, pad='VALID', stride=stride, **kwargs) -xs = int_shape(x) -return x[:, :(xs[1] - filter_size[0] + 1), int((filter_size[1] - 1) / 2):(xs[2] - int((filter_size[1] - 1) / 2)), :] - - -@add_arg_scope -def down_right_shifted_conv2d(x, num_filters, filter_size=[2, 2], stride=[1, 1], **kwargs): -x = tf.pad(x, [[0, 0], [filter_size[0] - 1, 0], [filter_size[1] - 1, 0], [0, 0]]) -return conv2d(x, num_filters, filter_size=filter_size, pad='VALID', stride=stride, **kwargs) - - -@add_arg_scope -def down_right_shifted_deconv2d(x, num_filters, filter_size=[2, 2], stride=[1, 1], **kwargs): -x = deconv2d(x, num_filters, filter_size=filter_size, pad='VALID', stride=stride, **kwargs) -xs = int_shape(x) - - -return x[:, :(xs[1] - filter_size[0] + 1):, :(xs[2] - filter_size[1] + 1), :] - - -# --------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------- -# https://github.com/pclucas14/pixel-cnn-pp/blob/master/model.py -# PIXELCNN++ - -self.u_init = down_shifted_conv2d(input_channels + 1, nr_filters, filter_size=(2, 3), - shift_output_down=True) - -self.ul_init = nn.ModuleList([down_shifted_conv2d(input_channels + 1, nr_filters, - filter_size=(1, 3), shift_output_down=True), - down_right_shifted_conv2d(input_channels + 1, nr_filters, - filter_size=(2, 1), shift_output_right=True)]) - -u_list = [self.u_init(x)] -ul_list = [self.ul_init[0](x) + self.ul_init[1](x)] - -self.right_shift_pad = nn.ZeroPad2d((1, 0, 0, 0)) -self.down_shift_pad = nn.ZeroPad2d((0, 0, 1, 0)) - - -''' utilities for shifting the image around, efficient alternative to masking convolutions ''' -def down_shift(x, pad=None): - # Pytorch ordering - xs = [int(y) for y in x.size()] - # when downshifting, the last row is removed - x = x[:, :, :xs[2] - 1, :] - # padding left, padding right, padding top, padding bottom - pad = nn.ZeroPad2d((0, 0, 1, 0)) if pad is None else pad - return pad(x) - - -def right_shift(x, pad=None): - # Pytorch ordering - xs = [int(y) for y in x.size()] - # when righshifting, the last column is removed - x = x[:, :, :, :xs[3] - 1] - # padding left, padding right, padding top, padding bottom - pad = nn.ZeroPad2d((1, 0, 0, 0)) if pad is None else pad - return pad(x) - - - -class down_shifted_conv2d(nn.Module): - def __init__(self, num_filters_in, num_filters_out, filter_size=(2, 3), stride=(1, 1), - shift_output_down=False, norm='weight_norm'): - super(down_shifted_conv2d, self).__init__() - - assert norm in [None, 'batch_norm', 'weight_norm'] - self.conv = nn.Conv2d(num_filters_in, num_filters_out, filter_size, stride) - self.shift_output_down = shift_output_down - self.norm = norm - self.pad = nn.ZeroPad2d((int((filter_size[1] - 1) / 2), # pad left - int((filter_size[1] - 1) / 2), # pad right - filter_size[0] - 1, # pad top - 0)) # pad down - - if norm == 'weight_norm': - self.conv = wn(self.conv) - elif norm == 'batch_norm': - self.bn = nn.BatchNorm2d(num_filters_out) - - if shift_output_down: - self.down_shift = lambda x: down_shift(x, pad=nn.ZeroPad2d((0, 0, 1, 0))) - - def forward(self, x): - x = self.pad(x) - x = self.conv(x) - x = self.bn(x) if self.norm == 'batch_norm' else x - return self.down_shift(x) if self.shift_output_down else x - - -class down_right_shifted_conv2d(nn.Module): - def __init__(self, num_filters_in, num_filters_out, filter_size=(2, 2), stride=(1, 1), - shift_output_right=False, norm='weight_norm'): - super(down_right_shifted_conv2d, self).__init__() - - assert norm in [None, 'batch_norm', 'weight_norm'] - self.pad = nn.ZeroPad2d((filter_size[1] - 1, 0, filter_size[0] - 1, 0)) - self.conv = nn.Conv2d(num_filters_in, num_filters_out, filter_size, stride=stride) - self.shift_output_right = shift_output_right - self.norm = norm - - if norm == 'weight_norm': - self.conv = wn(self.conv) - elif norm == 'batch_norm': - self.bn = nn.BatchNorm2d(num_filters_out) - - if shift_output_right: - self.right_shift = lambda x: right_shift(x, pad=nn.ZeroPad2d((1, 0, 0, 0))) - - def forward(self, x): - x = self.pad(x) - x = self.conv(x) - x = self.bn(x) if self.norm == 'batch_norm' else x - return self.right_shift(x) if self.shift_output_right else x - -# --------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------------------- -# https://github.com/jonathanventura/pixelcnn/blob/master/gated_pixelcnn.py - -def _apply_activation(x): - # split in half along channel axis - x0, x1 = tf.split(x, 2, axis=3) - - # apply separate activations - x_tanh = tf.nn.tanh(x0) - x_sigmoid = tf.nn.sigmoid(x1) - - # combine activations - return x_tanh * x_sigmoid - - -def _conv(inputs, num_outputs, filter_size, name): - # relu activation - # x = tf.nn.relu(inputs) - x = inputs - - # get number of input channels - num_channels_in = inputs.get_shape().as_list()[-1] - - # create filter weights - weights = tf.get_variable(name + '_weights', - shape=filter_size + [num_channels_in, num_outputs], - initializer=tf.glorot_uniform_initializer()) - - # create filter bias - bias = tf.get_variable(name + '_bias', - shape=(num_outputs,), - initializer=tf.zeros_initializer()) - - # apply filter and bias - x = tf.nn.conv2d(x, weights, [1, 1, 1, 1], 'VALID') - x = tf.nn.bias_add(x, bias) - - return x - - - -def _gated_pixel_cnn_layer(vinput, hinput, filter_size, num_filters, layer_index): - """Gated activation PixelCNN layer - Paper reference: https://arxiv.org/pdf/1606.05328.pdf - Code reference: https://github.com/dritchie/pixelCNN/blob/master/pixelCNN.lua - """ - k = filter_size - floork = int(floor(filter_size / 2)) - ceilk = int(ceil(filter_size / 2)) - - # kxk convolution for vertical stack - vinput_padded = tf.pad(vinput, [[0, 0], [ceilk, 0], [floork, floork], [0, 0]]) - vconv = _conv(vinput_padded, 2 * num_filters, [ceilk, k], 'vconv_%d' % layer_index) - vconv = vconv[:, :-1, :, :] - - # kx1 convolution for horizontal stack - hinput_padded = tf.pad(hinput, [[0, 0], [0, 0], [ceilk, 0], [0, 0]]) - hconv = _conv(hinput_padded, 2 * num_filters, [1, ceilk], 'hconv_%d' % layer_index) - if layer_index == 0: - hconv = hconv[:, :, :-1, :] - else: - hconv = hconv[:, :, 1:, :] - - # 1x1 transitional convolution for vstack - vconv1 = _conv(vconv, 2 * num_filters, [1, 1], 'vconv1_%d' % layer_index) - - # add vstack to hstack - hconv = hconv + vconv1 - - # apply activations - vconv = _apply_activation(vconv) - hconv = _apply_activation(hconv) - - # residual connection in hstack - if layer_index > 0: - hconv1 = _conv(hconv, num_filters, [1, 1], 'hconv1_%d' % layer_index) - hconv = hinput + hconv1 - - return vconv, hconv - - -def gated_pixelcnn(inputs, num_filters, num_layers, num_outputs): - """Builds Gated PixelCNN graph. - Args: - inputs: input tensor (B,H,W,C) - Returns: - Predicted tensor - """ - with tf.variable_scope('pixel_cnn') as sc: - vstack = inputs - hstack = inputs - - # first layer: masked 7x7 - vstack, hstack = _gated_pixel_cnn_layer(vstack, hstack, 7, num_filters, 0) - - # next layers: masked 3x3 - for i in range(num_layers): - vstack, hstack = _gated_pixel_cnn_layer(vstack, hstack, 3, num_filters, i + 1) - - # final layers - x = _conv(tf.nn.relu(hstack), num_filters, [1, 1], 'conv1') - logits = _conv(tf.nn.relu(x), num_outputs, [1, 1], 'logits') - - return logits - -