{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":15062,"databundleVersionId":545987,"sourceType":"competition"}],"dockerImageVersionId":30616,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**Importing liraries**","metadata":{"_uuid":"eafc1116-3601-430c-8e37-baae7851b2e6","_cell_guid":"11c912e2-7589-4b5c-8d9a-071ae380dd9f","trusted":true}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport matplotlib.pyplot as plt\n\nimport torch\nfrom torch import nn, optim\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms\nfrom torchvision.utils import save_image\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\n\nfrom tqdm import tqdm_notebook as tqdm","metadata":{"_uuid":"b8860b3c-05c3-4768-a5a2-1b18b3ac33f8","_cell_guid":"ce641211-e372-4660-931a-8424db313c9d","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:04:10.341871Z","iopub.execute_input":"2023-12-20T15:04:10.342127Z","iopub.status.idle":"2023-12-20T15:04:15.019491Z","shell.execute_reply.started":"2023-12-20T15:04:10.342102Z","shell.execute_reply":"2023-12-20T15:04:15.018638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Initializing Generator and Discriminator**","metadata":{"_uuid":"5c779928-3ae5-46f9-8a4c-49ee45c0cccf","_cell_guid":"066c0511-bcfe-4697-9b67-10d5ad40054f","trusted":true}},{"cell_type":"code","source":"import torch.nn as nn\n\n# Generator class\nclass Generator(nn.Module):\n    def __init__(self, nz=128, channels=3):\n        super(Generator, self).__init__()\n        \n        # Initialize input and output channels\n        self.nz = nz\n        self.channels = channels\n        \n        # Define a helper function to create a convolutional layer\n        def convlayer(n_input, n_output, k_size=4, stride=2, padding=0):\n            block = [\n                nn.ConvTranspose2d(n_input, n_output, kernel_size=k_size, stride=stride, padding=padding, bias=False),\n                nn.BatchNorm2d(n_output),\n                nn.ReLU(inplace=True),\n            ]\n            return block\n\n        # Build the model\n        self.model = nn.Sequential(\n            *convlayer(self.nz, 1024, 4, 1, 0), # First layer: Fully connected layer achieved through convolution.\n            *convlayer(1024, 512, 4, 2, 1),    # Second layer\n            *convlayer(512, 256, 4, 2, 1),     # Third layer\n            *convlayer(256, 128, 4, 2, 1),     # Fourth layer\n            *convlayer(128, 64, 4, 2, 1),      # Fifth layer\n            nn.ConvTranspose2d(64, self.channels, 3, 1, 1), # Final layer to produce output with desired number of channels\n            nn.Tanh() # Tanh activation function for the output\n        )\n\n    # Forward pass through the generator\n    def forward(self, z):\n        # Reshape input noise vector z to a batch of images\n        z = z.view(-1, self.nz, 1, 1)\n        img = self.model(z)\n        return img\n\n# Discriminator class\nclass Discriminator(nn.Module):\n    def __init__(self, channels=3):\n        super(Discriminator, self).__init__()\n        \n        # Initialize input channels\n        self.channels = channels\n\n        # Define a helper function to create a convolutional layer\n        def convlayer(n_input, n_output, k_size=4, stride=2, padding=0, bn=False):\n            block = [nn.Conv2d(n_input, n_output, kernel_size=k_size, stride=stride, padding=padding, bias=False)]\n            if bn:\n                block.append(nn.BatchNorm2d(n_output))\n            block.append(nn.LeakyReLU(0.2, inplace=True))\n            return block\n\n        # Build the model\n        self.model = nn.Sequential(\n            *convlayer(self.channels, 32, 4, 2, 1),  # First layer\n            *convlayer(32, 64, 4, 2, 1),             # Second layer\n            *convlayer(64, 128, 4, 2, 1, bn=True),   # Third layer with batch normalization\n            *convlayer(128, 256, 4, 2, 1, bn=True),  # Fourth layer with batch normalization\n            nn.Conv2d(256, 1, 4, 1, 0, bias=False),  # Final layer: Fully connected layer through convolution\n        )\n\n    # Forward pass through the discriminator\n    def forward(self, imgs):\n        # Pass the images through the model\n        out = self.model(imgs)\n        # Flatten the output\n        return out.view(-1, 1)","metadata":{"_uuid":"f1408077-c679-441b-870a-0e62a327de85","_cell_guid":"d0ce7d0f-5ea6-4355-8d00-b56fc73d95fe","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:04:15.021349Z","iopub.execute_input":"2023-12-20T15:04:15.021821Z","iopub.status.idle":"2023-12-20T15:04:15.039107Z","shell.execute_reply.started":"2023-12-20T15:04:15.021785Z","shell.execute_reply":"2023-12-20T15:04:15.038149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Importing Dataset**","metadata":{"_uuid":"31d6acef-692a-456a-8cfd-9102b06b4d90","_cell_guid":"da683f45-cce6-48cd-b3f5-6cf2b143b6b2","trusted":true}},{"cell_type":"code","source":"import zipfile  # Import the zipfile module to work with ZIP files\nimport os       # Import the os module for interacting with the operating system\n\n# Path to the zip file\nzip_path = '/kaggle/input/generative-dog-images/all-dogs.zip'  # Define the path to the ZIP file\n\n# Extract the zip file\nwith zipfile.ZipFile(zip_path, 'r') as zip_ref:  # Open the ZIP file in read mode\n    zip_ref.extractall('all-dogs')  # Extract all the contents of the ZIP file to a folder named 'all-dogs'\n\n# Now, you can list the contents of the extracted folder\nextracted_files = os.listdir('all-dogs')  # List all files in the newly created 'all-dogs' directory\nprint(extracted_files)  # Print the list of files","metadata":{"_uuid":"946752fc-d537-435a-8603-664aa9183f1d","_cell_guid":"88c4ce94-c939-4279-8239-49d7ce0a0acf","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:04:15.040485Z","iopub.execute_input":"2023-12-20T15:04:15.040815Z","iopub.status.idle":"2023-12-20T15:04:27.880753Z","shell.execute_reply.started":"2023-12-20T15:04:15.040786Z","shell.execute_reply":"2023-12-20T15:04:27.879797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Training Parameters**","metadata":{"_uuid":"13b68522-f51f-4ed9-bfc0-b28f81297bfe","_cell_guid":"f903c6b9-5b0a-4118-880f-e45360461c5c","trusted":true}},{"cell_type":"code","source":"# Training parameters\nbatch_size = 32  # Batch size for training. Determines how many samples are processed before the model is updated.\nlr = 0.001       # Learning rate. Determines the step size at each iteration while moving toward a minimum of a loss function.\nbeta1 = 0.5      # Beta1 hyperparameter for Adam optimizer. It's the exponential decay rate for the first moment estimates.\nepochs = 1     # Number of epochs. Determines how many times the learning algorithm will work through the entire training dataset.\n\n# Labeling for discriminator\nreal_label = 0.5  # Label for real images. Typically set to 1, but can be smoothed to prevent overconfidence.\nfake_label = 0    # Label for fake (generated) images.\n\n# Size of the latent vector (noise vector)\nnz = 128  # Dimension of the latent z vector. This is the size of the generator input.\n\n# Setting up the device for training\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")  \n# This will use CUDA (GPU acceleration) if available, otherwise CPU.","metadata":{"_uuid":"0f3e7f4f-560c-4dae-883b-6d1986f32905","_cell_guid":"5d07cae0-ea91-4102-804d-1fba2c2fafde","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:04:27.881963Z","iopub.execute_input":"2023-12-20T15:04:27.882736Z","iopub.status.idle":"2023-12-20T15:04:27.957451Z","shell.execute_reply.started":"2023-12-20T15:04:27.882707Z","shell.execute_reply":"2023-12-20T15:04:27.956155Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Loading dataset and applying preprocessing**","metadata":{"_uuid":"a612189c-bf2c-425e-92f4-c59af17d5371","_cell_guid":"63bba3cc-dfbb-42ec-ac8f-86c94ff340d9","trusted":true}},{"cell_type":"code","source":"from torch.utils.data import Dataset\nfrom PIL import Image\nimport os\n\nclass DogDataset(Dataset):\n    def __init__(self, img_dir, transform1=None, transform2=None):\n        # Constructor of the DogDataset class\n        self.img_dir = img_dir  # Directory where the images are stored\n        self.img_names = os.listdir(img_dir)  # List all files in the image directory\n        self.transform1 = transform1  # First transformation to apply to the images\n        self.transform2 = transform2  # Second transformation to apply to the images\n\n        # Load and optionally transform all images in the directory\n        self.imgs = []  # List to store the loaded images\n        for img_name in self.img_names:\n            # Open each image\n            img = Image.open(os.path.join(img_dir, img_name))\n\n            # Apply the first transformation if it is defined\n            if self.transform1 is not None:\n                img = self.transform1(img)\n\n            # Add the (transformed) image to the list\n            self.imgs.append(img)\n\n    def __getitem__(self, index):\n        # Method to get an image by index\n        img = self.imgs[index]  # Retrieve the image from the list\n\n        # Apply the second transformation if it is defined\n        if self.transform2 is not None:\n            img = self.transform2(img)\n\n        # Return the possibly transformed image\n        return img\n\n    def __len__(self):\n        # Method to get the total number of images\n        return len(self.imgs)  # Return the length of the image list","metadata":{"_uuid":"83fa5868-5433-45b2-b308-2a4533d368c1","_cell_guid":"3355abd6-d04b-4f71-b20b-591d0c54a62f","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:04:27.960453Z","iopub.execute_input":"2023-12-20T15:04:27.960802Z","iopub.status.idle":"2023-12-20T15:04:27.980247Z","shell.execute_reply.started":"2023-12-20T15:04:27.960775Z","shell.execute_reply":"2023-12-20T15:04:27.979197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Preprocessing code to apply to dataset**","metadata":{"_uuid":"af356a26-f3c4-4872-8fb6-17cb815884d1","_cell_guid":"e9253df9-742f-476c-a134-aaeef35d2375","trusted":true}},{"cell_type":"code","source":"# Image Preprocessing\ntransform1 = transforms.Compose([\n    transforms.Resize(64),        # Resize the image to 64x64 pixels\n    transforms.CenterCrop(64)     # Crop the image to a 64x64 square centered on the middle of the image\n])\n\n# Data augmentation and conversion to tensor\nrandom_transforms = [transforms.RandomRotation(degrees=5)]  # Define random rotation transformation\ntransform2 = transforms.Compose([\n    transforms.RandomHorizontalFlip(p=0.5),  # Randomly flip the images horizontally with a probability of 0.5\n    transforms.RandomApply(random_transforms, p=0.3),  # Apply the random rotation with a probability of 0.3\n    transforms.ToTensor(),  # Convert the images to PyTorch tensors\n    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # Normalize the tensors\n])\n\n# Creating the dataset\ntrain_dataset = DogDataset(img_dir='/kaggle/working/all-dogs/all-dogs/',\n                           transform1=transform1,\n                           transform2=transform2)\n\n# DataLoader for the dataset\ntrain_loader = DataLoader(dataset=train_dataset,\n                          batch_size=batch_size,  # Load data in batches\n                          shuffle=True,           # Shuffle the data to reduce model overfitting\n                          num_workers=4)          # Number of subprocesses to use for data loading\n\n# Loading a batch of images\nimgs = next(iter(train_loader))  # Get the first batch of images\nimgs = imgs.numpy().transpose(0, 2, 3, 1)  # Convert the images from PyTorch tensor to numpy array and change the shape","metadata":{"_uuid":"704000e4-f70f-462c-81c4-e1f8f48e3c34","_cell_guid":"2e1f6517-fb82-499f-a244-d2a77b506d37","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:04:27.981436Z","iopub.execute_input":"2023-12-20T15:04:27.981781Z","iopub.status.idle":"2023-12-20T15:05:15.250268Z","shell.execute_reply.started":"2023-12-20T15:04:27.981751Z","shell.execute_reply":"2023-12-20T15:05:15.249113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Preview of images from the dataset**","metadata":{"_uuid":"cfc7bfc4-0e53-4457-8b41-8404edc99d37","_cell_guid":"a9b512a0-b2df-4e96-84cd-e8bafa72efe4","trusted":true}},{"cell_type":"code","source":"fig = plt.figure(figsize=(25, 16))  # Create a new figure for plotting, with a specified size (width 25 inches, height 16 inches)\n\n# Loop through the images in the batch\nfor ii, img in enumerate(imgs):\n    # Create a subplot for each image\n    ax = fig.add_subplot(4, 8, ii + 1, xticks=[], yticks=[])  \n    # Adds a subplot at the ii-th position in a grid that's 4 rows by 8 columns.\n    # 'xticks=[]' and 'yticks=[]' remove the x and y tick marks from the plots.\n\n    # Display the image\n    plt.imshow((img+1)/2)  \n    # The images are normalized between -1 and 1, so (img+1)/2 rescales them to the range [0,1].\n    # This is necessary for matplotlib to display them correctly.","metadata":{"_uuid":"a4010c0b-37fa-44ee-aca4-126991175259","_cell_guid":"a03dbf1f-6faa-4543-981e-4eaf2f0b859f","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:05:15.252063Z","iopub.execute_input":"2023-12-20T15:05:15.252496Z","iopub.status.idle":"2023-12-20T15:05:17.085005Z","shell.execute_reply.started":"2023-12-20T15:05:15.252429Z","shell.execute_reply":"2023-12-20T15:05:17.083963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Creating both networks and applying an optimizer to both**","metadata":{"_uuid":"7ba6e83c-38fb-4a92-a231-f12ad0d040db","_cell_guid":"02ee4d0d-3079-4b35-b658-d929b61d96dd","trusted":true}},{"cell_type":"code","source":"# Creating the Generator network\nnetG = Generator(nz).to(device)  \n# Initialize the Generator with the specified noise vector size (nz).\n# The .to(device) method moves the model to a GPU if available, otherwise to the CPU.\n\n# Creating the Discriminator network\nnetD = Discriminator().to(device)\n# Initialize the Discriminator.\n# The .to(device) method moves the model to a GPU if available, otherwise to the CPU.\n\n# Setting up the loss function\ncriterion = nn.BCELoss()  \n# Binary Cross Entropy Loss is used, typical for GANs as the discriminator outputs a binary classification (real or fake).\n\n# Setting up the Optimizer for the Discriminator\noptimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))  \n# Use the Adam optimizer for the discriminator.\n# 'lr' is the learning rate, and 'betas' are the hyperparameters for the Adam optimizer.\n\n# Setting up the Optimizer for the Generator\noptimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))  \n# Use the Adam optimizer for the generator, similar to the discriminator.\n\n# Creating a fixed noise vector for evaluating the Generator's performance\nfixed_noise = torch.randn(25, nz, 1, 1, device=device)  \n# Generate a fixed random noise vector with 25 samples, each of size 'nz'.\n# The noise vector is used to evaluate the generator's output.\n# The .to(device) method moves the noise vector to the same device as the model.","metadata":{"_uuid":"6351f77e-5676-4e7b-987e-dd0c704c537a","_cell_guid":"390824f6-4870-43b2-b17f-61683da5c322","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:05:17.086253Z","iopub.execute_input":"2023-12-20T15:05:17.086569Z","iopub.status.idle":"2023-12-20T15:05:20.519213Z","shell.execute_reply.started":"2023-12-20T15:05:17.086542Z","shell.execute_reply":"2023-12-20T15:05:20.518420Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Generate an image from a random noisy input**","metadata":{"_uuid":"488c0a87-1580-4265-b657-95159a72243d","_cell_guid":"c067cfa9-ae6b-495c-92ef-4580869f442e","trusted":true}},{"cell_type":"code","source":"def show_generated_img():\n    # Generate a random noise vector\n    noise = torch.randn(1, nz, 1, 1, device=device)  \n    # Create a random noise vector with the same size as the generator's input.\n    # The noise vector has dimensions [1, nz, 1, 1], where 1 is the batch size.\n\n    # Generate an image from the noise vector using the generator network\n    gen_image = netG(noise).to(\"cpu\").clone().detach().squeeze(0)  \n    # Pass the noise through the generator to create an image.\n    # .to(\"cpu\") moves the image to the CPU for visualization.\n    # .clone().detach() creates a copy of the image tensor and detaches it from the current computation graph.\n    # .squeeze(0) removes the first dimension (batch dimension) to make it suitable for displaying.\n\n    # Convert the image tensor to a numpy array and adjust its dimensions for plotting\n    gen_image = gen_image.numpy().transpose(1, 2, 0)  \n    # Convert the PyTorch tensor to a numpy array.\n    # Transpose the dimensions from [channels, height, width] to [height, width, channels] for displaying.\n\n    # Display the image\n    plt.imshow((gen_image+1)/2)  \n    # The images are normalized between -1 and 1, so (gen_image+1)/2 rescales them to the range [0,1].\n    # This rescaling is necessary for matplotlib to display the images correctly.\n\n    plt.show()  # Show the image","metadata":{"_uuid":"e4e02edb-9427-4ed7-b78e-d92a46b641b9","_cell_guid":"02cd977d-a915-4876-a7cb-b4fe84664355","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:05:20.520505Z","iopub.execute_input":"2023-12-20T15:05:20.520880Z","iopub.status.idle":"2023-12-20T15:05:20.527860Z","shell.execute_reply.started":"2023-12-20T15:05:20.520847Z","shell.execute_reply":"2023-12-20T15:05:20.526950Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Training code**","metadata":{"_uuid":"dcfa9d1f-e8ca-455b-8379-4893dc5c0f12","_cell_guid":"ccec380c-ae3c-4edf-b6a7-9872d470916d","trusted":true}},{"cell_type":"code","source":"# Loop over all epochs\nfor epoch in range(epochs):\n    \n    # Loop over the batches of images\n    for ii, real_images in tqdm(enumerate(train_loader), total=len(train_loader)):\n        # Update Discriminator network:\n        # ----------------------------\n        netD.zero_grad()  # Reset gradients for the discriminator\n\n        # Move real images to the appropriate device (GPU or CPU)\n        real_images = real_images.to(device)\n\n        # Create labels for real images\n        batch_size = real_images.size(0)\n        labels = torch.full((batch_size, 1), real_label, device=device)\n\n        # Forward pass real images through D\n        outputR = netD(real_images)\n\n        # Generate fake images with G\n        noise = torch.randn(batch_size, nz, 1, 1, device=device)\n        fake = netG(noise)\n\n        # Forward pass fake images through D\n        outputF = netD(fake.detach())\n\n        # Calculate loss for D and perform backward pass\n        errD = (torch.mean((outputR - torch.mean(outputF) - labels) ** 2) + \n                torch.mean((outputF - torch.mean(outputR) + labels) ** 2)) / 2\n        errD.backward(retain_graph=True)\n        optimizerD.step()\n\n        # Update Generator network:\n        # -------------------------\n        netG.zero_grad()  # Reset gradients for the generator\n\n        # Forward pass fake images through D again\n        outputF = netD(fake)\n\n        # Calculate loss for G and perform backward pass\n        errG = (torch.mean((outputR - torch.mean(outputF) + labels) ** 2) +\n                torch.mean((outputF - torch.mean(outputR) - labels) ** 2)) / 2\n        errG.backward()\n        optimizerG.step()\n\n        # Print statistics\n        if (ii+1) % (len(train_loader)//2) == 0:\n            print('[%d/%d][%d/%d] Loss_D: %.4f Loss_G: %.4f'\n                  % (epoch + 1, epochs, ii+1, len(train_loader),\n                     errD.item(), errG.item()))\n\n    # Show a generated image after each epoch\n    show_generated_img()","metadata":{"_uuid":"893576d0-57b4-446a-a55e-7122adbb47d8","_cell_guid":"bfb2e170-be97-4188-b5e4-f6c58cef59b8","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:05:20.529455Z","iopub.execute_input":"2023-12-20T15:05:20.529843Z","iopub.status.idle":"2023-12-20T15:06:02.127769Z","shell.execute_reply.started":"2023-12-20T15:05:20.529812Z","shell.execute_reply":"2023-12-20T15:06:02.126773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Save both networks**","metadata":{"_uuid":"27951677-4e59-484d-a407-158d50da3263","_cell_guid":"474beea2-3222-44d9-b4d5-30fce5783940","trusted":true}},{"cell_type":"code","source":"# Save the Generator's state\ntorch.save(netG.state_dict(), 'generator.pth')  \n# 'netG.state_dict()' retrieves the state (weights and biases) of the generator network (netG).\n# This state is saved to a file named 'generator.pth'.\n# The '.pth' extension is commonly used for PyTorch model files.\n\n# Save the Discriminator's state\ntorch.save(netD.state_dict(), 'discriminator.pth')  \n# 'netD.state_dict()' retrieves the state (weights and biases) of the discriminator network (netD).\n# This state is saved to a file named 'discriminator.pth'.\n# The '.pth' extension is commonly used for PyTorch model files.","metadata":{"_uuid":"8be7cba6-2ff9-48c5-8184-ea7d2710c836","_cell_guid":"39aae3d8-89ae-4dd9-809c-05a042b1a07d","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:06:02.129359Z","iopub.execute_input":"2023-12-20T15:06:02.129759Z","iopub.status.idle":"2023-12-20T15:06:02.246314Z","shell.execute_reply.started":"2023-12-20T15:06:02.129722Z","shell.execute_reply":"2023-12-20T15:06:02.245516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Generate a random set of images**","metadata":{"_uuid":"8fa35ab8-8a88-42f3-8ef1-fe8fbf06419a","_cell_guid":"77a15eb5-56df-4560-b8ba-a38523f97a9c","trusted":true}},{"cell_type":"code","source":"# Generate random noise vectors\ngen_z = torch.randn(32, nz, 1, 1, device=device)  \n# This creates a batch of 32 random noise vectors, each of size 'nz'.\n# The noise vectors are the input to the generator.\n# The dimensions of the tensor are [batch_size, nz, 1, 1].\n# 'device=device' ensures that the operation is performed on the same device (GPU/CPU) as the model.\n\n# Generate images from the noise vectors using the generator\ngen_images = (netG(gen_z).to(\"cpu\").clone().detach() + 1) / 2\n# 'netG(gen_z)' passes the noise vectors through the generator to produce a batch of images.\n# '.to(\"cpu\")' moves the generated images to the CPU. This is necessary for converting them into numpy arrays later.\n# '.clone().detach()' creates a copy of the images and detaches them from the current computation graph.\n# '(... + 1) / 2' rescales the pixel values from the range [-1, 1] to [0, 1].\n# This rescaling is necessary because the generator often uses Tanh activation in the output layer, resulting in outputs in the range [-1, 1].\n\n# Convert the images to numpy arrays for visualization\ngen_images = gen_images.numpy().transpose(0, 2, 3, 1)  \n# '.numpy()' converts the tensor of generated images to a numpy array.\n# '.transpose(0, 2, 3, 1)' changes the shape of the array from [batch, channels, height, width] to [batch, height, width, channels].\n# This transposition is required for visualization purposes, as most visualization libraries expect the channel dimension to be last.","metadata":{"_uuid":"c6e9e897-7145-488e-b838-677b17b153a0","_cell_guid":"31128fbf-b341-4309-b75c-abb95aad003b","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:06:02.247510Z","iopub.execute_input":"2023-12-20T15:06:02.247819Z","iopub.status.idle":"2023-12-20T15:06:02.267420Z","shell.execute_reply.started":"2023-12-20T15:06:02.247792Z","shell.execute_reply":"2023-12-20T15:06:02.266680Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Display the randomly generated images**","metadata":{"_uuid":"4e625af1-c479-4557-9645-aba14487b82f","_cell_guid":"5cc0fe31-269f-4d27-9dc7-9fb76845567c","trusted":true}},{"cell_type":"code","source":"# Create a figure for plotting the images\nfig = plt.figure(figsize=(25, 16))  \n# 'figsize=(25, 16)' sets the size of the figure to 25 inches wide and 16 inches tall.\n# This large size helps in better visualization of multiple images.\n\n# Loop through the generated images\nfor ii, img in enumerate(gen_images):\n    # Add a subplot for each image in the figure\n    ax = fig.add_subplot(4, 8, ii + 1, xticks=[], yticks=[])  \n    # '4, 8' means the grid will have 4 rows and 8 columns.\n    # 'ii + 1' is the position of the subplot in the grid.\n    # 'xticks=[]' and 'yticks=[]' remove the x and y axis tick marks for each subplot.\n\n    # Display the image in the subplot\n    plt.imshow(img)  \n    # 'plt.imshow' displays the image. 'img' is the current image in the loop.\n\n# Show the complete figure with all subplots\nplt.show()  \n# 'plt.show()' renders the whole figure. It should be called after all subplots are added.","metadata":{"_uuid":"c70edae5-d72c-49ff-9fc9-926b7302bfef","_cell_guid":"bf7528ed-26de-4c41-8c17-721d6e3ec6b2","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:06:02.268450Z","iopub.execute_input":"2023-12-20T15:06:02.268737Z","iopub.status.idle":"2023-12-20T15:06:04.018560Z","shell.execute_reply.started":"2023-12-20T15:06:02.268713Z","shell.execute_reply":"2023-12-20T15:06:04.017699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Save generated images into a zip file**","metadata":{"_uuid":"ab351989-7f75-4605-a960-9db164c04c3b","_cell_guid":"56f0d819-5396-4286-9e48-82f49709f50f","trusted":true}},{"cell_type":"code","source":"# Check if the output directory exists, if not, create it\nif not os.path.exists('../output_images'):\n    os.mkdir('../output_images')  \n    # '../output_images' is the directory where generated images will be saved.\n    # 'os.mkdir' creates the directory if it does not already exist.\n\n# Define batch size for image generation and total number of images to generate\nim_batch_size = 50  # Number of images to generate in each batch.\nn_images = 10000    # Total number of images to generate.\n\n# Generate and save images in batches\nfor i_batch in range(0, n_images, im_batch_size):\n    # Generate a batch of noise vectors\n    gen_z = torch.randn(im_batch_size, nz, 1, 1, device=device)  \n    # 'torch.randn' generates a tensor of random values from a normal distribution.\n    # The tensor has dimensions [im_batch_size, nz, 1, 1].\n\n    # Generate images from the noise vectors\n    gen_images = (netG(gen_z) + 1) / 2  \n    # 'netG(gen_z)' generates a batch of images.\n    # '(... + 1) / 2' rescales the images from [-1, 1] to [0, 1].\n\n    # Prepare images for saving\n    images = gen_images.to(\"cpu\").clone().detach()  \n    # Move images to CPU and detach from computation graph.\n    images = images.numpy().transpose(0, 2, 3, 1)  \n    # Convert to numpy array and change shape from [batch, channels, height, width] to [batch, height, width, channels].\n\n    # Save each image in the batch to disk\n    for i_image in range(gen_images.size(0)):\n        # Construct the file path\n        image_path = os.path.join('../output_images', f'image_{i_batch+i_image:05d}.png')  \n        # 'f'image_{i_batch+i_image:05d}.png'' formats the filename with leading zeros.\n\n        # Save the image\n        save_image(gen_images[i_image, :, :, :], image_path)  \n        # 'save_image' saves the individual image to the specified path.\n\n# Compress the output directory into a ZIP file\n\n#shutil.make_archive('images', 'zip', '../output_images')  \n\n# 'shutil.make_archive' creates a ZIP archive.\n# 'images' is the name of the output ZIP file.\n# 'zip' is the format of the archive.\n# '../output_images' is the directory to be archived.","metadata":{"_uuid":"17a68e68-cd24-4c66-837b-ca65e03677f2","_cell_guid":"81f86d07-d7d3-430c-88db-144c7851914d","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-12-20T15:06:04.022167Z","iopub.execute_input":"2023-12-20T15:06:04.022849Z","iopub.status.idle":"2023-12-20T15:06:25.838647Z","shell.execute_reply.started":"2023-12-20T15:06:04.022810Z","shell.execute_reply":"2023-12-20T15:06:25.837418Z"},"trusted":true},"execution_count":null,"outputs":[]}]}