{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Description\n## Competition data\n|Title| [Generative Dog Images](https://www.kaggle.com/c/generative-dog-images)|\n|--\n|Characteristic| submit images; kernel only|\n|Deadline| August 9, 2019 11:59 PM UTC (2019/8/10 Sat 8:59 AM JST)|\n|Metric| MiFID - smaller is better.|\n|Submission| images.zip; contains 10,000 64x64x3 generated images| \n|Prohibited| Internet access, external data|\n\n## What I'm trying in this kernel\n* Build GAN by keras\n* Use flow_from_directory\n\n\n* preprocess: -1 to 1\n* generator: use tanh\n* gan: set trainable=false by wraped with Model\n* dataset/real: y=0.9 instead of 1.0\n\n### References:\n* https://keras.io/preprocessing/image/\n* https://machinelearningmastery.com/how-to-develop-a-generative-adversarial-network-for-an-mnist-handwritten-digits-from-scratch-in-keras/\n* https://github.com/keras-team/keras/issues/8585\n* https://github.com/eriklindernoren/Keras-GAN/blob/master/dcgan/dcgan.py\n* https://qiita.com/taku-buntu/items/0093a68bfae0b0ff879d"},{"metadata":{},"cell_type":"markdown","source":"# Import"},{"metadata":{"trusted":true},"cell_type":"code","source":"# numpy\nimport numpy as np\n# for operate list\nimport operator\nfrom functools import reduce\n# for models\nfrom keras.models import Sequential, Model\nfrom keras.layers import * # Dense, Conv2D, Flatten, Dropout, LeakyRelu\nfrom keras.optimizers import Adam, SGD\nfrom keras.utils.vis_utils import plot_model\nfrom keras_preprocessing.image import ImageDataGenerator\nfrom keras.models import load_model\n# for plot images\nimport matplotlib.pyplot as plt\n# for progress bar\nfrom tqdm import tqdm_notebook as tqdm\n# for make zip archive\nimport shutil\n# for mkdir\nimport pathlib\n# for save image to file\nfrom imageio import imsave","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Setting"},{"metadata":{"trusted":true},"cell_type":"code","source":"PATH_TRAIN_IMAGE = '../input/all-dogs'\nSHAPE_IMAGE = (64, 64, 3)\nBATCH_SIZE = 32\n# size of the latent space\nlatent_dim = 100\nnum_epoch = 100\nnum_batch = 20579 // BATCH_SIZE // 2\nprint(num_batch)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data loader"},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(\n    horizontal_flip = True,\n    rotation_range = 20,\n    width_shift_range = 0.2,\n    height_shift_range = 0.2,\n    preprocessing_function = lambda X: (X-127.5)/127.5,\n#     rescale = 1./255\n)\ndata_loader=datagen.flow_from_directory(\n    PATH_TRAIN_IMAGE, # directory\n    target_size = SHAPE_IMAGE[:-1], \n    class_mode = None,\n    batch_size = BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_real_samples(n_samples=BATCH_SIZE):\n    return next(data_loader)[:n_samples], np.ones((n_samples, 1))*0.9","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_images(ary, rows, cols):\n    plt.figure(figsize=(cols*3, rows*3))\n    for row in range(rows):\n        for col in range(cols):\n            plt.subplot(rows, cols, row*cols+col+1)\n            img = (ary[row*cols+col, :] + 1) / 2\n#             img = ary[row*cols+col]\n            plt.axis('off')\n            plt.title(f'{row*cols+col}')\n            plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data, y = generate_real_samples()\nprint('shape of data:', data.shape) # => (32, 64, 64, 3)\nprint('min, max of data:', data.min(), data.max()) # => 0.0 1.0\nprint('shape of y', y.shape) # => (32, 1)\nprint('min, max of y', y.min(), y.max()) # => 1.0 1.0\nprint('head 5 of y', y[:5]) # => [[1.] [1.] ...]\n\nshow_images(data,2, 5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Discriminator"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"def define_discriminator():\n    model = Sequential([\n        InputLayer(input_shape=SHAPE_IMAGE),\n        Conv2D(32, kernel_size=3, strides=2, padding='same'),\n        LeakyReLU(alpha=0.2),\n        Dropout(0.25),\n        Conv2D(64, kernel_size=3, strides=2, padding='same'),\n        ZeroPadding2D(padding=((0,1),(0,1))),\n        LeakyReLU(alpha=0.2),\n        BatchNormalization(momentum=0.8),\n        Dropout(0.25),\n        Conv2D(128, kernel_size=3, strides=2, padding='same'),\n        LeakyReLU(alpha=0.2),\n        Dropout(0.25),\n        BatchNormalization(momentum=0.8),\n        Conv2D(256, kernel_size=3, strides=2, padding='same'),\n        LeakyReLU(alpha=0.2),\n        Dropout(0.25),\n        Conv2D(512, kernel_size=3, strides=2, padding='same'),\n        LeakyReLU(alpha=0.2),\n        Dropout(0.25),\n        Flatten(),\n        Dense(1, activation='sigmoid')\n    ])\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# compile discriminator\ndiscriminator = define_discriminator()\ndiscriminator_opt = Adam(lr=0.0002, beta_1=0.5)\ndiscriminator.compile(loss='binary_crossentropy', optimizer=discriminator_opt)\ndiscriminator.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"def define_generator():\n    struct_ = (64, 8, 8)\n    n_nodes = reduce(operator.mul, struct_) # (reduce '* struct_)\n    print(f'generator input dim={n_nodes}')\n    model = Sequential([\n        Dense(n_nodes, activation='relu', input_shape=(latent_dim,)),\n        Reshape((*struct_[1:], struct_[0])),\n        BatchNormalization(momentum=0.8),\n        \n        # upsample to 16x16\n        UpSampling2D(),\n        Conv2D(struct_[0], kernel_size=3, padding='same'),\n        Activation('relu'),\n        BatchNormalization(momentum=0.8),\n        # upsample to 32x32\n        UpSampling2D(),\n        Conv2D(struct_[0]//2, kernel_size=3, padding='same'),\n        Activation('relu'),\n        BatchNormalization(momentum=0.8),\n        # upsample to 64x64\n        UpSampling2D(),\n        Conv2D(struct_[0]//4, kernel_size=3, padding='same'),\n        Activation('relu'),\n        BatchNormalization(momentum=0.8),\n        Conv2D(3, kernel_size=3, padding='same'),\n        Activation('tanh'),\n    ])\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# make generator\ngenerator = define_generator()\ngenerator.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# generate points in latent space as input for the generator\ndef generate_latent_points(latent_dim, n_samples):\n#     noize = np.random.normal(0.5, 1, (n_samples, latent_dim))\n    noize = np.random.uniform(-1, 1, (n_samples, latent_dim))\n    return noize","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# use the generator to generate n fake examples, with class labels\ndef generate_fake_samples(g_model, latent_dim, n_samples):\n    # generate points in latent space\n    x_input = generate_latent_points(latent_dim, n_samples)\n    # predict outputs\n    X = g_model.predict(x_input)\n    # create 'fake' class labels (0)\n    y = np.zeros((n_samples, 1))\n    return X, y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# generator output test\nX, y = generate_fake_samples(generator, latent_dim, 10)\nshow_images(X, 2, 5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# GAN"},{"metadata":{"trusted":true},"cell_type":"code","source":"# define the combined generator and discriminator model, for updating the generator\ndef define_gan(g_model, d_model):\n    d_model_fixed = Model(inputs=d_model.inputs, outputs=d_model.outputs)\n    d_model_fixed.trainable = False\n    # connect them\n    model = Sequential([\n        InputLayer(input_shape=(latent_dim,)),\n        g_model,\n        d_model_fixed\n    ])\n    # compile model\n    opt = Adam(lr=0.0002, beta_1=0.5)\n    model.compile(loss='binary_crossentropy', optimizer=opt)\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# compile gan\ngan = define_gan(generator, discriminator)\ngan.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train"},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_discriminator():\n    # get randomly selected 'real' samples\n    X_real, y_real = generate_real_samples(BATCH_SIZE//2)\n    loss_real = discriminator.train_on_batch(X_real, y_real)\n    # generate 'fake' examples\n    X_fake, y_fake = generate_fake_samples(generator, latent_dim, BATCH_SIZE//2)\n    loss_fake = discriminator.train_on_batch(X_fake, y_fake)\n    return (loss_real+loss_fake)*0.5\n# train_discriminator()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_gan(num_loop=1):\n    # prepare points in latent space as input for the generator\n    X = generate_latent_points(latent_dim, BATCH_SIZE)\n    # create inverted labels for the fake samples\n    y = np.ones((BATCH_SIZE, 1))*0.9\n    for i in range(num_loop):\n        loss = gan.train_on_batch(X, y)\n    return loss\n# train_gan()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train all\nhistory = np.zeros((num_epoch, num_batch, 2))\ndogs_at_epoch = np.zeros((num_epoch, *SHAPE_IMAGE))\n\nfor i in tqdm(range(num_epoch), desc='epoch'):\n    data_loader.reset()\n    pbar_batch = tqdm(range(num_batch), desc='batch')\n    \n    for j in pbar_batch:\n        d_loss = train_discriminator()\n        g_loss = train_gan()\n        pbar_batch.set_description(f'{i:>2}, d_loss:{d_loss:.2}, g_loss:{g_loss:.2}')\n        history[i, j, :] = d_loss, g_loss\n        \n    generated_imgs = generate_fake_samples(generator, latent_dim, 5)[0]\n    show_images(generated_imgs, 1, 5)\n    dogs_at_epoch[i, :] = generated_imgs[0,:]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# show_images(dogs_at_epoch[:, :, :, :], num_epoch//5, 5)\nplt.plot(history[:,-1,:])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"# Generate dog images"},{"metadata":{"trusted":true},"cell_type":"code","source":"# generate images\nlatent_points = generate_latent_points(latent_dim, 10000)\n# generate images\nX = generator.predict(latent_points)\n\nprint(X.shape, X[0].min(), X[0].max())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"show_images(X, 2, 5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"imgs = [((img+1) * 127.5).astype(np.uint8) for img in X]\nnp.array(imgs).min(), np.array(imgs).max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_DIR = pathlib.Path('images')\nif not IMG_DIR.exists():\n    IMG_DIR.mkdir()\n\nfor n in range(len(imgs)):\n    imsave(IMG_DIR/f'dog_{n}.png', imgs[n])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"shutil.make_archive('images', 'zip', 'images')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -rf images","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls","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}