{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "DLfuYXOZRLSc", "outputId": "7bb39bd1-b77c-4b73-a2cc-504307f87a5e" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Libraries imported successfully!\n" ] } ], "source": [ "# ============================================================\n", "# Transfer Learning using ResNet on CIFAR-10\n", "# ============================================================\n", "# Description : Pretend CIFAR-10 categories are Bengali\n", "# celebrities — same transfer learning concept\n", "# Model : ResNet18 pretrained on ImageNet\n", "# Author : Fatima\n", "# ============================================================\n", "\n", "# ── Import Required Libraries ────────────────────────────────\n", "import torch # PyTorch\n", "import torch.nn as nn # neural network\n", "import torchvision.models as models # pretrained models\n", "import torchvision.transforms as transforms # image transforms\n", "from torchvision.datasets import CIFAR10 # practice dataset\n", "from torch.utils.data import DataLoader # data feeding\n", "import matplotlib.pyplot as plt # visualization\n", "\n", "print(\"Libraries imported successfully!\")" ] }, { "cell_type": "code", "source": [ "# ── Step 2: Load Pretrained ResNet18 ────────────────────────\n", "# ResNet18 = pretrained on ImageNet (1000 categories)\n", "# already knows edges, shapes, textures, face patterns\n", "# we borrow this knowledge for our celebrity task\n", "\n", "print(\"Loading pretrained ResNet18...\")\n", "\n", "# weights=DEFAULT loads the pretrained ImageNet weights\n", "model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)\n", "\n", "# look at the final layer before we change it\n", "print(\"\\nOriginal final layer:\")\n", "print(model.fc)\n", "\n", "# ── Step 3: Replace Final Layer ─────────────────────────────\n", "# original final layer → Linear(512, 1000) = 1000 categories\n", "# we replace it → Linear(512, 10) = 10 celebrities\n", "# (in real project → Linear(512, 250) = 250 Bengali celebs)\n", "\n", "num_celebrities = 10 # pretending 10 CIFAR categories = 10 celebrities\n", "\n", "model.fc = nn.Linear(512, num_celebrities)\n", "\n", "print(\"\\nModified final layer:\")\n", "print(model.fc)\n", "\n", "print(\"\\nResNet18 ready for celebrity recognition! ✅\")" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "yk6ClSQrRwz9", "outputId": "bcf48079-0cc7-4798-adf6-14abc7bdbd69" }, "execution_count": 2, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Loading pretrained ResNet18...\n", "Downloading: \"https://download.pytorch.org/models/resnet18-f37072fd.pth\" to /root/.cache/torch/hub/checkpoints/resnet18-f37072fd.pth\n" ] }, { "output_type": "stream", "name": "stderr", "text": [ "100%|██████████| 44.7M/44.7M [00:00<00:00, 344MB/s]" ] }, { "output_type": "stream", "name": "stdout", "text": [ "\n", "Original final layer:\n", "Linear(in_features=512, out_features=1000, bias=True)\n", "\n", "Modified final layer:\n", "Linear(in_features=512, out_features=10, bias=True)\n", "\n", "ResNet18 ready for celebrity recognition! ✅\n" ] }, { "output_type": "stream", "name": "stderr", "text": [ "\n" ] } ] }, { "cell_type": "code", "source": [ "# ── Step 3: Load CIFAR-10 Dataset ───────────────────────────\n", "# CIFAR-10 = 60,000 images, 10 categories\n", "# we pretend each category = one Bengali celebrity\n", "# ResNet expects images of size 224x224\n", "# so we resize CIFAR-10 images from 32x32 to 224x224\n", "\n", "print(\"Loading CIFAR-10 dataset...\")\n", "\n", "# define image transformations\n", "transform = transforms.Compose([\n", " # resize to 224x224 (ResNet expected input size)\n", " transforms.Resize((224, 224)),\n", " # convert image to PyTorch tensor\n", " transforms.ToTensor(),\n", " # normalize using ImageNet mean and std\n", " # because ResNet was trained with these values\n", " transforms.Normalize(\n", " mean=[0.485, 0.456, 0.406],\n", " std=[0.229, 0.224, 0.225]\n", " )\n", "])\n", "\n", "# load training data\n", "train_dataset = CIFAR10(\n", " root=\"./data\", # where to save dataset\n", " train=True, # training set\n", " download=True, # download if not present\n", " transform=transform # apply transformations\n", ")\n", "\n", "# load test data\n", "test_dataset = CIFAR10(\n", " root=\"./data\",\n", " train=False, # test set\n", " download=True,\n", " transform=transform\n", ")\n", "\n", "# create dataloaders\n", "train_loader = DataLoader(\n", " train_dataset,\n", " batch_size=32, # 32 images at a time\n", " shuffle=True # randomize each epoch\n", ")\n", "\n", "test_loader = DataLoader(\n", " test_dataset,\n", " batch_size=32,\n", " shuffle=False # no need to shuffle test data\n", ")\n", "\n", "print(f\"Training images : {len(train_dataset)}\")\n", "print(f\"Test images : {len(test_dataset)}\")\n", "print(f\"Training batches: {len(train_loader)}\")\n", "print(\"Dataset ready! ✅\")" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "BtdWc1VLShty", "outputId": "4e5d120c-d273-46bc-99c0-dbfb4c2c154f" }, "execution_count": 3, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Loading CIFAR-10 dataset...\n" ] }, { "output_type": "stream", "name": "stderr", "text": [ "100%|██████████| 170M/170M [41:04<00:00, 69.2kB/s]\n" ] }, { "output_type": "stream", "name": "stdout", "text": [ "Training images : 50000\n", "Test images : 10000\n", "Training batches: 1563\n", "Dataset ready! ✅\n" ] } ] }, { "cell_type": "code", "source": [ "# ── Step 4: Training Loop (Fast Version) ────────────────────\n", "# use only 500 images instead of 50,000\n", "# so it runs fast on CPU\n", "# concept is exactly the same!\n", "\n", "from torch.utils.data import Subset\n", "\n", "# take only first 500 training images\n", "small_train = Subset(train_dataset, range(500))\n", "small_loader = DataLoader(small_train,\n", " batch_size=32,\n", " shuffle=True)\n", "\n", "# setup\n", "device = torch.device(\"cpu\")\n", "print(f\"Using device: {device}\")\n", "model = model.to(device)\n", "\n", "# loss and optimizer\n", "loss_fn = nn.CrossEntropyLoss()\n", "optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\n", "\n", "print(f\"Training on 500 images ({len(small_loader)} batches)\")\n", "print(\"Starting training...\\n\")\n", "\n", "for epoch in range(2):\n", "\n", " total_loss = 0\n", " correct = 0\n", " total = 0\n", "\n", " for batch_idx, (images, labels) in enumerate(small_loader):\n", "\n", " images = images.to(device)\n", " labels = labels.to(device)\n", "\n", " # Step 1: forward pass\n", " predictions = model(images)\n", "\n", " # Step 2: calculate loss\n", " loss = loss_fn(predictions, labels)\n", "\n", " # Step 3: backpropagation\n", " loss.backward()\n", "\n", " # Step 4: update weights\n", " optimizer.step()\n", "\n", " # Step 5: reset gradients\n", " optimizer.zero_grad()\n", "\n", " total_loss += loss.item()\n", " _, predicted = torch.max(predictions, 1)\n", " correct += (predicted == labels).sum().item()\n", " total += labels.size(0)\n", "\n", " print(f\"Epoch {epoch+1} | Batch {batch_idx+1}/{len(small_loader)} | Loss: {round(loss.item(), 4)}\")\n", "\n", " accuracy = round((correct / total) * 100, 2)\n", " avg_loss = round(total_loss / len(small_loader), 4)\n", " print(f\"\\nEpoch {epoch+1} Summary:\")\n", " print(f\" Average Loss : {avg_loss}\")\n", " print(f\" Accuracy : {accuracy}%\\n\")\n", "\n", "print(\"Training complete! ✅\")" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "jTWccloycRsU", "outputId": "c2cfce1a-9ee0-4c57-f833-3ebbefc49007" }, "execution_count": 7, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Using device: cpu\n", "Training on 500 images (16 batches)\n", "Starting training...\n", "\n", "Epoch 1 | Batch 1/16 | Loss: 0.7116\n", "Epoch 1 | Batch 2/16 | Loss: 1.3489\n", "Epoch 1 | Batch 3/16 | Loss: 1.1916\n", "Epoch 1 | Batch 4/16 | Loss: 1.2354\n", "Epoch 1 | Batch 5/16 | Loss: 1.4737\n", "Epoch 1 | Batch 6/16 | Loss: 1.1769\n", "Epoch 1 | Batch 7/16 | Loss: 1.4508\n", "Epoch 1 | Batch 8/16 | Loss: 1.3606\n", "Epoch 1 | Batch 9/16 | Loss: 1.7716\n", "Epoch 1 | Batch 10/16 | Loss: 1.4312\n", "Epoch 1 | Batch 11/16 | Loss: 1.4026\n", "Epoch 1 | Batch 12/16 | Loss: 0.9197\n", "Epoch 1 | Batch 13/16 | Loss: 2.539\n", "Epoch 1 | Batch 14/16 | Loss: 1.0783\n", "Epoch 1 | Batch 15/16 | Loss: 1.2675\n", "Epoch 1 | Batch 16/16 | Loss: 1.628\n", "\n", "Epoch 1 Summary:\n", " Average Loss : 1.3742\n", " Accuracy : 57.4%\n", "\n", "Epoch 2 | Batch 1/16 | Loss: 0.9406\n", "Epoch 2 | Batch 2/16 | Loss: 0.6462\n", "Epoch 2 | Batch 3/16 | Loss: 0.4644\n", "Epoch 2 | Batch 4/16 | Loss: 0.9182\n", "Epoch 2 | Batch 5/16 | Loss: 0.9893\n", "Epoch 2 | Batch 6/16 | Loss: 0.7247\n", "Epoch 2 | Batch 7/16 | Loss: 0.8304\n", "Epoch 2 | Batch 8/16 | Loss: 0.9865\n", "Epoch 2 | Batch 9/16 | Loss: 0.7683\n", "Epoch 2 | Batch 10/16 | Loss: 0.5611\n", "Epoch 2 | Batch 11/16 | Loss: 0.8494\n", "Epoch 2 | Batch 12/16 | Loss: 0.9649\n", "Epoch 2 | Batch 13/16 | Loss: 0.7664\n", "Epoch 2 | Batch 14/16 | Loss: 1.0289\n", "Epoch 2 | Batch 15/16 | Loss: 0.7901\n", "Epoch 2 | Batch 16/16 | Loss: 1.2206\n", "\n", "Epoch 2 Summary:\n", " Average Loss : 0.8406\n", " Accuracy : 69.8%\n", "\n", "Training complete! ✅\n" ] } ] }, { "cell_type": "code", "source": [ "# ── Step 5: Evaluate on Test Data ───────────────────────────\n", "# test the model on images it has NEVER seen before\n", "# this tells us if model truly learned or just memorized\n", "\n", "from torch.utils.data import Subset\n", "\n", "# take 200 test images\n", "small_test = Subset(test_dataset, range(200))\n", "small_test_loader = DataLoader(small_test,\n", " batch_size=32,\n", " shuffle=False)\n", "\n", "# switch model to evaluation mode\n", "# turns off dropout and batch normalization\n", "model.eval()\n", "\n", "correct = 0\n", "total = 0\n", "\n", "# torch.no_grad() = don't calculate gradients\n", "# we're just testing, not training\n", "with torch.no_grad():\n", " for images, labels in small_test_loader:\n", "\n", " images = images.to(device)\n", " labels = labels.to(device)\n", "\n", " # forward pass only\n", " predictions = model(images)\n", "\n", " # get predicted class\n", " _, predicted = torch.max(predictions, 1)\n", " correct += (predicted == labels).sum().item()\n", " total += labels.size(0)\n", "\n", "accuracy = round((correct / total) * 100, 2)\n", "print(f\"Test Accuracy: {accuracy}%\")\n", "print(f\"Correct: {correct}/{total}\")" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "UFj9_RLzlSFA", "outputId": "933ecf45-afc5-41a6-d90c-410dd5eed1f5" }, "execution_count": 8, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Test Accuracy: 52.0%\n", "Correct: 104/200\n" ] } ] } ] }