{"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":"code","source":"import tensorflow as tf              # TensorFlow is an open-source machine learning framework\nimport tensorflow_addons as tfa      # TensorFlow Addons is a library of advanced features and extensions\nimport os                             # Operating System interface for Python\nimport numpy as np                    # NumPy is a library for numerical computing with Python\nimport zipfile                        # Library for working with ZIP archives\n\n# Configuration variables\nEPOCHS = 250       # Number of epochs to train the model\nBUFFER_SIZE = 1000  # Shuffle buffer size for training dataset\nBATCH_SIZE = 32      # Batch size for training and evaluation\nIMG_WIDTH = 256     # Width of input images\nIMG_HEIGHT = 256    # Height of input images\n","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:22.793599Z","iopub.execute_input":"2023-04-28T14:45:22.794047Z","iopub.status.idle":"2023-04-28T14:45:22.801284Z","shell.execute_reply.started":"2023-04-28T14:45:22.794001Z","shell.execute_reply":"2023-04-28T14:45:22.800113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the dataset and preprocess images\ndef load_image(image_file):\n    \"\"\"\n    Load and preprocesses an image file.\n\n    Parameters:\n    image_file (str): The file path of the image.\n\n    Returns:\n    A preprocessed image tensor.\n    \"\"\"\n    image = tf.io.read_file(image_file)\n    image = tf.image.decode_jpeg(image)\n    image = tf.cast(image, tf.float32)\n    image = (image / 127.5) - 1\n    return image\n\ndef load_dataset(directory):\n    \"\"\"\n    Load and preprocesses a directory of image files.\n\n    Parameters:\n    directory (str): The directory path containing image files.\n\n    Returns:\n    A preprocessed dataset of image tensors.\n    \"\"\"\n    # Use tf.data to load the image files from the directory\n    dataset = tf.data.Dataset.list_files(directory + '/*.jpg')\n    # Map the load_image function to each image file in the dataset\n    dataset = dataset.map(load_image, num_parallel_calls=tf.data.AUTOTUNE)\n    # Shuffle and batch the preprocessed dataset\n    dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE)\n    return dataset\n\n# Load the Monet and photo datasets\nmonet_ds = load_dataset('/kaggle/input/gan-getting-started/monet_jpg')  # Preprocessed Monet dataset\nphoto_ds = load_dataset('/kaggle/input/gan-getting-started/photo_jpg')  # Preprocessed photo dataset","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:23.616151Z","iopub.execute_input":"2023-04-28T14:45:23.616529Z","iopub.status.idle":"2023-04-28T14:45:24.891694Z","shell.execute_reply.started":"2023-04-28T14:45:23.616494Z","shell.execute_reply":"2023-04-28T14:45:24.890682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def downsample(filters, size, apply_instancenorm=True):\n    \"\"\"\n    Creates a downsample block with a Conv2D layer, instance normalization, and LeakyReLU activation.\n\n    Parameters:\n    filters (int): The number of filters in the Conv2D layer.\n    size (int): The size of the filters in the Conv2D layer.\n    apply_instancenorm (bool): A boolean indicating whether or not to apply instance normalization.\n\n    Returns:\n    A Sequential model representing the downsample block.\n    \"\"\"\n    # Initialize the weights of the Conv2D layer.\n    initializer = tf.random_normal_initializer(0., 0.02)\n\n    # Create a Sequential model to represent the downsample block.\n    result = tf.keras.Sequential()\n\n    # Add a Conv2D layer to the model.\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same', kernel_initializer=initializer, use_bias=False))\n\n    # If apply_instancenorm is True, add an instance normalization layer to the model.\n    if apply_instancenorm:\n        result.add(tfa.layers.InstanceNormalization())\n\n    # Add a LeakyReLU activation layer to the model.\n    result.add(layers.LeakyReLU())\n\n    # Return the downsample block model.\n    return result","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:24.893667Z","iopub.execute_input":"2023-04-28T14:45:24.894282Z","iopub.status.idle":"2023-04-28T14:45:24.901736Z","shell.execute_reply.started":"2023-04-28T14:45:24.894238Z","shell.execute_reply":"2023-04-28T14:45:24.900831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n\n    result = tf.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())\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":"2023-04-28T14:45:24.958684Z","iopub.execute_input":"2023-04-28T14:45:24.959307Z","iopub.status.idle":"2023-04-28T14:45:24.965871Z","shell.execute_reply.started":"2023-04-28T14:45:24.959268Z","shell.execute_reply":"2023-04-28T14:45:24.964728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generator Model\n\nThe `Generator` function defines the generator model using a combination of upsample and downsample blocks. The generator model takes a low-resolution image as input and generates a high-resolution image as output. The model architecture is based on the U-Net architecture, which has skip connections that allow the model to transfer detailed information from the input to the output.\n\n## Implementation:\n\n- `inputs`: initializes the input layer with the shape [256, 256, 3].\n- `down_stack`: defines a list of downsample blocks using the `downsample` function with different numbers of filters and filter sizes. These blocks reduce the spatial dimensions of the input tensor while increasing the number of channels.\n- `up_stack`: defines a list of upsample blocks using the `upsample` function with different numbers of filters and filter sizes. These blocks increase the spatial dimensions of the input tensor while decreasing the number of channels.\n- `initializer`: initializes the weights of the last Conv2DTranspose layer.\n- `last`: defines the last Conv2DTranspose layer that outputs the high-resolution image.\n- `x`: sets the input tensor to `inputs`.\n- `skips`: initializes an empty list to store the skip connections.\n- The for loop that iterates through the down_stack blocks and applies them to the input tensor. The output tensor is added to the `skips` list.\n- `skips = reversed(skips[:-1])`: reverses the `skips` list and removes the last element, which is the output tensor of the last downsample block. This is done because the last downsample block doesn't have a skip connection.\n- The for loop that iterates through the up_stack blocks and applies them to the input tensor. The skip connection tensor is concatenated with the output tensor of the corresponding upsample block using the `Concatenate` layer. This transfers the detailed information from the input to the output.\n- `x = last(x)`: applies the last Conv2DTranspose layer to the output tensor of the last upsample block, and outputs the high-resolution image.\n\nThe generator model is a crucial part of the GAN architecture, as it's responsible for generating high-quality images from the noise input. In the next section, we'll define the discriminator model that evaluates the quality of the generated images.","metadata":{}},{"cell_type":"code","source":"def Generator():\n    inputs = layers.Input(shape=[256, 256, 3])\n\n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False),\n        downsample(128, 4),\n        downsample(256, 4),\n        downsample(512, 4),\n        downsample(512, 4),\n        downsample(512, 4),\n        downsample(512, 4),\n        downsample(512, 4),\n    ]\n\n    up_stack = [\n        upsample(512, 4, apply_dropout=True),\n        upsample(512, 4, apply_dropout=True),\n        upsample(512, 4, apply_dropout=True),\n        upsample(512, 4),\n        upsample(256, 4),\n        upsample(128, 4),\n        upsample(64, 4),\n    ]\n\n    initializer = tf.random_normal_initializer(0., 0.02)\n    last = layers.Conv2DTranspose(3, 4, strides=2, padding='same', kernel_initializer=initializer, activation='tanh')\n\n    x = inputs\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips\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 tf.keras.Model(inputs=inputs, outputs=x)","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:28.857915Z","iopub.execute_input":"2023-04-28T14:45:28.858567Z","iopub.status.idle":"2023-04-28T14:45:28.868847Z","shell.execute_reply.started":"2023-04-28T14:45:28.858526Z","shell.execute_reply":"2023-04-28T14:45:28.867621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Discriminator Model\n\nThe `Discriminator` function defines the discriminator model, which is used to evaluate the quality of the generated images. The discriminator takes a high-resolution image as input and outputs a score indicating whether the image is real or fake. The discriminator model is a convolutional neural network that has several downsampling layers followed by two fully connected layers.\n\n## Implementation:\n\n- `initializer`: initializes the weights of the convolutional layers.\n- `inp`: initializes the input layer with the shape [256, 256, 3] and the name 'input_image'.\n- `x`: sets the input tensor to `inp`.\n- The for loop that iterates through the first three downsample blocks and applies them to the input tensor.\n- `x = layers.ZeroPadding2D()(x)`: adds a zero-padding layer to the output tensor of the last downsample block.\n- `x = layers.Conv2D(512, 4, strides=1, kernel_initializer=initializer, use_bias=False)(x)`: adds a Conv2D layer with 512 filters, a filter size of 4, and `strides=1`, which means that the output has the same spatial dimensions as the input. `kernel_initializer=initializer` initializes the weights of the layer, and `use_bias=False` is used to skip the addition of bias.\n- `x = tfa.layers.InstanceNormalization()(x)`: applies instance normalization to the output of the Conv2D layer.\n- `x = layers.LeakyReLU()(x)`: applies LeakyReLU activation function to the output of the previous layer.\n- `x = layers.ZeroPadding2D()(x)`: adds a zero-padding layer to the output tensor of the previous layer.\n- `x = layers.Conv2D(1, 4, strides=1, kernel_initializer=initializer)(x)`: adds a Conv2D layer with 1 filter, a filter size of 4, and `strides=1`. This is the output layer that outputs the score indicating whether the image is real or fake.\n\nThe discriminator model is a crucial part of the GAN architecture, as it's responsible for distinguishing between real and fake images. In the next section, we'll define the loss functions and optimization algorithm that are used to train the GAN model.\n","metadata":{}},{"cell_type":"code","source":"def Discriminator():\n    initializer = tf.random_normal_initializer(0., 0.02)\n\n    inp = layers.Input(shape=[256, 256, 3], name='input_image')\n\n    x = inp\n\n    x = downsample(64, 4, False)(x)\n    x = downsample(128, 4)(x)\n    x = downsample(256, 4)(x)\n\n    x = layers.ZeroPadding2D()(x)\n    x = layers.Conv2D(512, 4, strides=1, kernel_initializer=initializer, use_bias=False)(x)\n    x = tfa.layers.InstanceNormalization()(x)\n    x = layers.LeakyReLU()(x)\n\n    x = layers.ZeroPadding2D()(x)\n    x = layers.Conv2D(1, 4, strides=1, kernel_initializer=initializer)(x)\n\n    return tf.keras.Model(inputs=inp, outputs=x)","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:30.527698Z","iopub.execute_input":"2023-04-28T14:45:30.528405Z","iopub.status.idle":"2023-04-28T14:45:30.536339Z","shell.execute_reply.started":"2023-04-28T14:45:30.528348Z","shell.execute_reply":"2023-04-28T14:45:30.535322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generator and Discriminator Instances\n\nIn this section, we create instances of the generator and discriminator models using the `Generator` and `Discriminator` functions that we defined earlier.\n\nWe create two instances of the `Generator` model: `generator_g` and `generator_f`. These models are responsible for generating high-resolution images from low-resolution images.\n\nWe also create two instances of the `Discriminator` model: `discriminator_x` and `discriminator_y`. These models are responsible for evaluating the quality of the generated images.\n\nFinally, we define the loss function that we'll use to train the models. We'll use the binary cross-entropy loss, which is a common loss function used for binary classification tasks. The `from_logits=True` argument means that the loss function will operate on the logits output by the models, rather than the output after applying a sigmoid activation function.\n","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import layers\n\ngenerator_g = Generator()\ngenerator_f = Generator()\n\ndiscriminator_x = Discriminator()\ndiscriminator_y = Discriminator()\n\nloss_object = tf.keras.losses.BinaryCrossentropy(from_logits=True)","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:35.143859Z","iopub.execute_input":"2023-04-28T14:45:35.144612Z","iopub.status.idle":"2023-04-28T14:45:38.112207Z","shell.execute_reply.started":"2023-04-28T14:45:35.144573Z","shell.execute_reply":"2023-04-28T14:45:38.111203Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loss Functions\n\nIn this section, we define the loss functions that we'll use to train the GAN model.\n\n## Generator Loss Function\n\nThe `generator_loss` function calculates the loss for the generator model. The generator's objective is to produce high-resolution images that are similar to the real images. Therefore, the loss function compares the generated images to an array of ones, indicating that the generator's output should be classified as real.\n\nThe `tf.ones_like(generated)` function generates a tensor of ones with the same shape as the generated image tensor. The `loss_object` function calculates the binary cross-entropy loss between the generated image and the tensor of ones.\n\n## Discriminator Loss Function\n\nThe `discriminator_loss` function calculates the loss for the discriminator model. The discriminator's objective is to distinguish between real and fake images. Therefore, the loss function calculates the binary cross-entropy loss between the real and generated images.\n\nThe `tf.ones_like(real)` function generates a tensor of ones with the same shape as the real image tensor, indicating that the real images should be classified as real. The `tf.zeros_like(generated)` function generates a tensor of zeros with the same shape as the generated image tensor, indicating that the generated images should be classified as fake.\n\nThe `real_loss` variable calculates the binary cross-entropy loss between the real images and the tensor of ones. The `generated_loss` variable calculates the binary cross-entropy loss between the generated images and the tensor of zeros.\n\nThe `total_disc_loss` variable calculates the sum of the `real_loss` and `generated_loss`. Finally, we multiply the `total_disc_loss` by 0.5 to scale the loss down, as the discriminator is trained twice for every generator update.\n","metadata":{}},{"cell_type":"code","source":"def generator_loss(generated):\n    return loss_object(tf.ones_like(generated), generated)\n\ndef discriminator_loss(real, generated):\n    real_loss = loss_object(tf.ones_like(real), real)\n    generated_loss = loss_object(tf.zeros_like(generated), generated)\n    total_disc_loss = real_loss + generated_loss\n    return total_disc_loss * 0.5","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:41.967996Z","iopub.execute_input":"2023-04-28T14:45:41.968699Z","iopub.status.idle":"2023-04-28T14:45:41.974647Z","shell.execute_reply.started":"2023-04-28T14:45:41.968657Z","shell.execute_reply":"2023-04-28T14:45:41.973557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Optimizers\n\nIn this section, we define the optimizers that we'll use to train the GAN model.\n\nWe define two instances of the Adam optimizer for the generator models: `generator_g_optimizer` and `generator_f_optimizer`. The optimizer uses a learning rate of 2e-4 and a beta1 value of 0.5.\n\nWe also define two instances of the Adam optimizer for the discriminator models: `discriminator_x_optimizer` and `discriminator_y_optimizer`. The optimizer uses the same learning rate and beta1 value as the generator optimizers.","metadata":{}},{"cell_type":"code","source":"learning_rate = 1e-2\n\ngenerator_g_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\ngenerator_f_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\n\ndiscriminator_x_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\ndiscriminator_y_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:43.655034Z","iopub.execute_input":"2023-04-28T14:45:43.656147Z","iopub.status.idle":"2023-04-28T14:45:43.671623Z","shell.execute_reply.started":"2023-04-28T14:45:43.656096Z","shell.execute_reply":"2023-04-28T14:45:43.670352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training Step\n\nIn this section, we define the `train_step` function which trains our GAN model. This function takes two arguments: `real_x` and `real_y`, which are batches of real images from the Monet and photo datasets, respectively.\n\nInside the function, we use a `tf.GradientTape` context to record the operations that are applied to the model variables. We then use these recorded operations to compute the loss and gradients for the generator and discriminator models.\n\nThe generator models are trained to minimize the adversarial loss (discriminator loss on generated images) and the cycle consistency loss (mean absolute difference between the original and cycled images). We also add an identity loss to ensure that the generator models preserve the input images when they are already in the target domain.\n\nThe discriminator models are trained to correctly classify real and generated images.\n\nAfter computing the gradients, we apply them to the model variables using the optimizer instances that we defined earlier: `generator_g_optimizer`, `generator_f_optimizer`, `discriminator_x_optimizer`, and `discriminator_y_optimizer`.","metadata":{}},{"cell_type":"code","source":"@tf.function\ndef train_step(real_x, real_y):\n    with tf.GradientTape(persistent=True) as tape:\n        fake_y = generator_g(real_x, training=True)\n        cycled_x = generator_f(fake_y, training=True)\n\n        fake_x = generator_f(real_y, training=True)\n        cycled_y = generator_g(fake_x, training=True)\n\n        same_x = generator_f(real_x, training=True)\n        same_y = generator_g(real_y, training=True)\n\n        disc_real_x = discriminator_x(real_x, training=True)\n        disc_real_y = discriminator_y(real_y, training=True)\n\n        disc_fake_x = discriminator_x(fake_x, training=True)\n        disc_fake_y = discriminator_y(fake_y, training=True)\n\n        gen_g_loss = generator_loss(disc_fake_y)\n        gen_f_loss = generator_loss(disc_fake_x)\n\n        total_cycle_loss = tf.reduce_mean(tf.abs(real_x - cycled_x)) + tf.reduce_mean(tf.abs(real_y - cycled_y))\n\n        total_gen_g_loss = gen_g_loss + total_cycle_loss + tf.reduce_mean(tf.abs(real_y - same_y))\n        total_gen_f_loss = gen_f_loss + total_cycle_loss + tf.reduce_mean(tf.abs(real_x - same_x))\n\n        disc_x_loss = discriminator_loss(disc_real_x, disc_fake_x)\n        disc_y_loss = discriminator_loss(disc_real_y, disc_fake_y)\n\n    generator_g_gradients = tape.gradient(total_gen_g_loss, generator_g.trainable_variables)\n    generator_f_gradients = tape.gradient(total_gen_f_loss, generator_f.trainable_variables)\n\n    discriminator_x_gradients = tape.gradient(disc_x_loss, discriminator_x.trainable_variables)\n    discriminator_y_gradients = tape.gradient(disc_y_loss, discriminator_y.trainable_variables)\n\n    generator_g_optimizer.apply_gradients(zip(generator_g_gradients, generator_g.trainable_variables))\n    generator_f_optimizer.apply_gradients(zip(generator_f_gradients, generator_f.trainable_variables))\n\n    discriminator_x_optimizer.apply_gradients(zip(discriminator_x_gradients, discriminator_x.trainable_variables))\n    discriminator_y_optimizer.apply_gradients(zip(discriminator_y_gradients, discriminator_y.trainable_variables))","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:45.079965Z","iopub.execute_input":"2023-04-28T14:45:45.080322Z","iopub.status.idle":"2023-04-28T14:45:45.096700Z","shell.execute_reply.started":"2023-04-28T14:45:45.080288Z","shell.execute_reply":"2023-04-28T14:45:45.095424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training\n\nFinally, we define a `train` function that trains our CycleGAN for a specified number of epochs using the Monet and photo datasets.\n\n","metadata":{}},{"cell_type":"code","source":"def train(epochs, monet_ds, photo_ds):\n    for epoch in range(epochs):\n        print(f\"Epoch: {epoch + 1}/{epochs}\")\n\n        for real_x, real_y in tf.data.Dataset.zip((monet_ds, photo_ds)):\n            train_step(real_x, real_y)","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:46.638094Z","iopub.execute_input":"2023-04-28T14:45:46.638790Z","iopub.status.idle":"2023-04-28T14:45:46.644354Z","shell.execute_reply.started":"2023-04-28T14:45:46.638751Z","shell.execute_reply":"2023-04-28T14:45:46.643208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training the Model\n\nTo train the CycleGAN model, we need to specify the following parameters:\n\nEPOCHS: The number of epochs to train the model for.\nmonet_ds: The preprocessed Monet dataset.\nphoto_ds: The preprocessed photo dataset.\nWe use the train function to train the model. This function consists of a loop over the specified number of epochs. Within each epoch, it loops over each pair of real Monet and photo images, and performs a single training step using the train_step function defined earlier.\n\nDuring training, the generator models (generator_g and generator_f) are trained to transform images from one domain to the other, while the discriminator models (discriminator_x and discriminator_y) are trained to distinguish between real and fake images. The generator models are trained to minimize the generator loss, which measures how well the generated images fool the discriminator models. The discriminator models are trained to minimize the discriminator loss, which measures how well they can distinguish between real and fake images.\n\nBy training the model for multiple epochs, the generator models learn to generate high-q","metadata":{}},{"cell_type":"code","source":"train(EPOCHS, monet_ds, photo_ds)","metadata":{"execution":{"iopub.status.busy":"2023-04-28T14:45:48.010081Z","iopub.execute_input":"2023-04-28T14:45:48.010801Z","iopub.status.idle":"2023-04-28T15:34:19.312004Z","shell.execute_reply.started":"2023-04-28T14:45:48.010762Z","shell.execute_reply":"2023-04-28T15:34:19.310419Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The save_transformed_images function saves a set of transformed images generated by the generator_g model on the photo_ds dataset. The function performs the following steps:\n\nCreates an output directory called \"transformed_images\" using os.makedirs\nLoops over a subset of images in the photo_ds dataset using photo_ds.take(10000)\nGenerates a transformed image using the generator_g model on the current image in the loop using generator_g(img, training=False)[0].numpy()\nRescales the transformed image pixel values from the range [-1, 1] to the range [0, 255] using (transformed_img * 0.5 + 0.5) * 255\nConverts the transformed image to a uint8 format using tf.cast(transformed_img, tf.uint8).numpy()\nSaves the transformed image as a JPEG file using tf.keras.preprocessing.image.save_img and a filename that includes the index of the current image in the loop. The saved images are located in the \"transformed_images\" directory.\nThis function is useful for visualizing the quality of the image transformations produced by the generator_g model on the photo_ds dataset.","metadata":{}},{"cell_type":"code","source":"def save_transformed_images(photo_ds, generator_g):\n    output_path = \"/kaggle/working/transformed_images\"\n    os.makedirs(output_path, exist_ok=True)\n\n    for i, img in enumerate(photo_ds.take(10000)):\n        transformed_img = generator_g(img, training=False)[0].numpy()\n        transformed_img = (transformed_img * 0.5 + 0.5) * 255\n        transformed_img = tf.cast(transformed_img, tf.uint8).numpy()\n\n        filename = os.path.join(output_path, f\"image_{i + 1}.jpg\")\n        tf.keras.preprocessing.image.save_img(filename, transformed_img)\n\nsave_transformed_images(photo_ds, generator_g)","metadata":{"execution":{"iopub.status.busy":"2023-04-16T23:24:01.295232Z","iopub.execute_input":"2023-04-16T23:24:01.296403Z","iopub.status.idle":"2023-04-16T23:33:23.256094Z","shell.execute_reply.started":"2023-04-16T23:24:01.296362Z","shell.execute_reply":"2023-04-16T23:33:23.255013Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The create_submission_zip function creates a compressed zip file of the transformed images in the \"transformed_images\" directory. This function uses the zipfile module to create the zip file, and iterates over each file in the \"transformed_images\" directory, adding it to the zip file. The os.walk function is used to recursively iterate over all the files in the directory, and os.path.join and os.path.relpath are used to create the file paths for the zip file.\n\nTo use this function, simply call it after generating the transformed images using the save_transformed_images function. Once the zip file is created, you can download it and submit it to the competition.","metadata":{}},{"cell_type":"code","source":"def create_submission_zip():\n    with zipfile.ZipFile(\"images.zip\", \"w\") as zf:\n        for root, _, files in os.walk(\"transformed_images\"):\n            for file in files:\n                filepath = os.path.join(root, file)\n                zf.write(filepath, os.path.relpath(filepath, \"transformed_images\"))\n\ncreate_submission_zip()","metadata":{"execution":{"iopub.status.busy":"2023-04-16T23:33:23.257619Z","iopub.execute_input":"2023-04-16T23:33:23.258628Z","iopub.status.idle":"2023-04-16T23:33:24.245239Z","shell.execute_reply.started":"2023-04-16T23:33:23.258587Z","shell.execute_reply":"2023-04-16T23:33:24.244221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}