{"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"},{"sourceId":12405102,"sourceType":"datasetVersion","datasetId":7823031}],"dockerImageVersionId":31089,"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.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import transforms, utils\nfrom PIL import Image\nimport numpy as np\nfrom tqdm import tqdm\nimport os\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:06.670113Z","iopub.execute_input":"2025-07-08T11:45:06.670313Z","iopub.status.idle":"2025-07-08T11:45:14.202420Z","shell.execute_reply.started":"2025-07-08T11:45:06.670292Z","shell.execute_reply":"2025-07-08T11:45:14.201866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# DCGAN Generator\nclass DCGANGenerator(nn.Module):\n    def __init__(self, z_dim=100, img_channels=3, feature_maps=64):\n        super().__init__()\n        self.model = nn.Sequential(\n            # Input: z_dim x 1 x 1\n            nn.ConvTranspose2d(z_dim, feature_maps * 8, 4, 1, 0, bias=False),\n            nn.BatchNorm2d(feature_maps * 8),\n            nn.ReLU(True),\n\n            # State: (feature_maps*8) x 4 x 4\n            nn.ConvTranspose2d(feature_maps * 8, feature_maps * 4, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(feature_maps * 4),\n            nn.ReLU(True),\n\n            # State: (feature_maps*4) x 8 x 8\n            nn.ConvTranspose2d(feature_maps * 4, feature_maps * 2, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(feature_maps * 2),\n            nn.ReLU(True),\n\n            # State: (feature_maps*2) x 16 x 16\n            nn.ConvTranspose2d(feature_maps * 2, feature_maps, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(feature_maps),\n            nn.ReLU(True),\n\n            # State: (feature_maps) x 32 x 32\n            nn.ConvTranspose2d(feature_maps, img_channels, 8, 8, 0, bias=False), # To 256x256\n            nn.Tanh()\n        )\n\n    def forward(self, z):\n        return self.model(z)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.203697Z","iopub.execute_input":"2025-07-08T11:45:14.204053Z","iopub.status.idle":"2025-07-08T11:45:14.209779Z","shell.execute_reply.started":"2025-07-08T11:45:14.204035Z","shell.execute_reply":"2025-07-08T11:45:14.209105Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# DCGAN Discriminator \nclass DCGANDiscriminator(nn.Module):\n    def __init__(self, img_channels=3, feature_maps=64):\n        super().__init__()\n        self.model = nn.Sequential(\n            nn.Conv2d(img_channels, feature_maps, 4, 2, 1, bias=False),\n            nn.LeakyReLU(0.2, inplace=True),\n            nn.Dropout(0.3),  \n\n            nn.Conv2d(feature_maps, feature_maps * 2, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(feature_maps * 2),\n            nn.LeakyReLU(0.2, inplace=True),\n            nn.Dropout(0.3),\n\n            nn.Conv2d(feature_maps * 2, feature_maps * 4, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(feature_maps * 4),\n            nn.LeakyReLU(0.2, inplace=True),\n            nn.Dropout(0.3),\n\n            nn.Conv2d(feature_maps * 4, feature_maps * 8, 4, 2, 1, bias=False),\n            nn.BatchNorm2d(feature_maps * 8),\n            nn.LeakyReLU(0.2, inplace=True),\n            nn.Dropout(0.3),\n\n            nn.Conv2d(feature_maps * 8, 1, 16, 1, 0, bias=False), # To 1x1\n            nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        return self.model(x).view(-1, 1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.210405Z","iopub.execute_input":"2025-07-08T11:45:14.210580Z","iopub.status.idle":"2025-07-08T11:45:14.233707Z","shell.execute_reply.started":"2025-07-08T11:45:14.210565Z","shell.execute_reply":"2025-07-08T11:45:14.233015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dataset for Monet paintings only\nclass MonetOnlyDataset(Dataset):\n    def __init__(self, root_monet, transform):\n        self.monet_files = sorted(os.listdir(root_monet))\n        self.monet_root = root_monet\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.monet_files)\n\n    def __getitem__(self, idx):\n        monet_path = os.path.join(self.monet_root, self.monet_files[idx])\n        monet = self.transform(Image.open(monet_path).convert('RGB'))\n        return monet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.234443Z","iopub.execute_input":"2025-07-08T11:45:14.235108Z","iopub.status.idle":"2025-07-08T11:45:14.253007Z","shell.execute_reply.started":"2025-07-08T11:45:14.235074Z","shell.execute_reply":"2025-07-08T11:45:14.252432Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MonetAndPhotoDataset(Dataset):\n    def __init__(self, root_monet, root_photo, transform):\n        self.files = sorted(os.listdir(root_monet)) + sorted(os.listdir(root_photo))\n        self.roots = [root_monet]*len(os.listdir(root_monet)) + [root_photo]*len(os.listdir(root_photo))\n        self.transform = transform\n    def __len__(self):\n        return len(self.files)\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.roots[idx], self.files[idx])\n        img = self.transform(Image.open(img_path).convert('RGB'))\n        return img","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.254586Z","iopub.execute_input":"2025-07-08T11:45:14.254770Z","iopub.status.idle":"2025-07-08T11:45:14.270338Z","shell.execute_reply.started":"2025-07-08T11:45:14.254755Z","shell.execute_reply":"2025-07-08T11:45:14.269729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data augmentation\ntransform = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(20),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5] * 3, [0.5] * 3)\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.271032Z","iopub.execute_input":"2025-07-08T11:45:14.271241Z","iopub.status.idle":"2025-07-08T11:45:14.290393Z","shell.execute_reply.started":"2025-07-08T11:45:14.271217Z","shell.execute_reply":"2025-07-08T11:45:14.289643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Hyperparameters\nz_dim = 100\nbatch_size = 64\nnum_epochs = 100\nlr = 2e-4\nbeta1 = 0.5\nnum_workers = 4","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.291277Z","iopub.execute_input":"2025-07-08T11:45:14.291711Z","iopub.status.idle":"2025-07-08T11:45:14.306363Z","shell.execute_reply.started":"2025-07-08T11:45:14.291687Z","shell.execute_reply":"2025-07-08T11:45:14.305817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dataset and dataloader\ndataset = MonetAndPhotoDataset(\n    root_monet=\"/kaggle/input/gan-getting-started/monet_jpg\",\n    root_photo=\"/kaggle/input/gan-getting-started/photo_jpg\",\n    transform=transform\n)\nloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers)\n\n# Models\nG = DCGANGenerator(z_dim=z_dim).to(device)\nD = DCGANDiscriminator().to(device)\n\n# Optimizers and loss\noptimizer_G = torch.optim.Adam(G.parameters(), lr=lr, betas=(beta1, 0.999))\noptimizer_D = torch.optim.Adam(D.parameters(), lr=lr, betas=(beta1, 0.999))\ncriterion = nn.BCELoss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.307027Z","iopub.execute_input":"2025-07-08T11:45:14.307242Z","iopub.status.idle":"2025-07-08T11:45:14.633232Z","shell.execute_reply.started":"2025-07-08T11:45:14.307218Z","shell.execute_reply":"2025-07-08T11:45:14.632647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"D_steps = 3  # number of discriminator updates per generator update\n\nfor epoch in range(1, num_epochs + 1):\n    G.train(); D.train()\n    for i, real_imgs in enumerate(tqdm(loader, desc=f\"Epoch {epoch}/{num_epochs}\")):\n        real_imgs = real_imgs.to(device)\n        batch = real_imgs.size(0)\n        real_labels = torch.full((batch, 1), 0.9, device=device)\n        fake_labels = torch.full((batch, 1), 0.0, device=device)\n\n        # Train discriminator (D_steps times)\n        for _ in range(D_steps):\n            optimizer_D.zero_grad()\n            # Real samples\n            out_real = D(real_imgs)\n            loss_real = criterion(out_real, real_labels)\n            # Fake samples\n            z = torch.randn(batch, z_dim, 1, 1, device=device)\n            fake_imgs = G(z)\n            out_fake = D(fake_imgs.detach())\n            loss_fake = criterion(out_fake, fake_labels)\n            # Total discriminator loss\n            loss_D = loss_real + loss_fake\n            loss_D.backward()\n            optimizer_D.step()\n\n        # Train generator (once)\n        optimizer_G.zero_grad()\n        z = torch.randn(batch, z_dim, 1, 1, device=device)\n        fake_imgs = G(z)\n        out_fake = D(fake_imgs)\n        loss_G = criterion(out_fake, real_labels)\n        loss_G.backward()\n        optimizer_G.step()\n\n    # Save checkpoints and clear cache\n    if epoch % 10 == 0 or epoch == num_epochs:\n        torch.save(G.state_dict(), f\"/kaggle/working/DCGAN_G_epoch{epoch}.pth\")\n        torch.save(D.state_dict(), f\"/kaggle/working/DCGAN_D_epoch{epoch}.pth\")\n    torch.cuda.empty_cache()\n    print(f\"Epoch {epoch}: D_loss={loss_D.item():.3f} G_loss={loss_G.item():.3f}\")\n\nprint(\"Training finished!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T11:45:14.633964Z","iopub.execute_input":"2025-07-08T11:45:14.634212Z","iopub.status.idle":"2025-07-08T14:31:33.037876Z","shell.execute_reply.started":"2025-07-08T11:45:14.634189Z","shell.execute_reply":"2025-07-08T14:31:33.037134Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generate images for submission\noutput_dir = \"/kaggle/working/gen_images_dcgan\"\nos.makedirs(output_dir, exist_ok=True)\nG.eval()\nnum_samples = 7000  # number of images to generate\n\nwith torch.no_grad():\n    for i in tqdm(range(num_samples)):\n        z = torch.randn(1, z_dim, 1, 1, device=device)\n        fake_img = G(z).cpu().squeeze(0)\n        img_np = (fake_img * 0.5 + 0.5).clamp(0, 1).numpy().transpose(1, 2, 0)\n        img_np = (img_np * 255).astype(np.uint8)\n        Image.fromarray(img_np).save(f\"{output_dir}/{i:05d}.jpg\", \"JPEG\")\n\n# Create archive\nimport shutil\nshutil.make_archive(\"/kaggle/working/images\", 'zip', output_dir)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T14:31:33.038881Z","iopub.execute_input":"2025-07-08T14:31:33.039137Z","iopub.status.idle":"2025-07-08T14:31:58.598428Z","shell.execute_reply.started":"2025-07-08T14:31:33.039113Z","shell.execute_reply":"2025-07-08T14:31:58.597811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# MiFID (FID) for DCGAN: compare generated vs real Monet paintings\n\nfrom torchvision.models import inception_v3\nimport torch.nn as nn\nfrom scipy import linalg\n\ndef get_inception_features(images, model, batch_size=8):\n    features = []\n    model.eval()\n    with torch.no_grad():\n        for i in range(0, images.shape[0], batch_size):\n            batch = images[i:i+batch_size].to(device)\n            batch = F.interpolate(batch, size=(299, 299), mode='bilinear', align_corners=False)\n            preds = model(batch)\n            if isinstance(preds, tuple):  # for older torchvision versions\n                preds = preds[0]\n            preds = preds.detach().cpu().numpy()\n            features.append(preds)\n    features = np.concatenate(features, axis=0)\n    return features\n\ndef calculate_fid(mu1, sigma1, mu2, sigma2, eps=1e-6):\n    diff = mu1 - mu2\n    covmean, _ = linalg.sqrtm(sigma1 @ sigma2, disp=False)\n    if np.iscomplexobj(covmean):\n        covmean = covmean.real\n    fid = diff.dot(diff) + np.trace(sigma1 + sigma2 - 2 * covmean)\n    return fid\n\ndef compute_fid_dcgan(fake_images, real_images, batch_size=16):\n    # compute FID between generated and real Monet paintings\n    model = inception_v3(pretrained=False, transform_input=False).to(device)\n    model.load_state_dict(torch.load(\"/kaggle/input/inception-v3-google-0cc3c7bd22-pth/inception_v3_google-0cc3c7bd.pth\"))\n    model.fc = nn.Identity()\n    model.eval()\n\n    # extract features\n    fake_feats = get_inception_features(fake_images, model, batch_size)\n    real_feats = get_inception_features(real_images, model, batch_size)\n\n    mu_fake, sigma_fake = fake_feats.mean(axis=0), np.cov(fake_feats, rowvar=False)\n    mu_real, sigma_real = real_feats.mean(axis=0), np.cov(real_feats, rowvar=False)\n\n    fid = calculate_fid(mu_fake, sigma_fake, mu_real, sigma_real)\n    return fid\n\n# Collect generated and real images for FID\nN = min(len(dataset), 300)  # limit for speed and fairness\n\n# Real Monet paintings\nreal_imgs = []\nfor i in range(N):\n    monet = dataset[i]\n    real_imgs.append(monet)\nreal_imgs = torch.stack(real_imgs)[:N]\n\n# Generated Monet paintings\nG.eval()\nfake_imgs = []\nwith torch.no_grad():\n    for i in range(N):\n        z = torch.randn(1, z_dim, 1, 1, device=device)\n        fake_img = G(z).cpu().squeeze(0)\n        fake_imgs.append(fake_img)\nfake_imgs = torch.stack(fake_imgs)[:N]\n\n# Compute FID\nfid = compute_fid_dcgan(fake_imgs, real_imgs, batch_size=16)\nprint(f\"FID (DCGAN): {fid:.4f}\")\n\n# If you really want MiFID (sum of two FIDs) for DCGAN\n# Usually FID alone is used since there's only one domain\nmifid = fid + fid\nprint(f\"MiFID (DCGAN): {mifid:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T14:31:58.599186Z","iopub.execute_input":"2025-07-08T14:31:58.599436Z","iopub.status.idle":"2025-07-08T14:32:12.484817Z","shell.execute_reply.started":"2025-07-08T14:31:58.599415Z","shell.execute_reply":"2025-07-08T14:32:12.484048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 1. Visualization of generated vs real Monet paintings\nN_vis = 10  # number of examples to show\n\ndef denorm_dcgan(t):\n    # assumes generator output in [-1, 1]\n    t = t.detach().cpu()\n    t = (t * 0.5) + 0.5\n    return t.clamp(0, 1).permute(1, 2, 0).numpy()\n\nfig, axs = plt.subplots(N_vis, 2, figsize=(6, 3 * N_vis))\nfor i in range(N_vis):\n    # generated image\n    axs[i, 0].imshow(denorm_dcgan(fake_imgs[i]))\n    axs[i, 0].set_title('Fake Monet (DCGAN)')\n    axs[i, 0].axis('off')\n    # real image\n    axs[i, 1].imshow(denorm_dcgan(real_imgs[i]))\n    axs[i, 1].set_title('Real Monet')\n    axs[i, 1].axis('off')\n\nplt.tight_layout()\nplt.show()\n\n# 2. Compute FID/MiFID\nfid = compute_fid_dcgan(fake_imgs, real_imgs, batch_size=16)\nprint(f\"FID (DCGAN): {fid:.4f}\")\nmifid = fid + fid\nprint(f\"MiFID (DCGAN): {mifid:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T23:58:12.053295Z","iopub.execute_input":"2025-07-08T23:58:12.053544Z","iopub.status.idle":"2025-07-08T23:58:13.933113Z","shell.execute_reply.started":"2025-07-08T23:58:12.053524Z","shell.execute_reply":"2025-07-08T23:58:13.932191Z"}},"outputs":[],"execution_count":null}]}