{"cells":[{"metadata":{"id":"YfIk2es3hJEd","trusted":true},"cell_type":"code","source":"from glob import glob\nfrom PIL import Image\nfrom IPython import display\nimport tensorflow.keras as kr\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport imageio\nimport os","execution_count":null,"outputs":[]},{"metadata":{"id":"xSzYytsVd9Hf","trusted":true},"cell_type":"code","source":"BATCH_SIZE = 128\nLATENT_DIM = 100\nSAMPLE_INTERVAL = 200\nEPOCHS = 10000","execution_count":null,"outputs":[]},{"metadata":{"id":"yv6y-Spcd9Hr"},"cell_type":"markdown","source":"**Utility Functions**"},{"metadata":{"id":"QqMtD4jpd9Hu","trusted":true},"cell_type":"code","source":"def generate_gif(gif_name='mnist_gan.gif', pattern='image*.png'):\n    with imageio.get_writer(gif_name, mode='I') as writer:\n        filenames = glob(pattern)\n        filenames = sorted(filenames)\n        last = -1\n        for i,filename in enumerate(filenames):\n            frame = 2*(i**0.5)\n            if round(frame) > round(last):\n                last = frame\n            else:\n                continue\n            image = imageio.imread(filename)\n            writer.append_data(image)\n            \n        image = imageio.imread(filename)\n        writer.append_data(image)\n\n    # this is a hack to display the gif inside the notebook\n    os.system('cp {} {}.png'.format(gif_name, gif_name))","execution_count":null,"outputs":[]},{"metadata":{"id":"vjZv8b1Ud9H4","trusted":true},"cell_type":"code","source":"def sample_images(generator, epoch, save=True, name='mnist'):\n    \"\"\" Sample images from generator, plot them and save as png\"\"\"\n\n    noise = np.random.normal(size=(5 * 5, LATENT_DIM))\n    gen_imgs = generator.predict(noise)\n    gen_imgs = 0.5 * gen_imgs + 0.5     # Rescale images 0-1\n\n    fig, axs = plt.subplots(5, 5)\n    c = 0\n    for i in range(5):\n        for j in range(5):\n            axs[i,j].imshow(gen_imgs[c, :,:,0], cmap='gray')\n            axs[i,j].axis('off')\n            c += 1\n            \n    if save:\n        fig.savefig(\"{}_{}.png\".format(name, epoch))\n        plt.close()\n    else:\n        plt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"iYn4MdZnKCey"},"cell_type":"markdown","source":"### Starting with MNIST\n\nWe are going start with MNIST dataset to train the generator and the discriminator. The generator will generate handwritten digits resembling the MNIST data."},{"metadata":{"id":"NFC2ghIdiZYE","trusted":true},"cell_type":"code","source":"(X, _), (_, _) = kr.datasets.mnist.load_data()\n\nX = X.reshape(X.shape[0], 28, 28, 1).astype('float32')\nX = (X - 127.5) / 127.5 # Normalize the images to [-1, 1]","execution_count":null,"outputs":[]},{"metadata":{"id":"XoGgYQaQd9IN"},"cell_type":"markdown","source":"### The Generator Model\n\nThe generator will try to create images that are good enough to fool the discriminator. First, we are going to start with a simple model - with Dense layers -."},{"metadata":{"id":"IUdGOqIAd9IR","outputId":"29b1a52b-6cf0-47bc-a968-e68a767ddc9d","trusted":true},"cell_type":"code","source":"def build_generator(output_shape=(28, 28, 1)): \n    model = kr.Sequential(name='generator')\n    \n    model.add(kr.layers.Dense(256, input_shape=(LATENT_DIM, )))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n\n    model.add(kr.layers.Dense(512))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n\n    model.add(kr.layers.Dense(1024))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n\n    model.add(kr.layers.Dense(np.prod(output_shape), activation='tanh'))\n    model.add(kr.layers.Reshape(output_shape))\n\n    return model\n\n\ngenerator = build_generator()\ngenerator.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"DUdqzJ8Id9Ic"},"cell_type":"markdown","source":"### The Discriminator model\n\nThe discriminator will try to distinguish fake images from real images. "},{"metadata":{"id":"DfIJVffJd9Ie","outputId":"0d72fadf-5b50-407f-a17f-dd6f243e5408","trusted":true},"cell_type":"code","source":"def build_discriminator(input_shape=(28, 28, 1)):\n    model = kr.Sequential(name='discriminator')\n\n    model.add(kr.layers.Flatten(input_shape=input_shape))\n    model.add(kr.layers.Dense(512))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n\n    model.add(kr.layers.Dense(256))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n\n    model.add(kr.layers.Dense(1, activation='sigmoid'))\n\n    return model\n\n\ndiscriminator = build_discriminator()\ndiscriminator.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"KJLdRQNHd9Im","outputId":"d248248e-7d54-4607-fac5-548402d10093","trusted":true},"cell_type":"code","source":"optimizer = kr.optimizers.Adam(0.0002, 0.5)\n\ndiscriminator.compile(loss='binary_crossentropy', optimizer=optimizer,  metrics=['acc'])\ndiscriminator.trainable = False    # For GAN we will only train the generator\n\nz = kr.Input(shape=(LATENT_DIM,)) \nvalid = discriminator(generator(z))\n\nmodel = kr.Model(z, valid)\nmodel.compile(loss='binary_crossentropy', optimizer=optimizer)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"Z0wn69SWd9I2","outputId":"4dd5d78d-cf54-477e-b34f-19049fd16210","trusted":true},"cell_type":"code","source":"# Adversarial ground truths\nvalid_labels = np.ones((BATCH_SIZE, 1))\nfake_labels = np.zeros((BATCH_SIZE, 1))\n\nfor epoch in range(EPOCHS):\n    noise = np.random.normal(size=(BATCH_SIZE, LATENT_DIM))\n    \n    # Shuffle and batch data \n    imgs = X[np.random.randint(0, X.shape[0], BATCH_SIZE)] \n    \n    loss_real = discriminator.train_on_batch(imgs, valid_labels)\n    loss_fake = discriminator.train_on_batch(generator.predict(noise), fake_labels)\n    d_loss, d_acc = 0.5 * np.add(loss_real, loss_fake)\n    \n    noise = np.random.normal(size=(BATCH_SIZE, LATENT_DIM))\n    g_loss = model.train_on_batch(noise, valid_labels)\n    display.clear_output(wait=True)\n    print (\"Epoch : %d [D loss: %f, acc.: %.2f%%] [G loss: %f]\" % (epoch, d_loss, 100*d_acc, g_loss))\n\n    # If at save interval => save generated image samples\n    if epoch % SAMPLE_INTERVAL == 0:\n        sample_images(generator, epoch, name='../working/mnist')","execution_count":null,"outputs":[]},{"metadata":{"id":"DXh66ULQd9I-","outputId":"1854e940-693e-4f12-8aa1-19b0e825efe0","trusted":true},"cell_type":"code","source":"sample_images(generator, None, save=False)","execution_count":null,"outputs":[]},{"metadata":{"id":"IDVQmrpHd9JF","outputId":"5a6f14db-0ec7-4aa2-8596-08d4a167b399","trusted":true},"cell_type":"code","source":"generate_gif(gif_name='../working/mnist_gan.gif', pattern='../working/mnist*.png')\ndisplay.Image(filename=\"../working/mnist_gan.gif.png\")","execution_count":null,"outputs":[]},{"metadata":{"id":"qKq3agV4d9JM"},"cell_type":"markdown","source":"It doesn't look bad for a starter model, does it?."},{"metadata":{"id":"OqhDxtQvd9JP"},"cell_type":"markdown","source":"## DC-GAN with Fashion MNIST\n\nNow, let's change our data to Fashion MNIST to generate clothes meanwhile using the Deep Convolutional GAN model."},{"metadata":{"id":"3es9LJjLd9JS","trusted":false},"cell_type":"code","source":"(X, _), (_, _) = kr.datasets.fashion_mnist.load_data()\n\nX = X.reshape(X.shape[0], 28, 28, 1).astype('float32')\nX = (X - 127.5) / 127.5 # Normalize the images to [-1, 1]","execution_count":0,"outputs":[]},{"metadata":{"id":"-tEyxE-GMC48"},"cell_type":"markdown","source":"### The Generator Model"},{"metadata":{"id":"6bpTcDqoLWjY","outputId":"9244696e-a710-464d-c867-b46678d7515f","trusted":false},"cell_type":"code","source":"def generator_model():\n    model = kr.Sequential()\n    \n    model.add(kr.layers.Dense(7 * 7 * 128, activation=\"relu\", input_shape=(LATENT_DIM,)))\n    model.add(kr.layers.Reshape((7, 7, 128)))\n    \n    model.add(kr.layers.UpSampling2D())\n    model.add(kr.layers.Conv2D(128, (3, 3), padding='same'))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n    model.add(kr.layers.ReLU())\n\n    model.add(kr.layers.UpSampling2D())\n    model.add(kr.layers.Conv2D(64, (3, 3), padding='same'))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n    model.add(kr.layers.ReLU())\n\n    model.add(kr.layers.Conv2D(1, (3, 3), padding='same', activation='tanh'))\n  \n    return model\n\n\ngenerator = generator_model()\ngenerator.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"D0IKnaCtg6WE"},"cell_type":"markdown","source":"### The Discriminator model\n\nIt's similar to a regular CNN-based image classifier."},{"metadata":{"id":"dw2tPLmk2pEP","outputId":"eb6caf35-2d08-43f8-e961-613675197eff","trusted":false},"cell_type":"code","source":"def discriminator_model():\n    model = kr.Sequential()\n    \n    model.add(kr.layers.Conv2D(32, (3, 3), strides=(2, 2), padding='same', input_shape=(28, 28, 1)))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.Dropout(0.25))\n      \n    model.add(kr.layers.Conv2D(64, (3, 3), strides=(2, 2), padding='same'))\n    # model.add(kr.layers.ZeroPadding2D(padding=((0,1),(0,1))))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.Dropout(0.25))\n    \n    model.add(kr.layers.Conv2D(128, (3, 3), strides=(2, 2), padding='same'))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.Dropout(0.25))\n    \n    model.add(kr.layers.Conv2D(256, (3, 3), strides=(2, 2), padding='same'))\n    model.add(kr.layers.BatchNormalization(momentum=0.8))\n    model.add(kr.layers.LeakyReLU(alpha=0.2))\n    model.add(kr.layers.Dropout(0.25))\n       \n    model.add(kr.layers.Flatten())\n    model.add(kr.layers.Dense(1, activation='sigmoid'))\n     \n    return model\n\n\ndiscriminator = discriminator_model()\ndiscriminator.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"Aak4cIMYd9Js"},"cell_type":"markdown","source":"**Compile our DC-GAN Model**"},{"metadata":{"id":"kwTx7Csxd9Jv","outputId":"204fd20f-7c22-4bc1-b6ae-e1914dc04add","trusted":false},"cell_type":"code","source":"optimizer = kr.optimizers.Adam(0.0002, 0.5)\n\ndiscriminator.compile(loss='binary_crossentropy', optimizer=optimizer,  metrics=['acc'])\ndiscriminator.trainable = False    # For GAN we will only train the generator\n\nz = kr.Input(shape=(LATENT_DIM,)) \nvalid = discriminator(generator(z))\n\nmodel = kr.Model(z, valid)\nmodel.compile(loss='binary_crossentropy', optimizer=optimizer)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"id":"jbp7DEl3d9J3","outputId":"76798198-3aa6-49d0-fd65-c7491fddc0dc","trusted":false},"cell_type":"code","source":"# Adversarial ground truths\nvalid_labels = np.ones((BATCH_SIZE, 1))\nfake_labels = np.zeros((BATCH_SIZE, 1))\n\nfor epoch in range(EPOCHS):\n    noise = np.random.normal(size=(BATCH_SIZE, LATENT_DIM))\n    \n    # Shuffle and batch data \n    imgs = X[np.random.randint(0, X.shape[0], BATCH_SIZE)] \n    \n    loss_real = discriminator.train_on_batch(imgs, valid_labels)\n    loss_fake = discriminator.train_on_batch(generator.predict(noise), fake_labels)\n    d_loss, d_acc = 0.5 * np.add(loss_real, loss_fake)\n    \n    noise = np.random.normal(size=(BATCH_SIZE, LATENT_DIM))\n    g_loss = model.train_on_batch(noise, valid_labels)\n    display.clear_output(wait=True)\n    print (\"Epoch : %d [D loss: %f, acc.: %.2f%%] [G loss: %f]\" % (epoch, d_loss, 100*d_acc, g_loss))\n\n    # If at save interval => save generated image samples\n    if epoch % SAMPLE_INTERVAL == 0:\n        sample_images(generator, epoch, name='../working/fmnist')","execution_count":null,"outputs":[]},{"metadata":{"id":"BRxBKX30d9J8","outputId":"56055b7f-bba6-4287-bfdf-ef8817d9b2f7","trusted":false},"cell_type":"code","source":"sample_images(generator, None, save=False, name='../working/fmnist')","execution_count":null,"outputs":[]},{"metadata":{"id":"lvR0CkqSd9KD","outputId":"2bfcea93-ea4e-4755-946f-138160f4b8b3","trusted":false},"cell_type":"code","source":"generate_gif(gif_name='../working/fmnist_gan.gif', pattern='../working/fmnist*.png')\ndisplay.Image(filename=\"../working/fmnist_gan.gif.png\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"colab":{"name":"Introduction to Generative Adversarial Networks (GAN).ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[]},"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.8"},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"accelerator":"GPU"},"nbformat":4,"nbformat_minor":4}