{"cells":[{"metadata":{},"cell_type":"markdown","source":"Version Changes:\n1. initial commit\n2. increase epochs from 50 -> 250\n3. actually do what's in 2... I didn't cancel the commit correctly\n4. - modify image scaling (was `x/255` now `(x-127.5)/127.5)\n   - modify generator kernel initializer\n   - remove uneeded comments and code\n   - clean up some constants\n5. - modify discriminator\n   - train generator more than discriminator at each training step\n   - print losses while training during each step\n   - 250 -> 150 epochs"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from __future__ import absolute_import, division, print_function, unicode_literals\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt, zipfile\nimport numpy as np\nimport glob\nimport imageio\nimport xml\nimport xml.etree.ElementTree as ET\nimport time\nimport PIL\nimport tensorflow as tf\ntf.enable_eager_execution()\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.preprocessing.image import array_to_img, img_to_array\nfrom tensorflow.keras.preprocessing.image import NumpyArrayIterator, ImageDataGenerator\nimport IPython\nfrom IPython import display\n\nimport os\nROOT = '../input/'\ndirs = os.listdir(ROOT)\nprint(dirs)\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"IMAGES_PATH = ROOT + 'all-dogs/all-dogs/'\nANNOTATIONS_PATH = ROOT + 'annotation/Annotation/'\n\nIMGS = os.listdir(IMAGES_PATH)\nBREEDS = os.listdir(ANNOTATIONS_PATH)\n\nBATCH_SIZE = 256","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Look at annotaions (xml) of the first image\nex = IMGS[0].split('.')[0] # format of filenames is `000000000_0000.jpg`\ndom = xml.dom.minidom.parse(glob.glob(ANNOTATIONS_PATH + ex[0].split('_')[0] + '*/' + ex)[0])\npretty_xml_as_string = dom.toprettyxml()\nprint(pretty_xml_as_string)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BREED_COUND = len(BREEDS)\nIMG_SIZE = 64\nCHANNELS = 3\n\ncropped_images = []\ncropped_image_labels = []\nfor breed in BREEDS:\n    breed_path = os.path.join(ANNOTATIONS_PATH, breed)\n    for record in os.listdir(breed_path):\n        tree = ET.parse(os.path.join(breed_path, record))\n        try:\n            orig_img_path = os.path.join(IMAGES_PATH, record + '.jpg')\n            orig_img = PIL.Image.open(orig_img_path)\n        except:\n            print('Image {} was not found'.format(record))\n            continue\n        root = tree.getroot()\n        for obj in root.findall('object'):\n            name = obj.find('name').text\n            pose = obj.find('pose').text\n            bndbox = obj.find('bndbox')\n            xmin = int(bndbox.find('xmin').text)\n            ymin = int(bndbox.find('ymin').text)\n            xmax = int(bndbox.find('xmax').text)\n            ymax = int(bndbox.find('ymax').text)\n            \n            \"\"\"\n            Using the minimum distance will prevent image distoration\n            since it keeps the image square\n            \"\"\"\n            if False:\n                min_dist = np.min((xmax - xmin, ymax - ymin))\n                crp_img = orig_img.crop((xmin, ymin, xmin + min_dist, ymin + min_dist))\n            crp_img = orig_img.crop((xmin, ymin, xmax, ymax))\n            crp_img = crp_img.resize((IMG_SIZE, IMG_SIZE), PIL.Image.ANTIALIAS)\n            cropped_images.append(np.asarray(crp_img))\n            cropped_image_labels.append(name)\n            \nIMG_COUNT = len(cropped_images)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Build training image generator\ntraining_image_gen = ImageDataGenerator(\n    horizontal_flip=True\n)\ntraining_examples = (np.asarray(cropped_images)-127.5)/127.5\ntraining_labels = np.asarray(cropped_image_labels)\ntraining_dataset = NumpyArrayIterator(training_examples,\n                                      training_labels,\n                                      shuffle=True,\n                                      image_data_generator=training_image_gen,\n                                      batch_size=BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Take a look at the images\nplt.figure(figsize=(15,15))\ni = 0\nw = 4\nh = 4\n# for val in [training_dataset[i] for i in range(w*h)]:\n#     print(val[0][0], val[1][0])\nfor image, label in [(val[0][0], val[1][0]) for val in [training_dataset[i] for i in range(w*h)]]:\n    plt.subplot(h,w,i+1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.grid(False)\n    image = (image*127.5+127.5).astype(int)\n    plt.imshow(image)\n    plt.xlabel(label, fontsize=12)\n    i += 1\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# For overriding default kernel initializer, glorot uniform\nKERNEL_INIT = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.25)\n\n# Generator function\ndef make_generator_model():\n    model = tf.keras.Sequential()\n    model.add(layers.Dense(8*8*512, use_bias=False, input_shape=(100,)))\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n\n    model.add(layers.Reshape((8, 8, 512)))\n    assert model.output_shape == (None, 8, 8, 512) # Note: None here is the batch size\n    \n    model.add(layers.Conv2DTranspose(256, (5, 5), \n                                     strides=(2, 2),\n                                     padding='same',\n                                     use_bias=False,\n                                     kernel_initializer=KERNEL_INIT))\n    assert model.output_shape == (None, 16, 16, 256)\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n\n    model.add(layers.Dropout(0.3))\n    model.add(layers.Conv2DTranspose(128, (5, 5),\n                                     strides=(2, 2),\n                                     padding='same',\n                                     use_bias=False,\n                                     kernel_initializer=KERNEL_INIT))\n    assert model.output_shape == (None, 32, 32, 128)\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n    \n    model.add(layers.Dropout(0.3))\n    model.add(layers.Conv2DTranspose(64, (5, 5),\n                                     strides=(2, 2), \n                                     padding='same', \n                                     use_bias=False,\n                                     kernel_initializer=KERNEL_INIT))\n    assert model.output_shape == (None, 64, 64, 64)\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n    \n    model.add(layers.Dense(3,use_bias=False, activation='tanh'))\n    assert model.output_shape == (None, IMG_SIZE, IMG_SIZE, CHANNELS)\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create and test generator\ngenerator = make_generator_model()\n\nnoise = tf.random.normal([1, 100])\ngenerated_image = generator(noise, training=False)\nrn_img = (generated_image[0,:,:,:].numpy()* 127.5 + 127.5).astype(int)\nplt.imshow(generated_image[0,:,:,:])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Discriminator function\ndef make_discriminator_model():\n    model = tf.keras.Sequential()\n    model.add(layers.Conv2D(64, (6, 6), strides=(2, 2), padding='same',\n                                     input_shape=[IMG_SIZE, IMG_SIZE, CHANNELS]))\n    model.add(layers.LeakyReLU())\n    model.add(layers.Dropout(0.2))\n\n    model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))\n    model.add(layers.LeakyReLU())\n    model.add(layers.Dropout(0.2))\n\n    model.add(layers.Flatten())\n    model.add(layers.Dense(1, activation='linear'))\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"discriminator = make_discriminator_model()\ndecision = discriminator(generated_image)\nprint (decision)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n\ndef discriminator_loss(real_output, fake_output):\n    real_loss = cross_entropy(tf.ones_like(real_output), real_output)\n    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)\n    print('         real loss: {}  fake loss: {}'.format(real_loss, fake_loss))\n    total_loss = real_loss + fake_loss\n    return total_loss\n\ndef generator_loss(fake_output):\n    return cross_entropy(tf.ones_like(fake_output), fake_output)\n\ngenerator_optimizer = tf.keras.optimizers.Adam(1e-4, beta_1=0.5)\ndiscriminator_optimizer = tf.keras.optimizers.Adam(1e-4, beta_1=0.5)\n\ncheckpoint_dir = './training_checkpoints'\ncheckpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")\ncheckpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,\n                                 discriminator_optimizer=discriminator_optimizer,\n                                 generator=generator,\n                                 discriminator=discriminator)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 150\nSTEPS_PER_EPOCH = IMG_COUNT / BATCH_SIZE\nnoise_dim = 100\nGRID_H = 4\nGRID_W = 4\nnum_examples_to_generate = GRID_H * GRID_W\n\n# Reuse this seed over time to visualize progress\nseed = tf.random.normal([num_examples_to_generate, noise_dim])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_step(images, step_of_epoch=None):\n    if step_of_epoch is not None and isinstance(step_of_epoch, int):\n        print('Training Step {}'.format(step_of_epoch))\n        \n    noise = tf.random.normal([BATCH_SIZE, noise_dim])\n\n#     with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n    # overtrain generator\n    for i in range(5):\n        with tf.GradientTape() as gen_tape:\n            noise = tf.random.normal([BATCH_SIZE, noise_dim])\n            generated_images = generator(noise, training=True)\n            fake_output = discriminator(generated_images, training=True)\n            gen_loss = generator_loss(fake_output)\n            gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n            generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))\n        \n#         generated_images = generator(noise, training=True)\n\n    with tf.GradientTape() as disc_tape:\n        real_output = discriminator(images, training=True)\n        fake_output = discriminator(generated_images, training=False)\n\n#         gen_loss = generator_loss(fake_output)\n        disc_loss = discriminator_loss(real_output, fake_output)\n        \n        print('gen loss: {}    disc loss: {}'.format(gen_loss, disc_loss))\n\n#     gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n    gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)\n\n#     generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))\n    discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train(dataset, epochs):\n    for epoch in range(epochs):\n        print('Training epoch {}/{}'.format(epoch, epochs))\n        start = time.time()\n    \n        i = 0\n        for image_batch in dataset:\n            train_step(image_batch)\n            i += 1\n            if i >= STEPS_PER_EPOCH:\n                break\n\n        # Produce images for a GIF as we go\n        display.clear_output(wait=True)\n        generate_and_save_images(generator,\n                                 epoch + 1,\n                                 seed)\n\n        # Save the model every 25 epochs\n        if (epoch + 1) % 25 == 0:\n            checkpoint.save(file_prefix = checkpoint_prefix)\n\n        print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))\n\n    # Generate after the final epoch\n    display.clear_output(wait=True)\n    generate_and_save_images(generator,\n                             epochs,\n                             seed)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_and_save_images(model, epoch, test_input):\n    # Notice `training` is set to False.\n    # This is so all layers run in inference mode (batchnorm).\n    predictions = model(test_input, training=False)\n\n    fig = plt.figure(figsize=(10, 10))\n\n    for i in range(predictions.shape[0]):\n        plt.subplot(GRID_H, GRID_W, i+1)\n        plt.imshow(((predictions[i, :, :, :]).numpy()*127.5+127.5).astype(int)) \n        plt.axis('off')\n\n    plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train(training_dataset, EPOCHS)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n\ndef display_image(epoch_no):\n  return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))\n\ndisplay_image(EPOCHS)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"anim_file = 'dcgan-dogs.gif' \n\nwith imageio.get_writer(anim_file, mode='I') as writer:\n    filenames = glob.glob('image*.png')\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        image = imageio.imread(filename)\n        writer.append_data(image)\n    display.Image(filename=anim_file)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n# Create submission file `images.zip`\nz = zipfile.PyZipFile('images.zip', mode='w')\n\nfilename = 'generator_model.h5'\ntf.keras.models.save_model(\n    generator,\n    filename,\n    overwrite=True,\n    include_optimizer=True,\n    save_format=None\n)\n\nfor k in range(10000):\n    # training = False sets all layers to run in inference mode\n    generated_image = generator(tf.random.normal([1, noise_dim]), training=False)\n    f = str(k)+'.png'\n    img = ((generated_image[0,:,:,:]).numpy()*127.5+127.5).astype(int)\n    tf.keras.preprocessing.image.save_img(\n        f,\n        img\n    )\n    z.write(f); os.remove(f)\nz.close()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls -al | grep .zip","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A few todos:\n - altering generator and discriminator models\n - hyperparameter tuning\n - grayscale -> gen image -> colorize (if even necessary)\n - loss analysis\n - longer training runs\n - img augmentation\n \n Good enough for now :)"}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}