{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<center><img src='https://claudemonetgallery.org/thumbnail/81000/81127/mini_small/Self-Portrait-With-A-Beret.jpg?ts=1459229076' height=350></center>\n<p>\n<h1><center> I’m Something of a Painter Myself </center></h1>\n<h2><center> Introduction to CycleGAN - Monet paintings </center></h2>\n\n#### This notebook is based on the [competition baseline](https://www.kaggle.com/amyjang/monet-cyclegan-tutorial), I just did some refactoring to create helper functions and make everything easier to experiment with, besides that the main contribution is adding the possibility to use data augmentations, as we have very little data here, this will probably help.\n\n#### CycleGAN references:\n- [Git repository](https://junyanz.github.io/CycleGAN/) with many cool informations.\n- [ArXiv paper](https://arxiv.org/pdf/1703.10593.pdf)\n- [Understanding and Implementing CycleGAN in TensorFlow](https://hardikbansal.github.io/CycleGANBlog/)\n\n\n### What is CycleGAN?\n\nFrom the authors:\n> We present an approach for learning to translate an image from a source domain X to a target domain Y in the absence of paired examples. Our goal is to learn a mapping G: X → Y, such that the distribution of images from G(X) is indistinguishable from the distribution Y using an adversarial loss. Because this mapping is highly under-constrained, we couple it with an inverse mapping F: Y → X and introduce a cycle consistency loss to push F(G(X)) ≈ X (and vice versa).\n\nIn essence it maps and image to a given domaind, if you are turning horses into zebra the image will be the horse and the domain is the zebras, in our case the photos are the image and the domain are the Monet paintings.\n\n#### Turning horses into zebras and zebras into horses\n![](https://raw.githubusercontent.com/dimitreOliveira/MachineLearning/master/Kaggle/I%E2%80%99m%20Something%20of%20a%20Painter%20Myself/cyclegan_horse-zebra.jpg)\n\n#### Turning photos into Monet paintings (our task)\n![](https://junyanz.github.io/CycleGAN/images/painting2photo.jpg)\n\n\nBut it doesn't always works as expected\n<img src='https://junyanz.github.io/CycleGAN/images/failure_putin.jpg' height=300, width=300>\n\n### CycleGAN architecture\n\nLooking at the code below may be hard to get what is happening, this image will help the understanding\n\n<img src='https://hardikbansal.github.io/CycleGANBlog/images/model.jpg' height=700, width=700>\n\nFirst, we get the regular generator discriminator thing, where the generator tries to generate images that seem to be drawn to the given domain (in the example will be creating zebra images), but it would be possible that the generator generates only the same zebra image or zebra images that do not look like the imputed horse image, this is why the model has a second generator, this second generator uses the first generated image and tries to recreate the original imputed horse image, this way the first generator has to generate zebra images that look like the imputed horse image.\n\n#### In the end, you will get 4 sub-models:\n- A generator that can generate zebras images\n- A generator that can generate horses images\n- A discriminator that can identify real zebras images\n- A discriminator that can identify real horses images\n\n\n#### Let's get to the code","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa\nfrom kaggle_datasets import KaggleDatasets\nfrom tensorflow.keras.callbacks import History\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport re\nimport os\nimport math\nimport random\nimport cv2\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Device:', tpu.master())\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept:\n    strategy = tf.distribute.get_strategy()","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","execution":{"iopub.status.busy":"2021-06-24T15:45:19.211036Z","iopub.execute_input":"2021-06-24T15:45:19.211513Z","iopub.status.idle":"2021-06-24T15:45:30.826623Z","shell.execute_reply.started":"2021-06-24T15:45:19.211398Z","shell.execute_reply":"2021-06-24T15:45:30.825813Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path('gan-getting-started')\nfn_monet = tf.io.gfile.glob(str(GCS_PATH + '/monet_jpg/*.jpg'))\nfn_photo = tf.io.gfile.glob(str(GCS_PATH + '/photo_jpg/*.jpg'))","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:30.828047Z","iopub.execute_input":"2021-06-24T15:45:30.828442Z","iopub.status.idle":"2021-06-24T15:45:31.963347Z","shell.execute_reply.started":"2021-06-24T15:45:30.828391Z","shell.execute_reply":"2021-06-24T15:45:31.962261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Display Images**","metadata":{}},{"cell_type":"code","source":"# View one image\nimport imageio\nphoto_image_names = os.listdir('../input/gan-getting-started/photo_jpg')\nphoto_img = imageio.imread(os.path.join('../input/gan-getting-started/photo_jpg', photo_image_names[105]))\nplt.imshow(photo_img)\n\nplt.figure()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:31.965713Z","iopub.execute_input":"2021-06-24T15:45:31.966127Z","iopub.status.idle":"2021-06-24T15:45:32.380441Z","shell.execute_reply.started":"2021-06-24T15:45:31.966083Z","shell.execute_reply":"2021-06-24T15:45:32.379444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# View one image\nimport imageio\nmonet_image_names = os.listdir('../input/gan-getting-started/monet_jpg')\nmonet_img = imageio.imread(os.path.join('../input/gan-getting-started/monet_jpg', monet_image_names[105]))\nplt.imshow(monet_img)\n\nplt.figure()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:32.382262Z","iopub.execute_input":"2021-06-24T15:45:32.382831Z","iopub.status.idle":"2021-06-24T15:45:32.582482Z","shell.execute_reply.started":"2021-06-24T15:45:32.382788Z","shell.execute_reply":"2021-06-24T15:45:32.581372Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Image preprocessing**","metadata":{}},{"cell_type":"code","source":"rand_monet = r\"../input/gan-getting-started/monet_jpg/0260d15306.jpg\"\nrand_photo = r\"../input/gan-getting-started/photo_jpg/000ded5c41.jpg\"","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:32.583921Z","iopub.execute_input":"2021-06-24T15:45:32.584201Z","iopub.status.idle":"2021-06-24T15:45:32.588087Z","shell.execute_reply.started":"2021-06-24T15:45:32.584173Z","shell.execute_reply":"2021-06-24T15:45:32.587029Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def color_graph(image_path, figsize=(16, 4)):\n    plt.figure(figsize=figsize)\n    \n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n    plt.subplot(1, 2, 1)\n    plt.imshow(img)\n    plt.axis('off')\n    \n    chans = cv2.split(img)\n    colors = (\"b\", \"g\", \"r\")\n    plt.subplot(1, 2, 2)\n    plt.title(\"'Flattened' Color Histogram\")\n    plt.xlabel(\"Bins\")\n    plt.ylabel(\"# of Pixels\")\n    features = []\n    # loop over the image channels\n    for (chan, color) in zip(chans, colors):\n        # create a histogram for the current channel and\n        # concatenate the resulting histograms for each\n        # channel\n        hist = cv2.calcHist([chan], [0], None, [256], [0, 256])\n        features.extend(hist)\n        # plot the histogram\n        plt.plot(hist, color = color)\n        plt.xlim([0, 256])\n    \n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:32.589179Z","iopub.execute_input":"2021-06-24T15:45:32.589507Z","iopub.status.idle":"2021-06-24T15:45:32.60313Z","shell.execute_reply.started":"2021-06-24T15:45:32.589408Z","shell.execute_reply":"2021-06-24T15:45:32.602198Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Monet: \")\ncolor_graph(rand_monet)\nprint(\"\\nPhoto: \")\ncolor_graph(rand_photo)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:32.60441Z","iopub.execute_input":"2021-06-24T15:45:32.604872Z","iopub.status.idle":"2021-06-24T15:45:33.158565Z","shell.execute_reply.started":"2021-06-24T15:45:32.604781Z","shell.execute_reply":"2021-06-24T15:45:33.157476Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE =  4\n\n\ndef parse_function(filename):\n    image_string = tf.io.read_file(filename)\n    image = tf.image.decode_jpeg(image_string, channels=3)\n    image = (tf.cast(image,tf.float32)/ 127.5) - 1\n    image = tf.reshape(image, [256, 256,3])\n    return image\n\ndef data_augment(image):\n    p_rotate = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    p_spatial = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    p_crop = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    if p_crop > .5:\n        image = tf.image.resize(image, [286, 286])\n        image = tf.image.random_crop(image, size=[256, 256, 3])\n        if p_crop > .9:\n            image = tf.image.resize(image, [300, 300])\n            image = tf.image.random_crop(image, size=[256, 256, 3])\n    \n    if p_rotate > .9:\n        image = tf.image.rot90(image, k=3)\n    elif p_rotate > .7:\n        image = tf.image.rot90(image, k=2)\n    elif p_rotate > .5:\n        image = tf.image.rot90(image, k=1)\n        \n    if p_spatial > .6:\n        image = tf.image.random_flip_left_right(image)\n        image = tf.image.random_flip_up_down(image)\n        if p_spatial > .9:\n            image = tf.image.transpose(image)\n    \n    return image\n\nnum_parallel_calls=tf.data.experimental.AUTOTUNE\ndef getSet(filenames):\n    dataset = tf.data.Dataset.from_tensor_slices(filenames)\n    dataset = dataset.shuffle(len(filenames))\n    dataset = dataset.map(parse_function, num_parallel_calls)\n    dataset = dataset.map(data_augment, num_parallel_calls)\n    dataset = dataset.repeat()\n    dataset = dataset.batch(BATCH_SIZE,drop_remainder=True)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(num_parallel_calls)\n    return dataset\n\nmonet_ds=getSet(fn_monet)\nphoto_ds=getSet(fn_photo)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:33.161139Z","iopub.execute_input":"2021-06-24T15:45:33.161453Z","iopub.status.idle":"2021-06-24T15:45:34.234487Z","shell.execute_reply.started":"2021-06-24T15:45:33.16142Z","shell.execute_reply":"2021-06-24T15:45:34.233354Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Model Building**","metadata":{}},{"cell_type":"markdown","source":"We'll be using a UNET architecture for our CycleGAN. To build our generator, let's first define our downsample and upsample methods.\n\nThe downsample, as the name suggests, reduces the 2D dimensions, the width and height, of the image by the stride. The stride is the length of the step the filter takes. Since the stride is 2, the filter is applied to every other pixel, hence reducing the weight and height by 2.\n\nWe'll be using an instance normalization instead of batch normalization. As the instance normalization is not standard in the TensorFlow API, we'll use the layer from TensorFlow Add-ons.","metadata":{}},{"cell_type":"code","source":"OUTPUT_CHANNELS = 3\n\ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = keras.Sequential()\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same',kernel_initializer=initializer, use_bias=False))\n\n    if apply_instancenorm:\n        result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n\n    result.add(layers.LeakyReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:34.236497Z","iopub.execute_input":"2021-06-24T15:45:34.236849Z","iopub.status.idle":"2021-06-24T15:45:34.243658Z","shell.execute_reply.started":"2021-06-24T15:45:34.23681Z","shell.execute_reply":"2021-06-24T15:45:34.24249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Upsample does the opposite of downsample and increases the dimensions of the of the image. Conv2DTranspose does basically the opposite of a Conv2D layer.","metadata":{}},{"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = keras.Sequential()\n    result.add(layers.Conv2DTranspose(filters, size, strides=2,padding='same',kernel_initializer=initializer,use_bias=False))\n\n    result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n\n    if apply_dropout:\n        result.add(layers.Dropout(0.5))\n\n    result.add(layers.ReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:34.24508Z","iopub.execute_input":"2021-06-24T15:45:34.245419Z","iopub.status.idle":"2021-06-24T15:45:34.26255Z","shell.execute_reply.started":"2021-06-24T15:45:34.245347Z","shell.execute_reply":"2021-06-24T15:45:34.261189Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Generator**","metadata":{}},{"cell_type":"markdown","source":"The generator first downsamples the input image and then upsample while establishing long skip connections. Skip connections are a way to help bypass the vanishing gradient problem by concatenating the output of a layer to multiple layers instead of only one. Here we concatenate the output of the downsample layer to the upsample layer in a symmetrical fashion.\n64-->512=Encoder, 512-->Till Upsample droput 512,4 with drop out is -->Transformer and 512-->64 is decoder","metadata":{}},{"cell_type":"code","source":"def Generator():\n    inputs = layers.Input(shape=[256,256,3])\n\n    # bs = batch size\n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False), # (bs, 128, 128, 64) 256/2（padding same，(256-4+1)/2）\n        downsample(128, 4), # (bs, 64, 64, 128)\n        downsample(256, 4), # (bs, 32, 32, 256)\n        downsample(512, 4), # (bs, 16, 16, 512)\n        downsample(512, 4), # (bs, 8, 8, 512)\n        downsample(512, 4), # (bs, 4, 4, 512)\n        downsample(512, 4), # (bs, 2, 2, 512)\n        downsample(512, 4), # (bs, 1, 1, 512)\n    ]\n\n    up_stack = [\n        upsample(512, 4, apply_dropout=True), # (bs, 2, 2, 1024) 1*2（padding same，(1*2-4+1)），，\n        upsample(512, 4, apply_dropout=True), # (bs, 4, 4, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 8, 8, 1024)\n        upsample(512, 4), # (bs, 16, 16, 1024)\n        upsample(256, 4), # (bs, 32, 32, 512)\n        upsample(128, 4), # (bs, 64, 64, 256)\n        upsample(64, 4), # (bs, 128, 128, 128)\n    ]\n\n    initializer = tf.random_normal_initializer(0., 0.02)\n    last = layers.Conv2DTranspose(OUTPUT_CHANNELS, 4,strides=2,padding='same',kernel_initializer=initializer,activation='tanh') # (bs, 256, 256, 3)，，\n\n    x = inputs\n\n  \n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n\n    skips = reversed(skips[:-1])\n\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = layers.Concatenate()([x, skip])\n\n    x = last(x)\n\n    return keras.Model(inputs=inputs, outputs=x)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:34.264157Z","iopub.execute_input":"2021-06-24T15:45:34.264488Z","iopub.status.idle":"2021-06-24T15:45:34.277184Z","shell.execute_reply.started":"2021-06-24T15:45:34.264459Z","shell.execute_reply":"2021-06-24T15:45:34.275953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tf.keras.utils.plot_model(Generator(), show_shapes=True, dpi=64)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:34.278434Z","iopub.execute_input":"2021-06-24T15:45:34.278786Z","iopub.status.idle":"2021-06-24T15:45:36.573634Z","shell.execute_reply.started":"2021-06-24T15:45:34.278757Z","shell.execute_reply":"2021-06-24T15:45:36.572446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Discriminator**","metadata":{}},{"cell_type":"markdown","source":"The discriminator takes in the input image and classifies it as real or fake (generated). Instead of outputing a single node, the discriminator outputs a smaller 2D image with higher pixel values indicating a real classification and lower values indicating a fake classification.\nEach layer of discriminator has a instance normal lization so as to normalize the model and last has the choice between real and fake done by sigmoid.","metadata":{}},{"cell_type":"code","source":"def Discriminator():\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    inp = layers.Input(shape=[256, 256, 3], name='input_image')\n\n    x = inp\n\n    down1 = downsample(64, 4, False)(x) # (bs, 128, 128, 64)\n    down2 = downsample(128, 4)(down1) # (bs, 64, 64, 128)\n    down3 = downsample(256, 4)(down2) # (bs, 32, 32, 256)\n\n    zero_pad1 = layers.ZeroPadding2D()(down3) # (bs, 34, 34, 256)\n    conv = layers.Conv2D(512, 4, strides=1,kernel_initializer=initializer,use_bias=False)(zero_pad1) # (bs, 31, 31, 512)\n\n    norm1 = tfa.layers.InstanceNormalization(gamma_initializer=gamma_init)(conv)\n\n    leaky_relu = layers.LeakyReLU()(norm1)\n\n    zero_pad2 = layers.ZeroPadding2D()(leaky_relu) # (bs, 33, 33, 512)\n\n    last = layers.Conv2D(1, 4, strides=1,\n                         kernel_initializer=initializer)(zero_pad2) # (bs, 30, 30, 1)，，\n\n    return tf.keras.Model(inputs=inp, outputs=last)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:36.575727Z","iopub.execute_input":"2021-06-24T15:45:36.576131Z","iopub.status.idle":"2021-06-24T15:45:36.587181Z","shell.execute_reply.started":"2021-06-24T15:45:36.576089Z","shell.execute_reply":"2021-06-24T15:45:36.586156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tf.keras.utils.plot_model(Discriminator(), show_shapes=True, dpi=64)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:36.589299Z","iopub.execute_input":"2021-06-24T15:45:36.58985Z","iopub.status.idle":"2021-06-24T15:45:36.957881Z","shell.execute_reply.started":"2021-06-24T15:45:36.589682Z","shell.execute_reply":"2021-06-24T15:45:36.956667Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **CycleGan**","metadata":{}},{"cell_type":"markdown","source":"**Training a CycleGAN**:\nIn the CycleGAN’s case, the architecture is complex, and as a result, we need a structure that allows us to keep accessing the original attributes and methods that we have defined. As a result, we will write out the CycleGAN as a Python class of its own with methods to build the Generator and Discriminator, and run the training.\n\nFor the training to execute we will need a seperate Generator() and discriminator() function which we will feed to CycleGAN as methods which in turn needs the upsample() and downsample() of image.\n\nFor downsampling we are using the Conv2D() as primary layer and LeakyReLU() as activation\nFor upsampling we are using the Conv2DTranspose() as primary layer and Dropout() at 0.3, ReLU() as secondary layers","metadata":{}},{"cell_type":"code","source":"class CycleGan(keras.Model):\n    def __init__(self,monet_generator,photo_generator,monet_discriminator,photo_discriminator,lambda_cycle=15):\n        super(CycleGan, self).__init__()\n        self.m_gen = monet_generator\n        self.p_gen = photo_generator\n        self.m_disc = monet_discriminator\n        self.p_disc = photo_discriminator\n        self.lambda_cycle = lambda_cycle\n        \n    def compile(self,m_gen_optimizer,p_gen_optimizer,m_disc_optimizer,p_disc_optimizer,gen_loss_fn,disc_loss_fn,cycle_loss_fn,identity_loss_fn):\n        super(CycleGan, self).compile()\n        self.m_gen_optimizer = m_gen_optimizer\n        self.p_gen_optimizer = p_gen_optimizer\n        self.m_disc_optimizer = m_disc_optimizer\n        self.p_disc_optimizer = p_disc_optimizer\n        \n        self.gen_loss_fn = gen_loss_fn\n        self.disc_loss_fn = disc_loss_fn\n        self.cycle_loss_fn = cycle_loss_fn\n        self.identity_loss_fn = identity_loss_fn\n        \n    def train_step(self, batch_data):\n        real_monet, real_photo = batch_data\n        #real_monet y，real_photo x，m_gen G，p_gen F，p_disc DX，m_disc DY\n        \n        with tf.GradientTape(persistent=True) as tape:\n            fake_monet = self.m_gen(real_photo, training=True)#G(x)\n            cycled_photo = self.p_gen(fake_monet, training=True)#F(G(x))\n\n            fake_photo = self.p_gen(real_monet, training=True)#F(y)\n            cycled_monet = self.m_gen(fake_photo, training=True)#G(F(y))\n\n            same_monet = self.m_gen(real_monet, training=True)#G(y)\n            same_photo = self.p_gen(real_photo, training=True)#F(x)\n\n            disc_real_monet = self.m_disc(real_monet, training=True)#DY(y)\n            disc_real_photo = self.p_disc(real_photo, training=True)#DX(x)\n\n            disc_fake_monet = self.m_disc(fake_monet, training=True)#DY(G(x))\n            disc_fake_photo = self.p_disc(fake_photo, training=True)#DX(F(y))\n\n            monet_gen_loss = self.gen_loss_fn(disc_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(disc_fake_photo)\n\n            total_cycle_loss = self.cycle_loss_fn(real_monet, cycled_monet, self.lambda_cycle) + self.cycle_loss_fn(real_photo, cycled_photo, self.lambda_cycle)\n\n            # evaluates total generator loss\n            total_monet_gen_loss = monet_gen_loss + total_cycle_loss + self.identity_loss_fn(real_monet, same_monet, self.lambda_cycle)\n            total_photo_gen_loss = photo_gen_loss + total_cycle_loss + self.identity_loss_fn(real_photo, same_photo, self.lambda_cycle)\n\n            # evaluates discriminator loss\n            monet_disc_loss = self.disc_loss_fn(disc_real_monet, disc_fake_monet)\n            photo_disc_loss = self.disc_loss_fn(disc_real_photo, disc_fake_photo)\n\n       \n        monet_generator_gradients = tape.gradient(total_monet_gen_loss,self.m_gen.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_gen_loss,self.p_gen.trainable_variables)\n        monet_discriminator_gradients = tape.gradient(monet_disc_loss,self.m_disc.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_disc_loss,self.p_disc.trainable_variables)\n\n        self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients,self.m_gen.trainable_variables))\n        self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients,self.p_gen.trainable_variables))\n        self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients,self.m_disc.trainable_variables))\n        self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients,self.p_disc.trainable_variables))\n        \n        '''if (iteration + 1) % sample_interval == 0:\n\n            # Save losses and accuracies so they can be plotted after training\n            losses.append((d_loss, g_loss))\n            accuracies.append(100.0 * accuracy)\n            iteration_checkpoints.append(iteration + 1)\n\n            # Output training progress\n            print(\"%d [D loss: %f, acc.: %.2f%%] [G loss: %f]\" %\n                  (iteration + 1, d_loss, 100.0 * accuracy, g_loss))\n\n            # Output a sample of generated image\n            sample_images(generator)'''\n        #https://github.com/GANs-in-Action/gans-in-action/blob/master/chapter-3/Chapter_3_GAN.ipynb\n        \n        return {\"monet_gen_loss\": total_monet_gen_loss,\"photo_gen_loss\": total_photo_gen_loss,\"monet_disc_loss\": monet_disc_loss,\"photo_disc_loss\": photo_disc_loss}","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:36.960114Z","iopub.execute_input":"2021-06-24T15:45:36.96055Z","iopub.status.idle":"2021-06-24T15:45:36.979937Z","shell.execute_reply.started":"2021-06-24T15:45:36.9605Z","shell.execute_reply":"2021-06-24T15:45:36.978841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Discrimiator's loss","metadata":{}},{"cell_type":"markdown","source":"The discriminator loss function below compares real images to a matrix of 1s and fake images to a matrix of 0s. The perfect discriminator will output all 1s for real images and all 0s for fake images. The discriminator loss outputs the average of the real and generated loss.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def discriminator_loss(real, generated):\n        real_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(real), real)#对于真实样本，通过鉴别器后与全1矩阵计算交叉熵，使得D对真实数据输出尽可能接近1\n        generated_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.zeros_like(generated), generated)#对于生成样本，通过鉴别器后与全0矩阵计算交叉熵，使得D对生成数据输出尽可能接近0\n        total_disc_loss = real_loss + generated_loss\n        return total_disc_loss * 0.5","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:36.981316Z","iopub.execute_input":"2021-06-24T15:45:36.981626Z","iopub.status.idle":"2021-06-24T15:45:36.999918Z","shell.execute_reply.started":"2021-06-24T15:45:36.981585Z","shell.execute_reply":"2021-06-24T15:45:36.998833Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The generator wants to fool the discriminator into thinking the generated image is real. The perfect generator will have the discriminator output only 1s. Thus, it compares the generated image to a matrix of 1s to find the loss.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def generator_loss(generated):\n        return tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(generated), generated)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:37.001519Z","iopub.execute_input":"2021-06-24T15:45:37.00199Z","iopub.status.idle":"2021-06-24T15:45:37.016197Z","shell.execute_reply.started":"2021-06-24T15:45:37.001946Z","shell.execute_reply":"2021-06-24T15:45:37.015163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We want our original photo and the twice transformed photo to be similar to one another. Thus, we can calculate the cycle consistency loss be finding the average of their difference.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n        loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))\n        return LAMBDA * loss1","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:37.017718Z","iopub.execute_input":"2021-06-24T15:45:37.018022Z","iopub.status.idle":"2021-06-24T15:45:37.032191Z","shell.execute_reply.started":"2021-06-24T15:45:37.017984Z","shell.execute_reply":"2021-06-24T15:45:37.031261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The identity loss compares the image with its generator (i.e. photo with photo generator). If given a photo as input, we want it to generate the same image as the image was originally a photo. The identity loss compares the input with the output of the generator.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def identity_loss(real_image, same_image, LAMBDA):\n        loss = tf.reduce_mean(tf.abs(real_image - same_image))\n        return LAMBDA * 0.5 * loss","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:37.034099Z","iopub.execute_input":"2021-06-24T15:45:37.03449Z","iopub.status.idle":"2021-06-24T15:45:37.044035Z","shell.execute_reply.started":"2021-06-24T15:45:37.034447Z","shell.execute_reply":"2021-06-24T15:45:37.043028Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    monet_generator_optimizer = tf.keras.optimizers.Adamax(2e-4, beta_1=0.5)\n    photo_generator_optimizer = tf.keras.optimizers.Adamax(2e-4, beta_1=0.5)\n    monet_discriminator_optimizer = tf.keras.optimizers.Adamax(2e-4, beta_1=0.5)\n    photo_discriminator_optimizer = tf.keras.optimizers.Adamax(2e-4, beta_1=0.5)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:37.04547Z","iopub.execute_input":"2021-06-24T15:45:37.045764Z","iopub.status.idle":"2021-06-24T15:45:37.056646Z","shell.execute_reply.started":"2021-06-24T15:45:37.045736Z","shell.execute_reply":"2021-06-24T15:45:37.055718Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"global im_to_gif\nim_to_gif = np.zeros((30,256,256,3))\nphoto = next(iter(monet_ds))\nnum_photo = 0\nplt.imshow(photo[num_photo]*0.5 + 0.5)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:37.057944Z","iopub.execute_input":"2021-06-24T15:45:37.058224Z","iopub.status.idle":"2021-06-24T15:45:40.693064Z","shell.execute_reply.started":"2021-06-24T15:45:37.058197Z","shell.execute_reply":"2021-06-24T15:45:40.692056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class GANMonitor(keras.callbacks.Callback):\n   \"\"\"A callback to generate and save images after each epoch\"\"\"\n\n   def on_epoch_end(self, epoch, logs=None):\n        prediction =  monet_generator(photo, training=False)[num_photo].numpy()\n        prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n        im_to_gif[epoch] = prediction   ","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:40.694435Z","iopub.execute_input":"2021-06-24T15:45:40.694763Z","iopub.status.idle":"2021-06-24T15:45:40.701127Z","shell.execute_reply.started":"2021-06-24T15:45:40.694733Z","shell.execute_reply":"2021-06-24T15:45:40.700018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    monet_generator=Generator()\n    photo_generator=Generator()\n    monet_discriminator=Discriminator()\n    photo_discriminator=Discriminator()\nwith strategy.scope():\n    cycle_gan_model = CycleGan(monet_generator,photo_generator,monet_discriminator,photo_discriminator)\n    cycle_gan_model.compile(\n            m_gen_optimizer = monet_generator_optimizer,\n            p_gen_optimizer = photo_generator_optimizer,\n            m_disc_optimizer = monet_discriminator_optimizer,\n            p_disc_optimizer = photo_discriminator_optimizer,\n            gen_loss_fn = generator_loss,\n            disc_loss_fn = discriminator_loss,\n            cycle_loss_fn = calc_cycle_loss,\n            identity_loss_fn = identity_loss\n        )\nplotter = GANMonitor()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:40.706441Z","iopub.execute_input":"2021-06-24T15:45:40.70687Z","iopub.status.idle":"2021-06-24T15:45:49.719963Z","shell.execute_reply.started":"2021-06-24T15:45:40.706827Z","shell.execute_reply":"2021-06-24T15:45:49.71886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping\nearly_stop = EarlyStopping(monitor='generator_loss', mode='min', patience=1,restore_best_weights=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:49.721941Z","iopub.execute_input":"2021-06-24T15:45:49.722222Z","iopub.status.idle":"2021-06-24T15:45:49.726923Z","shell.execute_reply.started":"2021-06-24T15:45:49.722194Z","shell.execute_reply":"2021-06-24T15:45:49.725933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"steps_per_epoch=(max(len(fn_monet), len(fn_photo)))//BATCH_SIZE\nhistory=cycle_gan_model.fit(tf.data.Dataset.zip((monet_ds, photo_ds)),epochs=25,steps_per_epoch=steps_per_epoch,callbacks=[History(),plotter])","metadata":{"execution":{"iopub.status.busy":"2021-06-24T15:45:49.728224Z","iopub.execute_input":"2021-06-24T15:45:49.728504Z","iopub.status.idle":"2021-06-24T17:40:21.918804Z","shell.execute_reply.started":"2021-06-24T15:45:49.728477Z","shell.execute_reply":"2021-06-24T17:40:21.91764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"monet_generator.save('gen_monet.h5')\nphoto_generator.save('gen_photo.h5')\nmonet_discriminator.save('disc_monet.h5')\nphoto_discriminator.save('disc_photo.h5')\nimport pickle","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:21.920733Z","iopub.execute_input":"2021-06-24T17:40:21.921195Z","iopub.status.idle":"2021-06-24T17:40:39.226508Z","shell.execute_reply.started":"2021-06-24T17:40:21.921103Z","shell.execute_reply":"2021-06-24T17:40:39.225548Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open('history.pkl','wb') as f:\n    pickle.dump(history.history, f)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:39.227731Z","iopub.execute_input":"2021-06-24T17:40:39.228041Z","iopub.status.idle":"2021-06-24T17:40:39.235031Z","shell.execute_reply.started":"2021-06-24T17:40:39.228013Z","shell.execute_reply":"2021-06-24T17:40:39.23415Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gen_monet = tf.keras.models.load_model('./gen_monet.h5')\ngen_photo = tf.keras.models.load_model('./gen_photo.h5')\ndisc_monet = tf.keras.models.load_model('./disc_monet.h5')\ndisc_photo = tf.keras.models.load_model('./disc_photo.h5')","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:39.235951Z","iopub.execute_input":"2021-06-24T17:40:39.236257Z","iopub.status.idle":"2021-06-24T17:40:43.79137Z","shell.execute_reply.started":"2021-06-24T17:40:39.236229Z","shell.execute_reply":"2021-06-24T17:40:43.790072Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"_*Got the idea from https://www.kaggle.com/matkneky/monet-cyclegan-trials*_","metadata":{}},{"cell_type":"code","source":"def plot_acc_and_loss(history, load=False):\n    monet_g = []\n    photo_g = []\n    monet_d = []\n    photo_d = []\n    if load==True:\n        for i in range(np.array(history[\"monet_gen_loss\"]).shape[0]):\n            monet_g.append(np.array(history[\"monet_gen_loss\"][i]).squeeze().mean())\n            photo_g.append(np.array(history[\"photo_gen_loss\"][i]).squeeze().mean())\n            monet_d.append(np.array(history[\"monet_disc_loss\"][i]).squeeze().mean())\n            photo_d.append(np.array(history[\"photo_disc_loss\"][i]).squeeze().mean())\n    else:\n        for i in range(np.array(history.history[\"monet_gen_loss\"]).shape[0]):\n            monet_g.append(np.array(history.history[\"monet_gen_loss\"][i]).squeeze().mean())\n            photo_g.append(np.array(history.history[\"photo_gen_loss\"][i]).squeeze().mean())\n            monet_d.append(np.array(history.history[\"monet_disc_loss\"][i]).squeeze().mean())\n            photo_d.append(np.array(history.history[\"photo_disc_loss\"][i]).squeeze().mean())\n    \n    fig, axs = plt.subplots(1, 2, figsize=(15, 5))\n    axs[0].plot(monet_g,label=\"Monet\")\n    axs[0].plot(photo_g,label=\"Photo\")\n    axs[0].set_title(\"generator loss\")\n    axs[0].legend()\n\n    axs[1].plot(monet_d, label=\"Monet\")\n    axs[1].plot(photo_d,label=\"Photo\")\n    axs[1].set_title(\"discriminator loss\")\n    axs[1].legend()\n\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:43.792571Z","iopub.execute_input":"2021-06-24T17:40:43.793059Z","iopub.status.idle":"2021-06-24T17:40:43.808533Z","shell.execute_reply.started":"2021-06-24T17:40:43.79301Z","shell.execute_reply":"2021-06-24T17:40:43.807421Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Call this if loading outputs\nimport pickle\nwith (open(\"./history.pkl\", \"rb\")) as openfile:\n    history = pickle.load(openfile)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:43.809704Z","iopub.execute_input":"2021-06-24T17:40:43.810006Z","iopub.status.idle":"2021-06-24T17:40:43.828092Z","shell.execute_reply.started":"2021-06-24T17:40:43.809977Z","shell.execute_reply":"2021-06-24T17:40:43.827303Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history.keys()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:43.829112Z","iopub.execute_input":"2021-06-24T17:40:43.829521Z","iopub.status.idle":"2021-06-24T17:40:43.844579Z","shell.execute_reply.started":"2021-06-24T17:40:43.829483Z","shell.execute_reply":"2021-06-24T17:40:43.843916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Switch 'load' to false if you have trained the model\nplot_acc_and_loss(history, load=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:43.84588Z","iopub.execute_input":"2021-06-24T17:40:43.846158Z","iopub.status.idle":"2021-06-24T17:40:44.189019Z","shell.execute_reply.started":"2021-06-24T17:40:43.846131Z","shell.execute_reply":"2021-06-24T17:40:44.188229Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_gif(num_photo=0, load=False):\n    if load == True:\n        anim_file = '../input/cyclegangif/CycleGAN.gif'\n    else:\n        # Creating a gif from each predictions\n        anim_file = 'CycleGAN.gif'\n        init_pic = np.array(photo[num_photo]*0.5 + 0.5)\n        with imageio.get_writer(anim_file, mode='I') as writer:\n            # Three first frames are the converted picture\n            writer.append_data(init_pic)\n            writer.append_data(init_pic)\n            writer.append_data(init_pic)\n            for i in range(im_to_gif.shape[0]):\n                writer.append_data(im_to_gif[i])\n                writer.append_data(im_to_gif[i])\n                writer.append_data(im_to_gif[i])\n            for i in range(int(im_to_gif.shape[0])):\n                writer.append_data(im_to_gif[-1])\n    return anim_file","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:44.190134Z","iopub.execute_input":"2021-06-24T17:40:44.190574Z","iopub.status.idle":"2021-06-24T17:40:44.198194Z","shell.execute_reply.started":"2021-06-24T17:40:44.190531Z","shell.execute_reply":"2021-06-24T17:40:44.19722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Switch 'load' to false if you have trained the model and put the correct photo number\nanim_file = create_gif(num_photo=num_photo,\n                       load=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:44.199573Z","iopub.execute_input":"2021-06-24T17:40:44.200042Z","iopub.status.idle":"2021-06-24T17:40:44.219229Z","shell.execute_reply.started":"2021-06-24T17:40:44.200001Z","shell.execute_reply":"2021-06-24T17:40:44.217942Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def gen_input_img(num_photo=0, load=False):\n    fig, ax = plt.subplots(figsize=(5,5))\n    \n    if load == True:\n        img = np.array(PIL.Image.open('../input/gan-getting-started/monet_jpg/000c1e3bff.jpg'))\n        plt.imshow(img)\n        ax.axis(\"off\")\n        \n    else:\n        img = photo[3]*0.5 + 0.5\n        plt.imshow(img)\n        ax.axis(\"off\")\n        plt.title('Input photo')    \n","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:44.220875Z","iopub.execute_input":"2021-06-24T17:40:44.221171Z","iopub.status.idle":"2021-06-24T17:40:44.23604Z","shell.execute_reply.started":"2021-06-24T17:40:44.221142Z","shell.execute_reply":"2021-06-24T17:40:44.234917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Switch 'load' to false if you have trained the model and put the correct photo number\ngen_input_img(num_photo=num_photo,\n              load=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:44.237753Z","iopub.execute_input":"2021-06-24T17:40:44.238193Z","iopub.status.idle":"2021-06-24T17:40:44.391287Z","shell.execute_reply.started":"2021-06-24T17:40:44.238161Z","shell.execute_reply":"2021-06-24T17:40:44.390132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import PIL\nfrom IPython import display\nimport imageio\n\nimport shutil","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:44.392866Z","iopub.execute_input":"2021-06-24T17:40:44.393274Z","iopub.status.idle":"2021-06-24T17:40:44.398424Z","shell.execute_reply.started":"2021-06-24T17:40:44.393231Z","shell.execute_reply":"2021-06-24T17:40:44.397071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Switch 'load' to false if you have trained the model and put the correct photo number\ngen_input_img(num_photo=num_photo,\n              load=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:44.400056Z","iopub.execute_input":"2021-06-24T17:40:44.400476Z","iopub.status.idle":"2021-06-24T17:40:45.431769Z","shell.execute_reply.started":"2021-06-24T17:40:44.400432Z","shell.execute_reply":"2021-06-24T17:40:45.430776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"! pip install git+https://github.com/tensorflow/docs","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:40:45.433718Z","iopub.execute_input":"2021-06-24T17:40:45.434249Z","iopub.status.idle":"2021-06-24T17:41:06.127834Z","shell.execute_reply.started":"2021-06-24T17:40:45.434198Z","shell.execute_reply":"2021-06-24T17:41:06.126909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow_docs.vis.embed as embed","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:06.129238Z","iopub.execute_input":"2021-06-24T17:41:06.129533Z","iopub.status.idle":"2021-06-24T17:41:06.162026Z","shell.execute_reply.started":"2021-06-24T17:41:06.129502Z","shell.execute_reply":"2021-06-24T17:41:06.160994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Prediction evolution according to epoch\nembed.embed_file(anim_file)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:06.163409Z","iopub.execute_input":"2021-06-24T17:41:06.163868Z","iopub.status.idle":"2021-06-24T17:41:06.797797Z","shell.execute_reply.started":"2021-06-24T17:41:06.163821Z","shell.execute_reply":"2021-06-24T17:41:06.795636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Switch 'load' to false if you have trained the model and put the correct photo number\nanim_file = create_gif(num_photo=num_photo,\n                       load=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:06.799935Z","iopub.execute_input":"2021-06-24T17:41:06.800538Z","iopub.status.idle":"2021-06-24T17:41:24.612791Z","shell.execute_reply.started":"2021-06-24T17:41:06.800486Z","shell.execute_reply":"2021-06-24T17:41:24.611729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Prediction evolution according to epoch\nembed.embed_file(anim_file)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:24.614171Z","iopub.execute_input":"2021-06-24T17:41:24.614457Z","iopub.status.idle":"2021-06-24T17:41:24.796217Z","shell.execute_reply.started":"2021-06-24T17:41:24.614429Z","shell.execute_reply":"2021-06-24T17:41:24.794566Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"testset = tf.data.Dataset.from_tensor_slices(fn_photo)\ntestset = testset.shuffle(len(fn_photo))\ntestset = testset.map(parse_function, num_parallel_calls)\ntestset=testset.batch(1)\ntestset = testset.prefetch(num_parallel_calls)\n\n\n_, ax = plt.subplots(5, 2, figsize=(20, 20))\nfor i, img in enumerate(testset.take(5)):\n    prediction = monet_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    img = (img[0] * 127.5 + 127.5).numpy().astype(np.uint8)\n\n    ax[i, 0].imshow(img)\n    ax[i, 1].imshow(prediction)\n    ax[i, 0].set_title(\"Input Photo\")\n    ax[i, 1].set_title(\"Monet-esque\")\n    ax[i, 0].axis(\"off\")\n    ax[i, 1].axis(\"off\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:24.7975Z","iopub.execute_input":"2021-06-24T17:41:24.797951Z","iopub.status.idle":"2021-06-24T17:41:27.334295Z","shell.execute_reply.started":"2021-06-24T17:41:24.797916Z","shell.execute_reply":"2021-06-24T17:41:27.333213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"testset = tf.data.Dataset.from_tensor_slices(fn_monet)\ntestset = testset.shuffle(len(fn_monet))\ntestset = testset.map(parse_function, num_parallel_calls)\ntestset=testset.batch(1)\ntestset = testset.prefetch(num_parallel_calls)\n\n\n_, ax = plt.subplots(5, 2, figsize=(20, 20))\nfor i, img in enumerate(testset.take(5)):\n    prediction = photo_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    img = (img[0] * 127.5 + 127.5).numpy().astype(np.uint8)\n    ax[i, 0].imshow(img)\n    ax[i, 1].imshow(prediction)\n    ax[i, 0].set_title(\"Input Photo\")\n    ax[i, 1].set_title(\"Photo-esque\")\n    ax[i, 0].axis(\"off\")\n    ax[i, 1].axis(\"off\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:27.335726Z","iopub.execute_input":"2021-06-24T17:41:27.336024Z","iopub.status.idle":"2021-06-24T17:41:29.925147Z","shell.execute_reply.started":"2021-06-24T17:41:27.335995Z","shell.execute_reply":"2021-06-24T17:41:29.923927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def evaluate_cycle(ds, generator_a, generator_b, n_samples=1):\n    fig, axes = plt.subplots(n_samples, 3, figsize=(22, (n_samples*6)))\n    axes = axes.flatten()\n    \n    ds_iter = iter(ds)\n    for n_sample in range(n_samples):\n        idx = n_sample*3\n        example_sample = next(ds_iter)\n        generated_a_sample = generator_a.predict(example_sample)\n        generated_b_sample = generator_b.predict(generated_a_sample)\n        \n        axes[idx].set_title('Input image', fontsize=18)\n        axes[idx].imshow(example_sample[0] * 0.5 + 0.5)\n        axes[idx].axis('off')\n        \n        axes[idx+1].set_title('Generated image', fontsize=18)\n        axes[idx+1].imshow(generated_a_sample[0] * 0.5 + 0.5)\n        axes[idx+1].axis('off')\n        \n        axes[idx+2].set_title('Cycled image', fontsize=18)\n        axes[idx+2].imshow(generated_b_sample[0] * 0.5 + 0.5)\n        axes[idx+2].axis('off')\n        \n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:29.926503Z","iopub.execute_input":"2021-06-24T17:41:29.926855Z","iopub.status.idle":"2021-06-24T17:41:29.937359Z","shell.execute_reply.started":"2021-06-24T17:41:29.926822Z","shell.execute_reply":"2021-06-24T17:41:29.936252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"evaluate_cycle(testset.take(10), monet_generator, photo_generator, n_samples=10)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:29.938744Z","iopub.execute_input":"2021-06-24T17:41:29.939151Z","iopub.status.idle":"2021-06-24T17:41:54.401999Z","shell.execute_reply.started":"2021-06-24T17:41:29.939113Z","shell.execute_reply":"2021-06-24T17:41:54.397434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import PIL\n\ndef predict_and_save(input_ds, generator_model, output_path):\n    i = 1\n    for img in input_ds:\n        prediction = generator_model(img, training=False)[0].numpy() # make predition\n        prediction = (prediction * 127.5 + 127.5).astype(np.uint8)   # re-scale\n        im = PIL.Image.fromarray(prediction)\n        im.save(output_path + str(i) + \".jpg\")\n        i += 1","metadata":{"execution":{"iopub.status.busy":"2021-06-24T17:41:54.403763Z","iopub.execute_input":"2021-06-24T17:41:54.404153Z","iopub.status.idle":"2021-06-24T17:41:54.411222Z","shell.execute_reply.started":"2021-06-24T17:41:54.404115Z","shell.execute_reply":"2021-06-24T17:41:54.41025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nos.makedirs('./images1') # Create folder to save generated images\npredict_and_save(photo_ds, monet_generator, './images1')","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:01:25.895475Z","iopub.execute_input":"2021-06-24T12:01:25.895861Z","iopub.status.idle":"2021-06-24T12:01:25.935651Z","shell.execute_reply.started":"2021-06-24T12:01:25.89583Z","shell.execute_reply":"2021-06-24T12:01:25.934461Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"shutil.make_archive('/kaggle/working/images1/', 'zip', './images1')#converting to zip\nprint(f\"Generated samples: {len([name for name in os.listdir('./images1/') if os.path.isfile(os.path.join('./images1/', name))])}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}