{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30299,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Introduction and Setup\n\nNotebook này sử dụng kiến trúc CycleGAN để thêm Monet-style vào ảnh. Đối với hướng dẫn này, chúng tôi sẽ sử dụng tập dữ liệu TFRecord. Import các package và thay đổi bộ tăng tốc thành TPU.\n\nĐể thêm thông tin, hãy xem các trang tài liệu CycleGAN của [TensorFlow](https://www.tensorflow.org/tutorials/generative/cyclegan)\nvà \n[Keras](https://keras.io/examples/generative/cyclegan/)","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa\n\nfrom kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Device:', tpu.master())\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept:\n    strategy = tf.distribute.get_strategy()\nprint('Number of replicas:', strategy.num_replicas_in_sync)\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n    \nprint(tf.__version__)","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","execution":{"iopub.status.busy":"2024-06-04T13:40:58.461001Z","iopub.execute_input":"2024-06-04T13:40:58.461415Z","iopub.status.idle":"2024-06-04T13:41:04.037776Z","shell.execute_reply.started":"2024-06-04T13:40:58.461335Z","shell.execute_reply":"2024-06-04T13:41:04.036604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Do TPU giới hạn nên đã đổi sang dùng GPU100","metadata":{}},{"cell_type":"markdown","source":"# Load in the data\n\nChúng tôi muốn giữ riêng tập dữ liệu ảnh và tập dữ liệu Monet. Đầu tiên, hãy tải filenames của TFRecords.","metadata":{}},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path()","metadata":{"execution":{"iopub.status.busy":"2024-06-04T13:43:02.775970Z","iopub.execute_input":"2024-06-04T13:43:02.776963Z","iopub.status.idle":"2024-06-04T13:43:03.275737Z","shell.execute_reply.started":"2024-06-04T13:43:02.776922Z","shell.execute_reply":"2024-06-04T13:43:03.274924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"MONET_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/monet_tfrec/*.tfrec'))\nprint('Monet TFRecord Files:', len(MONET_FILENAMES))\n\nPHOTO_FILENAMES = tf.io.gfile.glob(str(GCS_PATH + '/photo_tfrec/*.tfrec'))\nprint('Photo TFRecord Files:', len(PHOTO_FILENAMES))","metadata":{"execution":{"iopub.status.busy":"2024-06-04T13:43:11.029616Z","iopub.execute_input":"2024-06-04T13:43:11.029982Z","iopub.status.idle":"2024-06-04T13:43:14.098941Z","shell.execute_reply.started":"2024-06-04T13:43:11.029949Z","shell.execute_reply":"2024-06-04T13:43:14.097960Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tất cả hình ảnh cho cuộc thi đã được định cỡ thành 256x256. Vì những hình ảnh này là hình ảnh RGB, hãy đặt chanel thành 3. Ngoài ra, chúng ta cần phải chia tỷ lệ hình ảnh theo tỷ lệ [-1, 1]. Vì chúng ta đang xây dựng một mô hình tạo sinh, chúng ta không cần nhãn hoặc id hình ảnh nên chúng ta sẽ chỉ trả về hình ảnh từ TFRecord.","metadata":{}},{"cell_type":"code","source":"IMAGE_SIZE = [256, 256]\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = (tf.cast(image, tf.float32) / 127.5) - 1\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_tfrecord(example):\n    tfrecord_format = {\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    return image","metadata":{"execution":{"iopub.status.busy":"2024-06-04T13:55:19.827487Z","iopub.execute_input":"2024-06-04T13:55:19.827879Z","iopub.status.idle":"2024-06-04T13:55:19.836343Z","shell.execute_reply.started":"2024-06-04T13:55:19.827845Z","shell.execute_reply":"2024-06-04T13:55:19.835393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# hàm trích xuất hình ảnh từ các tập tin\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE)\n    return dataset","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:35:27.854379Z","iopub.execute_input":"2024-06-04T14:35:27.854797Z","iopub.status.idle":"2024-06-04T14:35:27.860471Z","shell.execute_reply.started":"2024-06-04T14:35:27.854762Z","shell.execute_reply":"2024-06-04T14:35:27.859375Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# tải dữ liệu vào tập dữ liệu custom\n\nmonet_ds = load_dataset(MONET_FILENAMES, labeled=True).batch(1)\nphoto_ds = load_dataset(PHOTO_FILENAMES, labeled=True).batch(1)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:41:05.299466Z","iopub.execute_input":"2024-06-04T14:41:05.299847Z","iopub.status.idle":"2024-06-04T14:41:05.340276Z","shell.execute_reply.started":"2024-06-04T14:41:05.299814Z","shell.execute_reply":"2024-06-04T14:41:05.339528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lấy một ví dụ ngẫu nhiên về monet và về ảnh\n\nexample_monet = next(iter(monet_ds))\nexample_photo = next(iter(photo_ds))","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:42:44.800507Z","iopub.execute_input":"2024-06-04T14:42:44.801259Z","iopub.status.idle":"2024-06-04T14:42:50.771029Z","shell.execute_reply.started":"2024-06-04T14:42:44.801213Z","shell.execute_reply":"2024-06-04T14:42:50.770177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# hiển thị ví dụ\n\nplt.subplot(121)\nplt.title('Photo')\nplt.imshow(example_photo[0] * 0.5 + 0.5)\n\nplt.subplot(122)\nplt.title('Monet')\nplt.imshow(example_monet[0] * 0.5 + 0.5)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:43:10.826809Z","iopub.execute_input":"2024-06-04T14:43:10.827838Z","iopub.status.idle":"2024-06-04T14:43:11.126454Z","shell.execute_reply.started":"2024-06-04T14:43:10.827796Z","shell.execute_reply":"2024-06-04T14:43:11.125500Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build the generator\n\nChúng ta sẽ sử dụng kiến trúc UNET cho CycleGAN của mình. Để xây dựng bộ tạo (generator), trước tiên hãy định nghĩa các phương thức downsample (giảm mẫu) và upsample (tăng mẫu).\n\nDownsample, như tên gọi của nó, giảm kích thước 2D (chiều rộng và chiều cao) của ảnh theo bước nhảy (stride). Bước nhảy là độ dài của bước di chuyển mà bộ lọc thực hiện. Do bước nhảy là 2, bộ lọc được áp dụng cho mọi pixel khác, do đó giảm chiều cao và chiều rộng đi 2.\n\nChúng ta sẽ sử dụng chuẩn hóa theo instance (instance normalization) thay vì chuẩn hóa theo batch (batch normalization). Vì chuẩn hóa theo instance không phải là chuẩn trong TensorFlow API, nên chúng ta sẽ sử dụng lớp từ TensorFlow Add-ons.","metadata":{}},{"cell_type":"code","source":"OUTPUT_CHANNELS = 3\n\ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = keras.Sequential()\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same',\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(layers.LeakyReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:49:17.922837Z","iopub.execute_input":"2024-06-04T14:49:17.923240Z","iopub.status.idle":"2024-06-04T14:49:17.930609Z","shell.execute_reply.started":"2024-06-04T14:49:17.923203Z","shell.execute_reply":"2024-06-04T14:49:17.929686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"'Upsample' thực hiện ngược lại với 'downsample' và tăng kích thước của hình ảnh. 'Conv2DTranspose' về cơ bản thực hiện ngược lại với lớp 'Conv2D'.","metadata":{}},{"cell_type":"code","source":"def upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    result = keras.Sequential()\n    result.add(layers.Conv2DTranspose(filters, size, strides=2,\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(layers.Dropout(0.5))\n\n    result.add(layers.ReLU())\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:50:59.086430Z","iopub.execute_input":"2024-06-04T14:50:59.087105Z","iopub.status.idle":"2024-06-04T14:50:59.094531Z","shell.execute_reply.started":"2024-06-04T14:50:59.087066Z","shell.execute_reply":"2024-06-04T14:50:59.093469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Được rồi, bắt tay vào xây dựng bộ tạo nào!\n\nBộ tạo đầu tiên sẽ downsample ảnh đầu vào và sau đó upsample lại trong khi thiết lập các kết nối bỏ qua dài (long skip connections). Kết nối bỏ qua là một cách giúp vượt qua vấn đề gradient biến mất (vanishing gradient problem) bằng cách nối đầu ra của một lớp với nhiều lớp khác nhau thay vì chỉ một lớp. Ở đây, chúng ta nối đối xứng đầu ra của lớp downsample với lớp upsample.","metadata":{}},{"cell_type":"code","source":"def Generator():\n    inputs = layers.Input(shape=[256,256,3])\n\n    # bs = batch size\n    down_stack = [\n        downsample(64, 4, apply_instancenorm=False), # (bs, 128, 128, 64)\n        downsample(128, 4), # (bs, 64, 64, 128)\n        downsample(256, 4), # (bs, 32, 32, 256)\n        downsample(512, 4), # (bs, 16, 16, 512)\n        downsample(512, 4), # (bs, 8, 8, 512)\n        downsample(512, 4), # (bs, 4, 4, 512)\n        downsample(512, 4), # (bs, 2, 2, 512)\n        downsample(512, 4), # (bs, 1, 1, 512)\n    ]\n\n    up_stack = [\n        upsample(512, 4, apply_dropout=True), # (bs, 2, 2, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 4, 4, 1024)\n        upsample(512, 4, apply_dropout=True), # (bs, 8, 8, 1024)\n        upsample(512, 4), # (bs, 16, 16, 1024)\n        upsample(256, 4), # (bs, 32, 32, 512)\n        upsample(128, 4), # (bs, 64, 64, 256)\n        upsample(64, 4), # (bs, 128, 128, 128)\n    ]\n\n    initializer = tf.random_normal_initializer(0., 0.02)\n    last = layers.Conv2DTranspose(OUTPUT_CHANNELS, 4,\n                                  strides=2,\n                                  padding='same',\n                                  kernel_initializer=initializer,\n                                  activation='tanh') # (bs, 256, 256, 3)\n\n    x = inputs\n\n    # hạ mẫu xuống thông qua mô hình\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n\n    skips = reversed(skips[:-1])\n\n    # tăng mẫu và thiết lập các kết nối bỏ qua\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = layers.Concatenate()([x, skip])\n\n    x = last(x)\n\n    return keras.Model(inputs=inputs, outputs=x)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:55:10.678225Z","iopub.execute_input":"2024-06-04T14:55:10.678616Z","iopub.status.idle":"2024-06-04T14:55:10.691217Z","shell.execute_reply.started":"2024-06-04T14:55:10.678581Z","shell.execute_reply":"2024-06-04T14:55:10.690284Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build the discriminator (Xây dựng bộ phân biệt)\n\nBộ phân biệt lấy hình ảnh đầu vào và phân loại thành thật hoặc giả (được tạo ra). Thay vì xuất ra một nút duy nhất, bộ phân biệt xuất ra một hình ảnh 2D nhỏ hơn với các giá trị pixel cao hơn biểu thị phân loại thực và các giá trị thấp hơn biểu thị phân loại giả.","metadata":{}},{"cell_type":"code","source":"def Discriminator():\n    initializer = tf.random_normal_initializer(0., 0.02)\n    gamma_init = keras.initializers.RandomNormal(mean=0.0, stddev=0.02)\n\n    inp = layers.Input(shape=[256, 256, 3], name='input_image')\n\n    x = inp\n\n    down1 = downsample(64, 4, False)(x) # (bs, 128, 128, 64)\n    down2 = downsample(128, 4)(down1) # (bs, 64, 64, 128)\n    down3 = downsample(256, 4)(down2) # (bs, 32, 32, 256)\n\n    zero_pad1 = layers.ZeroPadding2D()(down3) # (bs, 34, 34, 256)\n    conv = layers.Conv2D(512, 4, strides=1,\n                         kernel_initializer=initializer,\n                         use_bias=False)(zero_pad1) # (bs, 31, 31, 512)\n\n    norm1 = tfa.layers.InstanceNormalization(gamma_initializer=gamma_init)(conv)\n\n    leaky_relu = layers.LeakyReLU()(norm1)\n\n    zero_pad2 = layers.ZeroPadding2D()(leaky_relu) # (bs, 33, 33, 512)\n\n    last = layers.Conv2D(1, 4, strides=1,\n                         kernel_initializer=initializer)(zero_pad2) # (bs, 30, 30, 1)\n\n    return tf.keras.Model(inputs=inp, outputs=last)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:56:14.629524Z","iopub.execute_input":"2024-06-04T14:56:14.629901Z","iopub.status.idle":"2024-06-04T14:56:14.639828Z","shell.execute_reply.started":"2024-06-04T14:56:14.629868Z","shell.execute_reply":"2024-06-04T14:56:14.638909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    monet_generator = Generator() # biến đổi ảnh thành tranh theo phong cách Monet\n    photo_generator = Generator() # biến đổi các bức tranh của Monet thành giống ảnh chụp hơn\n\n    monet_discriminator = Discriminator() # phân biệt tranh Monet thực sự và tranh Monet được tạo ra\n    photo_discriminator = Discriminator() # phân biệt ảnh thật và ảnh được tạo ra","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:57:32.206147Z","iopub.execute_input":"2024-06-04T14:57:32.206540Z","iopub.status.idle":"2024-06-04T14:57:34.279475Z","shell.execute_reply.started":"2024-06-04T14:57:32.206506Z","shell.execute_reply":"2024-06-04T14:57:34.278457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Vì trình tạo của chúng tôi chưa được huấn luyện nên bức ảnh theo phong cách Monet được tạo ra không hiển thị những gì mong đợi tại thời điểm này.","metadata":{}},{"cell_type":"code","source":"to_monet = monet_generator(example_photo)\n\nplt.subplot(1, 2, 1)\nplt.title(\"Original Photo\")\nplt.imshow(example_photo[0] * 0.5 + 0.5)\n\nplt.subplot(1, 2, 2)\nplt.title(\"Monet-esque Photo\")\nplt.imshow(to_monet[0] * 0.5 + 0.5)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-04T14:58:17.113016Z","iopub.execute_input":"2024-06-04T14:58:17.113896Z","iopub.status.idle":"2024-06-04T14:58:23.787383Z","shell.execute_reply.started":"2024-06-04T14:58:17.113858Z","shell.execute_reply":"2024-06-04T14:58:23.786477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Xây dựng mô hình CycleGAN\n\nChúng ta sẽ phân lớp tf.keras.Model để có thể chạy fit(), sau đó để huấn luyện mô hình của mình. Trong bước huấn luyện, mô hình chuyển đổi một bức ảnh thành bức tranh của Monet rồi chuyển đổi lại thành ảnh. Sự khác biệt giữa ảnh gốc và ảnh đã chuyển đổi hai lần là mất đi tính nhất quán theo chu kỳ. Chúng ta muốn ảnh gốc và ảnh đã chuyển đổi hai lần giống nhau.\n\nCác tổn thất được định nghĩa trong phần tiếp theo.","metadata":{}},{"cell_type":"code","source":"class CycleGan(keras.Model):\n    def __init__(\n        self,\n        monet_generator,\n        photo_generator,\n        monet_discriminator,\n        photo_discriminator,\n        lambda_cycle=10,\n    ):\n        super(CycleGan, self).__init__()\n        self.m_gen = monet_generator\n        self.p_gen = photo_generator\n        self.m_disc = monet_discriminator\n        self.p_disc = photo_discriminator\n        self.lambda_cycle = lambda_cycle\n        \n    def compile(\n        self,\n        m_gen_optimizer,\n        p_gen_optimizer,\n        m_disc_optimizer,\n        p_disc_optimizer,\n        gen_loss_fn,\n        disc_loss_fn,\n        cycle_loss_fn,\n        identity_loss_fn\n    ):\n        super(CycleGan, self).compile()\n        self.m_gen_optimizer = m_gen_optimizer\n        self.p_gen_optimizer = p_gen_optimizer\n        self.m_disc_optimizer = m_disc_optimizer\n        self.p_disc_optimizer = p_disc_optimizer\n        self.gen_loss_fn = gen_loss_fn\n        self.disc_loss_fn = disc_loss_fn\n        self.cycle_loss_fn = cycle_loss_fn\n        self.identity_loss_fn = identity_loss_fn\n        \n    def train_step(self, batch_data):\n        real_monet, real_photo = batch_data\n        \n        with tf.GradientTape(persistent=True) as tape:\n            # photo to monet back to photo\n            fake_monet = self.m_gen(real_photo, training=True)\n            cycled_photo = self.p_gen(fake_monet, training=True)\n\n            # monet to photo back to monet\n            fake_photo = self.p_gen(real_monet, training=True)\n            cycled_monet = self.m_gen(fake_photo, training=True)\n\n            # generating itself (tự tạo ra chính nó)\n            same_monet = self.m_gen(real_monet, training=True)\n            same_photo = self.p_gen(real_photo, training=True)\n\n            # bộ phân biệt được sử dụng để kiểm tra, nhập hình ảnh thực\n            disc_real_monet = self.m_disc(real_monet, training=True)\n            disc_real_photo = self.p_disc(real_photo, training=True)\n\n            # bộ phân biệt dùng để kiểm tra, nhập hình ảnh giả\n            disc_fake_monet = self.m_disc(fake_monet, training=True)\n            disc_fake_photo = self.p_disc(fake_photo, training=True)\n\n            # đánh giá tổn thất bộ tạo\n            monet_gen_loss = self.gen_loss_fn(disc_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(disc_fake_photo)\n\n            # đánh giá tổng thể sự tổn thất tính nhất quán của chu kỳ\n            total_cycle_loss = self.cycle_loss_fn(real_monet, cycled_monet, self.lambda_cycle) + self.cycle_loss_fn(real_photo, cycled_photo, self.lambda_cycle)\n\n            # đánh giá tổng tổn thất bộ tạo\n            total_monet_gen_loss = monet_gen_loss + total_cycle_loss + self.identity_loss_fn(real_monet, same_monet, self.lambda_cycle)\n            total_photo_gen_loss = photo_gen_loss + total_cycle_loss + self.identity_loss_fn(real_photo, same_photo, self.lambda_cycle)\n\n            # đánh giá tổn thất phân biệt\n            monet_disc_loss = self.disc_loss_fn(disc_real_monet, disc_fake_monet)\n            photo_disc_loss = self.disc_loss_fn(disc_real_photo, disc_fake_photo)\n\n        # Tính toán gradients cho bộ tạo và bộ phân biệt\n        monet_generator_gradients = tape.gradient(total_monet_gen_loss,\n                                                  self.m_gen.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_gen_loss,\n                                                  self.p_gen.trainable_variables)\n\n        monet_discriminator_gradients = tape.gradient(monet_disc_loss,\n                                                      self.m_disc.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_disc_loss,\n                                                      self.p_disc.trainable_variables)\n\n        # Áp dụng các gradient cho trình tối ưu hóa\n        self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients,\n                                                 self.m_gen.trainable_variables))\n\n        self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients,\n                                                 self.p_gen.trainable_variables))\n\n        self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients,\n                                                  self.m_disc.trainable_variables))\n\n        self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients,\n                                                  self.p_disc.trainable_variables))\n        \n        return {\n            \"monet_gen_loss\": total_monet_gen_loss,\n            \"photo_gen_loss\": total_photo_gen_loss,\n            \"monet_disc_loss\": monet_disc_loss,\n            \"photo_disc_loss\": photo_disc_loss\n        }","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:06:18.765949Z","iopub.execute_input":"2024-06-04T15:06:18.766843Z","iopub.status.idle":"2024-06-04T15:06:18.791339Z","shell.execute_reply.started":"2024-06-04T15:06:18.766802Z","shell.execute_reply":"2024-06-04T15:06:18.790492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define loss functions\n\nLoss function phân biệt bên dưới so sánh ảnh thật với ma trận 1 và ảnh giả với ma trận 0. Bộ phân biệt hoàn hảo sẽ đưa ra tất cả 1 cho ảnh thật và tất cả 0 cho ảnh giả. Bộ phân biệt tổn thất đưa ra giá trị trung bình của tổn thất thực và tổn thất được tạo ra.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def discriminator_loss(real, generated):\n        real_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(real), real)\n\n        generated_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.zeros_like(generated), generated)\n\n        total_disc_loss = real_loss + generated_loss\n\n        return total_disc_loss * 0.5","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:08:03.765390Z","iopub.execute_input":"2024-06-04T15:08:03.766299Z","iopub.status.idle":"2024-06-04T15:08:03.772831Z","shell.execute_reply.started":"2024-06-04T15:08:03.766258Z","shell.execute_reply":"2024-06-04T15:08:03.771880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Bộ tạo muốn đánh lừa bộ phân biệt nghĩ rằng hình ảnh được tạo ra là thật. Bộ tạo hoàn hảo sẽ có bộ phân biệt chỉ xuất ra giá trị 1. Do đó, nó so sánh hình ảnh được tạo ra với ma trận toàn 1 để tìm ra sự mất mát.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def generator_loss(generated):\n        return tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(generated), generated)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:10:30.458917Z","iopub.execute_input":"2024-06-04T15:10:30.459326Z","iopub.status.idle":"2024-06-04T15:10:30.465521Z","shell.execute_reply.started":"2024-06-04T15:10:30.459289Z","shell.execute_reply":"2024-06-04T15:10:30.464370Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Chúng tôi muốn ảnh gốc và ảnh được chuyển đổi hai lần phải giống nhau. Do đó, chúng tôi có thể tính toán độ tổn thất tính nhất quán của chu kỳ bằng cách tìm giá trị trung bình của sự khác biệt của chúng.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n        loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))\n\n        return LAMBDA * loss1","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:11:07.908879Z","iopub.execute_input":"2024-06-04T15:11:07.909298Z","iopub.status.idle":"2024-06-04T15:11:07.915073Z","shell.execute_reply.started":"2024-06-04T15:11:07.909260Z","shell.execute_reply":"2024-06-04T15:11:07.914010Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Hàm loss identity so sánh hình ảnh với trình tạo của nó (tức là ảnh với trình tạo ảnh). Nếu đưa một bức ảnh làm đầu vào, chúng ta muốn nó tạo ra cùng một hình ảnh như hình ảnh ban đầu là một bức ảnh. Mất danh tính so sánh đầu vào với đầu ra của trình tạo.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    def identity_loss(real_image, same_image, LAMBDA):\n        loss = tf.reduce_mean(tf.abs(real_image - same_image))\n        return LAMBDA * 0.5 * loss","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:19:27.748577Z","iopub.execute_input":"2024-06-04T15:19:27.749595Z","iopub.status.idle":"2024-06-04T15:19:27.754801Z","shell.execute_reply.started":"2024-06-04T15:19:27.749558Z","shell.execute_reply":"2024-06-04T15:19:27.753869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train the CycleGAN\n\nHãy biên dịch mô hình của mình. Vì chúng ta đã sử dụng tf.keras.Model để xây dựng CycleGAN, chúng ta có thể chỉ cần sử dụng hàm fit để đào tạo mô hình của mình.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    monet_generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n    photo_generator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n\n    monet_discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)\n    photo_discriminator_optimizer = tf.keras.optimizers.Adam(2e-4, beta_1=0.5)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:23:15.313044Z","iopub.execute_input":"2024-06-04T15:23:15.313966Z","iopub.status.idle":"2024-06-04T15:23:15.320603Z","shell.execute_reply.started":"2024-06-04T15:23:15.313929Z","shell.execute_reply":"2024-06-04T15:23:15.319515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    cycle_gan_model = CycleGan(\n        monet_generator, photo_generator, monet_discriminator, photo_discriminator\n    )\n\n    cycle_gan_model.compile(\n        m_gen_optimizer = monet_generator_optimizer,\n        p_gen_optimizer = photo_generator_optimizer,\n        m_disc_optimizer = monet_discriminator_optimizer,\n        p_disc_optimizer = photo_discriminator_optimizer,\n        gen_loss_fn = generator_loss,\n        disc_loss_fn = discriminator_loss,\n        cycle_loss_fn = calc_cycle_loss,\n        identity_loss_fn = identity_loss\n    )","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:23:16.936497Z","iopub.execute_input":"2024-06-04T15:23:16.936882Z","iopub.status.idle":"2024-06-04T15:23:16.959065Z","shell.execute_reply.started":"2024-06-04T15:23:16.936848Z","shell.execute_reply":"2024-06-04T15:23:16.958261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cycle_gan_model.fit(\n    tf.data.Dataset.zip((monet_ds, photo_ds)),\n    epochs=25\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:23:18.606686Z","iopub.execute_input":"2024-06-04T15:23:18.607213Z","iopub.status.idle":"2024-06-04T15:58:30.615249Z","shell.execute_reply.started":"2024-06-04T15:23:18.607165Z","shell.execute_reply":"2024-06-04T15:58:30.614294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualize our Monet-esque photos","metadata":{}},{"cell_type":"code","source":"_, ax = plt.subplots(5, 2, figsize=(12, 12))\nfor i, img in enumerate(photo_ds.take(5)):\n    prediction = monet_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    img = (img[0] * 127.5 + 127.5).numpy().astype(np.uint8)\n\n    ax[i, 0].imshow(img)\n    ax[i, 1].imshow(prediction)\n    ax[i, 0].set_title(\"Input Photo\")\n    ax[i, 1].set_title(\"Monet-esque\")\n    ax[i, 0].axis(\"off\")\n    ax[i, 1].axis(\"off\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-04T15:58:53.180960Z","iopub.execute_input":"2024-06-04T15:58:53.181683Z","iopub.status.idle":"2024-06-04T15:58:57.404765Z","shell.execute_reply.started":"2024-06-04T15:58:53.181646Z","shell.execute_reply":"2024-06-04T15:58:57.403836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create submission file","metadata":{}},{"cell_type":"code","source":"import PIL\n! mkdir ../images","metadata":{"execution":{"iopub.status.busy":"2024-06-04T16:03:16.673657Z","iopub.execute_input":"2024-06-04T16:03:16.674381Z","iopub.status.idle":"2024-06-04T16:03:17.681601Z","shell.execute_reply.started":"2024-06-04T16:03:16.674343Z","shell.execute_reply":"2024-06-04T16:03:17.680444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i = 1\nfor img in photo_ds:\n    prediction = monet_generator(img, training=False)[0].numpy()\n    prediction = (prediction * 127.5 + 127.5).astype(np.uint8)\n    im = PIL.Image.fromarray(prediction)\n    im.save(\"../images/\" + str(i) + \".jpg\")\n    i += 1","metadata":{"execution":{"iopub.status.busy":"2024-06-04T16:10:11.068252Z","iopub.execute_input":"2024-06-04T16:10:11.068646Z","iopub.status.idle":"2024-06-04T16:16:19.850457Z","shell.execute_reply.started":"2024-06-04T16:10:11.068612Z","shell.execute_reply":"2024-06-04T16:16:19.849424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', \"/kaggle/images\")","metadata":{"execution":{"iopub.status.busy":"2024-06-04T16:19:59.766571Z","iopub.execute_input":"2024-06-04T16:19:59.766960Z","iopub.status.idle":"2024-06-04T16:20:04.120229Z","shell.execute_reply.started":"2024-06-04T16:19:59.766924Z","shell.execute_reply":"2024-06-04T16:20:04.119049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}