{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Deep Convolutional Generative Adversarial Network Example\n", "\n", "Build a deep convolutional generative adversarial network (DCGAN) to generate digit images from a noise distribution with TensorFlow v2.\n", "\n", "- Author: Aymeric Damien\n", "- Project: https://github.com/aymericdamien/TensorFlow-Examples/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## DCGAN Overview\n", "\n", "\"dcgan\"\n", "\n", "References:\n", "- [Unsupervised representation learning with deep convolutional generative adversarial networks](https://arxiv.org/pdf/1511.06434). A Radford, L Metz, S Chintala, 2016.\n", "- [Understanding the difficulty of training deep feedforward neural networks](http://proceedings.mlr.press/v9/glorot10a.html). X Glorot, Y Bengio. Aistats 9, 249-256\n", "- [Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift](https://arxiv.org/abs/1502.03167). Sergey Ioffe, Christian Szegedy. 2015.\n", "\n", "## MNIST Dataset Overview\n", "\n", "This example is using MNIST handwritten digits. The dataset contains 60,000 examples for training and 10,000 examples for testing. The digits have been size-normalized and centered in a fixed-size image (28x28 pixels) with values from 0 to 255. \n", "\n", "In this example, each image will be converted to float32 and normalized from [0, 255] to [0, 1].\n", "\n", "![MNIST Dataset](http://neuralnetworksanddeeplearning.com/images/mnist_100_digits.png)\n", "\n", "More info: http://yann.lecun.com/exdb/mnist/" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "from __future__ import absolute_import, division, print_function\n", "\n", "import tensorflow as tf\n", "from tensorflow.keras import Model, layers\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# MNIST Dataset parameters.\n", "num_features = 784 # data features (img shape: 28*28).\n", "\n", "# Training parameters.\n", "lr_generator = 0.0002\n", "lr_discriminator = 0.0002\n", "training_steps = 20000\n", "batch_size = 128\n", "display_step = 500\n", "\n", "# Network parameters.\n", "noise_dim = 100 # Noise data points." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# Prepare MNIST data.\n", "from tensorflow.keras.datasets import mnist\n", "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", "# Convert to float32.\n", "x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)\n", "# Normalize images value from [0, 255] to [0, 1].\n", "x_train, x_test = x_train / 255., x_test / 255." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# Use tf.data API to shuffle and batch data.\n", "train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", "train_data = train_data.repeat().shuffle(10000).batch(batch_size).prefetch(1)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# Create TF Model.\n", "class Generator(Model):\n", " # Set layers.\n", " def __init__(self):\n", " super(Generator, self).__init__()\n", " self.fc1 = layers.Dense(7 * 7 * 128)\n", " self.bn1 = layers.BatchNormalization()\n", " self.conv2tr1 = layers.Conv2DTranspose(64, 5, strides=2, padding='SAME')\n", " self.bn2 = layers.BatchNormalization()\n", " self.conv2tr2 = layers.Conv2DTranspose(1, 5, strides=2, padding='SAME')\n", "\n", " # Set forward pass.\n", " def call(self, x, is_training=False):\n", " x = self.fc1(x)\n", " x = self.bn1(x, training=is_training)\n", " x = tf.nn.leaky_relu(x)\n", " # Reshape to a 4-D array of images: (batch, height, width, channels)\n", " # New shape: (batch, 7, 7, 128)\n", " x = tf.reshape(x, shape=[-1, 7, 7, 128])\n", " # Deconvolution, image shape: (batch, 14, 14, 64)\n", " x = self.conv2tr1(x)\n", " x = self.bn2(x, training=is_training)\n", " x = tf.nn.leaky_relu(x)\n", " # Deconvolution, image shape: (batch, 28, 28, 1)\n", " x = self.conv2tr2(x)\n", " x = tf.nn.tanh(x)\n", " return x\n", "\n", "# Generator Network\n", "# Input: Noise, Output: Image\n", "# Note that batch normalization has different behavior at training and inference time,\n", "# we then use a placeholder to indicates the layer if we are training or not.\n", "class Discriminator(Model):\n", " # Set layers.\n", " def __init__(self):\n", " super(Discriminator, self).__init__()\n", " self.conv1 = layers.Conv2D(64, 5, strides=2, padding='SAME')\n", " self.bn1 = layers.BatchNormalization()\n", " self.conv2 = layers.Conv2D(128, 5, strides=2, padding='SAME')\n", " self.bn2 = layers.BatchNormalization()\n", " self.flatten = layers.Flatten()\n", " self.fc1 = layers.Dense(1024)\n", " self.bn3 = layers.BatchNormalization()\n", " self.fc2 = layers.Dense(2)\n", "\n", " # Set forward pass.\n", " def call(self, x, is_training=False):\n", " x = tf.reshape(x, [-1, 28, 28, 1])\n", " x = self.conv1(x)\n", " x = self.bn1(x, training=is_training)\n", " x = tf.nn.leaky_relu(x)\n", " x = self.conv2(x)\n", " x = self.bn2(x, training=is_training)\n", " x = tf.nn.leaky_relu(x)\n", " x = self.flatten(x)\n", " x = self.fc1(x)\n", " x = self.bn3(x, training=is_training)\n", " x = tf.nn.leaky_relu(x)\n", " return self.fc2(x)\n", "\n", "# Build neural network model.\n", "generator = Generator()\n", "discriminator = Discriminator()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Losses.\n", "def generator_loss(reconstructed_image):\n", " gen_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n", " logits=reconstructed_image, labels=tf.ones([batch_size], dtype=tf.int32)))\n", " return gen_loss\n", "\n", "def discriminator_loss(disc_fake, disc_real):\n", " disc_loss_real = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n", " logits=disc_real, labels=tf.ones([batch_size], dtype=tf.int32)))\n", " disc_loss_fake = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(\n", " logits=disc_fake, labels=tf.zeros([batch_size], dtype=tf.int32)))\n", " return disc_loss_real + disc_loss_fake\n", "\n", "# Optimizers.\n", "optimizer_gen = tf.optimizers.Adam(learning_rate=lr_generator)#, beta_1=0.5, beta_2=0.999)\n", "optimizer_disc = tf.optimizers.Adam(learning_rate=lr_discriminator)#, beta_1=0.5, beta_2=0.999)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# Optimization process. Inputs: real image and noise.\n", "def run_optimization(real_images):\n", " \n", " # Rescale to [-1, 1], the input range of the discriminator\n", " real_images = real_images * 2. - 1.\n", "\n", " # Generate noise.\n", " noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n", " \n", " with tf.GradientTape() as g:\n", " \n", " fake_images = generator(noise, is_training=True)\n", " disc_fake = discriminator(fake_images, is_training=True)\n", " disc_real = discriminator(real_images, is_training=True)\n", "\n", " disc_loss = discriminator_loss(disc_fake, disc_real)\n", " \n", " # Training Variables for each optimizer\n", " gradients_disc = g.gradient(disc_loss, discriminator.trainable_variables)\n", " optimizer_disc.apply_gradients(zip(gradients_disc, discriminator.trainable_variables))\n", " \n", " # Generate noise.\n", " noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n", " \n", " with tf.GradientTape() as g:\n", " \n", " fake_images = generator(noise, is_training=True)\n", " disc_fake = discriminator(fake_images, is_training=True)\n", "\n", " gen_loss = generator_loss(disc_fake)\n", " \n", " gradients_gen = g.gradient(gen_loss, generator.trainable_variables)\n", " optimizer_gen.apply_gradients(zip(gradients_gen, generator.trainable_variables))\n", " \n", " return gen_loss, disc_loss" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "initial: gen_loss: 0.694535, disc_loss: 1.403878\n", "step: 500, gen_loss: 2.154825, disc_loss: 0.429040\n", "step: 1000, gen_loss: 2.103464, disc_loss: 0.502638\n", "step: 1500, gen_loss: 2.282369, disc_loss: 0.572065\n", "step: 2000, gen_loss: 2.711531, disc_loss: 0.341791\n", "step: 2500, gen_loss: 2.835576, disc_loss: 0.322356\n", "step: 3000, gen_loss: 2.970127, disc_loss: 0.275483\n", "step: 3500, gen_loss: 2.836321, disc_loss: 0.190680\n", "step: 4000, gen_loss: 2.892855, disc_loss: 0.337681\n", "step: 4500, gen_loss: 2.948962, disc_loss: 0.329322\n", "step: 5000, gen_loss: 3.799061, disc_loss: 0.327288\n", "step: 5500, gen_loss: 4.090328, disc_loss: 0.274685\n", "step: 6000, gen_loss: 4.343777, disc_loss: 0.155223\n", "step: 6500, gen_loss: 3.806556, disc_loss: 0.155855\n", "step: 7000, gen_loss: 4.827947, disc_loss: 0.078372\n", "step: 7500, gen_loss: 3.708949, disc_loss: 0.140979\n", "step: 8000, gen_loss: 5.250406, disc_loss: 0.164736\n", "step: 8500, gen_loss: 5.491106, disc_loss: 0.110080\n", "step: 9000, gen_loss: 4.391072, disc_loss: 0.100240\n", "step: 9500, gen_loss: 5.074200, disc_loss: 0.105567\n", "step: 10000, gen_loss: 6.077592, disc_loss: 0.215981\n", "step: 10500, gen_loss: 4.468120, disc_loss: 0.099412\n", "step: 11000, gen_loss: 5.887744, disc_loss: 0.093534\n", "step: 11500, gen_loss: 5.656942, disc_loss: 0.075079\n", "step: 12000, gen_loss: 4.752551, disc_loss: 0.092505\n", "step: 12500, gen_loss: 5.682284, disc_loss: 0.077406\n", "step: 13000, gen_loss: 5.386811, disc_loss: 0.101259\n", "step: 13500, gen_loss: 4.646158, disc_loss: 0.039680\n", "step: 14000, gen_loss: 5.698145, disc_loss: 0.083461\n", "step: 14500, gen_loss: 4.513465, disc_loss: 0.077946\n", "step: 15000, gen_loss: 5.586041, disc_loss: 0.039668\n", "step: 15500, gen_loss: 6.316034, disc_loss: 0.084526\n", "step: 16000, gen_loss: 7.120735, disc_loss: 0.070267\n", "step: 16500, gen_loss: 5.511638, disc_loss: 0.053082\n", "step: 17000, gen_loss: 8.176781, disc_loss: 0.029212\n", "step: 17500, gen_loss: 6.144678, disc_loss: 0.140460\n", "step: 18000, gen_loss: 5.583275, disc_loss: 0.077311\n", "step: 18500, gen_loss: 5.840647, disc_loss: 0.042103\n", "step: 19000, gen_loss: 7.111396, disc_loss: 0.030435\n", "step: 19500, gen_loss: 4.391251, disc_loss: 0.043656\n", "step: 20000, gen_loss: 6.271616, disc_loss: 0.040568\n" ] } ], "source": [ "# Run training for the given number of steps.\n", "for step, (batch_x, _) in enumerate(train_data.take(training_steps + 1)):\n", " \n", " if step == 0:\n", " # Generate noise.\n", " noise = np.random.normal(-1., 1., size=[batch_size, noise_dim]).astype(np.float32)\n", " gen_loss = generator_loss(discriminator(generator(noise)))\n", " disc_loss = discriminator_loss(discriminator(batch_x), discriminator(generator(noise)))\n", " print(\"initial: gen_loss: %f, disc_loss: %f\" % (gen_loss, disc_loss))\n", " continue\n", " \n", " # Run the optimization.\n", " gen_loss, disc_loss = run_optimization(batch_x)\n", " \n", " if step % display_step == 0:\n", " print(\"step: %i, gen_loss: %f, disc_loss: %f\" % (step, gen_loss, disc_loss))" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "# Visualize predictions.\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "image/png": "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G6BUAfeJPv300/o8Wuzu//7v/4Dc/uzNN98E8vnj+1XelljedNNNATjhhBMAYg+nl4QiD4IgaHMmeUVuO6ddd90VgBtvvBHISsJmzPrKq4RqbqWVVgKyAlf17LbbbgAcddRRQDWaFGy00UYAXHXV+EUxyw2zxdZ2+pOfffZZAIYNGwZki6NKOAc2zn744YeBbOltu+22QFa5a6655jjvO+uss8Z5fxXw3Nh+++3rUTeipdFVWexyqQOtrssvvxyAPfbYA8jNGoKJIxR5EARBm9OnVm/9RTMbS7h7fvPNNwNw4YUXAnDXXXcB49dt2HDDDQH43e9+B1SjFocqbuuttwZyyzfbhtmqytZvVcAuULPPPjuQFRx0X5jKn6tytSz0kV988cVANVva3XTTTUCO3thggwm3sB00aBCQfeq29asCWqkzzjhj3RrUYvW582qmthaJDY4936644gogn2//7//9PwB22mmnxh5EL/CYbrjhBgCeeuopIFsT7vfYCm6uueaqr0nbwun7n5gibM1svhwEQRC0mAHvI/duusMOOwBw6aWXAtkPazSKu+i77LILAD/+8Y+BHD9eBbSeVAb6U7UejByokhIX49c7o6y4xefOoXOm+nvwwQeB7G/VIqkSK664IpDVWndoTc0333wAvP322/W12WpUlY888khdhWod6fM3WsfolXINpaWXrrUquOaaa4A8l1Wsl6K1YEPs008/Hchj9rG8p/PGG2/UI628frz11lsALLnkkgBMO+20APzmN78BYO655+7TWEORB0EQtDkDVpHrR15mmWWA7NdS1arE999/f6C2Ew/VjIAQ/cxmbI4cORLIfshNNtmk81+sAPqzVSjGSU8xxRR1dVL2H/q6Mdb6aI2YeP311wF4+eWXAVhkkUUaNfxeow+5pxx00EFAXqc9VfLN5Fvf+tZ4Vt+///1voPsMzddeew3IVpTHWYWIKtEC1F9vfoPWhhbS8ssvD9QsFMjr8u23366v1eeffx7I0XFa035PPv75z38Get/IJBR5EARBmzPgFLmxxXvttReQfXUqo9133x2AQw89FIAZZpgBqHZLL31wxotrXXg3Nya7ysdQjgGXCY3Z47777rsBWGeddQD44IMPgOzD1Fe+4IILAuP7ZVuJirO7uTn++OMBuOWWWwAYPHgwUI0oqZ7QnRJ3roy0cm5Vt+uu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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Testing\n", "# Generate images from noise, using the generator network.\n", "n = 6\n", "canvas = np.empty((28 * n, 28 * n))\n", "for i in range(n):\n", " # Noise input.\n", " z = np.random.normal(-1., 1., size=[n, noise_dim]).astype(np.float32)\n", " # Generate image from noise.\n", " g = generator(z).numpy()\n", " # Rescale to original [0, 1]\n", " g = (g + 1.) / 2\n", " # Reverse colours for better display\n", " g = -1 * (g - 1)\n", " for j in range(n):\n", " # Draw the generated digits\n", " canvas[i * 28:(i + 1) * 28, j * 28:(j + 1) * 28] = g[j].reshape([28, 28])\n", "\n", "plt.figure(figsize=(n, n))\n", "plt.imshow(canvas, origin=\"upper\", cmap=\"gray\")\n", "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.15" } }, "nbformat": 4, "nbformat_minor": 2 }