{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\nimport numpy as np\nimport os\nimport glob\nimport shutil\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm ","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:42.053911Z","iopub.execute_input":"2025-08-04T08:42:42.054119Z","iopub.status.idle":"2025-08-04T08:42:49.736592Z","shell.execute_reply.started":"2025-08-04T08:42:42.054094Z","shell.execute_reply":"2025-08-04T08:42:49.735993Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.738139Z","iopub.execute_input":"2025-08-04T08:42:49.738790Z","iopub.status.idle":"2025-08-04T08:42:49.801579Z","shell.execute_reply.started":"2025-08-04T08:42:49.738770Z","shell.execute_reply":"2025-08-04T08:42:49.800918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_WIDTH = 256\nIMG_HEIGHT = 256\nOUTPUT_CHANNELS = 3 \nBUFFER_SIZE = 1000\nBATCH_SIZE = 1\nEPOCHS = 50\nLAMBDA = 10.0\nLAMBDA_IDENTITY = 0.5\nLEARNING_RATE = 2e-4\nBETA_1 = 0.5 ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.802320Z","iopub.execute_input":"2025-08-04T08:42:49.802578Z","iopub.status.idle":"2025-08-04T08:42:49.820616Z","shell.execute_reply.started":"2025-08-04T08:42:49.802555Z","shell.execute_reply":"2025-08-04T08:42:49.819965Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"GCS_PATH = \"/kaggle/input/gan-getting-started\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.821340Z","iopub.execute_input":"2025-08-04T08:42:49.821568Z","iopub.status.idle":"2025-08-04T08:42:49.834950Z","shell.execute_reply.started":"2025-08-04T08:42:49.821551Z","shell.execute_reply":"2025-08-04T08:42:49.834305Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MONET_DIR = os.path.join(GCS_PATH, 'monet_jpg') \nPHOTO_DIR = os.path.join(GCS_PATH, 'photo_jpg')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.836648Z","iopub.execute_input":"2025-08-04T08:42:49.837204Z","iopub.status.idle":"2025-08-04T08:42:49.850581Z","shell.execute_reply.started":"2025-08-04T08:42:49.837186Z","shell.execute_reply":"2025-08-04T08:42:49.849895Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MONET_FILENAMES = glob.glob(str(MONET_DIR + '/*.jpg'))\nPHOTO_FILENAMES = glob.glob(str(PHOTO_DIR + '/*.jpg'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.851516Z","iopub.execute_input":"2025-08-04T08:42:49.851724Z","iopub.status.idle":"2025-08-04T08:42:49.983544Z","shell.execute_reply.started":"2025-08-04T08:42:49.851698Z","shell.execute_reply":"2025-08-04T08:42:49.982900Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f'Monet JPG Files: {len(MONET_FILENAMES)}')\nprint(f'Photo JPG Files: {len(PHOTO_FILENAMES)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.984220Z","iopub.execute_input":"2025-08-04T08:42:49.984424Z","iopub.status.idle":"2025-08-04T08:42:49.988007Z","shell.execute_reply.started":"2025-08-04T08:42:49.984403Z","shell.execute_reply":"2025-08-04T08:42:49.987306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform_train = transforms.Compose([\n    transforms.Resize(286, Image.BICUBIC), \n    transforms.RandomCrop((IMG_HEIGHT, IMG_WIDTH)),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)) \n])\n\ntransform_test = transforms.Compose([\n    transforms.Resize((IMG_HEIGHT, IMG_WIDTH), Image.BICUBIC),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:49.988735Z","iopub.execute_input":"2025-08-04T08:42:49.988965Z","iopub.status.idle":"2025-08-04T08:42:50.003032Z","shell.execute_reply.started":"2025-08-04T08:42:49.988948Z","shell.execute_reply":"2025-08-04T08:42:50.002485Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    def __init__(self, file_paths, transform=None):\n        self.file_paths = file_paths\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.file_paths)\n\n    def __getitem__(self, idx):\n        img_path = self.file_paths[idx]\n        image = Image.open(img_path).convert(\"RGB\")\n        if self.transform:\n            image = self.transform(image)\n        return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.003813Z","iopub.execute_input":"2025-08-04T08:42:50.004098Z","iopub.status.idle":"2025-08-04T08:42:50.017843Z","shell.execute_reply.started":"2025-08-04T08:42:50.004075Z","shell.execute_reply":"2025-08-04T08:42:50.017211Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"monet_train_ds = ImageDataset(MONET_FILENAMES, transform=transform_train)\nphoto_train_ds = ImageDataset(PHOTO_FILENAMES, transform=transform_train)\nphoto_test_ds_for_generation = ImageDataset(PHOTO_FILENAMES, transform=transform_test)\n\nmonet_train_loader = DataLoader(monet_train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=os.cpu_count(), pin_memory=True)\nphoto_train_loader = DataLoader(photo_train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=os.cpu_count(), pin_memory=True)\nphoto_test_loader_for_generation = DataLoader(photo_test_ds_for_generation, batch_size=BATCH_SIZE, shuffle=False, num_workers=os.cpu_count(), pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.018480Z","iopub.execute_input":"2025-08-04T08:42:50.018686Z","iopub.status.idle":"2025-08-04T08:42:50.034040Z","shell.execute_reply.started":"2025-08-04T08:42:50.018662Z","shell.execute_reply":"2025-08-04T08:42:50.033358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def weights_init(m):\n    classname = m.__class__.__name__\n    if classname.find('Conv') != -1:\n        nn.init.normal_(m.weight.data, 0.0, 0.02)\n    elif classname.find('BatchNorm') != -1:\n        nn.init.normal_(m.weight.data, 1.0, 0.02)\n        nn.init.constant_(m.bias.data, 0.0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.034677Z","iopub.execute_input":"2025-08-04T08:42:50.034930Z","iopub.status.idle":"2025-08-04T08:42:50.052421Z","shell.execute_reply.started":"2025-08-04T08:42:50.034914Z","shell.execute_reply":"2025-08-04T08:42:50.051792Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **Generator**","metadata":{}},{"cell_type":"code","source":"class Downsample(nn.Module):\n    def __init__(self, in_channels, out_channels, apply_instancenorm=True):\n        super(Downsample, self).__init__()\n        layers = [\n            nn.Conv2d(in_channels, out_channels, 4, 2, 1, bias=False)\n        ]\n        if apply_instancenorm:\n            layers.append(nn.InstanceNorm2d(out_channels))\n        layers.append(nn.LeakyReLU(0.2))\n        self.model = nn.Sequential(*layers)\n\n    def forward(self, x):\n        return self.model(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.053141Z","iopub.execute_input":"2025-08-04T08:42:50.053577Z","iopub.status.idle":"2025-08-04T08:42:50.068742Z","shell.execute_reply.started":"2025-08-04T08:42:50.053556Z","shell.execute_reply":"2025-08-04T08:42:50.068088Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Upsample(nn.Module):\n    def __init__(self, in_channels, out_channels, apply_dropout=False):\n        super(Upsample, self).__init__()\n        layers = [\n            nn.ConvTranspose2d(in_channels, out_channels, 4, 2, 1, bias=False),\n            nn.InstanceNorm2d(out_channels),\n            nn.ReLU(True)\n        ]\n        if apply_dropout:\n            layers.insert(2, nn.Dropout(0.5)) \n        self.model = nn.Sequential(*layers)\n\n    def forward(self, x):\n        return self.model(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.069436Z","iopub.execute_input":"2025-08-04T08:42:50.069661Z","iopub.status.idle":"2025-08-04T08:42:50.084167Z","shell.execute_reply.started":"2025-08-04T08:42:50.069642Z","shell.execute_reply":"2025-08-04T08:42:50.083497Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Generator(nn.Module):\n    def __init__(self):\n        super(Generator, self).__init__()\n        self.down1 = Downsample(OUTPUT_CHANNELS, 64, apply_instancenorm=False)\n        self.down2 = Downsample(64, 128)\n        self.down3 = Downsample(128, 256)\n        self.down4 = Downsample(256, 512)\n        self.down5 = Downsample(512, 512)\n        self.down6 = Downsample(512, 512)\n        self.down7 = Downsample(512, 512)\n        self.down8 = Downsample(512, 512, apply_instancenorm=False) \n\n        self.up1 = Upsample(512, 512, apply_dropout=True)\n        self.up2 = Upsample(1024, 512, apply_dropout=True)\n        self.up3 = Upsample(1024, 512, apply_dropout=True)\n        self.up4 = Upsample(1024, 512)\n        self.up5 = Upsample(1024, 256)\n        self.up6 = Upsample(512, 128)\n        self.up7 = Upsample(256, 64)\n\n        self.last = nn.Sequential(\n            nn.ConvTranspose2d(128, OUTPUT_CHANNELS, 4, 2, 1),\n            nn.Tanh()\n        )\n\n    def forward(self, x):\n        # Downsampling\n        d1 = self.down1(x)\n        d2 = self.down2(d1)\n        d3 = self.down3(d2)\n        d4 = self.down4(d3)\n        d5 = self.down5(d4)\n        d6 = self.down6(d5)\n        d7 = self.down7(d6)\n        d8 = self.down8(d7) \n\n        # Upsampling\n        u1 = self.up1(d8)\n        u2 = self.up2(torch.cat([u1, d7], 1))\n        u3 = self.up3(torch.cat([u2, d6], 1))\n        u4 = self.up4(torch.cat([u3, d5], 1))\n        u5 = self.up5(torch.cat([u4, d4], 1))\n        u6 = self.up6(torch.cat([u5, d3], 1))\n        u7 = self.up7(torch.cat([u6, d2], 1))\n\n        return self.last(torch.cat([u7, d1], 1))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.086707Z","iopub.execute_input":"2025-08-04T08:42:50.086960Z","iopub.status.idle":"2025-08-04T08:42:50.100252Z","shell.execute_reply.started":"2025-08-04T08:42:50.086946Z","shell.execute_reply":"2025-08-04T08:42:50.099536Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## **Discriminator**","metadata":{}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self):\n        super(Discriminator, self).__init__()\n        self.layer1 = nn.Sequential(\n            nn.Conv2d(OUTPUT_CHANNELS, 64, 4, 2, 1, bias=False),\n            nn.LeakyReLU(0.2)\n        )\n        self.layer2 = Downsample(64, 128)\n        self.layer3 = Downsample(128, 256)\n\n        self.last_conv = nn.Sequential(\n            nn.ZeroPad2d((1, 1, 1, 1)), \n            nn.Conv2d(256, 512, 4, 1, 0, bias=False),\n            nn.InstanceNorm2d(512),\n            nn.LeakyReLU(0.2),\n            nn.ZeroPad2d((1, 1, 1, 1)),\n            nn.Conv2d(512, 1, 4, 1, 0) \n        )\n\n    def forward(self, x):\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.last_conv(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.101005Z","iopub.execute_input":"2025-08-04T08:42:50.101237Z","iopub.status.idle":"2025-08-04T08:42:50.120412Z","shell.execute_reply.started":"2025-08-04T08:42:50.101218Z","shell.execute_reply":"2025-08-04T08:42:50.119761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion_GAN = nn.BCEWithLogitsLoss()\ncriterion_cycle = nn.L1Loss()\ncriterion_identity = nn.L1Loss() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.121212Z","iopub.execute_input":"2025-08-04T08:42:50.121741Z","iopub.status.idle":"2025-08-04T08:42:50.139701Z","shell.execute_reply.started":"2025-08-04T08:42:50.121720Z","shell.execute_reply":"2025-08-04T08:42:50.139183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Instantiate the models\nmonet_generator = Generator().to(device) \nphoto_generator = Generator().to(device) \n\nmonet_discriminator = Discriminator().to(device) \nphoto_discriminator = Discriminator().to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:50.140446Z","iopub.execute_input":"2025-08-04T08:42:50.140632Z","iopub.status.idle":"2025-08-04T08:42:51.390486Z","shell.execute_reply.started":"2025-08-04T08:42:50.140618Z","shell.execute_reply":"2025-08-04T08:42:51.389901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"monet_generator.apply(weights_init)\nphoto_generator.apply(weights_init)\nmonet_discriminator.apply(weights_init)\nphoto_discriminator.apply(weights_init)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.391321Z","iopub.execute_input":"2025-08-04T08:42:51.391580Z","iopub.status.idle":"2025-08-04T08:42:51.413225Z","shell.execute_reply.started":"2025-08-04T08:42:51.391557Z","shell.execute_reply":"2025-08-04T08:42:51.412681Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"monet_gen_optimizer = optim.Adam(monet_generator.parameters(), lr=LEARNING_RATE, betas=(BETA_1, 0.999))\nphoto_gen_optimizer = optim.Adam(photo_generator.parameters(), lr=LEARNING_RATE, betas=(BETA_1, 0.999))\nmonet_disc_optimizer = optim.Adam(monet_discriminator.parameters(), lr=LEARNING_RATE, betas=(BETA_1, 0.999))\nphoto_disc_optimizer = optim.Adam(photo_discriminator.parameters(), lr=LEARNING_RATE, betas=(BETA_1, 0.999))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.414144Z","iopub.execute_input":"2025-08-04T08:42:51.414408Z","iopub.status.idle":"2025-08-04T08:42:51.418902Z","shell.execute_reply.started":"2025-08-04T08:42:51.414392Z","shell.execute_reply":"2025-08-04T08:42:51.418243Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_lr_scheduler(optimizer, start_epoch, total_epochs):\n    def lambda_rule(epoch):\n        lr_l = 1.0 - max(0, epoch + 1 - start_epoch) / float(total_epochs - start_epoch + 1)\n        return lr_l\n    return optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.419541Z","iopub.execute_input":"2025-08-04T08:42:51.419963Z","iopub.status.idle":"2025-08-04T08:42:51.436296Z","shell.execute_reply.started":"2025-08-04T08:42:51.419945Z","shell.execute_reply":"2025-08-04T08:42:51.435636Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"decay_start_epoch = EPOCHS // 2\nmonet_gen_scheduler = get_lr_scheduler(monet_gen_optimizer, decay_start_epoch, EPOCHS)\nphoto_gen_scheduler = get_lr_scheduler(photo_gen_optimizer, decay_start_epoch, EPOCHS)\nmonet_disc_scheduler = get_lr_scheduler(monet_disc_optimizer, decay_start_epoch, EPOCHS)\nphoto_disc_scheduler = get_lr_scheduler(photo_disc_optimizer, decay_start_epoch, EPOCHS)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.436930Z","iopub.execute_input":"2025-08-04T08:42:51.437617Z","iopub.status.idle":"2025-08-04T08:42:51.452050Z","shell.execute_reply.started":"2025-08-04T08:42:51.437594Z","shell.execute_reply":"2025-08-04T08:42:51.451433Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_and_save_images(model, test_input, epoch=0, num_display=3):\n    model.eval() \n    with torch.no_grad():\n        prediction = model(test_input).cpu().data \n\n    plt.figure(figsize=(10, 5))\n\n    display_input = (test_input[0].cpu() * 0.5 + 0.5).permute(1, 2, 0)\n    display_prediction = (prediction[0] * 0.5 + 0.5).permute(1, 2, 0)\n\n    plt.subplot(1, 2, 1)\n    plt.title('Input Image')\n    plt.imshow(display_input) \n    plt.axis('off')\n\n    plt.subplot(1, 2, 2)\n    plt.title('Generated Image')\n    plt.imshow(display_prediction)\n    plt.axis('off')\n    \n    plt.suptitle(f'Epoch {epoch}', fontsize=16)\n    plt.show()\n    plt.close()\n    model.train() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.452637Z","iopub.execute_input":"2025-08-04T08:42:51.452931Z","iopub.status.idle":"2025-08-04T08:42:51.472827Z","shell.execute_reply.started":"2025-08-04T08:42:51.452905Z","shell.execute_reply":"2025-08-04T08:42:51.472215Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_photo_batch = next(iter(photo_train_loader))\nsample_photo = sample_photo_batch.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.473482Z","iopub.execute_input":"2025-08-04T08:42:51.473661Z","iopub.status.idle":"2025-08-04T08:42:51.692417Z","shell.execute_reply.started":"2025-08-04T08:42:51.473646Z","shell.execute_reply":"2025-08-04T08:42:51.691475Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = {\n    'monet_gen_loss': [],\n    'photo_gen_loss': [],\n    'monet_disc_loss': [],\n    'photo_disc_loss': []\n}\n\nfor epoch in tqdm(range(EPOCHS), desc=\"Epoch\"):\n    epoch_monet_gen_loss = 0\n    epoch_photo_gen_loss = 0\n    epoch_monet_disc_loss = 0\n    epoch_photo_disc_loss = 0\n    \n    monet_iter = iter(monet_train_loader)\n    photo_iter = iter(photo_train_loader)\n    \n    num_batches = min(len(monet_train_loader), len(photo_train_loader))\n\n    for i in range(num_batches):\n        try:\n            real_monet = next(monet_iter).to(device)\n            real_photo = next(photo_iter).to(device)\n        except StopIteration:\n            monet_iter = iter(monet_train_loader)\n            photo_iter = iter(photo_train_loader)\n            real_monet = next(monet_iter).to(device)\n            real_photo = next(photo_iter).to(device)\n\n        # Train Generators \n        monet_gen_optimizer.zero_grad()\n        photo_gen_optimizer.zero_grad()\n\n        # Photo to Monet\n        fake_monet = monet_generator(real_photo)\n        cycled_photo = photo_generator(fake_monet)\n        \n        # Monet to Photo\n        fake_photo = photo_generator(real_monet)\n        cycled_monet = monet_generator(fake_photo)\n\n        # Identity Mapping\n        same_monet = monet_generator(real_monet)\n        same_photo = photo_generator(real_photo)\n\n        # Generator Adversarial Loss\n        gen_monet_output = monet_discriminator(fake_monet)\n        gen_photo_output = photo_discriminator(fake_photo)\n        \n        loss_G_monet_adv = criterion_GAN(gen_monet_output, torch.ones_like(gen_monet_output))\n        loss_G_photo_adv = criterion_GAN(gen_photo_output, torch.ones_like(gen_photo_output))\n\n        # Cycle Consistency Loss\n        loss_cycle_monet = criterion_cycle(cycled_monet, real_monet) * LAMBDA\n        loss_cycle_photo = criterion_cycle(cycled_photo, real_photo) * LAMBDA\n\n        # Identity Loss\n        loss_identity_monet = criterion_identity(same_monet, real_monet) * LAMBDA * LAMBDA_IDENTITY\n        loss_identity_photo = criterion_identity(same_photo, real_photo) * LAMBDA * LAMBDA_IDENTITY\n\n        # Total Generator Losses\n        total_monet_gen_loss = loss_G_monet_adv + loss_cycle_monet + loss_identity_monet\n        total_photo_gen_loss = loss_G_photo_adv + loss_cycle_photo + loss_identity_photo\n        \n        # Backpropagate and update generators\n        total_monet_gen_loss.backward(retain_graph=True)\n        total_photo_gen_loss.backward()\n        \n        monet_gen_optimizer.step()\n        photo_gen_optimizer.step()\n\n        # Train Discriminators \n        monet_disc_optimizer.zero_grad()\n        photo_disc_optimizer.zero_grad()\n\n        # Discriminator Monet\n        disc_real_monet_output = monet_discriminator(real_monet)\n        disc_fake_monet_output = monet_discriminator(fake_monet.detach()) # Detach to stop grad flow to generator\n        loss_D_monet = (criterion_GAN(disc_real_monet_output, torch.ones_like(disc_real_monet_output)) +\n                        criterion_GAN(disc_fake_monet_output, torch.zeros_like(disc_fake_monet_output))) * 0.5\n        \n        # Discriminator Photo\n        disc_real_photo_output = photo_discriminator(real_photo)\n        disc_fake_photo_output = photo_discriminator(fake_photo.detach()) # Detach\n        loss_D_photo = (criterion_GAN(disc_real_photo_output, torch.ones_like(disc_real_photo_output)) +\n                        criterion_GAN(disc_fake_photo_output, torch.zeros_like(disc_fake_photo_output))) * 0.5\n\n        # Backpropagate and update discriminators\n        loss_D_monet.backward()\n        loss_D_photo.backward()\n\n        monet_disc_optimizer.step()\n        photo_disc_optimizer.step()\n\n        # Accumulate losses for epoch average\n        epoch_monet_gen_loss += total_monet_gen_loss.item()\n        epoch_photo_gen_loss += total_photo_gen_loss.item()\n        epoch_monet_disc_loss += loss_D_monet.item()\n        epoch_photo_disc_loss += loss_D_photo.item()\n\n    # Update learning rate schedulers\n    monet_gen_scheduler.step()\n    photo_gen_scheduler.step()\n    monet_disc_scheduler.step()\n    photo_disc_scheduler.step()\n\n    # Average losses for the epoch\n    avg_monet_gen_loss = epoch_monet_gen_loss / num_batches\n    avg_photo_gen_loss = epoch_photo_gen_loss / num_batches\n    avg_monet_disc_loss = epoch_monet_disc_loss / num_batches\n    avg_photo_disc_loss = epoch_photo_disc_loss / num_batches\n\n    history['monet_gen_loss'].append(avg_monet_gen_loss)\n    history['photo_gen_loss'].append(avg_photo_gen_loss)\n    history['monet_disc_loss'].append(avg_monet_disc_loss)\n    history['photo_disc_loss'].append(avg_photo_disc_loss)\n\n    print(f\"Epoch {epoch+1}/{EPOCHS} | \"\n          f\"G_Monet_Loss: {avg_monet_gen_loss:.4f} | \"\n          f\"G_Photo_Loss: {avg_photo_gen_loss:.4f} | \"\n          f\"D_Monet_Loss: {avg_monet_disc_loss:.4f} | \"\n          f\"D_Photo_Loss: {avg_photo_disc_loss:.4f}\")\n\n    if (epoch + 1) % 5 == 0:\n        generate_and_save_images(monet_generator, sample_photo, epoch=epoch+1)\n\nprint(\"\\nTraining Completed!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T08:42:51.693573Z","iopub.execute_input":"2025-08-04T08:42:51.694234Z","iopub.status.idle":"2025-08-04T09:30:45.627037Z","shell.execute_reply.started":"2025-08-04T08:42:51.694198Z","shell.execute_reply":"2025-08-04T09:30:45.625938Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"output_dir = '/kaggle/working/images'\nos.makedirs(output_dir, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T09:52:01.513441Z","iopub.execute_input":"2025-08-04T09:52:01.514197Z","iopub.status.idle":"2025-08-04T09:52:01.518172Z","shell.execute_reply.started":"2025-08-04T09:52:01.514168Z","shell.execute_reply":"2025-08-04T09:52:01.517534Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_count = 0\nmonet_generator.eval()\nwith torch.no_grad():\n    for i, img_batch in enumerate(tqdm(photo_test_loader_for_generation, desc=\"Generating Images\")):\n        img_batch = img_batch.to(device)\n        prediction = monet_generator(img_batch).cpu().data[0]\n\n        prediction = (prediction * 0.5 + 0.5) * 255.0\n        prediction = prediction.permute(1, 2, 0).numpy().astype(np.uint8)\n\n        im = Image.fromarray(prediction)\n        im.save(os.path.join(output_dir, f\"{i}.jpg\"), quality=80) \n        image_count += 1\n        if image_count >= 10000:\n            break\n\nprint(f\"Generated {image_count} images.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T09:52:50.920228Z","iopub.execute_input":"2025-08-04T09:52:50.920475Z","iopub.status.idle":"2025-08-04T09:53:58.242181Z","shell.execute_reply.started":"2025-08-04T09:52:50.920458Z","shell.execute_reply":"2025-08-04T09:53:58.241273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"zip_output_filename = 'images.zip'\nzip_output_path_full = os.path.join('/kaggle/working', zip_output_filename)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T09:57:11.773490Z","iopub.execute_input":"2025-08-04T09:57:11.774297Z","iopub.status.idle":"2025-08-04T09:57:11.778194Z","shell.execute_reply.started":"2025-08-04T09:57:11.774263Z","shell.execute_reply":"2025-08-04T09:57:11.777472Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"shutil.make_archive(zip_output_path_full.replace('.zip', ''), 'zip', output_dir)\nprint(f\"Submission file created!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T09:57:51.619076Z","iopub.execute_input":"2025-08-04T09:57:51.619348Z","iopub.status.idle":"2025-08-04T09:57:55.361189Z","shell.execute_reply.started":"2025-08-04T09:57:51.619331Z","shell.execute_reply":"2025-08-04T09:57:55.360580Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if os.path.exists(output_dir):\n    print(f\"Cleaning up temporary image directory: {output_dir}\")\n    shutil.rmtree(output_dir) \n    print(\"Temporary image directory removed!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-04T09:58:11.573274Z","iopub.execute_input":"2025-08-04T09:58:11.573542Z","iopub.status.idle":"2025-08-04T09:58:11.773406Z","shell.execute_reply.started":"2025-08-04T09:58:11.573521Z","shell.execute_reply":"2025-08-04T09:58:11.772819Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}