{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30762,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Import libraries\nimport os\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport cv2\nimport shutil\nfrom PIL import Image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:12.598173Z","iopub.execute_input":"2025-03-28T09:29:12.598571Z","iopub.status.idle":"2025-03-28T09:29:14.920007Z","shell.execute_reply.started":"2025-03-28T09:29:12.598527Z","shell.execute_reply":"2025-03-28T09:29:14.919175Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check for GPU availability and set device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\nif torch.cuda.is_available():\n    print(f\"Number of GPUs: {torch.cuda.device_count()}\")\n    for i in range(torch.cuda.device_count()):\n        print(f\"GPU {i}: {torch.cuda.get_device_name(i)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:14.921739Z","iopub.execute_input":"2025-03-28T09:29:14.922198Z","iopub.status.idle":"2025-03-28T09:29:15.002533Z","shell.execute_reply.started":"2025-03-28T09:29:14.922158Z","shell.execute_reply":"2025-03-28T09:29:15.001485Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define directories\nmonet_jpg_directory = '/kaggle/input/gan-getting-started/monet_jpg'\nphoto_jpg_directory = '/kaggle/input/gan-getting-started/photo_jpg'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:15.003558Z","iopub.execute_input":"2025-03-28T09:29:15.003879Z","iopub.status.idle":"2025-03-28T09:29:15.007786Z","shell.execute_reply.started":"2025-03-28T09:29:15.003851Z","shell.execute_reply":"2025-03-28T09:29:15.006868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to get image paths\ndef get_image_paths(path):\n    image_names = []\n    for dirname, _, filenames in os.walk(path):\n        for filename in filenames:\n            fullpath = os.path.join(dirname, filename)\n            image_names.append(fullpath)\n    return image_names\n\nmonet_images_path = get_image_paths(monet_jpg_directory)\nphoto_images_path = get_image_paths(photo_jpg_directory)\n\nprint(f\"Number of Monet images: {len(monet_images_path)}\")\nprint(f\"Number of Photo images: {len(photo_images_path)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:15.009596Z","iopub.execute_input":"2025-03-28T09:29:15.009892Z","iopub.status.idle":"2025-03-28T09:29:18.002970Z","shell.execute_reply.started":"2025-03-28T09:29:15.009867Z","shell.execute_reply":"2025-03-28T09:29:18.002074Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Custom Dataset class\nclass ImageDataset(Dataset):\n    def __init__(self, image_paths, transform=None):\n        self.image_paths = image_paths\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        image = Image.open(self.image_paths[idx]).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        return image\n\n# Define transforms with augmentation\ntransform = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomApply([transforms.ColorJitter(brightness=0.1)], p=0.5),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])  # Normalize to [-1, 1]\n])\n\n# Create datasets and dataloaders\nmonet_dataset = ImageDataset(monet_images_path, transform=transform)\nphoto_dataset = ImageDataset(photo_images_path, transform=transform)\n\nBATCH_SIZE = 4  # Increased batch size for faster training\nmonet_loader = DataLoader(monet_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True)\nphoto_loader = DataLoader(photo_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:18.004197Z","iopub.execute_input":"2025-03-28T09:29:18.004544Z","iopub.status.idle":"2025-03-28T09:29:18.012357Z","shell.execute_reply.started":"2025-03-28T09:29:18.004501Z","shell.execute_reply":"2025-03-28T09:29:18.011480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Downsampling block\nclass Downsample(nn.Module):\n    def __init__(self, in_channels, out_channels, kernel_size, apply_instancenorm=True):\n        super(Downsample, self).__init__()\n        layers = [\n            nn.Conv2d(in_channels, out_channels, kernel_size, stride=2, padding=kernel_size//2, bias=False)\n        ]\n        if apply_instancenorm:\n            layers.append(nn.InstanceNorm2d(out_channels))\n        layers.append(nn.LeakyReLU(0.2, inplace=True))\n        self.model = nn.Sequential(*layers)\n\n    def forward(self, x):\n        return self.model(x)\n\n# Upsampling block (fixed to match skip connection sizes)\nclass Upsample(nn.Module):\n    def __init__(self, in_channels, out_channels, kernel_size, apply_dropout=False):\n        super(Upsample, self).__init__()\n        self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n        layers = [\n            nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=kernel_size//2, bias=False),\n            nn.InstanceNorm2d(out_channels)\n        ]\n        if apply_dropout:\n            layers.append(nn.Dropout(0.5))\n        layers.append(nn.ReLU(inplace=True))\n        self.conv = nn.Sequential(*layers)\n\n    def forward(self, x, target_size):\n        x = self.upsample(x)\n        # Ensure the upsampled size matches the target size (from skip connection)\n        x = torch.nn.functional.interpolate(x, size=target_size, mode='bilinear', align_corners=True)\n        x = self.conv(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:18.013534Z","iopub.execute_input":"2025-03-28T09:29:18.013906Z","iopub.status.idle":"2025-03-28T09:29:18.034314Z","shell.execute_reply.started":"2025-03-28T09:29:18.013870Z","shell.execute_reply":"2025-03-28T09:29:18.033669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generator model (modified to pass target sizes)\nclass Generator(nn.Module):\n    def __init__(self):\n        super(Generator, self).__init__()\n        \n        # Encoder (downsampling)\n        self.down_stack = nn.ModuleList([\n            Downsample(3, 64, 4, apply_instancenorm=False),  # (bs, 128, 128, 64)\n            Downsample(64, 128, 4),  # (bs, 64, 64, 128)\n            Downsample(128, 256, 4),  # (bs, 32, 32, 256)\n            Downsample(256, 512, 4),  # (bs, 16, 16, 512)\n            Downsample(512, 512, 4),  # (bs, 8, 8, 512)\n            Downsample(512, 512, 4),  # (bs, 4, 4, 512)\n            Downsample(512, 512, 4),  # (bs, 2, 2, 512)\n            Downsample(512, 512, 4),  # (bs, 1, 1, 512)\n        ])\n        \n        # Decoder (upsampling)\n        self.up_stack = nn.ModuleList([\n            Upsample(512, 512, 4, apply_dropout=True),  # (bs, 2, 2, 512)\n            Upsample(1024, 512, 4, apply_dropout=True),  # (bs, 4, 4, 512)\n            Upsample(1024, 512, 4, apply_dropout=True),  # (bs, 8, 8, 512)\n            Upsample(1024, 512, 4),  # (bs, 16, 16, 512)\n            Upsample(1024, 256, 4),  # (bs, 32, 32, 256)\n            Upsample(512, 128, 4),  # (bs, 64, 64, 128)\n            Upsample(256, 64, 4),  # (bs, 128, 128, 64)\n        ])\n        \n        self.last = nn.Sequential(\n            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),\n            nn.Conv2d(128, 3, 4, stride=1, padding=1),\n            nn.Tanh()\n        )\n\n    def forward(self, x):\n        skips = []\n        for down in self.down_stack:\n            x = down(x)\n            skips.append(x)\n        \n        skips = list(reversed(skips[:-1]))\n        for up, skip in zip(self.up_stack, skips):\n            # Pass the target size (skip connection size) to the upsample block\n            target_size = (skip.size(2), skip.size(3))\n            x = up(x, target_size)\n            x = torch.cat([x, skip], dim=1)\n        \n        x = self.last(x)\n        return x\n\n# Discriminator model\nclass Discriminator(nn.Module):\n    def __init__(self):\n        super(Discriminator, self).__init__()\n        self.model = nn.Sequential(\n            Downsample(3, 64, 4, apply_instancenorm=False),  # (bs, 128, 128, 64)\n            Downsample(64, 128, 4),  # (bs, 64, 64, 128)\n            Downsample(128, 256, 4),  # (bs, 32, 32, 256)\n            nn.ZeroPad2d(1),  # (bs, 34, 34, 256)\n            nn.Conv2d(256, 512, 4, stride=1, padding=0, bias=False),  # (bs, 31, 31, 512)\n            nn.InstanceNorm2d(512),\n            nn.LeakyReLU(0.2, inplace=True),\n            nn.ZeroPad2d(1),  # (bs, 33, 33, 512)\n            nn.Conv2d(512, 1, 4, stride=1, padding=0)  # (bs, 30, 30, 1)\n        )\n\n    def forward(self, x):\n        return self.model(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:18.035681Z","iopub.execute_input":"2025-03-28T09:29:18.036076Z","iopub.status.idle":"2025-03-28T09:29:18.051017Z","shell.execute_reply.started":"2025-03-28T09:29:18.036039Z","shell.execute_reply":"2025-03-28T09:29:18.050421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Initialize models\nmonet_generator = Generator().to(device)\nphoto_generator = Generator().to(device)\nmonet_discriminator = Discriminator().to(device)\nphoto_discriminator = Discriminator().to(device)\n\n# Use DataParallel for multi-GPU support\nif torch.cuda.device_count() > 1:\n    print(\"Using DataParallel for multi-GPU training\")\n    monet_generator = nn.DataParallel(monet_generator)\n    photo_generator = nn.DataParallel(photo_generator)\n    monet_discriminator = nn.DataParallel(monet_discriminator)\n    photo_discriminator = nn.DataParallel(photo_discriminator)\n\n# Loss functions\nadversarial_loss = nn.BCEWithLogitsLoss()\nl1_loss = nn.L1Loss()\n\n# Optimizers with TTUR\ngen_optimizer = optim.Adam(\n    list(monet_generator.parameters()) + list(photo_generator.parameters()),\n    lr=2e-4, betas=(0.5, 0.999)\n)\ndisc_optimizer = optim.Adam(\n    list(monet_discriminator.parameters()) + list(photo_discriminator.parameters()),\n    lr=1e-4, betas=(0.5, 0.999)\n)\n\n# Learning rate scheduler\ndef lr_schedule(epoch, initial_lr, total_epochs, decay_start_epoch):\n    if epoch < decay_start_epoch:\n        return initial_lr\n    else:\n        decay_factor = (total_epochs - epoch) / (total_epochs - decay_start_epoch)\n        return initial_lr * decay_factor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:18.051968Z","iopub.execute_input":"2025-03-28T09:29:18.052184Z","iopub.status.idle":"2025-03-28T09:29:19.290291Z","shell.execute_reply.started":"2025-03-28T09:29:18.052163Z","shell.execute_reply":"2025-03-28T09:29:19.289337Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Training loop\nEPOCHS = 35\nDECAY_START_EPOCH = 10\nLAMBDA_CYCLE = 15\nLAMBDA_IDENTITY = 5\n\nhistory = {\n    'monet_gen_loss': [],\n    'photo_gen_loss': [],\n    'monet_disc_loss': [],\n    'photo_disc_loss': []\n}\n\nfor epoch in range(EPOCHS):\n    print(f\"Epoch {epoch + 1}/{EPOCHS}\")\n    \n    # Update learning rates\n    gen_lr = lr_schedule(epoch, 2e-4, EPOCHS, DECAY_START_EPOCH)\n    disc_lr = lr_schedule(epoch, 1e-4, EPOCHS, DECAY_START_EPOCH)\n    for param_group in gen_optimizer.param_groups:\n        param_group['lr'] = gen_lr\n    for param_group in disc_optimizer.param_groups:\n        param_group['lr'] = disc_lr\n    \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    num_batches = 0\n    \n    # Create iterator for paired training\n    data_iter = iter(zip(monet_loader, photo_loader))\n    for (real_monet, real_photo) in data_iter:\n        real_monet = real_monet.to(device)\n        real_photo = real_photo.to(device)\n        batch_size = real_monet.size(0)\n        \n        # Labels with label smoothing\n        real_label = torch.full((batch_size, 1, 30, 30), 0.9, device=device)\n        fake_label = torch.full((batch_size, 1, 30, 30), 0.1, device=device)\n        \n        # Train Discriminators\n        disc_optimizer.zero_grad()\n        \n        # Monet Discriminator\n        fake_monet = monet_generator(real_photo)\n        disc_real_monet = monet_discriminator(real_monet)\n        disc_fake_monet = monet_discriminator(fake_monet.detach())\n        monet_disc_loss_real = adversarial_loss(disc_real_monet, real_label)\n        monet_disc_loss_fake = adversarial_loss(disc_fake_monet, fake_label)\n        monet_disc_loss = (monet_disc_loss_real + monet_disc_loss_fake) * 0.5\n        \n        # Photo Discriminator\n        fake_photo = photo_generator(real_monet)\n        disc_real_photo = photo_discriminator(real_photo)\n        disc_fake_photo = photo_discriminator(fake_photo.detach())\n        photo_disc_loss_real = adversarial_loss(disc_real_photo, real_label)\n        photo_disc_loss_fake = adversarial_loss(disc_fake_photo, fake_label)\n        photo_disc_loss = (photo_disc_loss_real + photo_disc_loss_fake) * 0.5\n        \n        total_disc_loss = monet_disc_loss + photo_disc_loss\n        total_disc_loss.backward()\n        disc_optimizer.step()\n        \n        # Train Generators\n        gen_optimizer.zero_grad()\n        \n        # Adversarial Loss\n        disc_fake_monet = monet_discriminator(fake_monet)\n        disc_fake_photo = photo_discriminator(fake_photo)\n        monet_gen_loss = adversarial_loss(disc_fake_monet, real_label)\n        photo_gen_loss = adversarial_loss(disc_fake_photo, real_label)\n        \n        # Cycle Loss\n        cycled_photo = photo_generator(fake_monet)\n        cycled_monet = monet_generator(fake_photo)\n        cycle_loss = (l1_loss(cycled_photo, real_photo) + l1_loss(cycled_monet, real_monet)) * LAMBDA_CYCLE\n        \n        # Identity Loss\n        same_monet = monet_generator(real_monet)\n        same_photo = photo_generator(real_photo)\n        identity_loss = (l1_loss(same_monet, real_monet) + l1_loss(same_photo, real_photo)) * LAMBDA_IDENTITY * 0.5\n        \n        total_gen_loss = monet_gen_loss + photo_gen_loss + cycle_loss + identity_loss\n        total_gen_loss.backward()\n        gen_optimizer.step()\n        \n        # Accumulate losses\n        epoch_monet_gen_loss += monet_gen_loss.item()\n        epoch_photo_gen_loss += photo_gen_loss.item()\n        epoch_monet_disc_loss += monet_disc_loss.item()\n        epoch_photo_disc_loss += photo_disc_loss.item()\n        num_batches += 1\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    # Store losses\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\"Monet Gen Loss: {avg_monet_gen_loss:.4f}, Photo Gen Loss: {avg_photo_gen_loss:.4f}, \"\n          f\"Monet Disc Loss: {avg_monet_disc_loss:.4f}, Photo Disc Loss: {avg_photo_disc_loss:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:19.291572Z","iopub.execute_input":"2025-03-28T09:29:19.291933Z","iopub.status.idle":"2025-03-28T09:29:20.978157Z","shell.execute_reply.started":"2025-03-28T09:29:19.291906Z","shell.execute_reply":"2025-03-28T09:29:20.976406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot losses\nplt.figure(figsize=(10, 5))\nplt.plot(history['monet_gen_loss'], label='Monet Generator Loss')\nplt.plot(history['photo_gen_loss'], label='Photo Generator Loss')\nplt.plot(history['monet_disc_loss'], label='Monet Discriminator Loss')\nplt.plot(history['photo_disc_loss'], label='Photo Discriminator Loss')\nplt.title('Training Losses Over Epochs')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.savefig('/kaggle/working/loss_plot.png')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:20.979513Z","iopub.status.idle":"2025-03-28T09:29:20.980040Z","shell.execute_reply.started":"2025-03-28T09:29:20.979781Z","shell.execute_reply":"2025-03-28T09:29:20.979805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Display generated images\ndef display_generated_images(dataloader, num_images=5):\n    monet_generator.eval()\n    plt.figure(figsize=(15, 5))\n    with torch.no_grad():\n        for i, photo in enumerate(dataloader):\n            if i >= num_images:\n                break\n            photo = photo.to(device)\n            fake_monet = monet_generator(photo)\n            photo = photo.cpu().numpy()[0].transpose(1, 2, 0) * 0.5 + 0.5  # Denormalize\n            fake_monet = fake_monet.cpu().numpy()[0].transpose(1, 2, 0) * 0.5 + 0.5  # Denormalize\n            \n            plt.subplot(2, num_images, i + 1)\n            plt.imshow(photo)\n            plt.title(\"Original Photo\")\n            plt.axis('off')\n            \n            plt.subplot(2, num_images, i + 1 + num_images)\n            plt.imshow(fake_monet)\n            plt.title(\"Monet Style\")\n            plt.axis('off')\n    plt.savefig('/kaggle/working/generated_samples.png')\n    plt.show()\n    monet_generator.train()\n\ndisplay_generated_images(photo_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:20.981717Z","iopub.status.idle":"2025-03-28T09:29:20.982155Z","shell.execute_reply.started":"2025-03-28T09:29:20.981931Z","shell.execute_reply":"2025-03-28T09:29:20.981953Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to generate submission file\ndef generate_submission(dataloader, num_images=7000):\n    monet_generator.eval()\n    os.makedirs('/kaggle/working/images', exist_ok=True)\n    with torch.no_grad():\n        for i, photo in enumerate(dataloader):\n            if i >= num_images:\n                break\n            photo = photo.to(device)\n            fake_monet = monet_generator(photo)\n            fake_monet = fake_monet.cpu().numpy()[0].transpose(1, 2, 0) * 0.5 + 0.5  # Denormalize\n            fake_monet = (fake_monet * 255).astype(np.uint8)\n            fake_monet = cv2.cvtColor(fake_monet, cv2.COLOR_RGB2BGR)  # Convert to BGR for OpenCV\n            cv2.imwrite(f'/kaggle/working/images/{i+1}.jpg', fake_monet)\n    \n    # Create zip file for submission\n    shutil.make_archive('/kaggle/working/images', 'zip', '/kaggle/working/images')\n    print(\"Submission file created: /kaggle/working/images.zip\")\n    monet_generator.train()\n\n# Generate submission\ngenerate_submission(photo_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-28T09:29:20.984077Z","iopub.status.idle":"2025-03-28T09:29:20.984524Z","shell.execute_reply.started":"2025-03-28T09:29:20.984284Z","shell.execute_reply":"2025-03-28T09:29:20.984307Z"}},"outputs":[],"execution_count":null}]}