{"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":"none","dataSources":[{"sourceId":21755,"databundleVersionId":1475600,"sourceType":"competition"}],"dockerImageVersionId":30761,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        pass\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-09-15T17:53:44.078942Z","iopub.execute_input":"2024-09-15T17:53:44.079460Z","iopub.status.idle":"2024-09-15T17:53:46.114289Z","shell.execute_reply.started":"2024-09-15T17:53:44.079419Z","shell.execute_reply":"2024-09-15T17:53:46.113001Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Import necessary libraries\nimport os\nimport zipfile\nfrom random import sample\n\nimport torch\nimport torch.nn as nn\nimport torchvision.transforms as transforms\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# Dataset class to load Monet and photo images\nclass Images(Dataset):\n    def __init__(self, photo_path, monet_path, transform):\n        self.photo_path = photo_path\n        self.monet_path = monet_path\n        self.transform = transform\n        self.photos = os.listdir(photo_path)\n        self.monets = os.listdir(monet_path)\n\n        self.photos = sample(self.photos, min(50, len(self.photos)))\n        self.monets = sample(self.monets, min(50, len(self.monets)))\n\n        self.l_photo = len(self.photos)\n        self.l_monet = len(self.monets)\n\n    def __len__(self):\n        return max(len(self.photos), len(self.monets))\n\n    def __getitem__(self, idx):\n        # Load and transform images\n        photo = Image.open(self.photo_path + self.photos[idx % self.l_photo]).convert(\"RGB\")\n        monet = Image.open(self.monet_path + self.monets[idx % self.l_monet]).convert(\"RGB\")\n\n        photo = self.transform(photo)\n        monet = self.transform(monet)\n\n        return photo, monet\n\n\n# Image transformations\ntransform = transforms.Compose([transforms.Resize((128, 128)),  # Rescale to 128x128 to reduce memory consumption\n                                transforms.ToTensor(), transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])\n\n# Define dataset and dataloader\nphoto_path = '/kaggle/input/gan-getting-started/photo_jpg/'\nmonet_path = '/kaggle/input/gan-getting-started/monet_jpg/'\ndataset = Images(photo_path, monet_path, transform)\ndataloader = DataLoader(dataset, batch_size=8, shuffle=True)\n\n\nclass Generator(nn.Module):\n    def __init__(self):\n        super(Generator, self).__init__()\n        self.model = nn.Sequential(  # Input is a 100-dimensional noise vector\n            nn.Linear(100, 128 * 8 * 8),  # First layer to map noise to a feature map\n            nn.ReLU(True), nn.Unflatten(1, (128, 8, 8)),\n\n            # Upsample to 16x16\n            nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1), nn.BatchNorm2d(128), nn.ReLU(True),\n\n            # Upsample to 32x32\n            nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1), nn.BatchNorm2d(64), nn.ReLU(True),\n\n            # Upsample to 64x64\n            nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1), nn.BatchNorm2d(32), nn.ReLU(True),\n\n            # Upsample to 128x128\n            nn.ConvTranspose2d(32, 3, kernel_size=4, stride=2, padding=1), nn.Tanh()\n            # Output range [-1, 1] to match normalized image data\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n\nclass Discriminator(nn.Module):\n    def __init__(self):\n        super(Discriminator, self).__init__()\n        self.model = nn.Sequential(  # Input is a 3-channel image (128x128)\n            nn.Conv2d(3, 64, kernel_size=4, stride=2, padding=1), nn.LeakyReLU(0.2, inplace=True),\n\n            nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1), nn.BatchNorm2d(128),\n            nn.LeakyReLU(0.2, inplace=True),\n\n            nn.Conv2d(128, 128, kernel_size=4, stride=2, padding=1), nn.BatchNorm2d(128),\n            nn.LeakyReLU(0.2, inplace=True),\n\n            nn.Flatten(), nn.Linear(128 * 16 * 16, 1), nn.Sigmoid()  # Output between 0 (fake) and 1 (real)\n        )\n\n    def forward(self, x):\n        return self.model(x)\n\n\n# Loss and optimizers\nadversarial_loss = nn.BCELoss()\n\ngenerator = Generator().to(device)\ndiscriminator = Discriminator().to(device)\n\noptimizer_G = torch.optim.Adam(generator.parameters(), lr=0.0002, betas=(0.5, 0.999))\noptimizer_D = torch.optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))\n\noutput_dir = \"output/\"\nos.makedirs(output_dir, exist_ok=True)\n\n# Training the GAN\nepochs = 100\nfor epoch in range(epochs):\n    running_d_loss = 0.0\n    running_g_loss = 0.0\n    for i, (photos, _) in enumerate(dataloader):\n        # Adversarial ground truths\n        valid = torch.ones((photos.size(0), 1), requires_grad=False).to(device)\n        fake = torch.zeros((photos.size(0), 1), requires_grad=False).to(device)\n\n        # Real photos to be processed by the discriminator\n        real_photos = photos.to(device)\n\n        # Sample random noise for the generator\n        noise = torch.randn((photos.size(0), 100)).to(device)\n\n        # ---------------------\n        #  Train Generator\n        # ---------------------\n        optimizer_G.zero_grad()\n\n        # Generate fake photos\n        generated_photos = generator(noise)\n\n        # Loss measures generator's ability to fool the discriminator\n        g_loss = adversarial_loss(discriminator(generated_photos), valid)\n        running_g_loss += g_loss.item()\n\n        # Backward propagation and optimization\n        g_loss.backward()\n        optimizer_G.step()\n\n        # ---------------------\n        #  Train Discriminator\n        # ---------------------\n        optimizer_D.zero_grad()\n\n        # Measure discriminator's ability to classify real from generated samples\n        real_loss = adversarial_loss(discriminator(real_photos), valid)\n        fake_loss = adversarial_loss(discriminator(generated_photos.detach()), fake)\n\n        # Total discriminator loss\n        d_loss = (real_loss + fake_loss) / 2\n        running_d_loss += d_loss.item()\n\n        # Backward propagation and optimization\n        d_loss.backward()\n        optimizer_D.step()\n\n    # Print progress at the end of each epoch\n    print(f\"Epoch [{epoch + 1}/{epochs}] | Discriminator loss: {running_d_loss / len(dataloader):.4f} | Generator loss:\"\n          f\" {running_g_loss / len(dataloader):.4f}\")\n\n# Set the number of images to generate (between 7,000-10,000 as required by the competition)\ntotal_images = 7000  # You can adjust this between 7,000 and 10,000 as needed\nbatch_size = 100  # Batch size for image generation\nimages_generated = 0\n\n# Resize transform to ensure output images are 256x256\nresize_transform = transforms.Compose([transforms.Resize((256, 256)), transforms.ToPILImage()])\n\n# Generate images in batches\nfor batch_num in range(total_images // batch_size):\n    noise = torch.randn(batch_size, 100).to(device)\n    gen_imgs = generator(noise)\n    gen_imgs = gen_imgs.view(gen_imgs.size(0), 3, 128, 128)\n\n    # Rescale images from [-1, 1] to [0, 1] and resize to 256x256\n    gen_imgs = (gen_imgs.permute(0, 2, 3, 1).cpu().detach().numpy() * 0.5 + 0.5)\n\n    # Save the generated images\n    for i in range(batch_size):\n        img = resize_transform(torch.tensor(gen_imgs[i]).permute(2, 0, 1))\n        img.save(f\"{output_dir}/image_{images_generated + i}.png\")\n\n    images_generated += batch_size\n    print(f\"Generated {images_generated} images.\")\n\nzip_filename = \"images.zip\"\n# Zip all the images into a zip file\nwith zipfile.ZipFile(zip_filename, 'w') as img_zip:\n    for foldername, subfolders, filenames in os.walk(output_dir):\n        for filename in filenames:\n            img_zip.write(os.path.join(foldername, filename), filename)\n\nprint(f\"All images have been generated and zipped into {zip_filename}.\")\n","metadata":{"execution":{"iopub.status.busy":"2024-09-15T17:53:46.116777Z","iopub.execute_input":"2024-09-15T17:53:46.117289Z","iopub.status.idle":"2024-09-15T18:02:37.880119Z","shell.execute_reply.started":"2024-09-15T17:53:46.117227Z","shell.execute_reply":"2024-09-15T18:02:37.878904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}