{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"![](https://i.postimg.cc/vZDMpL4x/Screenshot-2025-05-20-114410.png)","metadata":{}},{"cell_type":"markdown","source":"# Youtube Video Link-->> https://youtu.be/H9Va1APdbRI","metadata":{}},{"cell_type":"markdown","source":"# Importing Required Libraries 📦","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader\nimport matplotlib.pyplot as plt\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:31.094400Z","iopub.execute_input":"2025-06-29T14:21:31.094971Z","iopub.status.idle":"2025-06-29T14:21:38.150896Z","shell.execute_reply.started":"2025-06-29T14:21:31.094946Z","shell.execute_reply":"2025-06-29T14:21:38.150107Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Hyperparameters Settings","metadata":{}},{"cell_type":"code","source":"# ----- Settings -----\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nlatent_dim = 20\nnum_classes = 10\nbatch_size = 128\nimg_size = 28\nimg_channels = 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:38.151658Z","iopub.execute_input":"2025-06-29T14:21:38.151998Z","iopub.status.idle":"2025-06-29T14:21:38.216638Z","shell.execute_reply.started":"2025-06-29T14:21:38.151972Z","shell.execute_reply":"2025-06-29T14:21:38.216006Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Getting Dataset","metadata":{}},{"cell_type":"code","source":"# ----- Data -----\ntransform = transforms.ToTensor()\ntrain_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:38.218332Z","iopub.execute_input":"2025-06-29T14:21:38.218602Z","iopub.status.idle":"2025-06-29T14:21:39.834319Z","shell.execute_reply.started":"2025-06-29T14:21:38.218577Z","shell.execute_reply":"2025-06-29T14:21:39.833612Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def one_hot(labels, num_classes=10):\n    return F.one_hot(labels, num_classes).float()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:39.835008Z","iopub.execute_input":"2025-06-29T14:21:39.835231Z","iopub.status.idle":"2025-06-29T14:21:39.838876Z","shell.execute_reply.started":"2025-06-29T14:21:39.835204Z","shell.execute_reply":"2025-06-29T14:21:39.838184Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"> Simple autoencoders, while useful for feature extraction and compression, have limitations for generative modeling because their latent space is deterministic and can lead to blurry or distorted reconstructions. Variational autoencoders (VAEs) address these limitations by introducing a probabilistic approach, encoding the input data into a distribution rather than a single point in the latent space. This allows VAEs to sample and generate diverse and novel data, making them well-suited for tasks like image generation and style transfer","metadata":{}},{"cell_type":"markdown","source":"# Conditional VAE Model","metadata":{}},{"cell_type":"code","source":"\n# ----- CVAE Model -----\nclass CVAE(nn.Module):\n    def __init__(self, latent_dim, num_classes):\n        super(CVAE, self).__init__()\n        self.latent_dim = latent_dim\n        self.num_classes = num_classes\n\n        # Encoder\n        self.encoder = nn.Sequential(\n            nn.Linear(28*28 + num_classes, 400),\n            nn.ReLU()\n        )\n        self.fc_mu = nn.Linear(400, latent_dim)\n        self.fc_logvar = nn.Linear(400, latent_dim)\n\n        # Decoder\n        self.decoder_input = nn.Linear(latent_dim + num_classes, 400)\n        self.decoder = nn.Sequential(\n            nn.ReLU(),\n            nn.Linear(400, 28*28),\n            # Sigmoid is used because pixel values are between 0 and 1.\n            nn.Sigmoid()\n        )\n\n    def encode(self, x, labels):\n        x = x.view(x.size(0), -1)\n        #  Why add the label? -->> To condition the encoding\n        # we want the latent space to know the label so it can generate class-specific samples.\n        x = torch.cat([x, labels], dim=1)\n        h = self.encoder(x)\n        mu = self.fc_mu(h) # mu = mean of the latent Gaussian distribution.\n        logvar = self.fc_logvar(h) # logvar = log of variance (because variance must be positive).\n\n        return mu, logvar\n\n    def reparameterize(self, mu, logvar):\n        std = torch.exp(0.5 * logvar)\n        eps = torch.randn_like(std)\n        return mu + eps * std # eps ~ N(0,1).\n         # The VAE assumes a latent variable z ~ N(mu, sigma^2) — we need mu and logvar to sample z.\n\n    def decode(self, z, labels):\n        z = torch.cat([z, labels], dim=1)\n        h = self.decoder_input(z)\n        return self.decoder(h).view(-1, 1, 28, 28)\n\n    def forward(self, x, labels):\n        mu, logvar = self.encode(x, labels)\n        z = self.reparameterize(mu, logvar)\n        recon = self.decode(z, labels)\n        return recon, mu, logvar","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:39.839597Z","iopub.execute_input":"2025-06-29T14:21:39.839888Z","iopub.status.idle":"2025-06-29T14:21:39.857881Z","shell.execute_reply.started":"2025-06-29T14:21:39.839864Z","shell.execute_reply":"2025-06-29T14:21:39.857247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----- Loss -----\ndef loss_function(recon_x, x, mu, logvar):\n    BCE = F.binary_cross_entropy(recon_x, x, reduction='sum')\n    KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())\n    return BCE + KLD","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:39.858532Z","iopub.execute_input":"2025-06-29T14:21:39.858818Z","iopub.status.idle":"2025-06-29T14:21:39.876390Z","shell.execute_reply.started":"2025-06-29T14:21:39.858793Z","shell.execute_reply":"2025-06-29T14:21:39.875679Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"\n# ----- Training -----\nmodel = CVAE(latent_dim, num_classes).to(device)\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n\nepochs = 100\nmodel.train()\nfor epoch in range(epochs):\n    total_loss = 0\n    for imgs, labels in train_loader:\n        imgs = imgs.to(device)\n        labels_onehot = one_hot(labels).to(device)\n\n        optimizer.zero_grad()\n        recon, mu, logvar = model(imgs, labels_onehot)\n        loss = loss_function(recon, imgs, mu, logvar)\n        loss.backward()\n        optimizer.step()\n        total_loss += loss.item()\n    \n    avg_loss = total_loss / len(train_loader.dataset)\n    if epoch % 10 == 0 or epoch == 0:\n        print(f\"Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.4f}\")\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:21:39.877121Z","iopub.execute_input":"2025-06-29T14:21:39.877364Z","iopub.status.idle":"2025-06-29T14:31:28.175465Z","shell.execute_reply.started":"2025-06-29T14:21:39.877339Z","shell.execute_reply":"2025-06-29T14:31:28.174921Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Testing Trained Model Capabilities","metadata":{}},{"cell_type":"code","source":"def show_generated_digit(model, digit=4):\n    model.eval()\n    with torch.no_grad():\n        z = torch.randn(1, latent_dim).to(device)  # always one sample\n        labels = one_hot(torch.tensor([digit]), num_classes).to(device)\n        generated = model.decode(z, labels).cpu()\n        \n        plt.imshow(generated[0].squeeze(), cmap='gray')\n        plt.axis('off')\n        plt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:31:28.176201Z","iopub.execute_input":"2025-06-29T14:31:28.176403Z","iopub.status.idle":"2025-06-29T14:31:28.181265Z","shell.execute_reply.started":"2025-06-29T14:31:28.176385Z","shell.execute_reply":"2025-06-29T14:31:28.180615Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_generated_digit(model, digit=4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-29T14:37:12.832777Z","iopub.execute_input":"2025-06-29T14:37:12.833267Z","iopub.status.idle":"2025-06-29T14:37:12.881539Z","shell.execute_reply.started":"2025-06-29T14:37:12.833231Z","shell.execute_reply":"2025-06-29T14:37:12.881008Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color: white; background-color: red; padding: 10px 20px; border-radius: 10px; font-size: 36px; font-weight: bold;\">Smash That Upvote !! Thank you ! ❤️ 🚀😃</span>","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}