{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.8.17","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpu1vmV38","dataSources":[{"sourceId":10889858,"sourceType":"datasetVersion","datasetId":6767145},{"sourceId":10890076,"sourceType":"datasetVersion","datasetId":6767313}],"dockerImageVersionId":30529,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Dependencies","metadata":{"id":"XgSPG-vBMRfM"}},{"cell_type":"code","source":"%matplotlib inline\nimport warnings\nwarnings.simplefilter(action='ignore', category=(RuntimeWarning, FutureWarning, UserWarning))\n\nimport gc\nimport time\nimport os\nimport urllib.request\nimport shutil\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom matplotlib.pyplot import subplots\nfrom PIL import Image\nimport pathlib\nimport tensorflow as tf\nimport keras\nfrom keras import layers, Model\nfrom keras.models import Sequential\nfrom keras.layers import Layer, Dense, Activation, Dropout, Input, concatenate, Average, Concatenate, Conv2D, Conv2DTranspose, BatchNormalization, ReLU, LeakyReLU, ZeroPadding2D, GaussianNoise\nfrom keras.optimizers import Adam\nfrom keras.utils import array_to_img\nfrom keras.callbacks import Callback\nfrom keras.initializers import RandomNormal\nfrom keras.losses import BinaryCrossentropy\n\n\nnp.random.seed(0)\ntf.random.set_seed(0)\n\nIN_COLAB = os.getenv(\"COLAB_RELEASE_TAG\") is not None\nON_KAGGLE = os.getenv(\"KAGGLE_KERNEL_RUN_TYPE\") is not None\n\nAUTOTUNE = tf.data.AUTOTUNE\n\nprint(\"tf version: \", tf.__version__)","metadata":{"id":"KmQY5gdYMRfN","outputId":"345fec3d-894d-4e94-b4ea-16f88d1316cd","_kg_hide-output":true,"_kg_hide-input":true,"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:50.802812Z","iopub.execute_input":"2025-03-02T04:10:50.804056Z","iopub.status.idle":"2025-03-02T04:10:50.816647Z","shell.execute_reply.started":"2025-03-02T04:10:50.804009Z","shell.execute_reply":"2025-03-02T04:10:50.815841Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Download the <a href=\"https://github.com/mit-han-lab/data-efficient-gans/blob/master/DiffAugment-stylegan2/DiffAugment_tf.py\">DiffAugment_tf.py</a> file from the data-efficient-gans project and import all the functions from it. Alternatively we can copy the code of all these functions to notebook.","metadata":{"id":"d_H-y_lrMRfQ"}},{"cell_type":"code","source":"url = 'https://raw.githubusercontent.com/mit-han-lab/data-efficient-gans/master/DiffAugment-stylegan2/DiffAugment_tf.py'\nfilename = 'DiffAugment_tf.py'\nurllib.request.urlretrieve(url, filename)\nimport DiffAugment_tf\n\nfunction_names = [name for name in dir(DiffAugment_tf) if callable(getattr(DiffAugment_tf, name))]\nprint(\"DiffAugment functions:\\n\", function_names)","metadata":{"id":"ihjI1HZrMRfQ","outputId":"0a939164-3db1-4511-809a-6bc82f3e6686","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:50.818232Z","iopub.execute_input":"2025-03-02T04:10:50.818504Z","iopub.status.idle":"2025-03-02T04:10:51.082831Z","shell.execute_reply.started":"2025-03-02T04:10:50.818482Z","shell.execute_reply":"2025-03-02T04:10:51.082149Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## TPU configuration","metadata":{"id":"HeyboZcqMRfR"}},{"cell_type":"code","source":"# MONET_DIR = pathlib.Path('/kaggle/input/something-painter-v2-512/something-painter-v2-512/monet_jpg')\n# PHOTO_DIR = pathlib.Path('/kaggle/input/something-painter-v2-512/something-painter-v2-512/photo_jpg')\nMONET_DIR = pathlib.Path('/kaggle/input/wikiart-monet-512-v1/wikiart_monet_512_v1')\nPHOTO_DIR = pathlib.Path('/kaggle/input/landscape-pictures-512-v1/landscape_pictures_512_v1')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:51.083753Z","iopub.execute_input":"2025-03-02T04:10:51.084037Z","iopub.status.idle":"2025-03-02T04:10:51.088380Z","shell.execute_reply.started":"2025-03-02T04:10:51.084010Z","shell.execute_reply":"2025-03-02T04:10:51.087687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if ON_KAGGLE:\n    try:\n        tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect(tpu=\"local\")\n        strategy = tf.distribute.TPUStrategy(tpu)\n        print(\"on TPU\")\n    except tf.errors.NotFoundError:\n        print(\"not on TPU\")\n        strategy = tf.distribute.MirroredStrategy()\n\n    print(\"REPLICAS: \", strategy.num_replicas_in_sync)","metadata":{"id":"6mXZSWJEMRfR","_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:51.089296Z","iopub.execute_input":"2025-03-02T04:10:51.089529Z","iopub.status.idle":"2025-03-02T04:10:59.516658Z","shell.execute_reply.started":"2025-03-02T04:10:51.089508Z","shell.execute_reply":"2025-03-02T04:10:59.515845Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Collection <a class=\"anchor\" id=\"datacollection\"></a>","metadata":{"id":"Mkc0z38zMRfS"}},{"cell_type":"code","source":"if strategy.num_replicas_in_sync == 1:\n    BATCH_SIZE = 2\n    BUFFER_SIZE = 512  # Tăng buffer size cho ảnh lớn hơn\nelse:\n    BATCH_SIZE_PER_REPLICA = 1\n    BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync\n    TEST_BATCH_SIZE_PER_REPLICA = 32\n    TEST_BATCH_SIZE = TEST_BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync","metadata":{"id":"a3x55SuwMRfb","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:59.518649Z","iopub.execute_input":"2025-03-02T04:10:59.518889Z","iopub.status.idle":"2025-03-02T04:10:59.523364Z","shell.execute_reply.started":"2025-03-02T04:10:59.518865Z","shell.execute_reply":"2025-03-02T04:10:59.522629Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_image(image_path):\n    shape = [512, 512, 3]  # Thay đổi kích thước thành 512x512\n    image = tf.io.read_file(image_path)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [512, 512])\n    image = (tf.cast(image, tf.float32) / 127.5) - 1\n    image = tf.reshape(image, shape)\n    return image\n\ndef load_dataset_from_dir(directory):\n    files = tf.data.Dataset.list_files(str(directory / '*.jpg'))\n    dataset = files.map(decode_image, num_parallel_calls=AUTOTUNE)\n    return dataset\n\ndef data_augment(image):\n    image = tf.image.resize(image, [572, 572])  # Tăng kích thước crop để phù hợp với 512x512\n    image = tf.image.random_crop(image, size=[BATCH_SIZE, 512, 512, 3])\n    image = tf.image.random_flip_left_right(image)\n    return image","metadata":{"id":"twKzxNMPMRfb","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:59.524250Z","iopub.execute_input":"2025-03-02T04:10:59.524496Z","iopub.status.idle":"2025-03-02T04:10:59.537058Z","shell.execute_reply.started":"2025-03-02T04:10:59.524473Z","shell.execute_reply":"2025-03-02T04:10:59.536308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def prepare_gan_ds(photo_dir, monet_dir, augment_fn=None, repeat=True, shuffle=True, cache=True, batch_size=1):\n    monet_ds = load_dataset_from_dir(monet_dir)\n    photo_ds = load_dataset_from_dir(photo_dir)\n    if cache:\n        monet_ds = monet_ds.cache()\n        photo_ds = photo_ds.cache()\n    if repeat:\n        monet_ds = monet_ds.repeat()\n        photo_ds = photo_ds.repeat()\n    if shuffle:\n        monet_ds_entries = monet_ds.reduce(0, lambda x,_: x+1).numpy()\n        photo_ds_entries = photo_ds.reduce(0, lambda x,_: x+1).numpy()\n        monet_ds = monet_ds.shuffle(monet_ds_entries)\n        photo_ds = photo_ds.shuffle(photo_ds_entries)\n    monet_ds = monet_ds.batch(batch_size)\n    photo_ds = photo_ds.batch(batch_size)\n    if augment_fn:\n        monet_ds = monet_ds.map(augment_fn, num_parallel_calls=AUTOTUNE)\n        photo_ds = photo_ds.map(augment_fn, num_parallel_calls=AUTOTUNE)\n    monet_ds = monet_ds.prefetch(AUTOTUNE)\n    photo_ds = photo_ds.prefetch(AUTOTUNE)\n    gan_ds = tf.data.Dataset.zip((photo_ds, monet_ds))\n    return gan_ds","metadata":{"id":"2pYptugtMRf1","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:59.538052Z","iopub.execute_input":"2025-03-02T04:10:59.538292Z","iopub.status.idle":"2025-03-02T04:10:59.548407Z","shell.execute_reply.started":"2025-03-02T04:10:59.538271Z","shell.execute_reply":"2025-03-02T04:10:59.547673Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = prepare_gan_ds(\n    PHOTO_DIR,\n    MONET_DIR,\n    augment_fn=data_augment,\n    repeat=True,\n    shuffle=False,\n    cache=False,\n    batch_size=BATCH_SIZE\n)\ntest_photo_ds = load_dataset_from_dir(PHOTO_DIR).batch(TEST_BATCH_SIZE).prefetch(TEST_BATCH_SIZE)","metadata":{"id":"k-D7sA5WMRf2","outputId":"116287e5-5966-42ac-fe66-9531656a1507","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:10:59.549376Z","iopub.execute_input":"2025-03-02T04:10:59.549617Z","iopub.status.idle":"2025-03-02T04:11:00.580152Z","shell.execute_reply.started":"2025-03-02T04:10:59.549595Z","shell.execute_reply":"2025-03-02T04:11:00.579117Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Check datasets.","metadata":{}},{"cell_type":"markdown","source":"# Model Training and Evaluation <a class=\"anchor\" id=\"trainandevaluate\"></a>","metadata":{"id":"XwFmmRd5MRf5"}},{"cell_type":"markdown","source":"First, create the necessary classes, functions, and objects.","metadata":{"id":"yAu_w8KUMRf5"}},{"cell_type":"code","source":"def diffaug_fn(image):\n    return DiffAugment_tf.DiffAugment(image, \"color,translation,cutout\")","metadata":{"id":"txfVfx1wMRf5","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.581339Z","iopub.execute_input":"2025-03-02T04:11:00.581631Z","iopub.status.idle":"2025-03-02T04:11:00.585797Z","shell.execute_reply.started":"2025-03-02T04:11:00.581603Z","shell.execute_reply":"2025-03-02T04:11:00.584975Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Generator, Discriminator <a class=\"anchor\" id=\"gendisc\"></a>","metadata":{"id":"pNMhkCUoMRf6"}},{"cell_type":"code","source":"class InstanceNormalization(tf.keras.layers.Layer):\n    def __init__(self, epsilon=1e-5):\n        super(InstanceNormalization, self).__init__()\n        self.epsilon = epsilon\n\n    def build(self, input_shape):\n        self.scale = self.add_weight(\n            name='scale',\n            shape=input_shape[-1:],\n            initializer=tf.random_normal_initializer(1., 0.02),\n            trainable=True)\n        self.offset = self.add_weight(\n            name='offset',\n            shape=input_shape[-1:],\n            initializer='zeros',\n            trainable=True)\n\n    def call(self, x):\n        mean, variance = tf.nn.moments(x, axes=[1, 2], keepdims=True)\n        inv = tf.math.rsqrt(variance + self.epsilon)\n        normalized = (x - mean) * inv\n        return self.scale * normalized + self.offset","metadata":{"id":"pFdFcLtmMRf6","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.586759Z","iopub.execute_input":"2025-03-02T04:11:00.587059Z","iopub.status.idle":"2025-03-02T04:11:00.597373Z","shell.execute_reply.started":"2025-03-02T04:11:00.587034Z","shell.execute_reply":"2025-03-02T04:11:00.596668Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def downsample(filters, size, apply_instancenorm=True, add_noise=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    result = keras.Sequential()\n    result.add(Conv2D(filters, size, strides=2, padding='same',\n                      kernel_initializer=initializer, use_bias=False))\n    if add_noise:\n        result.add(GaussianNoise(0.2))\n    if apply_instancenorm:\n        result.add(InstanceNormalization())\n    result.add(LeakyReLU())\n    return result\n\ndef upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    result = Sequential()\n    result.add(Conv2DTranspose(filters, size, strides=2,\n                               padding='same',\n                               kernel_initializer=initializer,\n                               use_bias=False))\n    result.add(InstanceNormalization())\n    if apply_dropout:\n        result.add(Dropout(0.5))\n    result.add(ReLU())\n    return result","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.598181Z","iopub.execute_input":"2025-03-02T04:11:00.598437Z","iopub.status.idle":"2025-03-02T04:11:00.611566Z","shell.execute_reply.started":"2025-03-02T04:11:00.598416Z","shell.execute_reply":"2025-03-02T04:11:00.610865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_generator():\n    inputs = Input(shape=[512, 512, 3])  # Thay đổi input thành 512x512\n    output_channels = 3\n\n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False),  # (bs, 256, 256, 64)\n        downsample(128, 4),  # (bs, 128, 128, 128)\n        downsample(256, 4),  # (bs, 64, 64, 256)\n        downsample(512, 4),  # (bs, 32, 32, 512)\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)\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(512, 4),  # (bs, 32, 32, 1024)\n        upsample(256, 4),  # (bs, 64, 64, 512)\n        upsample(128, 4),  # (bs, 128, 128, 256)\n        upsample(64, 4),   # (bs, 256, 256, 128)\n    ]\n\n    initializer = tf.random_normal_initializer(0., 0.02)\n    last = Conv2DTranspose(output_channels, 4,\n                           strides=2,\n                           padding='same',\n                           kernel_initializer=initializer,\n                           activation='tanh')  # (bs, 512, 512, 3)\n\n    x = inputs\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 = Concatenate()([x, skip])\n    outputs = last(x)\n    return keras.Model(inputs=inputs, outputs=outputs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.612454Z","iopub.execute_input":"2025-03-02T04:11:00.612700Z","iopub.status.idle":"2025-03-02T04:11:00.622467Z","shell.execute_reply.started":"2025-03-02T04:11:00.612680Z","shell.execute_reply":"2025-03-02T04:11:00.621782Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_discriminator(add_noise=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    inputs = Input(shape=[512, 512, 3], name='input_image')  # Thay đổi input thành 512x512\n    x = inputs\n    x = downsample(64, 4, False, add_noise=add_noise)(x)  # (bs, 256, 256, 64)\n    x = downsample(128, 4, add_noise=add_noise)(x)  # (bs, 128, 128, 128)\n    x = downsample(256, 4, add_noise=add_noise)(x)  # (bs, 64, 64, 256)\n    x = downsample(512, 4, add_noise=add_noise)(x)  # (bs, 32, 32, 512)\n    x = ZeroPadding2D()(x)  # (bs, 34, 34, 512)\n    x = Conv2D(512, 4, strides=1,\n               kernel_initializer=initializer,\n               use_bias=False)(x)  # (bs, 31, 31, 512)\n    if add_noise:\n        x = GaussianNoise(0.2)(x)\n    x = InstanceNormalization()(x)\n    x = LeakyReLU()(x)\n    x = ZeroPadding2D()(x)  # (bs, 33, 33, 512)\n    outputs = Conv2D(1, 4, strides=1,\n                     kernel_initializer=initializer)(x)  # (bs, 30, 30, 1)\n    return tf.keras.Model(inputs=inputs, outputs=outputs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.623316Z","iopub.execute_input":"2025-03-02T04:11:00.623538Z","iopub.status.idle":"2025-03-02T04:11:00.637163Z","shell.execute_reply.started":"2025-03-02T04:11:00.623518Z","shell.execute_reply":"2025-03-02T04:11:00.636467Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model <a class=\"anchor\" id=\"model\"></a>","metadata":{}},{"cell_type":"code","source":"class TrainingGAN(Model):\n    def __init__(self, generator_g, discriminator_x, generator_f, discriminator_y,\n                 lambda_loss=10, gamma_loss=1e-4, lambda_id_loss=1e-5, diffaug_fn=None, batch_size=32, **kwargs):\n        super().__init__(**kwargs)\n        self.generator_g = generator_g\n        self.generator_f = generator_f\n        self.discriminator_x = discriminator_x\n        self.discriminator_y = discriminator_y\n        self.lambda_loss = lambda_loss\n        self.gamma_loss = gamma_loss\n        self.lambda_id_loss = lambda_id_loss\n        self.diffaug_fn = diffaug_fn\n        disc_inputs = Input(shape=[None, None, 3], name='input_image')\n        self.discriminator_features_x = Model(inputs=self.discriminator_x.input,\n                                              outputs=self.discriminator_x.layers[-2].output)\n        self.discriminator_features_y = Model(inputs=self.discriminator_y.input,\n                                              outputs=self.discriminator_y.layers[-2].output)\n\n    def compile(self, generator_g_optimizer, discriminator_x_optimizer,\n                generator_f_optimizer, discriminator_y_optimizer):\n        super().compile()\n        self.generator_g_optimizer = generator_g_optimizer\n        self.discriminator_x_optimizer = discriminator_x_optimizer\n        self.generator_f_optimizer = generator_f_optimizer\n        self.discriminator_y_optimizer = discriminator_y_optimizer\n\n    def _discriminator_bce_loss(self, real, generated):\n        real_loss = BinaryCrossentropy(\n            from_logits=True,\n            reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(real), real)\n        real_loss = tf.reduce_mean(real_loss)\n        generated_loss = BinaryCrossentropy(\n            from_logits=True,\n            reduction=tf.keras.losses.Reduction.NONE)(tf.zeros_like(generated), generated)\n        generated_loss = tf.reduce_mean(generated_loss)\n        total_disc_loss = real_loss + generated_loss\n        return total_disc_loss * 0.5\n\n    def _generator_bce_loss(self, generated):\n        loss = BinaryCrossentropy(\n            from_logits=True,\n            reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(generated), generated)\n        loss = tf.reduce_mean(loss)\n        return loss\n\n    def _cycle_loss(self, real_image, cycled_image):\n        loss = tf.reduce_mean(tf.abs(real_image - cycled_image))\n        return loss\n\n    def _identity_loss(self, real_image, same_image):\n        loss = tf.reduce_mean(tf.abs(real_image - same_image))\n        return 0.5 * loss\n\n    @tf.function\n    def train_step(self, batch):\n        (real_x, real_y) = batch\n        batch_size = tf.shape(real_y)[0]\n        with tf.GradientTape(persistent=True) as tape:\n            fake_y = self.generator_g(real_x, training=True)\n            cycled_x = self.generator_f(fake_y, training=True)\n            fake_x = self.generator_f(real_y, training=True)\n            cycled_y = self.generator_g(fake_x, training=True)\n            same_x = self.generator_f(real_x, training=True)\n            same_y = self.generator_g(real_y, training=True)\n\n            if self.diffaug_fn:\n                both_y = tf.concat([real_y, fake_y], axis=0)\n                aug_y = self.diffaug_fn(both_y)\n                aug_real_y = aug_y[:batch_size]\n                aug_fake_y = aug_y[batch_size:]\n                disc_real_y = self.discriminator_y(aug_real_y, training=True)\n                disc_fake_y = self.discriminator_y(aug_fake_y, training=True)\n            else:\n                disc_real_y = self.discriminator_y(real_y, training=True)\n                disc_fake_y = self.discriminator_y(fake_y, training=True)\n\n            disc_real_x = self.discriminator_x(real_x, training=True)\n            disc_fake_x = self.discriminator_x(fake_x, training=True)\n\n            disc_feat_x = self.discriminator_features_x(real_x, training=True)\n            disc_feat_cycled_x = self.discriminator_features_x(cycled_x, training=True)\n            disc_feat_y = self.discriminator_features_x(real_y, training=True)\n            disc_feat_cycled_y = self.discriminator_features_x(cycled_y, training=True)\n\n            gen_g_loss = self._generator_bce_loss(disc_fake_y)\n            gen_f_loss = self._generator_bce_loss(disc_fake_x)\n            cycle_loss_x = ((1 - self.gamma_loss) * self._cycle_loss(real_x, cycled_x) +\n                            self.gamma_loss * self._cycle_loss(disc_feat_x, disc_feat_cycled_x))\n            cycle_loss_y = ((1 - self.gamma_loss) *  self._cycle_loss(real_y, cycled_y) +\n                            self.gamma_loss * self._cycle_loss(disc_feat_y, disc_feat_cycled_y))\n            total_cycle_loss = self.lambda_loss * (cycle_loss_x + cycle_loss_y)\n            id_loss_y = self.lambda_id_loss * self._identity_loss(real_y, same_y)\n            id_loss_x = self.lambda_id_loss * self._identity_loss(real_x, same_x)\n            total_gen_g_loss = gen_g_loss + total_cycle_loss + id_loss_y\n            total_gen_f_loss = gen_f_loss + total_cycle_loss + id_loss_x\n\n            disc_x_loss = self._discriminator_bce_loss(disc_real_x, disc_fake_x)\n            disc_y_loss = self._discriminator_bce_loss(disc_real_y, disc_fake_y)\n\n        generator_g_gradients = tape.gradient(total_gen_g_loss, self.generator_g.trainable_variables)\n        generator_f_gradients = tape.gradient(total_gen_f_loss, self.generator_f.trainable_variables)\n        discriminator_x_gradients = tape.gradient(disc_x_loss, self.discriminator_x.trainable_variables)\n        discriminator_y_gradients = tape.gradient(disc_y_loss, self.discriminator_y.trainable_variables)\n\n        self.generator_g_optimizer.apply_gradients(zip(generator_g_gradients, self.generator_g.trainable_variables))\n        self.generator_f_optimizer.apply_gradients(zip(generator_f_gradients, self.generator_f.trainable_variables))\n        self.discriminator_x_optimizer.apply_gradients(zip(discriminator_x_gradients, self.discriminator_x.trainable_variables))\n        self.discriminator_y_optimizer.apply_gradients(zip(discriminator_y_gradients, self.discriminator_y.trainable_variables))\n\n        return {\n            \"disc_x_loss\": disc_x_loss,\n            \"disc_y_loss\": disc_y_loss,\n            \"total_gen_g_loss\": total_gen_g_loss,\n            \"total_gen_f_loss\": total_gen_f_loss,\n            \"gen_g_loss\": cycle_loss_x,\n            \"gen_f_loss\": cycle_loss_y,\n            }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.640934Z","iopub.execute_input":"2025-03-02T04:11:00.641219Z","iopub.status.idle":"2025-03-02T04:11:00.663683Z","shell.execute_reply.started":"2025-03-02T04:11:00.641198Z","shell.execute_reply":"2025-03-02T04:11:00.663008Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Callbacks <a class=\"anchor\" id=\"callbacks\"></a>","metadata":{"id":"E4GBmFKAMRgB"}},{"cell_type":"code","source":"class PlotPredictionsCallback(keras.callbacks.Callback):\n    def __init__(self, input_image, model_generator, epoch_interval=None, nrows=1, figsize=(11, 11)):\n        self.input_image = input_image\n        self.model_generator = model_generator\n        self.epoch_interval = epoch_interval\n        self.nrows = nrows\n        self.figsize=(11, 11)\n\n    def _plot_test_and_pred(self):\n        preds = self.model_generator.predict(self.input_image, verbose=0)\n        (fig, axes) = subplots(nrows=self.nrows, ncols=2, figsize=self.figsize)\n        if self.nrows == 1:\n            axes = [axes]\n        for (ax, inp, pred) in zip(axes, self.input_image, preds):\n            ax[0].imshow(array_to_img(inp))\n            ax[0].set_title(\"Input Image\")\n            ax[0].set_axis_off()\n            ax[1].imshow(array_to_img(pred))\n            ax[1].set_title(\"Prediction\")\n            ax[1].set_axis_off()\n        plt.show()\n\n    def on_epoch_end(self, epoch, logs=None):\n        if self.epoch_interval and epoch % self.epoch_interval == 0:\n            self._plot_test_and_pred()\n\n    def on_train_end(self, logs=None):\n        self._plot_test_and_pred()","metadata":{"id":"82rM_VCnMRgC","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.664577Z","iopub.execute_input":"2025-03-02T04:11:00.664822Z","iopub.status.idle":"2025-03-02T04:11:00.678260Z","shell.execute_reply.started":"2025-03-02T04:11:00.664799Z","shell.execute_reply":"2025-03-02T04:11:00.677578Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FIDCalculator(object):\n    def __init__(self, images_x_ds, images_y_ds, model_generator, fid_model_base):\n        self.images_x_ds = images_x_ds\n        self.images_y_ds = images_y_ds\n        self.model_generator = model_generator\n        self.fid_model_base = fid_model_base\n        self.initialized = False\n        self.history = []\n\n    def init_stat_x(self):\n        self.mu_2, self.sigma_2 = self._calculate_activation_statistics_mod(self.images_y_ds, self.fid_model_base)\n        self.initialized = True\n\n    def _calculate_activation_statistics_mod(self, images, fid_model):\n        act = tf.cast(fid_model.predict(images, verbose=0), tf.float32)\n        mu = tf.reduce_mean(act, axis=0)\n        mean_x = tf.reduce_mean(act, axis=0, keepdims=True)\n        mx = tf.matmul(tf.transpose(mean_x), mean_x)\n        vx = tf.matmul(tf.transpose(act), act)/tf.cast(tf.shape(act)[0], tf.float32)\n        sigma = vx - mx\n        return mu, sigma\n\n    def _calculate_frechet_distance(self, mu_1, sigma_1, mu_2, sigma_2):\n        fid_epsilon = 1e-14\n        covmean = tf.linalg.sqrtm(tf.cast(tf.matmul(sigma_1, sigma_2), tf.complex64))\n        covmean = tf.cast(tf.math.real(covmean), tf.float32)\n        tr_covmean = tf.linalg.trace(covmean)\n        fid_value = tf.matmul(\n            tf.expand_dims(mu_1 - mu_2, axis=0),\n            tf.expand_dims(mu_1 - mu_2, axis=1)\n            ) + tf.linalg.trace(sigma_1) + tf.linalg.trace(sigma_2) - 2 * tr_covmean\n        return fid_value\n\n    def _get_gen_plus_fid_model(self):\n        inputs = layers.Input(shape=[512, 512, 3], name='input_image')  # Thay đổi input thành 512x512\n        x = self.model_generator(inputs)\n        outputs = self.fid_model_base(x)\n        fid_model = tf.keras.Model(inputs=inputs, outputs=outputs)\n        return fid_model\n\n    def calc_fid(self):\n        if not self.initialized:\n            self.init_stat_x()\n        fid_model_plus = self._get_gen_plus_fid_model()\n        mu_1, sigma_1 = self._calculate_activation_statistics_mod(self.images_x_ds, fid_model_plus)\n        fid_value = self._calculate_frechet_distance(mu_1, sigma_1, self.mu_2, self.sigma_2)\n        return fid_value","metadata":{"id":"z4ZmwkIPMRgD","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.679160Z","iopub.execute_input":"2025-03-02T04:11:00.679382Z","iopub.status.idle":"2025-03-02T04:11:00.693010Z","shell.execute_reply.started":"2025-03-02T04:11:00.679362Z","shell.execute_reply":"2025-03-02T04:11:00.692341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_fid_inception_model():\n    inception_model_base = tf.keras.applications.InceptionV3(\n        input_shape=(512, 512, 3),  # Thay đổi input thành 512x512\n        pooling=\"avg\",\n        include_top=False)\n    mix3  = inception_model_base.get_layer(\"mixed9\").output\n    f0 = tf.keras.layers.GlobalAveragePooling2D()(mix3)\n    inception_model = tf.keras.Model(inputs=inception_model_base.input, outputs=f0)\n    inception_model.trainable = False\n    return inception_model","metadata":{"id":"kv8Is0BYMRgD","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.693959Z","iopub.execute_input":"2025-03-02T04:11:00.694231Z","iopub.status.idle":"2025-03-02T04:11:00.706876Z","shell.execute_reply.started":"2025-03-02T04:11:00.694207Z","shell.execute_reply":"2025-03-02T04:11:00.706232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FIDCallback(Callback):\n    def __init__(self, fid_calculator, epoch_interval=None):\n        self.fid_calculator = fid_calculator\n        self.epoch_interval = epoch_interval\n\n    def _get_fid(self):\n        fid = self.fid_calculator.calc_fid()\n        print(\"FID score:\", fid.numpy()[0,0])\n\n    def on_epoch_end(self, epoch, logs=None):\n        if self.epoch_interval and epoch % self.epoch_interval == 0:\n            self._get_fid()\n\n    def on_train_end(self, logs=None):\n        self._get_fid()","metadata":{"id":"WuxICgo4MRgE","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.707801Z","iopub.execute_input":"2025-03-02T04:11:00.708101Z","iopub.status.idle":"2025-03-02T04:11:00.716141Z","shell.execute_reply.started":"2025-03-02T04:11:00.708077Z","shell.execute_reply":"2025-03-02T04:11:00.715397Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class UpdateLearningRateCallback(Callback):\n    def __init__(self, epochs_count, lr_start=2e-4, lr_end=5e-6):\n        super().__init__()\n        self.epoch_min = epochs_count // 2\n        epochs_update_count = epochs_count - self.epoch_min\n        self.lr_values = np.linspace(lr_start, lr_end, epochs_update_count)\n\n    def _scheduler_fn(self, epoch, lr):\n        if epoch < self.epoch_min:\n            return lr\n        else:\n            return self.lr_values[epoch-self.epoch_min]\n\n    def on_epoch_begin(self, epoch, logs=None):\n        self.model.generator_g_optimizer.lr = self._scheduler_fn(epoch, self.model.generator_g_optimizer.lr)\n        self.model.discriminator_x_optimizer.lr = self._scheduler_fn(epoch, self.model.generator_g_optimizer.lr)\n        self.model.generator_f_optimizer.lr = self._scheduler_fn(epoch, self.model.generator_g_optimizer.lr)\n        self.model.discriminator_y_optimizer.lr = self._scheduler_fn(epoch, self.model.generator_g_optimizer.lr)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.717070Z","iopub.execute_input":"2025-03-02T04:11:00.717320Z","iopub.status.idle":"2025-03-02T04:11:00.728577Z","shell.execute_reply.started":"2025-03-02T04:11:00.717297Z","shell.execute_reply":"2025-03-02T04:11:00.727893Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class UpdateLossWeightsCallback(Callback):\n    def __init__(self, epochs, lambda_start=10, lambda_end=1e-4, gamma_start=1e-4, gamma_end=0.999):\n        super().__init__()\n        self.epochs = epochs\n        self.lambda_start = lambda_start\n        self.lambda_end = lambda_end\n        self.gamma_start = gamma_start\n        self.gamma_end = gamma_end\n\n    def on_train_begin(self, logs=None):\n        self.lambda_values = np.linspace(self.lambda_start, self.lambda_end, self.epochs)\n        self.gamma_values = np.linspace(self.gamma_start, self.gamma_end, self.epochs)\n\n    def on_epoch_begin(self, epoch, logs=None):\n        self.model.lambda_loss = self.lambda_values[epoch]\n        self.model.gamma_loss = self.gamma_values[epoch]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.729405Z","iopub.execute_input":"2025-03-02T04:11:00.729641Z","iopub.status.idle":"2025-03-02T04:11:00.739186Z","shell.execute_reply.started":"2025-03-02T04:11:00.729619Z","shell.execute_reply":"2025-03-02T04:11:00.738481Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Objects for training <a class=\"anchor\" id=\"objects\"></a>","metadata":{"id":"52IPZaJLMRgE"}},{"cell_type":"code","source":"EPOCHS = 20 # 43\nSTEPS_PER_EPOCH = 3000\nLAMBDA_START = 3\nLAMBDA_END = 1e-4\nGAMMA_START = 1e-4\nGAMMA_END = 0.999\nLAMBDA_ID = 1e-4\nEPOCH_INTERVAL_PLOT = 2\nEPOCH_INTERVAL_FID = None\nLR_START = 2e-4\nLR_END = 5e-6","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.740097Z","iopub.execute_input":"2025-03-02T04:11:00.740348Z","iopub.status.idle":"2025-03-02T04:11:00.752500Z","shell.execute_reply.started":"2025-03-02T04:11:00.740326Z","shell.execute_reply":"2025-03-02T04:11:00.751779Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    update_lr_cb = UpdateLearningRateCallback(EPOCHS, lr_start=LR_START, lr_end=LR_END)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.753401Z","iopub.execute_input":"2025-03-02T04:11:00.753646Z","iopub.status.idle":"2025-03-02T04:11:00.762543Z","shell.execute_reply.started":"2025-03-02T04:11:00.753625Z","shell.execute_reply":"2025-03-02T04:11:00.761807Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    generator_g = create_generator()\n    generator_f = create_generator()\n    discriminator_x = create_discriminator(add_noise=True)\n    discriminator_y = create_discriminator(add_noise=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:00.763455Z","iopub.execute_input":"2025-03-02T04:11:00.763703Z","iopub.status.idle":"2025-03-02T04:11:24.439267Z","shell.execute_reply.started":"2025-03-02T04:11:00.763680Z","shell.execute_reply":"2025-03-02T04:11:24.438134Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# generator_g.summary()","metadata":{"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:24.440258Z","iopub.execute_input":"2025-03-02T04:11:24.440506Z","iopub.status.idle":"2025-03-02T04:11:24.444023Z","shell.execute_reply.started":"2025-03-02T04:11:24.440482Z","shell.execute_reply":"2025-03-02T04:11:24.443245Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# discriminator_x.summary()","metadata":{"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:24.444908Z","iopub.execute_input":"2025-03-02T04:11:24.445169Z","iopub.status.idle":"2025-03-02T04:11:24.455931Z","shell.execute_reply.started":"2025-03-02T04:11:24.445147Z","shell.execute_reply":"2025-03-02T04:11:24.455136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_pred_photo = next(iter(test_photo_ds.take(1)))\nplot_pred_photo = np.expand_dims(plot_pred_photo[0], axis=0)\nwith strategy.scope():\n    plot_pred_cb = PlotPredictionsCallback(\n        input_image=plot_pred_photo,\n        model_generator=generator_g,\n        epoch_interval=EPOCH_INTERVAL_PLOT)","metadata":{"id":"agbraWSxMRgG","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:24.456905Z","iopub.execute_input":"2025-03-02T04:11:24.457182Z","iopub.status.idle":"2025-03-02T04:11:24.751201Z","shell.execute_reply.started":"2025-03-02T04:11:24.457158Z","shell.execute_reply":"2025-03-02T04:11:24.750119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fid_photo_ds = load_dataset_from_dir(PHOTO_DIR).take(1024).batch(64).prefetch(AUTOTUNE)\nfid_monet_ds = load_dataset_from_dir(MONET_DIR).batch(64).prefetch(AUTOTUNE)","metadata":{"id":"bIdXefNqMRgH","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:24.752391Z","iopub.execute_input":"2025-03-02T04:11:24.752784Z","iopub.status.idle":"2025-03-02T04:11:24.955136Z","shell.execute_reply.started":"2025-03-02T04:11:24.752755Z","shell.execute_reply":"2025-03-02T04:11:24.953671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    fid_model = create_fid_inception_model()\n    fid_calc = FIDCalculator(\n        images_x_ds=fid_photo_ds,\n        images_y_ds=fid_monet_ds,\n        model_generator=generator_g,\n        fid_model_base=fid_model)\n    fid_calc.init_stat_x()\n    fid_cb = FIDCallback(fid_calculator=fid_calc, epoch_interval=EPOCH_INTERVAL_FID)","metadata":{"id":"ueutu8udMRgH","outputId":"7f0cfebb-dd63-4c6e-ad92-df40d7e61a21","_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:11:24.957242Z","iopub.execute_input":"2025-03-02T04:11:24.957774Z","iopub.status.idle":"2025-03-02T04:12:36.083506Z","shell.execute_reply.started":"2025-03-02T04:11:24.957724Z","shell.execute_reply":"2025-03-02T04:12:36.081951Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    update_weights_cb = UpdateLossWeightsCallback(\n        EPOCHS,\n        lambda_start=LAMBDA_START,\n        lambda_end=LAMBDA_END,\n        gamma_start=GAMMA_START,\n        gamma_end=GAMMA_END)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:12:36.085001Z","iopub.execute_input":"2025-03-02T04:12:36.085320Z","iopub.status.idle":"2025-03-02T04:12:36.090719Z","shell.execute_reply.started":"2025-03-02T04:12:36.085294Z","shell.execute_reply":"2025-03-02T04:12:36.089633Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training <a class=\"anchor\" id=\"training\"></a>","metadata":{"id":"QZ42eBNmMRgH"}},{"cell_type":"code","source":"with strategy.scope():\n    model_cycleGAN = TrainingGAN(\n        generator_g=generator_g,\n        generator_f=generator_f,\n        discriminator_x=discriminator_x,\n        discriminator_y=discriminator_y,\n        lambda_loss=LAMBDA_START,\n        lambda_id_loss=LAMBDA_ID,\n        gamma_loss=GAMMA_START,\n        diffaug_fn=diffaug_fn\n    )\n    generator_g_optimizer = Adam(learning_rate=LR_START, beta_1=0.5)\n    discriminator_x_optimizer = Adam(learning_rate=LR_START, beta_1=0.5)\n    generator_f_optimizer = Adam(learning_rate=LR_START, beta_1=0.5)\n    discriminator_y_optimizer = Adam(learning_rate=LR_START, beta_1=0.5)\n\n    model_cycleGAN.compile(\n        generator_g_optimizer=generator_g_optimizer,\n        discriminator_x_optimizer=discriminator_x_optimizer,\n        generator_f_optimizer=generator_f_optimizer,\n        discriminator_y_optimizer=discriminator_y_optimizer,\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:12:36.092027Z","iopub.execute_input":"2025-03-02T04:12:36.092289Z","iopub.status.idle":"2025-03-02T04:12:36.335270Z","shell.execute_reply.started":"2025-03-02T04:12:36.092267Z","shell.execute_reply":"2025-03-02T04:12:36.333852Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\nhistory = model_cycleGAN.fit(\n    train_ds,\n    epochs=EPOCHS,\n    callbacks=[plot_pred_cb, fid_cb, update_weights_cb, update_lr_cb],\n    steps_per_epoch=STEPS_PER_EPOCH\n)","metadata":{"id":"OIAA6r2wMRgI","outputId":"44217e46-0647-47a1-bd31-88f4f59bc943","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T04:12:36.336627Z","iopub.execute_input":"2025-03-02T04:12:36.337191Z","iopub.status.idle":"2025-03-02T10:01:04.477534Z","shell.execute_reply.started":"2025-03-02T04:12:36.337164Z","shell.execute_reply":"2025-03-02T10:01:04.475865Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Save and Using Model","metadata":{}},{"cell_type":"code","source":"generator_g.save('/kaggle/working/generator_g_trained_model.h5')\ndiscriminator_x.save('/kaggle/working/discriminator_x_trained_model.h5')\ngenerator_f.save('/kaggle/working/generator_f_trained_model.h5')\ndiscriminator_y.save('/kaggle/working/discriminator_y_trained_model.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:10:01.142408Z","iopub.execute_input":"2025-03-02T10:10:01.143271Z","iopub.status.idle":"2025-03-02T10:10:03.076420Z","shell.execute_reply.started":"2025-03-02T10:10:01.143238Z","shell.execute_reply":"2025-03-02T10:10:03.075137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"generator_g.save('/kaggle/working/generator_g_trained_model2.h5')\ndiscriminator_x.save('/kaggle/working/discriminator_x_trained_model2.h5')\ngenerator_f.save('/kaggle/working/generator_f_trained_model2.h5')\ndiscriminator_y.save('/kaggle/working/discriminator_y_trained_model2.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:10:58.793079Z","iopub.execute_input":"2025-03-02T10:10:58.793476Z","iopub.status.idle":"2025-03-02T10:10:59.942097Z","shell.execute_reply.started":"2025-03-02T10:10:58.793446Z","shell.execute_reply":"2025-03-02T10:10:59.941030Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Plot","metadata":{}},{"cell_type":"code","source":"print(np.array(list(history.history.values())).shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:01:05.695350Z","iopub.execute_input":"2025-03-02T10:01:05.695641Z","iopub.status.idle":"2025-03-02T10:01:05.700874Z","shell.execute_reply.started":"2025-03-02T10:01:05.695603Z","shell.execute_reply":"2025-03-02T10:01:05.699829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# Chuyển đổi từ history.history sang numpy array\nloss_values = np.array(list(history.history.values()))\nepochs = np.arange(1, len(loss_values[0]) + 1)  # Số epoch\n\n# Các tên loss\nloss_names = list(history.history.keys())\n\n# Vẽ biểu đồ đường\nplt.figure(figsize=(18, 6))  # Chiều ngang lớn hơn\n\nfor i in range(len(loss_names)):\n    plt.plot(epochs, loss_values[i], marker='o', linestyle='-', label=loss_names[i])\n\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training Loss over Epochs\")\nplt.legend(loc='upper right')  # Đưa chú thích lên góc trên bên phải\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:01:05.702015Z","iopub.execute_input":"2025-03-02T10:01:05.702306Z","iopub.status.idle":"2025-03-02T10:01:06.013975Z","shell.execute_reply.started":"2025-03-02T10:01:05.702284Z","shell.execute_reply":"2025-03-02T10:01:06.012881Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# Chuyển đổi từ history.history sang numpy array\nloss_values = np.array(list(history.history.values()))\nepochs = np.arange(1, len(loss_values[0]) + 1)  # Số epoch\n\n# Các tên loss\nloss_names = list(history.history.keys())\n\n# Vẽ biểu đồ đường\nplt.figure(figsize=(18, 6))  # Chiều ngang lớn hơn\n\nfor i in range(len(loss_names)):\n    plt.plot(epochs, loss_values[i], linestyle='-', label=loss_names[i])  # Xóa marker='o'\n\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training Loss over Epochs\")\nplt.legend(loc='upper right')  # Đưa chú thích lên góc trên bên phải\nplt.grid(False)  # Tắt lưới\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:01:06.015175Z","iopub.execute_input":"2025-03-02T10:01:06.015458Z","iopub.status.idle":"2025-03-02T10:01:06.309449Z","shell.execute_reply.started":"2025-03-02T10:01:06.015432Z","shell.execute_reply":"2025-03-02T10:01:06.308456Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Plot predictions <a class=\"anchor\" id=\"plotpredictions\"></a>","metadata":{"id":"2W7-r9O4MRgM"}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef plot_predictions(model, ds):\n    ds_iter = iter(ds)  # Tạo iterator từ dataset\n    num_samples = sum(1 for _ in ds)  # Đếm số lượng phần tử trong dataset\n    num_iter = min(8, num_samples)  # Giới hạn số lần lặp trong phạm vi hợp lệ\n\n    for n_sample in range(num_iter):\n        try:\n            example_sample = next(ds_iter)  # Lấy một mẫu từ dataset\n            generated_sample = model.predict(example_sample, verbose=0)\n\n            f = plt.figure(figsize=(13, 13))\n            plt.subplot(121)\n            plt.title('Input image')\n            plt.imshow(example_sample[0] * 0.5 + 0.5)\n            plt.axis('off')\n\n            plt.subplot(122)\n            plt.title('Generated image')\n            plt.imshow(generated_sample[0] * 0.5 + 0.5)\n            plt.axis('off')\n\n            plt.show()\n        except StopIteration:\n            print(\"Hết dữ liệu để hiển thị!\")\n            break","metadata":{"id":"ZvdbAD2NMRgM","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:01:06.310731Z","iopub.execute_input":"2025-03-02T10:01:06.311034Z","iopub.status.idle":"2025-03-02T10:01:06.318745Z","shell.execute_reply.started":"2025-03-02T10:01:06.311007Z","shell.execute_reply":"2025-03-02T10:01:06.317836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_predictions(generator_g, test_photo_ds.shuffle(1000))","metadata":{"id":"Y-g-HxD7MRgM","outputId":"9f597efa-33ad-4cae-e59a-085841c3b07a","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:01:06.319760Z","iopub.execute_input":"2025-03-02T10:01:06.320021Z","iopub.status.idle":"2025-03-02T10:02:04.328876Z","shell.execute_reply.started":"2025-03-02T10:01:06.319999Z","shell.execute_reply":"2025-03-02T10:02:04.327537Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission <a class=\"anchor\" id=\"submission\"></a>","metadata":{"id":"MbPPPH-eMRgN"}},{"cell_type":"code","source":"import zipfile\ndef generate_and_save(generator, ds):\n    count = 0\n    if os.path.exists('images.zip'):\n        os.remove('images.zip')\n    with zipfile.ZipFile('images.zip', 'w') as zipf:\n        for images_batch in ds:\n            predictions = generator(images_batch, training=False)\n            for pred in predictions:\n                count += 1\n                generated_image = array_to_img(pred)\n                generated_image_bytes = BytesIO()\n                generated_image.save(generated_image_bytes, format='JPEG')\n                generated_image_bytes.seek(0)\n                zipf.writestr(f'generated_image_{count}.jpg', generated_image_bytes.getvalue())\n                if count % 1024 == 0:\n                    print(f'Archived images: {count}')","metadata":{"id":"0pChjahqMRgO","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:03:11.370954Z","iopub.execute_input":"2025-03-02T10:03:11.371995Z","iopub.status.idle":"2025-03-02T10:03:11.379190Z","shell.execute_reply.started":"2025-03-02T10:03:11.371918Z","shell.execute_reply":"2025-03-02T10:03:11.378239Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\ngenerate_and_save(generator_g, test_photo_ds)","metadata":{"id":"bPtTNii4MRgO","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T10:03:14.775616Z","iopub.execute_input":"2025-03-02T10:03:14.776641Z","iopub.status.idle":"2025-03-02T10:03:53.715511Z","shell.execute_reply.started":"2025-03-02T10:03:14.776598Z","shell.execute_reply":"2025-03-02T10:03:53.714403Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"[Paper: Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks](https://arxiv.org/abs/1703.10593)\n\n[Paper: CycleGAN with Better Cycles](https://www.tongzhouwang.info/better_cycles/report.pdf)\n\n[Paper: Improved Techniques for Training GANs](https://arxiv.org/abs/1606.03498)\n\n[Paper: Differentiable Augmentation for Data-Efficient GAN Training](https://arxiv.org/abs/2006.10738)\n\n[GitHub: Transforming the World Into Paintings with CycleGAN](https://github.com/sebtheiler/tutorials/tree/main/cyclegan)\n\n[Article: pix2pix: Image-to-image translation with a conditional GAN](https://www.tensorflow.org/tutorials/generative/pix2pix)\n\n[Notebook: Two-objective discriminator by UnfriendlyAI](https://www.kaggle.com/code/unfriendlyai/two-objective-discriminator)","metadata":{"id":"2SX6LFWPMRgP"}}]}