{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","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":30747,"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\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\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-07-17T18:19:19.299525Z","iopub.execute_input":"2024-07-17T18:19:19.299823Z","iopub.status.idle":"2024-07-17T18:19:20.339431Z","shell.execute_reply.started":"2024-07-17T18:19:19.299796Z","shell.execute_reply":"2024-07-17T18:19:20.335723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch #design and train neural networks\nfrom torch.utils.data import Dataset, DataLoader #customize dataset, feed data\nimport torchvision #image to tensors, resize and normalize images\nimport matplotlib.pyplot as plt #visualize data and nn training progress\nfrom PIL import Image # open load images","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:19:29.049750Z","iopub.execute_input":"2024-07-17T18:19:29.050359Z","iopub.status.idle":"2024-07-17T18:19:33.636718Z","shell.execute_reply.started":"2024-07-17T18:19:29.050327Z","shell.execute_reply":"2024-07-17T18:19:33.635882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create PyTorch Dataset Class","metadata":{}},{"cell_type":"code","source":"class TorchDataset(Dataset):\n    def __init__(self, data_path, transform=None):\n        self.data_path = data_path\n        self.transform = transform\n        self.image_paths = [os.path.join(data_path, file) for file in os.listdir(data_path) if file.endswith('.jpg')]\n        \n    def __len__(self):\n        return len(self.image_paths)\n    \n    def __getitem__(self, idx):\n        img_path = self.image_paths[idx]\n        image = Image.open(img_path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        return image\n","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:19:33.638249Z","iopub.execute_input":"2024-07-17T18:19:33.638788Z","iopub.status.idle":"2024-07-17T18:19:33.645515Z","shell.execute_reply.started":"2024-07-17T18:19:33.638762Z","shell.execute_reply":"2024-07-17T18:19:33.644689Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Transformations \n* Resize image to 256*256 to unify input dimensions\n* Convert to tensors for neural networks\n* Normalize the tensor value to be between -1~1 by setting mean=0.5 sd=0.5 (across 3 RGB channels)","metadata":{}},{"cell_type":"code","source":"transforms = torchvision.transforms.Compose([\n    torchvision.transforms.Resize((256,256)),\n    torchvision.transforms.ToTensor(),\n    torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n])","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:19:34.380229Z","iopub.execute_input":"2024-07-17T18:19:34.380598Z","iopub.status.idle":"2024-07-17T18:19:34.386640Z","shell.execute_reply.started":"2024-07-17T18:19:34.380569Z","shell.execute_reply":"2024-07-17T18:19:34.385537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"root_path = \"/kaggle/input/gan-getting-started\"\nprint(os.listdir(root_path))\ndata_path_monet = f\"{root_path}/monet_jpg\"\ndata_path_photo = f\"{root_path}/photo_jpg\"","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:19:35.364441Z","iopub.execute_input":"2024-07-17T18:19:35.365268Z","iopub.status.idle":"2024-07-17T18:19:35.370914Z","shell.execute_reply.started":"2024-07-17T18:19:35.365233Z","shell.execute_reply":"2024-07-17T18:19:35.369984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# DataLoader and Exploratory data analysis","metadata":{}},{"cell_type":"code","source":"MonetDataset = TorchDataset(data_path=data_path_monet, transform=transforms)\nPhotoDataset = TorchDataset(data_path=data_path_photo, transform=transforms)\n\nMonetLoader = DataLoader(MonetDataset, batch_size=16, shuffle=True, num_workers=2)\nPhotoLoader = DataLoader(PhotoDataset, batch_size=16, shuffle=True, num_workers=2)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:26:40.201748Z","iopub.execute_input":"2024-07-17T18:26:40.202588Z","iopub.status.idle":"2024-07-17T18:26:40.228235Z","shell.execute_reply.started":"2024-07-17T18:26:40.202556Z","shell.execute_reply":"2024-07-17T18:26:40.227469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(PhotoDataset), len(MonetDataset))","metadata":{"execution":{"iopub.status.busy":"2024-07-17T19:08:18.412803Z","iopub.execute_input":"2024-07-17T19:08:18.413682Z","iopub.status.idle":"2024-07-17T19:08:18.418736Z","shell.execute_reply.started":"2024-07-17T19:08:18.413647Z","shell.execute_reply":"2024-07-17T19:08:18.417728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# fetch the next batch(16 images in tensor format) from the two DataLoaders \nmonets = next(iter(MonetLoader))\nphotos = next(iter(PhotoLoader))\nprint(monets.shape, photos.shape)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:26:40.890264Z","iopub.execute_input":"2024-07-17T18:26:40.890884Z","iopub.status.idle":"2024-07-17T18:26:41.794773Z","shell.execute_reply.started":"2024-07-17T18:26:40.890852Z","shell.execute_reply":"2024-07-17T18:26:41.793625Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(monets[0].min(), monets[0].max())\nprint(photos[0].min(), photos[0].max())","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:26:43.568629Z","iopub.execute_input":"2024-07-17T18:26:43.569370Z","iopub.status.idle":"2024-07-17T18:26:43.578193Z","shell.execute_reply.started":"2024-07-17T18:26:43.569332Z","shell.execute_reply":"2024-07-17T18:26:43.577192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# tansform data to be readable by plt.imshow\nmonets_imshow = []\nfor monet in monets:\n    monet = monet.permute(1, 2, 0) #tensor[C,H,W] -> imshow[H,W,C] height,weight,channel\n    monet = monet.numpy() # tensor -> numpy_arr readable by imshow\n    monet = monet * 0.5 + 0.5 # un-normalize the data\n    monets_imshow.append(monet)\n\nphotos_imshow = []    \nfor photo in photos:\n    photo = photo.permute(1, 2, 0) #tensor[C,H,W] -> imshow[H,W,C] height,weight,channel\n    photo = photo.numpy() # tensor -> numpy_arr readable by imshow\n    photo = photo * 0.5 + 0.5 # un-normalize the data\n    photos_imshow.append(photo)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:26:43.897321Z","iopub.execute_input":"2024-07-17T18:26:43.898016Z","iopub.status.idle":"2024-07-17T18:26:43.923577Z","shell.execute_reply.started":"2024-07-17T18:26:43.897980Z","shell.execute_reply":"2024-07-17T18:26:43.922684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(monets_imshow[3].shape)  # Check the shape of one of the images\nprint(monets_imshow[3].dtype) ","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:26:45.486807Z","iopub.execute_input":"2024-07-17T18:26:45.487791Z","iopub.status.idle":"2024-07-17T18:26:45.492864Z","shell.execute_reply.started":"2024-07-17T18:26:45.487748Z","shell.execute_reply":"2024-07-17T18:26:45.491857Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Monets\nfig1 = plt.figure(figsize=(15, 5)) \n\nplt.subplot(1, 4, 1)  \nplt.imshow(monets_imshow[0])\nplt.axis('off')  \n\nplt.subplot(1, 4, 2)  \nplt.imshow(monets_imshow[1])\nplt.axis('off')  \n\nplt.subplot(1, 4, 3)  \nplt.imshow(monets_imshow[2])\nplt.axis('off')  \n\nplt.subplot(1, 4, 4)  \nplt.imshow(monets_imshow[3])\nplt.axis('off')  \n\n\n# Add a title for the entire figure\nfig1.suptitle('Monets', fontsize=16)\n\n# Display all the plots\nplt.show()\n\n\n\n\n# Photos\nfig2 = plt.figure(figsize=(15, 5)) \n\nplt.subplot(1, 4, 1)  \nplt.imshow(photos_imshow[0])\nplt.axis('off')  \n\nplt.subplot(1, 4, 2)  \nplt.imshow(photos_imshow[1])\nplt.axis('off')  \n\nplt.subplot(1, 4, 3)  \nplt.imshow(photos_imshow[2])\nplt.axis('off')  \n\nplt.subplot(1, 4, 4)  \nplt.imshow(photos_imshow[3])\nplt.axis('off')  \n\n\n# Add a title for the entire figure\nfig2.suptitle('Photos', fontsize=16)\n\n# Display all the plots\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:26:46.911654Z","iopub.execute_input":"2024-07-17T18:26:46.912509Z","iopub.status.idle":"2024-07-17T18:26:47.649883Z","shell.execute_reply.started":"2024-07-17T18:26:46.912476Z","shell.execute_reply":"2024-07-17T18:26:47.649071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# CycleGAN Model\n### Define Block Class\nIn neural network architecture design, a \"block\" typically refers to a repeatable and modular unit of layers that perform a set of operations, which can be stacked or repeated with similar or varying parameters to build more complex architectures. The advantage of designing neural networks in blocks includes:\n* Reusability: Once defined, blocks can be reused within different parts of the network or in different network architectures.\n* Readability and Maintainability: Organizing code into logical units (blocks) makes it easier to manage and understand.\n* Modularity: Blocks can be designed, tested, and optimized independently.","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\n\nclass Block(nn.Module):\n    # define states (attributes) -> NN layers\n    def __init__(self, in_channels, out_channels, stride):\n        # inherent from the parent class -> nn.Module\n        super().__init__()\n        self.cnn = nn.Sequential(\n            # Convolution layer\n            nn.Conv2d(\n                in_channels,\n                out_channels,\n                kernel_size=4,\n                stride=stride,\n                padding=1,\n                bias=True,\n                padding_mode=\"reflect\", # reflect padding reduces artifact\n            ), \n            # Normalization layer -> Instance rather than Batch\n            # helps preserve artistic style integrity, \n            # regardless of the variability in content across different images in a batch\n            nn.InstanceNorm2d(out_channels), \n            # Activation Func\n            nn.LeakyReLU(0.2, inplace=True), \n        )\n    \n    # define behaviors (methods) -> feed forward \n    def forward(self, x):\n        return self.cnn(x)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:00.363807Z","iopub.execute_input":"2024-07-17T18:20:00.364706Z","iopub.status.idle":"2024-07-17T18:20:00.371623Z","shell.execute_reply.started":"2024-07-17T18:20:00.364671Z","shell.execute_reply":"2024-07-17T18:20:00.370564Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Discriminator\n* Initial Convolution: The network starts with a convolution layer to process the initial input. This layer uses a stride of 2 to reduce dimensions and applies a leaky ReLU for non-linear activation.\n\n* Blocks: The discriminator sequentially applies several Block instances, each performing a convolution, normalization, and activation. These layers progressively increase the number of channels while reducing the spatial size (depending on the stride).\n\n* Final Convolution: A convolutional layer that maps the deep feature representation to a single output channel.\n* Sigmoid Activation: After the final layer, a sigmoid activation function is applied to squish the output to a [0,1] range, indicating the probability of the input being real.","metadata":{}},{"cell_type":"code","source":"class Discriminator(nn.Module):\n    def __init__(self, in_channels=3, features=[64, 128, 256, 512]):\n        super().__init__()\n        self.initial = nn.Sequential(\n            nn.Conv2d(\n                in_channels,\n                features[0],\n                kernel_size=4,\n                stride=2,\n                padding=1,\n                padding_mode=\"reflect\",\n            ),\n            nn.LeakyReLU(0.2, inplace=True),\n        )\n\n        layers = []\n        in_channels = features[0]\n        for feature in features[1:]:\n            layers.append(\n                Block(in_channels, feature, stride=1 if feature == features[-1] else 2)\n            )\n            in_channels = feature\n        layers.append(\n            nn.Conv2d(\n                in_channels,\n                1,\n                kernel_size=4,\n                stride=1,\n                padding=1,\n                padding_mode=\"reflect\",\n            )\n        )\n        # * unpack layers list into separate arguments\n        # layer in layers forms feed-forward NNs\n        self.model = nn.Sequential(*layers)\n\n    \n    # define how the input tensor x pass thru the NNs in forward:\n    # x -> first pass thru the initial layer defined above\n    # -> then thru the model layer(dynamically added based on the features list)\n    # -> sigmoid last layer to map output to 0~1 probability (fake~real)\n    def forward(self, x):\n        x = self.initial(x)\n        return torch.sigmoid(self.model(x))","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:02.307999Z","iopub.execute_input":"2024-07-17T18:20:02.308684Z","iopub.status.idle":"2024-07-17T18:20:02.318178Z","shell.execute_reply.started":"2024-07-17T18:20:02.308652Z","shell.execute_reply":"2024-07-17T18:20:02.317186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Test Discriminator\n\nmodel_dis_test = Discriminator()\n\n# creates random tensor x to simulates a batch of 2 images\ntest_input = torch.randn((2, 3, 256, 256))\n\n# Forward the test input through the model\nprint(model_dis_test(test_input).shape)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:03.724432Z","iopub.execute_input":"2024-07-17T18:20:03.725288Z","iopub.status.idle":"2024-07-17T18:20:03.937449Z","shell.execute_reply.started":"2024-07-17T18:20:03.725259Z","shell.execute_reply":"2024-07-17T18:20:03.936436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Define CNNBlock and ResidualBlock \n* **CNNBlock** is used to build both ***downsampling*** (standard convolution -> reduces the spatial dimensions while increasing feature depth to abstract higher-level features) and ***upsampling*** (transposed convolution -> increases the spatial dimensions while refining features to reconstruct detailed and high-quality images) layers within the Generator.\n* **ResidualBlock** adds input of the block to its output, mitigating the vanishing gradient problem ->  important for training deeper networks effectively; and enabling the network to learn identity mappings efficiently -> ensuring that essential information is not lost as data passes through multiple layers of transformations.","metadata":{}},{"cell_type":"code","source":"class CNNBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, down=True, use_act=True, **kwargs):\n        super().__init__()\n        \n        if down:\n            conv_layer = nn.Conv2d(in_channels, out_channels, padding_mode=\"reflect\", **kwargs)\n        else:\n            conv_layer = nn.ConvTranspose2d(in_channels, out_channels, **kwargs)\n        \n        if use_act:\n            act_layer = nn.ReLU(inplace=True)\n        else:\n            act_layer = nn.Identity()\n        \n        self.cnn = nn.Sequential(\n            conv_layer,\n            nn.InstanceNorm2d(out_channels),\n            act_layer,\n        )\n\n    def forward(self, x):\n        return self.cnn(x)\n\n\nclass ResidualBlock(nn.Module):\n    def __init__(self, channels):\n        super().__init__()\n        self.block = nn.Sequential(\n            CNNBlock(channels, channels, kernel_size=3, padding=1),\n            CNNBlock(channels, channels, use_act=False, kernel_size=3, padding=1),\n        )\n\n    def forward(self, x):\n        return x + self.block(x)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:06.044673Z","iopub.execute_input":"2024-07-17T18:20:06.045048Z","iopub.status.idle":"2024-07-17T18:20:06.054568Z","shell.execute_reply.started":"2024-07-17T18:20:06.045019Z","shell.execute_reply":"2024-07-17T18:20:06.053627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Generator\n* Initial Convolution: Processes the initial input to expand the feature space without changing spatial dimensions, using a 7x7 kernel size with a stride of 1 and padding set to 3. \n* Downsampling Blocks: Reduces the spatial dimensions while increasing the feature depth, using multiple CNNBlock instances each with a stride of 2. Halve dimensions and double feature channels.\n* Residual Blocks: Use multiple ResidualBlock instances to enhances feature representations without altering their spatial dimensions.\n* Upsampling Blocks: Restores the spatial dimensions to match the original input size while refining features, ussing CNNBlock instances configured for upsampling through transposed convolution. \n* Final Convolution: Reconstructs the final image from the refined feature maps, employing a large 7x7 convolutional layer to ensure detailed spatial capture. Use tanh activation function to map output to [-1,1]","metadata":{}},{"cell_type":"code","source":"class Generator(nn.Module):\n    def __init__(self, img_channels, num_features=64, num_residuals=9):\n        super().__init__()\n        self.initial = nn.Sequential(\n            nn.Conv2d(\n                img_channels,\n                num_features,\n                kernel_size=7,\n                stride=1,\n                padding=3,\n                padding_mode=\"reflect\",\n            ),\n            nn.InstanceNorm2d(num_features),\n            nn.ReLU(inplace=True),\n        )\n        self.down_blocks = nn.ModuleList(\n            [\n                CNNBlock(\n                    num_features, \n                    num_features * 2, \n                    kernel_size=3, \n                    stride=2, \n                    padding=1\n                ),\n                CNNBlock(\n                    num_features * 2,\n                    num_features * 4,\n                    kernel_size=3,\n                    stride=2,\n                    padding=1,\n                ),\n            ]\n        )\n        self.res_blocks = nn.Sequential(\n            *[ResidualBlock(num_features * 4) for _ in range(num_residuals)]\n        )\n        self.up_blocks = nn.ModuleList(\n            [\n                CNNBlock(\n                    num_features * 4,\n                    num_features * 2,\n                    down=False,\n                    kernel_size=3,\n                    stride=2,\n                    padding=1,\n                    output_padding=1,\n                ),\n                CNNBlock(\n                    num_features * 2,\n                    num_features * 1,\n                    down=False,\n                    kernel_size=3,\n                    stride=2,\n                    padding=1,\n                    output_padding=1,\n                ),\n            ]\n        )\n\n        self.last = nn.Conv2d(\n            num_features * 1,\n            img_channels,\n            kernel_size=7,\n            stride=1,\n            padding=3,\n            padding_mode=\"reflect\",\n        )\n\n    def forward(self, x):\n        x = self.initial(x)\n        for layer in self.down_blocks:\n            x = layer(x)\n        x = self.res_blocks(x)\n        for layer in self.up_blocks:\n            x = layer(x)\n        return torch.tanh(self.last(x))","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:07.235852Z","iopub.execute_input":"2024-07-17T18:20:07.236525Z","iopub.status.idle":"2024-07-17T18:20:07.248028Z","shell.execute_reply.started":"2024-07-17T18:20:07.236494Z","shell.execute_reply":"2024-07-17T18:20:07.247042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Test Generator \n\nimg_channels = 3\nimg_size = 256\n# creates random tensor x to simulates a batch of 2 images\ntest_input = torch.randn((2, img_channels, img_size, img_size))\n\nmodel_gen_test = Generator(img_channels, 9)\n\n# Forward the test input through the model\nprint(model_gen_test(test_input).shape)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:08.809785Z","iopub.execute_input":"2024-07-17T18:20:08.810169Z","iopub.status.idle":"2024-07-17T18:20:09.007674Z","shell.execute_reply.started":"2024-07-17T18:20:08.810138Z","shell.execute_reply":"2024-07-17T18:20:09.006678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check for available device\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(f\"Using device: {device}\")","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:20:09.058167Z","iopub.execute_input":"2024-07-17T18:20:09.058939Z","iopub.status.idle":"2024-07-17T18:20:09.086474Z","shell.execute_reply.started":"2024-07-17T18:20:09.058893Z","shell.execute_reply":"2024-07-17T18:20:09.085514Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch.utils.data import random_split\n\n# Define the size of the validation set\nval_size_monet = int(0.2 * len(MonetDataset))\ntrain_size_monet = len(MonetDataset) - val_size_monet\n\nval_size_photo = int(0.2 * len(PhotoDataset))\ntrain_size_photo = len(PhotoDataset) - val_size_photo\n\n\n# Split datasets into training and validation\ntrain_MonetDataset, val_MonetDataset = random_split(MonetDataset, [train_size_monet, val_size_monet])\ntrain_PhotoDataset, val_PhotoDataset = random_split(PhotoDataset, [train_size_photo, val_size_photo])\n\n# Create data loaders for training and validation datasets\ntrain_MonetLoader = DataLoader(train_MonetDataset, batch_size=16, shuffle=True, num_workers=2)\nval_MonetLoader = DataLoader(val_MonetDataset, batch_size=16, shuffle=False, num_workers=2)\n\ntrain_PhotoLoader = DataLoader(train_PhotoDataset, batch_size=16, shuffle=True, num_workers=2)\nval_PhotoLoader = DataLoader(val_PhotoDataset, batch_size=16, shuffle=False, num_workers=2)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:27:03.228314Z","iopub.execute_input":"2024-07-17T18:27:03.228689Z","iopub.status.idle":"2024-07-17T18:27:03.238130Z","shell.execute_reply.started":"2024-07-17T18:27:03.228652Z","shell.execute_reply":"2024-07-17T18:27:03.237131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"import torch.optim as optim\nfrom torch.cuda.amp import GradScaler, autocast\nfrom tqdm import tqdm\n\n\n# Initialize models\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nG_Photo2Monet = Generator(img_channels=3).to(device)\nG_Monet2Photo = Generator(img_channels=3).to(device)\nD_Photo = Discriminator(in_channels=3).to(device)\nD_Monet = Discriminator(in_channels=3).to(device)\n\n# Initialize optimizers\noptimizer_G_Photo2Monet = optim.Adam(G_Photo2Monet.parameters(), lr=1e-4, betas=(0.5, 0.5))\noptimizer_G_Monet2Photo = optim.Adam(G_Monet2Photo.parameters(), lr=1e-4, betas=(0.5, 0.5))\noptimizer_D_Photo = optim.Adam(D_Photo.parameters(), lr=1e-4, betas=(0.5, 0.5))\noptimizer_D_Monet = optim.Adam(D_Monet.parameters(), lr=1e-4, betas=(0.5, 0.5))\n\n# Loss functions\ncriterion_GAN = nn.MSELoss()\ncriterion_cycle = nn.L1Loss()\n\n# Mixed precision training\nscaler = GradScaler()\n\n# Hyperparameters\nlambda_cycle = 5\nnum_epochs = 160\nbatch_size = 16\n\n\n# Training loop\nfor epoch in range(num_epochs):\n    epoch_loss_G_Photo2Monet = 0\n    epoch_loss_G_Monet2Photo = 0\n    epoch_loss_D_Photo = 0\n    epoch_loss_D_Monet = 0\n    \n    for i, (real_photo, real_monet) in enumerate(zip(train_PhotoLoader, train_MonetLoader)):\n        real_photo = real_photo.to(device)\n        real_monet = real_monet.to(device)\n        batch_size = real_photo.size(0)\n\n        # Generate fake images\n        fake_monet = G_Photo2Monet(real_photo).detach()\n        fake_photo = G_Monet2Photo(real_monet).detach()\n\n        # Get discriminator output sizes\n        D_Photo_real = D_Photo(real_photo)\n        D_Monet_real = D_Monet(real_monet)\n        D_Photo_output_shape = D_Photo_real.shape\n        D_Monet_output_shape = D_Monet_real.shape\n\n        # Create labels with the correct shape\n        real_label_Photo = torch.ones(D_Photo_output_shape, dtype=torch.float, device=device)\n        fake_label_Photo = torch.zeros(D_Photo_output_shape, dtype=torch.float, device=device)\n        real_label_Monet = torch.ones(D_Monet_output_shape, dtype=torch.float, device=device)\n        fake_label_Monet = torch.zeros(D_Monet_output_shape, dtype=torch.float, device=device)\n\n        # ----------------------\n        # Train Discriminators\n        # ----------------------\n\n        with autocast():\n            # Discriminator D_Photo\n            D_Photo_real = D_Photo(real_photo)\n            D_Photo_fake = D_Photo(fake_photo)\n            loss_D_Photo_real = criterion_GAN(D_Photo_real, real_label_Photo)\n            loss_D_Photo_fake = criterion_GAN(D_Photo_fake, fake_label_Photo)\n            loss_D_Photo = (loss_D_Photo_real + loss_D_Photo_fake) * 0.5\n\n            # Discriminator D_Monet\n            D_Monet_real = D_Monet(real_monet)\n            D_Monet_fake = D_Monet(fake_monet)\n            loss_D_Monet_real = criterion_GAN(D_Monet_real, real_label_Monet)\n            loss_D_Monet_fake = criterion_GAN(D_Monet_fake, fake_label_Monet)\n            loss_D_Monet = (loss_D_Monet_real + loss_D_Monet_fake) * 0.5\n\n        optimizer_D_Photo.zero_grad()\n        scaler.scale(loss_D_Photo).backward()\n        scaler.step(optimizer_D_Photo)\n\n        optimizer_D_Monet.zero_grad()\n        scaler.scale(loss_D_Monet).backward()\n        scaler.step(optimizer_D_Monet)\n\n        # Accumulate discriminator loss\n        epoch_loss_D_Photo += loss_D_Photo.item()\n        epoch_loss_D_Monet += loss_D_Monet.item()\n\n        # ----------------------\n        # Train Generators\n        # ----------------------\n\n        with autocast():\n            # GAN loss\n            fake_monet = G_Photo2Monet(real_photo)\n            fake_photo = G_Monet2Photo(real_monet)\n            loss_GAN_Photo2Monet = criterion_GAN(D_Monet(fake_monet), real_label_Monet)\n            loss_GAN_Monet2Photo = criterion_GAN(D_Photo(fake_photo), real_label_Photo)\n\n            # Cycle loss\n            cycle_photo = G_Monet2Photo(fake_monet)\n            cycle_monet = G_Photo2Monet(fake_photo)\n            loss_cycle_photo = criterion_cycle(cycle_photo, real_photo)\n            loss_cycle_monet = criterion_cycle(cycle_monet, real_monet)\n\n            # Total generator loss\n            loss_G_Photo2Monet = loss_GAN_Photo2Monet + lambda_cycle * loss_cycle_photo\n            loss_G_Monet2Photo = loss_GAN_Monet2Photo + lambda_cycle * loss_cycle_monet\n\n        optimizer_G_Photo2Monet.zero_grad()\n        scaler.scale(loss_G_Photo2Monet).backward()\n        scaler.step(optimizer_G_Photo2Monet)\n\n        optimizer_G_Monet2Photo.zero_grad()\n        scaler.scale(loss_G_Monet2Photo).backward()\n        scaler.step(optimizer_G_Monet2Photo)\n\n        scaler.update()\n\n        # Accumulate generator loss\n        epoch_loss_G_Photo2Monet += loss_G_Photo2Monet.item()\n        epoch_loss_G_Monet2Photo += loss_G_Monet2Photo.item()\n\n    # Calculate average losses for the epoch\n    epoch_loss_G_Photo2Monet /= len(train_PhotoLoader)\n    epoch_loss_G_Monet2Photo /= len(train_MonetLoader)\n    epoch_loss_D_Photo /= len(train_PhotoLoader)\n    epoch_loss_D_Monet /= len(train_MonetLoader)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}] - \"\n          f\"loss_G_Photo2Monet: {epoch_loss_G_Photo2Monet:.4f}, \"\n          f\"loss_G_Monet2Photo: {epoch_loss_G_Monet2Photo:.4f}, \"\n          f\"loss_D_Photo: {epoch_loss_D_Photo:.4f}, \"\n          f\"loss_D_Monet: {epoch_loss_D_Monet:.4f}\")\n\n    # Validation and display results every 10 epochs\n    if (epoch + 1) % 10 == 0:\n        with torch.no_grad():\n            G_Photo2Monet.eval()\n            example_photo = next(iter(val_PhotoLoader)).to(device)\n            fake_monet = G_Photo2Monet(example_photo)\n\n            # Display images\n            plt.figure(figsize=(12, 6))\n            for i in range(4):\n                plt.subplot(2, 4, i + 1)\n                plt.imshow(((example_photo[i].cpu().numpy().transpose(1, 2, 0) + 1) / 2))\n                plt.axis('off')\n                plt.subplot(2, 4, i + 5)\n                plt.imshow(((fake_monet[i].cpu().numpy().transpose(1, 2, 0) + 1) / 2))\n                plt.axis('off')\n            plt.show()\n\n            G_Photo2Monet.train()\n\n# Save final model\nfinal_model_path = 'final_model.pth'\ntorch.save({\n    'G_Photo2Monet': G_Photo2Monet.state_dict(),\n    'G_Monet2Photo': G_Monet2Photo.state_dict(),\n    'D_Photo': D_Photo.state_dict(),\n    'D_Monet': D_Monet.state_dict(),\n    'optimizer_G_Photo2Monet': optimizer_G_Photo2Monet.state_dict(),\n    'optimizer_G_Monet2Photo': optimizer_G_Monet2Photo.state_dict(),\n    'optimizer_D_Photo': optimizer_D_Photo.state_dict(),\n    'optimizer_D_Monet': optimizer_D_Monet.state_dict(),\n}, final_model_path)","metadata":{"execution":{"iopub.status.busy":"2024-07-17T18:27:50.940443Z","iopub.execute_input":"2024-07-17T18:27:50.940799Z","iopub.status.idle":"2024-07-17T18:47:57.559062Z","shell.execute_reply.started":"2024-07-17T18:27:50.940770Z","shell.execute_reply":"2024-07-17T18:47:57.558116Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inferencing ","metadata":{}},{"cell_type":"code","source":"import shutil\nfrom zipfile import ZipFile\n\n# Load the trained model\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nmodel_path = '/kaggle/working/final_model.pth'\ncheckpoint = torch.load(model_path)\n\n\nG_Photo2Monet = Generator(img_channels=3).to(device)\nG_Photo2Monet.load_state_dict(checkpoint['G_Photo2Monet'])\nG_Photo2Monet.eval()\n\ntest_loader = DataLoader(PhotoDataset, batch_size=1, shuffle=False)\n\n\n# Create a directory to save the generated images\nos.makedirs('../tmp/images', exist_ok=True)\n\n# Generate Monet-style images and save them\nwith torch.no_grad():\n    for i, img in enumerate(test_loader):\n        img = img.to(device)\n        fake_monet = G_Photo2Monet(img)[0].cpu().permute(1, 2, 0).numpy() * 0.5 + 0.5\n        im = Image.fromarray((fake_monet * 255).astype(np.uint8))\n        im.save(f\"../tmp/images/{i}.jpg\")\n\n# Zip the generated images\noutput_zip_path = \"/kaggle/working/images.zip\"\nwith ZipFile(output_zip_path, 'w') as zipf:\n    for root, _, files in os.walk('../tmp/images'):\n        for file in files:\n            zipf.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), '../tmp/images'))\n\nprint(f\"Submission file created at {output_zip_path}\")","metadata":{"execution":{"iopub.status.busy":"2024-07-17T19:15:05.916314Z","iopub.execute_input":"2024-07-17T19:15:05.917269Z","iopub.status.idle":"2024-07-17T19:15:46.628164Z","shell.execute_reply.started":"2024-07-17T19:15:05.917225Z","shell.execute_reply":"2024-07-17T19:15:46.627046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}