{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install -q tensorflow-gpu==2.0.0-beta1","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"import tensorflow as tf\ntf.__version__","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import xml.etree.ElementTree as ET # For parsing XML\nfrom PIL import Image # to read image\nimport glob\nfrom tqdm import tqdm_notebook\nimport urllib\nimport tarfile\nfrom imageio import imread, imsave, mimsave\nimport shutil\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport numpy as np\nimport os\nfrom tensorflow.keras import layers\nimport time","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1. Crop images"},{"metadata":{},"cell_type":"markdown","source":"### Create Datasets"},{"metadata":{},"cell_type":"markdown","source":"**此部分单独参考** [https://www.kaggle.com/cmalla94/dcgan-generating-dog-images-with-tensorflow#Training-loop](https://www.kaggle.com/cmalla94/dcgan-generating-dog-images-with-tensorflow#Training-loop)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Code slightly modified from user: cdeotte | https://www.kaggle.com/cdeotte/supervised-generative-dog-net\n\nROOT = '../input/'\n# list of all image file names in all-dogs\nIMAGES = os.listdir(ROOT + 'all-dogs/all-dogs')\n# list of all the annotation directories, each directory is a dog breed\nbreeds = os.listdir(ROOT + 'annotation/Annotation/') \n\nidxIn = 0; namesIn = []\nimagesIn = np.zeros((25000,64,64,3))\n\n# CROP WITH BOUNDING BOXES TO GET DOGS ONLY\n# iterate through each directory in annotation\nfor breed in breeds:\n    # iterate through each file in the directory\n    for dog in os.listdir(ROOT+'annotation/Annotation/'+breed):\n        try: img = Image.open(ROOT+'all-dogs/all-dogs/'+dog+'.jpg') \n        except: continue           \n        # Element Tree library allows for parsing xml and getting specific tag values    \n        tree = ET.parse(ROOT+'annotation/Annotation/'+breed+'/'+dog)\n        # take a look at the print out of an xml previously to get what is going on\n        root = tree.getroot() # <annotation>\n        objects = root.findall('object') # <object>\n        for o in objects:\n            bndbox = o.find('bndbox') # <bndbox>\n            xmin = int(bndbox.find('xmin').text) # <xmin>\n            ymin = int(bndbox.find('ymin').text) # <ymin>\n            xmax = int(bndbox.find('xmax').text) # <xmax>\n            ymax = int(bndbox.find('ymax').text) # <ymax>\n            w = np.min((xmax - xmin, ymax - ymin))\n            img2 = img.crop((xmin, ymin, xmin+w, ymin+w))\n            img2 = img2.resize((64,64), Image.ANTIALIAS)\n            imagesIn[idxIn,:,:,:] = np.asarray(img2)\n            namesIn.append(breed)\n            idxIn += 1         ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Inspect what the previous code created\nprint(\"imagesIn is a {} with {} {} by {} rgb({}) images. Shape: {}\".format(type(imagesIn), imagesIn.shape[0], imagesIn.shape[1], imagesIn.shape[2], imagesIn.shape[3], imagesIn.shape))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# normalize the pixel values\nimagesIn = (imagesIn[:idxIn,:,:,:]-127.5)/127.5 # Normalize the images to [-1, 1]\n\n# this is needed because the gradient functions from TF require float32 instead of float64\nimagesIn = tf.cast(imagesIn, 'float32')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Batch and shuffle the data\nBUFFER_SIZE = 60000\nBATCH_SIZE = 256\n\ntrain_dataset = tf.data.Dataset.from_tensor_slices(imagesIn).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)\nprint(train_dataset)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**TODO: Images 增强处理**"},{"metadata":{},"cell_type":"markdown","source":"## 2. DCGAN"},{"metadata":{},"cell_type":"markdown","source":"## Greate the models\n\nBoth the generator and discriminator are defined using the Keras Sequential API"},{"metadata":{},"cell_type":"markdown","source":"### The Generator\n\nThe generator uses [tf.keras.layers.Conv2DTranspose](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/layers/Conv2DTranspose) (upsampling) layers to produce an image from a seed (random noise). Start with a Dense layer that takes this seed as input, then upsample several times until you reach the desired image size of 64x64x3. Notice the [tf.keras.layers.LeakyReLU](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/layers/LeakyReLU) activation for each layer, except the output layer which uses tanh.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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)\n    \n    model.add(layers.Conv2DTranspose(256, (5, 5), strides=(1, 1),  padding='same', use_bias=False))\n    assert model.output_shape == (None, 8, 8, 256)\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n    \n    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(2, 2),  padding='same', use_bias=False))\n    assert model.output_shape == (None, 16, 16, 128)\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n    \n    model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2),  padding='same', use_bias=False))\n    assert model.output_shape == (None, 32, 32, 64)\n    model.add(layers.BatchNormalization())\n    model.add(layers.LeakyReLU())\n    \n    model.add(layers.Conv2DTranspose(3, (5,5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))\n    assert model.output_shape == (None, 64, 64, 3)\n    #model.add(layers.Dense(3, activation='tanh', use_bias=False))\n    print(\"GENERATOR\")\n    model.summary()\n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Use the (as yet untrained) generator to create an image."},{"metadata":{"trusted":true},"cell_type":"code","source":"generator = make_generator_model()\n\nnoise = tf.random.normal([1, 100])\n\ngenerated_image = generator(noise,training=False)\n\nplt.imshow(generated_image[0, :, :, 0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":""},{"metadata":{"trusted":true},"cell_type":"code","source":"print(generated_image.shape)\nprint(noise.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### The Discriminator\n\nThe discriminator is a CNN-based image classifer."},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_discriminator_model():\n    model = tf.keras.Sequential()\n    model.add(layers.Conv2D(64, (4, 4), \n                            strides=(2, 2), \n                            padding='same', \n                            input_shape=[64, 64, 3]))\n    model.add(layers.LeakyReLU())\n    model.add(layers.Dropout(0.3))\n    \n    model.add(layers.Conv2D(128, (4, 4), \n                            strides=(2, 2), \n                            padding='same'))\n    model.add(layers.LeakyReLU())\n    model.add(layers.Dropout(0.3))\n    \n    model.add(layers.Flatten())\n    model.add(layers.Dense(1, activation='sigmoid'))\n    \n    print(\"DISCRIMINATOR\")\n    model.summary()\n    \n    return model\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Use the (as yet untrained) discriminator to classify the generated images as real or fake. The model will be trained to output positive values for real images, and negative values for fake images."},{"metadata":{"trusted":true},"cell_type":"code","source":"discriminator = make_discriminator_model()\ndecision = discriminator(generated_image)\nprint (decision)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Define the loss and optimizers\n\nDefine loss function and optimizers for both models."},{"metadata":{"trusted":true},"cell_type":"code","source":"# This method returns a helper funciton to compute cross entropy loss\ncross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Discriminator loss\n\nThis method quantifies how well the discriminator is able to distinguish real images from fakes. It compares the discriminator's predictions on real images to an array of 1s, and the discriminator's predictions on fake(generated) images to an array of 0s."},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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    total_loss = real_loss + fake_loss\n    return total_loss","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Generator loss\n\nThe generator's loss quantifies how well it was able to trick the discriminator. Intuitively, if the generator is performing well, the discriminator will classify the fake images as real (or 1). Here, we will compare the discriminators decisions on the generated images to an array of 1s."},{"metadata":{"trusted":true},"cell_type":"code","source":"def generator_loss(fake_output):\n    return cross_entropy(tf.ones_like(fake_output), fake_output)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The discriminator and the generator optimizers are different since we will train two networks separately."},{"metadata":{"trusted":true},"cell_type":"code","source":"generator_optimizer = tf.keras.optimizers.Adam(1e-4)\ndiscriminator_optimizer = tf.keras.optimizers.Adam(1e-4)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Save checkpoints"},{"metadata":{},"cell_type":"markdown","source":"## Define the training loop"},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 200\nnoise_dim = 100\nnum_examples_to_generate = 16\n\n# we will reuse this seed overtime (so it's easier)\n# to visualize progress in the animated\nseed = tf.random.normal([num_examples_to_generate, noise_dim])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The training loop begins with generator receiving a random seed as input. That seed is used to produce an image. The discriminator is then used to classify real images (drawn from the training set) and fakes images (produced by the generator). The loss is calculated for each of these models, and the gradients are used to update the generator and discriminator."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Notice the use of `tf.function`\n# This annotation causes the function to be \"compiled\".\n@tf.function\ndef train_step(images):\n    noise = tf.random.normal([BATCH_SIZE, noise_dim])\n\n    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n        generated_images = generator(noise, training=True)\n        real_output = discriminator(images, training=True)\n        fake_output = discriminator(generated_images, training=True)\n        \n        gen_loss = generator_loss(fake_output)\n        disc_loss = discriminator_loss(real_output, fake_output)\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":{},"cell_type":"markdown","source":"### Generate and save images"},{"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=(8,8))\n    for i in range(predictions.shape[0]):\n        plt.subplot(4, 4, i + 1)\n        plt.imshow((predictions[i, :, :, :] + 1.)/2.)\n        plt.axis('off')\n    plt.savefig('image_at_epoch_{}.png'.format(epoch))\n    plt.show\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train(dataset, epochs):\n    for epoch in range(epochs):\n        start = time.time()\n        \n        for image_batch in dataset:\n            train_step(image_batch)\n    # Generate after the final epoch     \n    generate_and_save_images(generator,epochs,seed)    \n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Train the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\ntrain(train_dataset, EPOCHS)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Submit to Kaggle"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nif not os.path.exists('../output_images'):\n    os.mkdir('../output_images')\ni_batch_size = 50\nn_images = 10000\nfor i_batch in tqdm_notebook(range(0, n_images, i_batch_size)):\n    noise = np.random.uniform(-1.0, 1.0, [i_batch_size, noise_dim]).astype(np.float32)\n    gen_images = generator(noise, training=False)\n    gen_images = gen_images * 127.5 + 127.5\n    for j in range(i_batch_size):\n        img = image.array_to_img(gen_images[j])\n        imsave(os.path.join('../output_images',f'sample_{i_batch + j + 1}.png'), img)\n        if i_batch + j + 1 == n_images:\n            break\nprint(len(os.listdir('../output_images')))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if os.path.exists('images.zip'):\n    os.remove('images.zip')\nshutil.make_archive('images', 'zip', '../output_images')\n","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}