{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "e7fa91fa-54b3-4a92-9aaa-e2e3a8405d5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import torch\n",
    "from torchvision import datasets, transforms as T, models\n",
    "from torch import nn, optim\n",
    "from torch.utils.data import DataLoader\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "from tqdm import tqdm\n",
    "from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score, classification_report\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import seaborn as sns\n",
    "from sklearn.metrics import roc_curve\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.metrics import auc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f9455de-e558-4a40-9790-94de2f4746cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "from PIL import Image \n",
    "import os\n",
    "\n",
    "input_root = 'dataset/'\n",
    "output_root = 'output/'\n",
    "resize_size = (256, 256)  # Change size as needed\n",
    "\n",
    "# Create output root if it doesn't exist\n",
    "os.makedirs(output_root, exist_ok=True)\n",
    "\n",
    "# Iterate through each class folder\n",
    "for class_name in os.listdir(input_root):\n",
    "    class_path = os.path.join(input_root, class_name)\n",
    "    \n",
    "# Skip if it's not a directory or if it's the 'masks' folder\n",
    "    if not os.path.isdir(class_path) or class_name == 'masks':\n",
    "        continue\n",
    "        \n",
    "# Create output path for each class\n",
    "    output_class_path = os.path.join(output_root, class_name)\n",
    "    os.makedirs(output_class_path, exist_ok=True)\n",
    "        \n",
    "    for image_name in os.listdir(class_path):\n",
    "        if image_name.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')):\n",
    "            input_image_path = os.path.join(class_path, image_name)\n",
    "            output_image_name = os.path.splitext(image_name)[0] + '.jpg'\n",
    "            output_image_path = os.path.join(output_class_path, output_image_name)            \n",
    "            try:\n",
    "                with Image.open(input_image_path) as img:\n",
    "                    img = img.convert('RGB')\n",
    "                    img = img.resize(resize_size)\n",
    "                    img.save(output_image_path, 'JPEG')\n",
    "                    print(f\"Processed: {input_image_path} -> {output_image_path}\")\n",
    "            except Exception as e:\n",
    "               print(f\"Failed to process {input_image_path}: {e}\")\n",
    "            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf80f000-11b4-48c7-a424-17654a1943cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, shutil\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "\n",
    "original_dataset_dir = 'output'\n",
    "base_dir = 'new dataset'\n",
    "train_dir = os.path.join(base_dir, 'train')\n",
    "validation_dir = os.path.join(base_dir, 'validation')\n",
    "test_dir = os.path.join(base_dir, 'test')\n",
    "\n",
    "for dir in [train_dir, validation_dir, test_dir]:\n",
    "    os.makedirs(dir, exist_ok=True)\n",
    "    \n",
    "train_ratio, validation_ratio, test_ratio = 0.7, 0.1, 0.2\n",
    "\n",
    "def split_and_copy_files(class_dir, dest_base_dir):\n",
    "    all_images = [os.path.join(class_dir, f) for f in\n",
    "        \n",
    "os.listdir(class_dir) if os.path.isfile(os.path.join(class_dir, f))]\n",
    "    train_images, temp_images = train_test_split(all_images, test_size=(1 - train_ratio), random_state=42)\n",
    "    validation_images, test_images = train_test_split(temp_images, test_size=test_ratio/(validation_ratio + test_ratio), random_state=42)\n",
    "    \n",
    "    for images, folder_name in zip([train_images, validation_images, test_images], ['train', 'validation', 'test']):\n",
    "        dest_dir = os.path.join(dest_base_dir, folder_name, os.path.basename(class_dir))\n",
    "        os.makedirs(dest_dir, exist_ok=True)        \n",
    "        \n",
    "        for file in images:            \n",
    "            shutil.copy(file, dest_dir)\n",
    "            \n",
    "# Apply to all class subfolders\n",
    "            \n",
    "for class_dir in os.listdir(original_dataset_dir):\n",
    "    full_class_dir = os.path.join(original_dataset_dir, class_dir)\n",
    "    if os.path.isdir(full_class_dir):\n",
    "        split_and_copy_files(full_class_dir, base_dir)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "00e8867d-e7c9-4250-98f6-224cd28d7f6b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Classes: ['Lilly', 'Lotus', 'Orchid', 'Sunflower', 'Tulip'], Total: 5\n"
     ]
    }
   ],
   "source": [
    "def get_classes(data_dir):\n",
    "    all_data = datasets.ImageFolder(data_dir)\n",
    "    return all_data.classes\n",
    "\n",
    "classes = get_classes(\"new dataset/test\")\n",
    "print(f\"Classes: {classes}, Total: {len(classes)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "461a94df-1040-4e1a-a4d1-ea7c05aaa2fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_data_loaders(data_dir, batch_size, train=False):\n",
    "    \"\"\"Prepare data loaders for training, validation, and testing.\"\"\"\n",
    "    if train:        \n",
    "        # Data augmentation for training\n",
    "         transform = T.Compose([\n",
    "             T.RandomHorizontalFlip(),\n",
    "             T.RandomVerticalFlip(),\n",
    "             T.RandomRotation(10),\n",
    "             T.Resize((299, 299)),            \n",
    "             T.ToTensor(),            \n",
    "             T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])\n",
    "        \n",
    "         train_data = datasets.ImageFolder(os.path.join(data_dir, \"train/\"), transform=transform)        \n",
    "         train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=4)        \n",
    "         return train_loader, len(train_data)    \n",
    "    else:        \n",
    "        # Validation and test data transformations        \n",
    "        transform = T.Compose([            \n",
    "            T.Resize((299, 299)),            \n",
    "            T.ToTensor(),            \n",
    "            T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])        \n",
    "        ])        \n",
    "        val_data = datasets.ImageFolder(os.path.join(data_dir, \"validation/\"), transform=transform)        \n",
    "        test_data = datasets.ImageFolder(os.path.join(data_dir, \"test/\"), transform=transform)        \n",
    "        val_loader = DataLoader(val_data, batch_size=batch_size, shuffle=False, num_workers=4)        \n",
    "        test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=4)        \n",
    "        return val_loader, test_loader, len(val_data), len(test_data) \n",
    "        # Dataset paths and loaders \n",
    "dataset_path = \"new dataset\"\n",
    "batch_size = 32  \n",
    "(train_loader, train_data_len) = get_data_loaders(dataset_path, batch_size, train=True) \n",
    "(val_loader, test_loader, valid_data_len, test_data_len) = get_data_loaders(dataset_path, batch_size, train=False) \n",
    "        \n",
    "classes = get_classes(\"new dataset/train\") \n",
    "num_classes = len(classes) \n",
    "        \n",
    "dataloaders = {\"train\": train_loader, \"val\": val_loader} \n",
    "dataset_sizes = {\"train\": train_data_len, \"val\": valid_data_len} \n",
    "# Device setup \n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "id": "4c447a4c-4ba9-443f-85ff-1e29a86bcba9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the pre-trained Inception-v3 model\n",
    "model = models.efficientnet_b0(pretrained=True)\n",
    "# Update the fully connected layer for the number of classes \n",
    "model.classifier[1] = nn.Linear(model.classifier[1].in_features, num_classes) \n",
    "model = model.to(device) \n",
    "# Loss function and optimizer \n",
    "criterion = nn.CrossEntropyLoss() \n",
    "criterion = criterion.to(device) \n",
    "optimizer = optim.AdamW(model.parameters(), lr=0.001) \n",
    "# Learning rate scheduler \n",
    "exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.97)\n",
    "\n",
    "def train_model(model, criterion, optimizer, scheduler, num_epochs=20):    \n",
    "    \"\"\"Train and validate the model.\"\"\"    \n",
    "    for epoch in range(num_epochs):        \n",
    "        print(f\"Epoch {epoch+1}/{num_epochs}\")        \n",
    "        print('-' * 10)        \n",
    "        \n",
    "        # Each epoch has a training and validation phase        \n",
    "        for phase in ['train', 'val']:            \n",
    "            if phase == 'train':                \n",
    "                model.train()  # Set model to training mode            \n",
    "            else:                \n",
    "                model.eval()  # Set model to evaluation mode\n",
    "                \n",
    "            running_loss = 0.0            \n",
    "            running_corrects = 0            \n",
    "            \n",
    "            # Iterate over data            \n",
    "            for inputs, labels in dataloaders[phase]:                \n",
    "                inputs = inputs.to(device)                \n",
    "                labels = labels.to(device)                \n",
    "                # Zero the parameter gradients                \n",
    "                optimizer.zero_grad()                \n",
    "                # Forward pass                \n",
    "                with torch.set_grad_enabled(phase == 'train'):                    \n",
    "                    outputs = model(inputs)                    \n",
    "                    # Handle auxiliary logits for Inception-v3                    \n",
    "                    if isinstance(outputs, tuple) or hasattr(outputs, \"logits\"):                        \n",
    "                        main_outputs = outputs.logits if hasattr(outputs, \"logits\") else outputs[0]                        \n",
    "                        aux_outputs = outputs.aux_logits if hasattr(outputs, \"aux_logits\") else None                        \n",
    "                        loss = criterion(main_outputs, labels)                        \n",
    "                        if aux_outputs is not None:  # Add auxiliary loss if present                            \n",
    "                            loss += 0.4 * criterion(aux_outputs, labels)                    \n",
    "                    else:                        \n",
    "                        main_outputs = outputs                        \n",
    "                        loss = criterion(main_outputs, labels)\n",
    "                        _, preds = torch.max(main_outputs, 1)  # Extract predictions from logits                    \n",
    "                            # Backward pass and optimization in training phase                    \n",
    "                    if phase == 'train':                        \n",
    "                        loss.backward()                        \n",
    "                        optimizer.step()                # Statistics                \n",
    "                running_loss += loss.item() * inputs.size(0)                \n",
    "                running_corrects += torch.sum(preds == labels.data)            \n",
    "                                # Step the scheduler in the training phase            \n",
    "                    \n",
    "            if phase == 'train':                \n",
    "                scheduler.step()            \n",
    "            epoch_loss = running_loss / dataset_sizes[phase]            \n",
    "            epoch_acc = running_corrects.double() / dataset_sizes[phase]  \n",
    "            \n",
    "            print(f\"{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}\")  \n",
    "            \n",
    "        return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "id": "f04c8dcf-ab92-4c96-88e3-53aa3fad4544",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████████████████████████████████████████████████████████████████████████████| 32/32 [01:51<00:00,  3.48s/it]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy: 0.19621513944223107\n",
      "Precision : 0.19954225316528412\n",
      "Recall: 0.19621513944223107\n",
      "F1-score: 0.16970263464206647\n",
      "Classification Report:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.23      0.19      0.21       200\n",
      "           1       0.17      0.11      0.14       201\n",
      "           2       0.19      0.10      0.13       201\n",
      "           3       0.22      0.06      0.09       201\n",
      "           4       0.19      0.52      0.28       201\n",
      "\n",
      "    accuracy                           0.20      1004\n",
      "   macro avg       0.20      0.20      0.17      1004\n",
      "weighted avg       0.20      0.20      0.17      1004\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 600x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def evaluate_model(model_name, y_true, y_pred, classes):         \n",
    "    \n",
    "    accuracy = accuracy_score(y_true, y_pred)    \n",
    "    precision = precision_score(y_true, y_pred, average='weighted')      \n",
    "    recall = recall_score(y_true, y_pred, average='weighted')    \n",
    "    f1 = f1_score(y_true, y_pred, average='weighted')    \n",
    "    cm = confusion_matrix(y_true, y_pred)        \n",
    "    \n",
    "    print(\"Accuracy:\", accuracy)    \n",
    "    print(\"Precision :\", precision)    \n",
    "    print(\"Recall:\", recall)    \n",
    "    print(\"F1-score:\" , f1)        \n",
    "    print(\"Classification Report:\")    \n",
    "    print(classification_report(y_true, y_pred))            \n",
    "    \n",
    "    # Plot confusion matrix    \n",
    "    \n",
    "    plt.figure(figsize=(6, 5))    \n",
    "    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=classes, yticklabels=classes)    \n",
    "    plt.xlabel(\"Predicted Label\")    \n",
    "    plt.ylabel(\"True Label\")    \n",
    "    plt.title(\"Confusion Matrix\")    \n",
    "    plt.show()\n",
    "\n",
    "y_true = []\n",
    "y_proba = []\n",
    "y_pred = [] \n",
    "\n",
    "# Disable gradient calculation \n",
    "\n",
    "with torch.no_grad():    \n",
    "    for x, y in tqdm(test_loader):        \n",
    "        # Forward pass        \n",
    "        output = model(x.to(device)) \n",
    "        \n",
    "        # Store true labels        \n",
    "        y_true.extend(y.numpy())                \n",
    "        \n",
    "        # Get predicted probabilities (softmax output)        \n",
    "        probabilities = torch.softmax(output, dim=1)  # Assuming it's a multi-class classification\n",
    "\n",
    "        y_proba.extend(probabilities.cpu().numpy())  # Store probabilities                \n",
    "        # Get predicted classes (argmax for final class prediction)        \n",
    "        pred = torch.argmax(probabilities, axis=1).cpu().numpy()\n",
    "        y_pred.extend(pred) \n",
    "# Convert to numpy arrays for further processing \n",
    "\n",
    "y_true = np.array(y_true) \n",
    "y_proba = np.array(y_proba) \n",
    "y_pred = np.array(y_pred)\n",
    "\n",
    "evaluate_model(\"Resnet18\", y_true, y_pred,classes)\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e431b0c-b2d3-4e7d-9cd2-5e020e828347",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = models.densenet121(pretrained= False)\n",
    "\n",
    "# Replace the classifier to match the number of output classes\n",
    "model.classifier = nn.Linear(model.classifier.in_features, num_classes)\n",
    "model = model.to(device) \n",
    "# Loss function and optimizer\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "criterion = criterion.to(device)\n",
    "optimizer = optim.AdamW(model.parameters(), lr= 0.001)\n",
    "\n",
    "# Learning rate scheduler\n",
    "exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.97) \n",
    "model = train_model(model, criterion, optimizer, exp_lr_scheduler, num_epochs=1) \n",
    "y_true = []\n",
    "y_proba = []\n",
    "y_pred = []\n",
    "\n",
    "# Disable gradient calculation\n",
    "with torch.no_grad():\n",
    "    for x, y in tqdm(test_loader):        \n",
    "        # Forward pass        \n",
    "        output = model(x.to(device))  \n",
    "        \n",
    "        # Store true labels       \n",
    "        y_true.extend(y.numpy())                \n",
    "        \n",
    "        # Get predicted probabilities (softmax output)        \n",
    "        \n",
    "        probabilities = torch.softmax(output, dim=1)  # Assuming it's a multi-class classification\n",
    "        \n",
    "        y_proba.extend(probabilities.cpu().numpy())  # Store probabilities                \n",
    "        \n",
    "        # Get predicted classes (argmax for final class prediction)       \n",
    "        \n",
    "        pred = torch.argmax(probabilities, axis=1).cpu().numpy()       \n",
    "        y_pred.extend(pred) \n",
    "        \n",
    "# Convert to numpy arrays for further processing \n",
    "        \n",
    "y_true = np.array(y_true) \n",
    "\n",
    "y_pred = np.array(y_pred) \n",
    "\n",
    "evaluate_model(\"Densenet121\", y_true, y_pred,classes)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7bb0a8f1-619b-4c3a-9d3d-982d266704fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initialize VGG16 from scratch (no pretrained weights)\n",
    "model = models.vgg16(pretrained= False)\n",
    "# Replace the classifier to match the number of output classes \n",
    "model.classifier[6] = nn.Linear(model.classifier[6].in_features, num_classes) \n",
    "model = model.to(device) \n",
    "# Loss function and optimizer \n",
    "criterion = nn.CrossEntropyLoss() \n",
    "criterion = criterion.to(device) \n",
    "optimizer = optim.AdamW(model.parameters(), lr=0.001) \n",
    "# Learning rate scheduler \n",
    "exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.97) \n",
    "model = train_model(model, criterion, optimizer, exp_lr_scheduler, num_epochs=1) \n",
    "y_true = []\n",
    "y_proba = []\n",
    "y_pred = [] # Disable gradient calculation \n",
    "\n",
    "with torch.no_grad():    \n",
    "    for x, y in tqdm(test_loader):        \n",
    "        # Forward pass        \n",
    "        output = model(x.to(device))                \n",
    "        # Store true labels        \n",
    "        \n",
    "        y_true.extend(y.numpy())                \n",
    "        \n",
    "        # Get predicted probabilities (softmax output)        \n",
    "        \n",
    "        probabilities = torch.softmax(output, dim=1)  # Assuming it's a multi-class classification       \n",
    "        y_proba.extend(probabilities.cpu().numpy())  # Store probabilities                \n",
    "        # Get predicted classes (argmax for final class prediction)        \n",
    "        pred = torch.argmax(probabilities, axis=1).cpu().numpy()        \n",
    "        y_pred.extend(pred)\n",
    "        \n",
    "# Convert to numpy arrays for further processing \n",
    "y_true = np.array(y_true)\n",
    "y_pred = np.array(y_pred)\n",
    "evaluate_model( \"vgg16\" , y_true, y_pred,classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67f0efdf-7326-4dc8-8a6f-fcce65d0f20c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load InceptionV3 model without pretraining \n",
    "model = models.inception_v3(pretrained= False , aux_logits= True) \n",
    "# Replace the final fully connected layer to match the number of output classes \n",
    "model.fc = nn.Linear(model.fc.in_features, num_classes)\n",
    "# Also replace the auxiliary classifier's fully connected layer (if used) \n",
    "if model.aux_logits:    \n",
    "    model.AuxLogits.fc = nn.Linear(model.AuxLogits.fc.in_features, num_classes)\n",
    "model = model.to(device)\n",
    "# Loss function and optimizer \n",
    "criterion = nn.CrossEntropyLoss().to(device) \n",
    "optimizer = optim.AdamW(model.parameters(), lr=0.001) \n",
    "# Learning rate scheduler \n",
    "exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.97) \n",
    "model = train_model(model, criterion, optimizer, exp_lr_scheduler, num_epochs=1)\n",
    "y_true = [] \n",
    "y_proba = [] \n",
    "y_pred = [] \n",
    "# Disable gradient calculation \n",
    "with torch.no_grad():    \n",
    "    for x, y in tqdm(test_loader):        \n",
    "        # Forward pass        \n",
    "        output = model(x.to(device))                \n",
    "        # Store true labels        \n",
    "        y_true.extend(y.numpy())                \n",
    "        # Get predicted probabilities (softmax output)        \n",
    "        probabilities = torch.softmax(output, dim=1)  # Assuming it's a multi-class classification       \n",
    "        y_proba.extend(probabilities.cpu().numpy())  # Store probabilities                \n",
    "        # Get predicted classes (argmax for final class prediction)        \n",
    "        pred = torch.argmax(probabilities, axis=1).cpu().numpy()        \n",
    "        y_pred.extend(pred) \n",
    "# Convert to numpy arrays for further processing \n",
    "\n",
    "y_true = np.array(y_true) \n",
    "y_pred = np.array(y_pred) \n",
    "evaluate_model(\"inception_v3\", y_true, y_pred,classes)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4489692-2b4f-48dc-a08c-bb477ca9025c",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c26338cd-6472-4cc0-87f8-f43016312020",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
