{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":21755,"databundleVersionId":1475600}],"dockerImageVersionId":30299,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"#1\nimport tensorflow as tf\nfrom tensorflow.keras import layers\nimport numpy as np\nfrom tensorflow import keras\nfrom kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt\nimport PIL\nimport os\nimport shutil\nimport zipfile\nimport gc\n\nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"2\"\nos.environ[\"TF_DISABLE_XLA_LAYOUT_OPTIMIZER\"] = \"1\"\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\nstrategy = tf.distribute.MirroredStrategy()\nprint('Number of replicas:', strategy.num_replicas_in_sync)","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:33:24.776048Z","iopub.execute_input":"2026-06-04T03:33:24.776301Z","iopub.status.idle":"2026-06-04T03:33:55.755753Z","shell.execute_reply.started":"2026-06-04T03:33:24.776279Z","shell.execute_reply":"2026-06-04T03:33:55.754791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#2\nclass InstanceNormalization(tf.keras.layers.Layer):\n    def __init__(self, epsilon=1e-5, **kwargs):\n        super(InstanceNormalization, self).__init__(**kwargs)\n        self.epsilon = epsilon\n    def build(self, input_shape):\n        self.scale = self.add_weight(name='scale',\n                                     shape=(input_shape[-1],),\n                                     initializer='ones',\n                                     trainable=True)\n        self.offset = self.add_weight(name='offset',\n                                      shape=(input_shape[-1],),\n                                      initializer='zeros',\n                                      trainable=True)\n        super().build(input_shape)\n    def call(self, inputs):\n        mean, var = tf.nn.moments(inputs, axes=[1,2], keepdims=True)\n        return self.scale * (inputs - mean) / tf.sqrt(var + self.epsilon) + self.offset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:33:55.756431Z","iopub.execute_input":"2026-06-04T03:33:55.756812Z","iopub.status.idle":"2026-06-04T03:33:56.001616Z","shell.execute_reply.started":"2026-06-04T03:33:55.756793Z","shell.execute_reply":"2026-06-04T03:33:56.000449Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#3\ndef find_data_root():\n    for item in os.listdir(\"/kaggle/input\"):\n        subdir = os.path.join(\"/kaggle/input\", item)\n        if os.path.isdir(subdir):\n            try:\n                contents = os.listdir(subdir)\n                if 'monet_tfrec' in contents or 'photo_tfrec' in contents:\n                    return subdir\n            except:\n                continue\n    raise RuntimeError(\"Cannot find Monet/Photo TFRecord directories in /kaggle/input\")\n\nDATA_ROOT = find_data_root()\nMONET_DIR = os.path.join(DATA_ROOT, 'monet_tfrec')\nPHOTO_DIR = os.path.join(DATA_ROOT, 'photo_tfrec')\nMONET_FILENAMES = tf.io.gfile.glob(os.path.join(MONET_DIR, '*.tfrec'))\nPHOTO_FILENAMES = tf.io.gfile.glob(os.path.join(PHOTO_DIR, '*.tfrec'))\n\nprint('Monet TFRecord Files:', len(MONET_FILENAMES))\nprint('Photo TFRecord Files:', len(PHOTO_FILENAMES))\nTEST_FILENAMES = PHOTO_FILENAMES\n\nIMAGE_SIZE = [256, 256]\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 = {\"image\": tf.io.FixedLenFeature([], tf.string)}\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    return image\n\ndef load_dataset(filenames, batch_size=1, is_train=True):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTOTUNE)\n    if is_train:\n        dataset = dataset.repeat()\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n\ndef augment(image):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    image = tf.image.random_brightness(image, max_delta=0.05)\n    return image\n\nmonet_ds = load_dataset(MONET_FILENAMES, batch_size=1, is_train=True).map(augment, AUTOTUNE)\nphoto_ds = load_dataset(PHOTO_FILENAMES, batch_size=1, is_train=True).map(augment, AUTOTUNE)\ninfer_photo_ds = load_dataset(PHOTO_FILENAMES, batch_size=1, is_train=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:33:56.002280Z","iopub.execute_input":"2026-06-04T03:33:56.002480Z","iopub.status.idle":"2026-06-04T03:33:56.043841Z","shell.execute_reply.started":"2026-06-04T03:33:56.002462Z","shell.execute_reply":"2026-06-04T03:33:56.042846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#4\nOUTPUT_CHANNELS = 3\n\ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    result = keras.Sequential()\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same',\n                             kernel_initializer=initializer, use_bias=False))\n    if apply_instancenorm:\n        result.add(InstanceNormalization())\n    result.add(layers.LeakyReLU())\n    return result\n\ndef upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\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    result.add(InstanceNormalization())\n    if apply_dropout:\n        result.add(layers.Dropout(0.5))\n    result.add(layers.ReLU())\n    return result\n\ndef Generator():\n    inputs = layers.Input(shape=[256,256,3])\n    down_stack = [\n        downsample(32, 4, apply_instancenorm=False),\n        downsample(64, 4),\n        downsample(128, 4),\n        downsample(256, 4),\n        downsample(256, 4),\n        downsample(256, 4),\n    ]\n    up_stack = [\n        upsample(256, 4, apply_dropout=True),\n        upsample(256, 4, apply_dropout=True),\n        upsample(256, 4, apply_dropout=True),\n        upsample(256, 4),\n        upsample(128, 4),\n        upsample(64, 4),\n        upsample(32, 4),\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')\n    x = inputs\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n    skips = reversed(skips[:-1])\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = layers.Concatenate()([x, skip])\n    x = last(x)\n    return keras.Model(inputs=inputs, outputs=x)\n\ndef Discriminator():\n    initializer = tf.random_normal_initializer(0., 0.02)\n    inp = layers.Input(shape=[256, 256, 3], name='input_image')\n    x = inp\n    down1 = downsample(64, 4, False)(x)\n    down2 = downsample(128, 4)(down1)\n    down3 = downsample(256, 4)(down2)\n    zero_pad1 = layers.ZeroPadding2D()(down3)\n    conv = layers.Conv2D(512, 4, strides=1,\n                         kernel_initializer=initializer,\n                         use_bias=False)(zero_pad1)\n    norm1 = InstanceNormalization()(conv)\n    leaky_relu = layers.LeakyReLU()(norm1)\n    zero_pad2 = layers.ZeroPadding2D()(leaky_relu)\n    last = layers.Conv2D(1, 4, strides=1,\n                         kernel_initializer=initializer)(zero_pad2)\n    return tf.keras.Model(inputs=inp, outputs=last)\n\nwith strategy.scope():\n    monet_generator = Generator()\n    photo_generator = Generator()\n    monet_discriminator = Discriminator()\n    photo_discriminator = Discriminator()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:33:56.044500Z","iopub.execute_input":"2026-06-04T03:33:56.044699Z","iopub.status.idle":"2026-06-04T03:33:56.048615Z","shell.execute_reply.started":"2026-06-04T03:33:56.044682Z","shell.execute_reply":"2026-06-04T03:33:56.047735Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#5\nclass CycleGan(keras.Model):\n    def __init__(self, monet_generator, photo_generator, monet_discriminator, photo_discriminator, lambda_cycle=10):\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 call(self, inputs, training=False):\n        return self.m_gen(inputs, training=training)\n        \n    def compile(self, m_gen_optimizer, p_gen_optimizer, m_disc_optimizer, p_disc_optimizer,\n                gen_loss_fn, disc_loss_fn, cycle_loss_fn, identity_loss_fn):\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        with tf.GradientTape(persistent=True) as tape:\n            fake_monet = self.m_gen(real_photo, training=True)\n            cycled_photo = self.p_gen(fake_monet, training=True)\n            fake_photo = self.p_gen(real_monet, training=True)\n            cycled_monet = self.m_gen(fake_photo, training=True)\n            same_monet = self.m_gen(real_monet, training=True)\n            same_photo = self.p_gen(real_photo, training=True)\n            disc_real_monet = self.m_disc(real_monet, training=True)\n            disc_real_photo = self.p_disc(real_photo, training=True)\n            disc_fake_monet = self.m_disc(fake_monet, training=True)\n            disc_fake_photo = self.p_disc(fake_photo, training=True)\n            \n            monet_gen_loss = self.gen_loss_fn(disc_fake_monet)\n            photo_gen_loss = self.gen_loss_fn(disc_fake_photo)\n            total_cycle_loss = self.cycle_loss_fn(real_monet, cycled_monet, self.lambda_cycle) + \\\n                               self.cycle_loss_fn(real_photo, cycled_photo, self.lambda_cycle)\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            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        monet_generator_gradients = tape.gradient(total_monet_gen_loss, self.m_gen.trainable_variables)\n        photo_generator_gradients = tape.gradient(total_photo_gen_loss, self.p_gen.trainable_variables)\n        monet_discriminator_gradients = tape.gradient(monet_disc_loss, self.m_disc.trainable_variables)\n        photo_discriminator_gradients = tape.gradient(photo_disc_loss, self.p_disc.trainable_variables)\n        \n        self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients, self.m_gen.trainable_variables))\n        self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients, self.p_gen.trainable_variables))\n        self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients, self.m_disc.trainable_variables))\n        self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients, 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        }\n\nwith 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        generated_loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.zeros_like(generated), generated)\n        return (tf.reduce_mean(real_loss) + tf.reduce_mean(generated_loss)) * 0.5\n\n    def generator_loss(generated):\n        loss = tf.keras.losses.BinaryCrossentropy(from_logits=True, reduction=tf.keras.losses.Reduction.NONE)(tf.ones_like(generated), generated)\n        return tf.reduce_mean(loss)\n\n    def calc_cycle_loss(real_image, cycled_image, LAMBDA):\n        return LAMBDA * tf.reduce_mean(tf.abs(real_image - cycled_image))\n\n    def identity_loss(real_image, same_image, LAMBDA):\n        return LAMBDA * 0.5 * tf.reduce_mean(tf.abs(real_image - same_image))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:33:56.049158Z","iopub.execute_input":"2026-06-04T03:33:56.049334Z","iopub.status.idle":"2026-06-04T03:33:56.059085Z","shell.execute_reply.started":"2026-06-04T03:33:56.049319Z","shell.execute_reply":"2026-06-04T03:33:56.058192Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#6\nwith 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    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)\n\nwith strategy.scope():\n    cycle_gan_model = CycleGan(monet_generator, photo_generator, monet_discriminator, photo_discriminator)\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    )\n\nEPOCHS = 5\ncycle_gan_model.fit(\n    tf.data.Dataset.zip((monet_ds, photo_ds)),\n    epochs=EPOCHS,\n    steps_per_epoch=300\n)\n\nmonet_generator.save_weights('/kaggle/working/monet_generator_weights.weights.h5')\n\ndel cycle_gan_model\ndel monet_discriminator, photo_discriminator\ndel photo_generator\ndel monet_ds, photo_ds\ngc.collect()\ntf.keras.backend.clear_session()\nprint(\"Memory cleaned.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:33:56.059732Z","iopub.execute_input":"2026-06-04T03:33:56.059902Z","iopub.status.idle":"2026-06-04T03:34:01.614726Z","shell.execute_reply.started":"2026-06-04T03:33:56.059887Z","shell.execute_reply":"2026-06-04T03:34:01.613627Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#8\nOUTPUT_CHANNELS = 3\n\ndef downsample(filters, size, apply_instancenorm=True):\n    initializer = tf.random_normal_initializer(0., 0.02)\n    result = keras.Sequential()\n    result.add(layers.Conv2D(filters, size, strides=2, padding='same',\n                             kernel_initializer=initializer, use_bias=False))\n    if apply_instancenorm:\n        result.add(InstanceNormalization())\n    result.add(layers.LeakyReLU())\n    return result\n\ndef upsample(filters, size, apply_dropout=False):\n    initializer = tf.random_normal_initializer(0., 0.02)\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    result.add(InstanceNormalization())\n    if apply_dropout:\n        result.add(layers.Dropout(0.5))\n    result.add(layers.ReLU())\n    return result\n\ndef Generator():\n    inputs = layers.Input(shape=[256,256,3])\n    down_stack = [\n        downsample(32, 4, apply_instancenorm=False),\n        downsample(64, 4),\n        downsample(128, 4),\n        downsample(256, 4),\n        downsample(256, 4),\n        downsample(256, 4),\n    ]\n    up_stack = [\n        upsample(256, 4, apply_dropout=True),\n        upsample(256, 4, apply_dropout=True),\n        upsample(256, 4, apply_dropout=True),\n        upsample(256, 4),\n        upsample(128, 4),\n        upsample(64, 4),\n        upsample(32, 4),\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')\n    x = inputs\n    skips = []\n    for down in down_stack:\n        x = down(x)\n        skips.append(x)\n    skips = reversed(skips[:-1])\n    for up, skip in zip(up_stack, skips):\n        x = up(x)\n        x = layers.Concatenate()([x, skip])\n    x = last(x)\n    return keras.Model(inputs=inputs, outputs=x)\n\nmonet_generator = Generator()\nmonet_generator.load_weights('/kaggle/working/monet_generator_weights.weights.h5')\nprint(\"Generator weights reloaded successfully.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:34:01.615225Z","iopub.execute_input":"2026-06-04T03:34:01.615409Z","iopub.status.idle":"2026-06-04T03:34:01.624624Z","shell.execute_reply.started":"2026-06-04T03:34:01.615392Z","shell.execute_reply":"2026-06-04T03:34:01.623813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#9\nTARGET_NUM = 7038\noutput_dir = \"/kaggle/tmp_gen\"\nos.makedirs(output_dir, exist_ok=True)\n\ntotal_generated = 0\nwhile total_generated < TARGET_NUM:\n    for img_batch in infer_photo_ds:\n        pred = monet_generator(img_batch, training=False)[0].numpy()\n        pred_img = pred * 127.5 + 127.5\n        pred_img = np.clip(pred_img, 0, 255).astype(np.uint8)\n        im = PIL.Image.fromarray(pred_img)\n        im.save(os.path.join(output_dir, f\"{total_generated + 1}.jpg\"))\n        total_generated += 1\n        if total_generated >= TARGET_NUM:\n            break\n\nprint(f\"Total images generated: {total_generated}\")\n\nzip_path = \"/kaggle/working/images.zip\"\nwith zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:\n    for filename in sorted(os.listdir(output_dir)):\n        file_path = os.path.join(output_dir, filename)\n        zipf.write(file_path, arcname=filename)\n\nshutil.rmtree(output_dir)\nos.remove('/kaggle/working/monet_generator_weights.weights.h5')\nprint(f\"Submission ZIP file created at {zip_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-04T03:34:01.625119Z","iopub.execute_input":"2026-06-04T03:34:01.625278Z","iopub.status.idle":"2026-06-04T03:34:02.157108Z","shell.execute_reply.started":"2026-06-04T03:34:01.625264Z","shell.execute_reply":"2026-06-04T03:34:02.155979Z"}},"outputs":[],"execution_count":null}]}