{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"from __future__ import absolute_import, division, print_function\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, optimizers, applications, Sequential\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, LearningRateScheduler\nimport tensorflow_addons as tfa\nimport tensorflow_datasets as tfds\n\nfrom kaggle_datasets import KaggleDatasets\nimport glob\nimport imageio\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport os\nimport csv\n\nimport sys\nprint(\"Python version:\", sys.version)\nimport PIL\nimport time\nimport shutil\n\nfrom functools import partial\nfrom albumentations import (\n    Compose, RandomBrightness, JpegCompression, HueSaturationValue, RandomContrast, HorizontalFlip,\n    Rotate\n)\n\nfrom IPython import display\n\nfrom functools import partial\nfrom albumentations import (\n    Compose, RandomBrightness, JpegCompression, HueSaturationValue, RandomContrast, HorizontalFlip,\n    Rotate\n)\n\nfrom tensorflow.python.eager import def_function\nfrom tensorflow.python.framework import dtypes\nfrom tensorflow.python.framework import tensor_shape\nfrom tensorflow.python.keras import backend as K\nfrom tensorflow.python.keras import layers\nfrom tensorflow.python.keras import initializers\nfrom tensorflow.python.ops import array_ops\nfrom tensorflow.python.ops import math_ops","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"try:\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()\nprint('Number of replicas:', strategy.num_replicas_in_sync)\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\nprint(tf.__version__)\nprint(tfa.__version__)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import re\n\nGCS_PATH = KaggleDatasets().get_gcs_path()\n\nMONET_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nprint('Monet TFRecord Files:', len(MONET_FILENAMES))\n\nPHOTO_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\nprint('Photo TFRecord Files:', len(PHOTO_FILENAMES))\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nn_monet_samples = count_data_items(MONET_FILENAMES)\nn_photo_samples = count_data_items(PHOTO_FILENAMES)\n\nprint(f'Monet TFRecord files: {len(MONET_FILENAMES)}')\nprint(f'Monet image files: {n_monet_samples}')\nprint(f'Photo TFRecord files: {len(PHOTO_FILENAMES)}')\nprint(f'Photo image files: {n_photo_samples}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def normalize(image):\n    return (tf.cast(image, tf.float32) / 127.5) - 1\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.reshape(image, [256, 256, 3])\n    return image\n\ndef random_rotate(image):\n    p = tf.random.uniform([], 0, 1.0, dtype=tf.float32)\n    if p < 0.2:\n        image = tf.image.rot90(image, k=1)\n    elif p < 0.4:\n        image = tf.image.rot90(image, k=2)\n    elif p < 0.6:\n        image = tf.image.rot90(image, k=3)\n    return image\n\ndef random_crop(image):\n    cropped_image = tf.image.random_crop(image, size=[256, 256, 3])\n    return cropped_image\n\ndef random_jitter(image):\n    # resizing to 286 x 286 x 3 \n    image = tf.image.resize(image, [int(256*1.3), int(256*1.3)],\n                          method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n    # randomly cropping to 256 x 256 x 3\n    image = random_crop(image)\n    return image\n\ndef random_jitter_and_flip(image):\n    # resizing to 286 x 286 x 3 \n    image = tf.image.resize(image, [int(256*1.3), int(256*1.3)],\n                          method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n    # randomly cropping to 256 x 256 x 3\n    image = random_crop(image)\n    # random mirroring\n    image = random_flip(image)\n    return image\n\ndef random_flip(image):\n    return tf.image.flip_left_right(image)\n\ndef preprocess_image_train(image, label=None):\n    image = random_jitter(image)\n    return image\n\ndef read_tfrecord(example):\n    tfrecord_format = {\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    return image\n\ndef load_dataset(filenames, labeled=False, ordered=False, repeats=200, noise=False):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE);\n    if noise == True:\n        dataset = dataset.concatenate(dataset.map(random_jitter_and_flip, num_parallel_calls=AUTOTUNE).shuffle(10000))\n        dataset = dataset.concatenate(dataset.map(random_flip, num_parallel_calls=AUTOTUNE).shuffle(100000))\n        dataset = dataset.concatenate(dataset.map(random_rotate, num_parallel_calls=AUTOTUNE).shuffle(100000))\n        dataset = dataset.concatenate(dataset.map(random_jitter, num_parallel_calls=AUTOTUNE).shuffle(10000, reshuffle_each_iteration=True).repeat(repeats))\n    dataset = dataset.map(normalize, num_parallel_calls=AUTOTUNE).shuffle(10000)\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"OUTPUT_CHANNELS = 3\nLATENT_DIM = 1024\n\ndef downsample(filters, size, apply_dropout=False, apply_instancenorm=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.Conv2D(filters, size, strides=1, padding='same',\n    #                         kernel_initializer=initializer, use_bias=False))\n\n    if apply_instancenorm:\n        result.add(layers.Conv2D(filters, size, strides=2, padding='same',\n                         kernel_initializer=initializer, use_bias=False))\n        result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n    else:\n        result.add(layers.Conv2D(filters, size, strides=2, padding='same',\n                         kernel_initializer=initializer, use_bias=False))\n        \n    if apply_dropout:\n        result.add(layers.Dropout(0.2))\n\n    result.add(layers.ReLU())\n\n    return result","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False, apply_instancenorm=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    \n    if apply_instancenorm:\n        result.add(layers.Conv2DTranspose(filters, size, strides=2,\n                                  padding='same',\n                                  kernel_initializer=initializer,\n                                  use_bias=False))\n\n        result.add(tfa.layers.InstanceNormalization(gamma_initializer=gamma_init))\n    else:\n        result.add(layers.Conv2DTranspose(filters, size, strides=2,\n                                  padding='same',\n                                  kernel_initializer=initializer,\n                                  use_bias=False))\n\n    if apply_dropout:\n        result.add(layers.Dropout(0.2))\n\n    result.add(layers.ReLU())\n\n    return result","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 25\n\nLR_G = 2e-4\nLR_D = 5e-4\nbeta_1 = .5\n\nreal_label = .9\nfake_label = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def CycleGenerator():\n    inputs = layers.Input(shape=[256,256,3])\n\n    # bs = batch size\n    down_stack = [\n        downsample(64, 4), # (bs, 128, 128, 64)\n        downsample(128, 4, apply_instancenorm=True), # (bs, 64, 64, 128)\n        downsample(256, 4, apply_instancenorm=True), # (bs, 32, 32, 256)\n        downsample(512, 4, apply_instancenorm=True), # (bs, 16, 16, 512)\n        downsample(512, 4, apply_instancenorm=True), # (bs, 8, 8, 512)\n        downsample(512, 4, apply_instancenorm=True), # (bs, 4, 4, 512)\n        downsample(512, 4, apply_instancenorm=True), # (bs, 2, 2, 512)\n        downsample(512, 4, apply_instancenorm=True), # (bs, 1, 1, 512)\n    ]\n\n    up_stack = [\n        upsample(512, 4, apply_dropout=True, apply_instancenorm=True), # (bs, 2, 2, 1024)\n        upsample(512, 4, apply_dropout=True, apply_instancenorm=True), # (bs, 4, 4, 1024)\n        upsample(512, 4, apply_dropout=True, apply_instancenorm=True), # (bs, 8, 8, 1024)\n        upsample(512, 4, apply_instancenorm=True), # (bs, 16, 16, 1024)\n        upsample(256, 4, apply_instancenorm=True), # (bs, 32, 32, 512)\n        upsample(128, 4, apply_instancenorm=True), # (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,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=initializer,\n                                  activation='tanh') # (bs, 256, 256, 3)\n\n    x = inputs\n\n    # Downsampling through the model\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n\n    skips = reversed(skips[:-1])\n\n    # Upsampling and establishing the skip connections\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = layers.Concatenate()([x, skip])\n    x = last(x)\n\n    return keras.Model(inputs=inputs, outputs=x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def CycleDiscriminator():\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, apply_dropout=True, apply_instancenorm=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,\n                         kernel_initializer=initializer,\n                         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_conv = layers.Conv2D(1, 4, strides=1,\n                         kernel_initializer=initializer)(zero_pad2) # (bs, 30, 30, 1)\n\n    last_relu = layers.LeakyReLU(alpha=0.2)(last_conv)\n    last_pool = layers.Flatten()(last_relu)\n    #last = layers.Dense(1, activation='sigmoid')(last_pool)\n\n    return tf.keras.Model(inputs=inp, outputs=last_pool)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    monet_cycle_generator = CycleGenerator() # transforms photos to Monet-esque paintings\n    photo_cycle_generator = CycleGenerator() # transforms Monet paintings to be more like photos\n\n    monet_cycle_discriminator = CycleDiscriminator() # differentiates real Monet paintings and generated Monet paintings\n    photo_cycle_discriminator = CycleDiscriminator() # differentiates real photos and generated photos","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class CycleGan(keras.Model):\n    def __init__(\n        self,\n        monet_generator,\n        photo_generator,\n        monet_discriminator,\n        photo_discriminator,\n        lambda_cycle=10,\n        real_label=.5\n    ):\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        self.real_label = real_label\n        \n    def compile(\n        self,\n        m_gen_optimizer,\n        p_gen_optimizer,\n        m_disc_optimizer,\n        p_disc_optimizer,\n        gen_loss_fn,\n        disc_loss_fn,\n        cycle_loss_fn,\n        identity_loss_fn\n    ):\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        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        \n        batch_size = tf.shape(real_photo)[0]\n        labels_real = tf.zeros((batch_size, 1)) + self.real_label\n        labels_real += 0.05 * tf.random.uniform(tf.shape(labels_real))        \n        \n        with tf.GradientTape(persistent=True) as tape:\n            # photo to monet back to photo\n            fake_monet = self.m_gen(real_photo, training=True)\n            cycled_photo = self.p_gen(fake_monet, training=True)\n\n            # monet to photo back to monet\n            fake_photo = self.p_gen(real_monet, training=True)\n            cycled_monet = self.m_gen(fake_photo, training=True)\n\n            # generating itself\n            same_monet = self.m_gen(real_monet, training=True)\n            same_photo = self.p_gen(real_photo, training=True)\n\n            # discriminator used to check, inputing real images\n            disc_real_monet = self.m_disc(real_monet, training=True)\n            disc_real_photo = self.p_disc(real_photo, training=True)\n\n            # discriminator used to check, inputing fake images\n            disc_fake_monet = self.m_disc(fake_monet, training=True)\n            disc_fake_photo = self.p_disc(fake_photo, training=True)\n\n            # evaluates generator loss\n            monet_gen_loss = self.gen_loss_fn(disc_real_monet, disc_fake_monet, labels_real)\n            photo_gen_loss = self.gen_loss_fn(disc_real_photo, disc_fake_photo, labels_real)\n\n            # evaluates total cycle consistency loss\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, labels_real)\n            photo_disc_loss = self.disc_loss_fn(disc_real_photo, disc_fake_photo, labels_real)\n\n        # Calculate the gradients for generator and discriminator\n        monet_generator_gradients = tape.gradient(total_monet_gen_loss,\n                                                  self.m_gen.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_gen_loss,\n                                                  self.p_gen.trainable_variables)\n\n        monet_discriminator_gradients = tape.gradient(monet_disc_loss,\n                                                      self.m_disc.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_disc_loss,\n                                                      self.p_disc.trainable_variables)\n\n        # Apply the gradients to the optimizer\n        self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients,\n                                                 self.m_gen.trainable_variables))\n\n        self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients,\n                                                 self.p_gen.trainable_variables))\n\n        self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients,\n                                                  self.m_disc.trainable_variables))\n\n        self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients,\n                                                  self.p_disc.trainable_variables))\n        \n        return {\n            \"monet_gen_loss\": total_monet_gen_loss,\n            \"photo_gen_loss\": total_photo_gen_loss,\n            \"monet_disc_loss\": monet_disc_loss,\n            \"photo_disc_loss\": photo_disc_loss\n        }","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    def discriminator_loss(predictions_real, predictions_gen, labels_real):\n        return (tf.reduce_mean((predictions_gen  - tf.reduce_mean(predictions_real) + labels_real) ** 2) +\n                tf.reduce_mean((predictions_real - tf.reduce_mean(predictions_gen)  - labels_real) ** 2))/2\n    \n    def generator_loss(predictions_real, predictions_gen, labels_real):\n        return (tf.reduce_mean((predictions_real - tf.reduce_mean(predictions_gen)  + labels_real) ** 2) +\n                tf.reduce_mean((predictions_gen  - tf.reduce_mean(predictions_real) - labels_real) ** 2)) / 2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"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\n        return LAMBDA * loss1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"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","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    monet_generator_optimizer = tf.keras.optimizers.Adam(LR_G, beta_1=0.5)\n    photo_generator_optimizer = tf.keras.optimizers.Adam(LR_G, beta_1=0.5)\n\n    monet_discriminator_optimizer = tf.keras.optimizers.Adam(LR_D, beta_1=0.5)\n    photo_discriminator_optimizer = tf.keras.optimizers.Adam(LR_D, beta_1=0.5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    cycle_gan_model = CycleGan(\n        monet_cycle_generator, photo_cycle_generator, monet_cycle_discriminator, photo_cycle_discriminator, real_label=0.66\n    )\n\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    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"history = cycle_gan_model.fit(\n    tf.data.Dataset.zip((load_dataset(MONET_FILENAMES, labeled=True, repeats=45, noise=True).batch(128, drop_remainder=True), load_dataset(PHOTO_FILENAMES, labeled=True, repeats=2, noise=True).batch(128, drop_remainder=True))),\n    epochs=EPOCHS\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['photo_gen_loss'], label='photo_gen_loss')\nplt.plot(history.history['photo_disc_loss'], label='photo_disc_loss')\nplt.title('Photo GAN')\nplt.ylabel('Loss value')\nplt.xlabel('No. Epoch')\nplt.legend(loc=\"upper left\")\nplt.show()\n\nplt.plot(history.history['monet_gen_loss'], label='monet_gen_loss')\nplt.plot(history.history['monet_disc_loss'], label='monet_disc_loss')\nplt.title('Monet GAN')\nplt.ylabel('Loss value')\nplt.xlabel('No. Epoch')\nplt.legend(loc=\"upper left\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"_, ax = plt.subplots(2, 5, figsize=(25, 5))\nfor i, img in enumerate(load_dataset(PHOTO_FILENAMES, labeled=True, repeats=2, noise=True).batch(100, drop_remainder=True).take(5)):\n    prediction = monet_cycle_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[0, i].imshow(img)\n    ax[1, i].imshow(prediction)\n    ax[0, i].set_title(\"Input Photo\")\n    ax[1, i].set_title(\"Monet-esque\")\n    ax[0, i].axis(\"off\")\n    ax[1, i].axis(\"off\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import PIL\n! mkdir ../images\n! mkdir ../logs","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 1\nfor img in load_dataset(PHOTO_FILENAMES, labeled=True, repeats=1).batch(1):\n    prediction = monet_cycle_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    im = PIL.Image.fromarray(prediction)\n    im.save(\"../images/\" + str(i) + \".jpg\")\n    i += 1    \nprint(i)\n\nwith open(\"../logs/log.csv\", \"w\") as f:\n    writer = csv.writer(f)\n    writer.writerow(['photo_gen_loss', 'photo_disc_loss', 'monet_gen_loss', 'monet_disc_loss'])\n    for i in range(len(history.history['photo_gen_loss'])):\n        row = [history.history['photo_gen_loss'][i], history.history['photo_disc_loss'][i], history.history['monet_gen_loss'][i], history.history['monet_disc_loss'][i]]\n        writer.writerow(row)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")\nshutil.make_archive(\"/kaggle/working/logs\", 'zip', \"/kaggle/logs\")","execution_count":null,"outputs":[]}],"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":4}