{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"}],"dockerImageVersionId":29985,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Introduction\nI guess **generating additional data by GANs or other generators can be an approach to improve the performance** of CNN classifier. So I've applied one of the best-known GAN \"CycleGAN\" on the Cassava Leaf dataset.","metadata":{}},{"cell_type":"markdown","source":"## Import Libraries","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nimport tensorflow_addons as tfa\nimport math\n\nimport pandas as pd\nimport cv2\nfrom PIL import Image\nimport numpy as np\nimport os\nimport matplotlib.pyplot as plt\nfrom kaggle_datasets import KaggleDatasets","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-12-01T11:38:28.963173Z","iopub.execute_input":"2024-12-01T11:38:28.963464Z","iopub.status.idle":"2024-12-01T11:38:33.700021Z","shell.execute_reply.started":"2024-12-01T11:38:28.963439Z","shell.execute_reply":"2024-12-01T11:38:33.699269Z"}},"outputs":[],"execution_count":null},{"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)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T11:38:33.701606Z","iopub.execute_input":"2024-12-01T11:38:33.701893Z","iopub.status.idle":"2024-12-01T11:38:33.712237Z","shell.execute_reply.started":"2024-12-01T11:38:33.701857Z","shell.execute_reply":"2024-12-01T11:38:33.711377Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Pipeline\nData pipeline is defined in this section. \n\nNoted that the labels are not used as target value in Cycle GAN. Labels are just used for filtering the inputs.","metadata":{}},{"cell_type":"code","source":"IMAGE_SIZE = [512, 512]\nINPUT_SIZE = [256, 256] #just for fast training\nAUTO = tf.data.experimental.AUTOTUNE\n\ndef data_pipeline(filenames, label_num, batch_size, is_train=True):\n    label_num = tf.constant(label_num, tf.int32)\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTO)\n    dataset = dataset.filter(lambda x, y: y == label_num)# only seletcted label number can go through\n    dataset = dataset.map(lambda x, y: x, num_parallel_calls=AUTO)# do not use label\n    if is_train:\n        dataset = dataset.map(data_augmentation, num_parallel_calls=AUTO)\n        dataset = dataset.shuffle(64)\n        dataset = dataset.repeat()\n    dataset = dataset.map(resize_and_normalize, num_parallel_calls=AUTO)   \n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef read_tfrecord(example):\n    tfrecord_format = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.int64)\n        }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    img = decode_img(example['image'])\n    label = tf.cast(example['target'], tf.int32)\n    return img, label\n\ndef decode_img(img):\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.cast(img, tf.float32)\n    img = tf.reshape(img, [*IMAGE_SIZE, 3])\n    return img\n\ndef data_augmentation(img):\n    # horizontal flip\n    if tf.random.uniform(()) > 0.5:\n        img = img[:, ::-1, :]\n    \n    # vertical flip\n    if tf.random.uniform(()) > 0.5:\n        img = img[::-1, :, :]\n        \n    # random crop\n    if tf.random.uniform(()) > 0.3:\n        height = tf.random.uniform(shape=(), minval=450, maxval=500)\n        width = tf.random.uniform(shape=(), minval=450, maxval=500)\n        img = tf.image.random_crop(img, [height,width,3])\n    \n    # change brightness and contrast\n    contrast_rate = tf.random.uniform(shape=(), minval=0.8, maxval=1.2)\n    img = tf.image.adjust_contrast(img, contrast_rate)\n\n    brightness_shift = tf.random.uniform(shape=(), minval=-20, maxval=20)\n    img = tf.image.adjust_brightness(img, brightness_shift)\n    img = tf.clip_by_value(img, 0, 255)\n\n    # change hue\n    hue_rate = tf.random.uniform(shape=(), minval=-0.05, maxval=0.05)\n    img = tf.image.adjust_hue(img, hue_rate)\n\n    return img\n      \ndef resize_and_normalize(img):\n    img = img[tf.newaxis, ...]\n    img = tf.image.resize(img, tuple(INPUT_SIZE))[0,:,:,:]\n    img = (img/127.5) - 1\n    return img","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T11:38:33.713627Z","iopub.execute_input":"2024-12-01T11:38:33.713992Z","iopub.status.idle":"2024-12-01T11:38:33.7329Z","shell.execute_reply.started":"2024-12-01T11:38:33.713955Z","shell.execute_reply":"2024-12-01T11:38:33.732136Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Define Cycle GAN Model\nModels are based on Amy Jang's great notebook. Thank you for sharing!\n\nhttps://www.kaggle.com/amyjang/monet-cyclegan-tutorial#Visualize-our-Monet-esque-photos\n\nThe only difference is the discriminator. I use pretrained model of VGG16 instead of the model given in the original notebook because this classification task is much more difficult than the original task(Monet or photo). \n","metadata":{}},{"cell_type":"code","source":"def build_gan_models():\n    generator_x2y = build_generator()\n    generator_y2x = build_generator()\n    discriminator_x = build_discriminator()\n    discriminator_y = build_discriminator()\n    return generator_x2y, generator_y2x, discriminator_x, discriminator_y\n\n\ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = tf.keras.Sequential()\n    result.add(tf.keras.layers.Conv2D(filters, size, strides=2, padding='same',\n                             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(tf.keras.layers.LeakyReLU())\n\n    return result\n\ndef upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = tf.keras.Sequential()\n    result.add(tf.keras.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\n    if apply_dropout:\n        result.add(tf.keras.layers.Dropout(0.5))\n\n    result.add(tf.keras.layers.ReLU())\n\n    return result\n\ndef build_generator():\n    inputs = tf.keras.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 = tf.keras.layers.Conv2DTranspose(3, 4,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=initializer,\n                                  activation='tanh')\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 = tf.keras.layers.Concatenate()([x, skip])\n\n    x = last(x)\n\n    return tf.keras.Model(inputs=inputs, outputs=x)\n\ndef build_discriminator():\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    inputs = tf.keras.layers.Input(shape=[256, 256, 3], name='input_image')\n    \n    # I use pretrained model of VGG instead of the model given in the original notebook,\n    # because this classification task is much more difficult than the original task(Monet or photo). \n    pretrained_model = tf.keras.applications.VGG16(weights=\"imagenet\", include_top=False ,input_shape=[256,256, 3])\n    x = pretrained_model(inputs)\n    \"\"\"\n    \n    down1 = downsample(64, 4, False)(inputs)\n    down2 = downsample(128, 4)(down1)\n    down3 = downsample(256, 4)(down2)\n\n    zero_pad1 = tf.keras.layers.ZeroPadding2D()(down3)\n    conv = tf.keras.layers.Conv2D(512, 4, strides=1,\n                         kernel_initializer=initializer,\n                         use_bias=False)(zero_pad1)\n\n    norm1 = tfa.layers.InstanceNormalization(gamma_initializer=gamma_init)(conv)\n\n    leaky_relu = tf.keras.layers.LeakyReLU()(norm1)\n\n    zero_pad2 = tf.keras.layers.ZeroPadding2D()(leaky_relu)\n\n    last = tf.keras.layers.Conv2D(1, 4, strides=1,\n                         kernel_initializer=initializer)(zero_pad2)\n    \"\"\"\n    last =  tf.keras.layers.Conv2D(1, 4, strides=1, kernel_initializer=initializer)(x)\n    \n    return tf.keras.Model(inputs=inputs, outputs=last)\n\n\ndef discriminator_loss(real, generated):\n    bc_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)\n    real_loss = bc_loss(tf.ones_like(real), real)# real as real\n    generated_loss = bc_loss(tf.zeros_like(generated), generated)# fake as fake\n    #loss = tf.reduce_mean(tf.abs(real_image - same_image))\n    return (real_loss + generated_loss) * 0.5\n\ndef generator_loss(generated):\n    bc_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)# fake as real\n    return bc_loss(tf.ones_like(generated), generated)\n\ndef calc_cycle_loss(real_image, cycled_image):\n    loss = tf.reduce_mean(tf.abs(real_image - cycled_image))\n    return 10 * loss\n\ndef identity_loss(real_image, same_image):\n    loss = tf.reduce_mean(tf.abs(real_image - same_image))\n    return 5 * loss\n\n\nclass CycleGan(tf.keras.Model):\n    def __init__(\n        self,\n        generator_x2y,\n        generator_y2x,\n        discriminator_x,\n        discriminator_y,\n    ):\n        super(CycleGan, self).__init__()\n        self.generator_x2y = generator_x2y\n        self.generator_y2x = generator_y2x\n        self.discriminator_x = discriminator_x\n        self.discriminator_y = discriminator_y\n        \n    def compile(\n        self,\n        generator_x2y_optimizer,\n        generator_y2x_optimizer,\n        discriminator_x_optimizer,\n        discriminator_y_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.generator_x2y_optimizer = generator_x2y_optimizer\n        self.generator_y2x_optimizer = generator_y2x_optimizer\n        self.discriminator_x_optimizer = discriminator_x_optimizer\n        self.discriminator_y_optimizer = discriminator_y_optimizer\n        self.generator_loss = gen_loss_fn\n        self.discriminator_loss = disc_loss_fn\n        self.cycle_loss = cycle_loss_fn\n        self.identity_loss = identity_loss_fn\n        \n    def train_step(self, batch_data):\n        real_x, real_y = batch_data\n        with tf.GradientTape(persistent=True) as tape:\n            # Generator G translates X -> Y\n            # Generator F translates Y -> X.\n\n            fake_y = self.generator_x2y(real_x, training=True)\n            cycled_x = self.generator_y2x(fake_y, training=True)\n\n            fake_x = self.generator_y2x(real_y, training=True)\n            cycled_y = self.generator_x2y(fake_x, training=True)\n\n            # same_x and same_y are used for identity loss. in half cycle, do not change original image\n            same_x = self.generator_y2x(real_x, training=True)\n            same_y = self.generator_x2y(real_y, training=True)\n\n            # discriminate 4 images in total\n            disc_real_x = self.discriminator_x(real_x, training=True)\n            disc_real_y = self.discriminator_y(real_y, training=True)\n\n            disc_fake_x = self.discriminator_x(fake_x, training=True)\n            disc_fake_y = self.discriminator_y(fake_y, training=True)\n\n            # calculate the loss\n            gen_x2y_loss = self.generator_loss(disc_fake_y)\n            gen_y2x_loss = self.generator_loss(disc_fake_x)\n\n            total_cycle_loss = self.cycle_loss(real_x, cycled_x) + self.cycle_loss(real_y, cycled_y)\n\n            # Total generator loss = adversarial loss + cycle loss\n            total_gen_x2y_loss = gen_x2y_loss + total_cycle_loss + self.identity_loss(real_y, same_y)\n            total_gen_y2x_loss = gen_y2x_loss + total_cycle_loss + self.identity_loss(real_x, same_x)\n\n            disc_x_loss = self.discriminator_loss(disc_real_x, disc_fake_x)\n            disc_y_loss = self.discriminator_loss(disc_real_y, disc_fake_y)\n      \n        # Calculate the gradients for generator and discriminator\n        generator_x2y_gradients = tape.gradient(total_gen_x2y_loss, \n                                              self.generator_x2y.trainable_variables)\n        generator_y2x_gradients = tape.gradient(total_gen_y2x_loss, \n                                              self.generator_y2x.trainable_variables)\n        \n        discriminator_x_gradients = tape.gradient(disc_x_loss, \n                                                  self.discriminator_x.trainable_variables)\n        discriminator_y_gradients = tape.gradient(disc_y_loss, \n                                                  self.discriminator_y.trainable_variables)\n      \n        # Apply the gradients to the optimizer\n        generator_x2y_optimizer.apply_gradients(zip(generator_x2y_gradients, \n                                                  self.generator_x2y.trainable_variables))\n    \n        generator_y2x_optimizer.apply_gradients(zip(generator_y2x_gradients, \n                                                  self.generator_y2x.trainable_variables))\n      \n        discriminator_x_optimizer.apply_gradients(zip(discriminator_x_gradients,\n                                                      self.discriminator_x.trainable_variables))\n        \n        discriminator_y_optimizer.apply_gradients(zip(discriminator_y_gradients,\n                                                      discriminator_y.trainable_variables))\n    \n    \n        return {\n                \"gen_x2y_loss\": total_gen_x2y_loss,\n                \"gen_y2x_loss\": total_gen_y2x_loss,\n                \"disc_x_loss\": disc_x_loss,\n                \"disc_y_loss\": disc_y_loss\n                }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T11:38:33.734126Z","iopub.execute_input":"2024-12-01T11:38:33.734359Z","iopub.status.idle":"2024-12-01T11:38:33.768931Z","shell.execute_reply.started":"2024-12-01T11:38:33.734335Z","shell.execute_reply":"2024-12-01T11:38:33.768174Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Build Data Pipeline\nCycle GAN requires two inputs:\n- **Images of Source Domain**\n- **Images of Target Domain**\n\nFirst, I choose \"CBSD\" as source and \"Healthy\" as target.  ","metadata":{}},{"cell_type":"code","source":"batch_size = 1\nGCS_PATH = KaggleDatasets().get_gcs_path()\nfilenames = tf.io.gfile.glob(GCS_PATH + '/train_tfrecords/ld_train*.tfrec')\nCBSD_data = data_pipeline(filenames, label_num=1, batch_size=batch_size, is_train=True)\nHealthy_data = data_pipeline(filenames, label_num=4, batch_size=batch_size, is_train=True)\n\nnum_samples = 4\nfig, axes = plt.subplots(2,num_samples, figsize=(12,6))\nfor i, data in enumerate(CBSD_data.take(num_samples)):\n    axes[0, i].imshow(data[0].numpy() * 0.5 + 0.5)\n    axes[0, i].title.set_text(\"CBSD\")\n    \nfor i, data in enumerate(Healthy_data.take(num_samples)):\n    axes[1, i].imshow(data[0].numpy() * 0.5 + 0.5)\n    axes[1, i].title.set_text(\"Healthy\")    \n\nfig.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T11:38:33.772064Z","iopub.execute_input":"2024-12-01T11:38:33.77242Z","iopub.status.idle":"2024-12-01T11:38:49.593291Z","shell.execute_reply.started":"2024-12-01T11:38:33.772392Z","shell.execute_reply":"2024-12-01T11:38:49.592494Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Train Models\nLet's start training! \n\nAs I don't want to break the pretrained weights of disciminators(VGG16) at initial epochs, weights are frozen at first 3 epochs. Then train all layers.","metadata":{}},{"cell_type":"code","source":"data_num = 2000\nlearning_rate = 1e-4\nbatch_size = 1\n\nwith strategy.scope(): \n    generator_x2y, generator_y2x, discriminator_x, discriminator_y = build_gan_models()\n\n\n    # loss and learning rate\n    generator_x2y_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\n    generator_y2x_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\n\n    discriminator_x_optimizer = tf.keras.optimizers.Adam(learning_rate/10, beta_1=0.5)\n    discriminator_y_optimizer = tf.keras.optimizers.Adam(learning_rate/10, beta_1=0.5)\n\n    cycle_gan_model = CycleGan(generator_x2y, generator_y2x, discriminator_x, discriminator_y)\n\n\n    def compile_and_fit(epochs):\n        cycle_gan_model.compile(generator_x2y_optimizer, generator_y2x_optimizer,\n                                discriminator_x, discriminator_y,\n                                generator_loss,\n                                discriminator_loss,\n                                calc_cycle_loss,\n                                identity_loss\n                                )\n        cycle_gan_model.fit(tf.data.Dataset.zip((CBSD_data, Healthy_data)),\n                            batch_size = batch_size,\n                            steps_per_epoch = data_num//batch_size, \n                            epochs=epochs\n                           )    \n\n\n    # I don't want to break the pretrained weight of disciminator at initial epochs.\n    discriminator_x.trainable = False\n    discriminator_y.trainable = False\n    compile_and_fit(epochs=3)\n\n    discriminator_x.trainable = True\n    discriminator_y.trainable = True\n    compile_and_fit(epochs=10)\n\n    cycle_gan_model.save_weights(\"gan_10ep_CBSD_healthy.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T11:38:49.594934Z","iopub.execute_input":"2024-12-01T11:38:49.595283Z","iopub.status.idle":"2024-12-01T13:56:53.099126Z","shell.execute_reply.started":"2024-12-01T11:38:49.595244Z","shell.execute_reply":"2024-12-01T13:56:53.098182Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Check Generated Image","metadata":{}},{"cell_type":"markdown","source":"### CBSD to HEALTHY","metadata":{}},{"cell_type":"code","source":"def generate_images(model, test_input):\n    prediction = model(test_input)\n    plt.figure(figsize=(10, 10))\n    display_list = [test_input[0], prediction[0]]\n    title = ['Original Image', 'Generated Image']\n    for i in range(2):\n        plt.subplot(1, 2, i+1)\n        plt.title(title[i])\n        plt.imshow(display_list[i] * 0.5 + 0.5)\n        plt.axis('off')\n    plt.show()\n\nfor inputs in CBSD_data.take(12):\n    generate_images(generator_x2y, inputs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T13:56:53.101068Z","iopub.execute_input":"2024-12-01T13:56:53.101341Z","iopub.status.idle":"2024-12-01T13:57:01.431807Z","shell.execute_reply.started":"2024-12-01T13:56:53.101313Z","shell.execute_reply":"2024-12-01T13:57:01.43101Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Some images look healthy comparing with the original images.","metadata":{}},{"cell_type":"markdown","source":"### HEALTHY to CBSD","metadata":{}},{"cell_type":"code","source":"for inputs in Healthy_data.take(12):\n    generate_images(generator_y2x, inputs) ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T13:57:01.433681Z","iopub.execute_input":"2024-12-01T13:57:01.433992Z","iopub.status.idle":"2024-12-01T13:57:09.436969Z","shell.execute_reply.started":"2024-12-01T13:57:01.433948Z","shell.execute_reply":"2024-12-01T13:57:09.436144Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Colors and patterns of leaves look slightly changing. They are somewhat unhealthy.","metadata":{}},{"cell_type":"markdown","source":"## Other Transforms","metadata":{}},{"cell_type":"markdown","source":"### 1. CBB <-> HEALTHY","metadata":{}},{"cell_type":"code","source":"CBB_data = data_pipeline(filenames, label_num=0, batch_size=batch_size, is_train=True)\nwith strategy.scope(): \n    generator_x2y, generator_y2x, discriminator_x, discriminator_y = build_gan_models()\n\n    generator_x2y_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\n    generator_y2x_optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.5)\n    discriminator_x_optimizer = tf.keras.optimizers.Adam(learning_rate/10, beta_1=0.5)\n    discriminator_y_optimizer = tf.keras.optimizers.Adam(learning_rate/10, beta_1=0.5)\n    \n    cycle_gan_model = CycleGan(generator_x2y, generator_y2x, discriminator_x, discriminator_y)\n\n    def compile_and_fit(epochs):\n        cycle_gan_model.compile(generator_x2y_optimizer, generator_y2x_optimizer,\n                                discriminator_x, discriminator_y,\n                                generator_loss,\n                                discriminator_loss,\n                                calc_cycle_loss,\n                                identity_loss\n                                )\n        cycle_gan_model.fit(tf.data.Dataset.zip((CBB_data, Healthy_data)),\n                            batch_size = batch_size,\n                            steps_per_epoch = data_num//batch_size, \n                            epochs=epochs\n                           )    \n\n\n    # I don't want to break the pretrained weight of disciminator at initial epochs.\n    discriminator_x.trainable = False\n    discriminator_y.trainable = False\n    compile_and_fit(epochs=3)\n\n    discriminator_x.trainable = True\n    discriminator_y.trainable = True\n    compile_and_fit(epochs=10)\n\n    cycle_gan_model.save_weights(\"gan_10ep_CBB_healthy.h5\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-01T13:57:09.439199Z","iopub.execute_input":"2024-12-01T13:57:09.439646Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### CBB to HEALTHY","metadata":{}},{"cell_type":"code","source":"for inputs in CBB_data.take(12):\n    generate_images(generator_x2y, inputs)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### HEALTHY to CBB","metadata":{}},{"cell_type":"code","source":"for inputs in Healthy_data.take(12):\n    generate_images(generator_y2x, inputs)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Training multiple CycleGANs takes a lot of time. So I'm plannning to implement StarGANv2 to create images across multiple domain (multiple classes).","metadata":{}},{"cell_type":"code","source":"## Next Step\nGenerated images shown above are not so good at this point. So the next step is tuning models and hyper parameters to generate better images. Then train classifier with newly generated images (fake images) and compare its performance with the original classifier.\n\nThank you for reading!","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}