{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# TechTrident\n", "\n", "AI system detects defects/degradation (cracks, hotspots, soiling) using tabular performance data + images. ONNX export required. Public datasets only.\n", "\n", "Classes: ['Bird-drop', 'Clean', 'Dusty', 'Electrical-damage', 'Physical-Damage', 'Snow-Covered']\n", "\n", "Datasets: [PV Panel Defect Dataset](https://www.kaggle.com/datasets/alicjalena/pv-panel-defect-dataset) TechTrident | Wadla 4.0 Hackathon 2025\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "g77qbAOimwT8", "outputId": "d7630c7b-a22e-4796-8183-388d6441a587" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/18.1 MB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[91m━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.5/18.1 MB\u001b[0m \u001b[31m45.8 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m8.6/18.1 MB\u001b[0m \u001b[31m126.2 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m\u001b[90m━\u001b[0m \u001b[32m17.4/18.1 MB\u001b[0m \u001b[31m242.1 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m \u001b[32m18.1/18.1 MB\u001b[0m \u001b[31m244.7 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m18.1/18.1 MB\u001b[0m \u001b[31m115.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m17.4/17.4 MB\u001b[0m \u001b[31m119.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.0/46.0 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.8/86.8 kB\u001b[0m \u001b[31m7.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25h" ] } ], "source": [ "!pip install -q torch torchvision torchaudio\n", "!pip install -q scikit-learn matplotlib seaborn\n", "!pip install -q onnx onnxruntime\n", "!pip install -q tqdm\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "GMkcprwXrbBX", "outputId": "0d33e958-6054-4215-e54d-de76432d6bbb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using Colab cache for faster access to the 'solarpanel' dataset.\n", "Path to dataset files: /kaggle/input/solarpanel\n" ] } ], "source": [ "import kagglehub\n", "\n", "# Download latest version\n", "path = kagglehub.dataset_download(\"pkdarabi/solarpanel\")\n", "\n", "print(\"Path to dataset files:\", path)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "KKazlbdSros-", "outputId": "03cdf94e-b7d8-48b7-f7e5-ea4aef35a623" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using Colab cache for faster access to the 'pv-panel-defect-dataset' dataset.\n", "Path to dataset files: /kaggle/input/pv-panel-defect-dataset\n" ] } ], "source": [ "import kagglehub\n", "\n", "# Download latest version\n", "path = kagglehub.dataset_download(\"alicjalena/pv-panel-defect-dataset\")\n", "\n", "print(\"Path to dataset files:\", path)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "pFgkSY2Srjyg", "outputId": "3cef8f66-230b-46c8-925e-1a1c8d07e9ec" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using Colab cache for faster access to the 'pvel-ad-electroluminescence-pv-defect-dataset' dataset.\n", "Path to dataset files: /kaggle/input/pvel-ad-electroluminescence-pv-defect-dataset\n" ] } ], "source": [ "import kagglehub\n", "\n", "# Download latest version\n", "path = kagglehub.dataset_download(\"programmer3/pvel-ad-electroluminescence-pv-defect-dataset\")\n", "\n", "print(\"Path to dataset files:\", path)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Wk-TX_1DsZtS", "outputId": "3728b24f-240e-41b4-ae21-3c56f2eecfe5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "============================================================\n", "DATASET: RGB_SolarPanel\n", "PATH: /root/.cache/kagglehub/datasets/pkdarabi/solarpanel/versions/49\n", "============================================================\n", "\n", "============================================================\n", "DATASET: EL_PV_Defect\n", "PATH: /root/.cache/kagglehub/datasets/programmer3/pvel-ad-electroluminescence-pv-defect-dataset/versions/1\n", "============================================================\n", "\n", "============================================================\n", "DATASET: Thermal_PV\n", "PATH: /kaggle/input/pv-panel-defect-dataset\n", "============================================================\n", "ROOT: /kaggle/input/pv-panel-defect-dataset\n", "SUBFOLDERS: ['val', 'test', 'train']\n", "FILES COUNT: 0\n", "SAMPLE FILES: []\n" ] } ], "source": [ "# STEP 3: Inspect each dataset structure (one by one)\n", "\n", "import os\n", "\n", "dataset_paths = {\n", " \"RGB_SolarPanel\": \"/root/.cache/kagglehub/datasets/pkdarabi/solarpanel/versions/49\",\n", " \"EL_PV_Defect\": \"/root/.cache/kagglehub/datasets/programmer3/pvel-ad-electroluminescence-pv-defect-dataset/versions/1\",\n", " \"Thermal_PV\": \"/kaggle/input/pv-panel-defect-dataset\"\n", "}\n", "\n", "def inspect_dataset(name, path):\n", " print(\"\\n\" + \"=\"*60)\n", " print(f\"DATASET: {name}\")\n", " print(f\"PATH: {path}\")\n", " print(\"=\"*60)\n", "\n", " for root, dirs, files in os.walk(path):\n", " print(\"ROOT:\", root)\n", " print(\"SUBFOLDERS:\", dirs)\n", " print(\"FILES COUNT:\", len(files))\n", " print(\"SAMPLE FILES:\", files[:5])\n", " break\n", "\n", "for name, path in dataset_paths.items():\n", " inspect_dataset(name, path)\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "7tHZWUP3s2ON", "outputId": "03518ec6-efa1-42c1-f590-1f935fd14f31" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--- TRAIN ---\n", "Classes: ['Snow-Covered', 'Dusty', 'Electrical-damage', 'Clean', 'Bird-drop', 'Physical-Damage']\n", "\n", "--- VAL ---\n", "Classes: ['Snow-Covered', 'Dusty', 'Electrical-damage', 'Clean', 'Bird-drop', 'Physical-Damage']\n", "\n", "--- TEST ---\n", "Classes: ['Snow-Covered', 'Dusty', 'Electrical-damage', 'Clean', 'Bird-drop', 'Physical-Damage']\n" ] } ], "source": [ "# STEP 4: Inspect class folders inside Thermal dataset\n", "\n", "import os\n", "\n", "thermal_path = \"/kaggle/input/pv-panel-defect-dataset\"\n", "\n", "for split in [\"train\", \"val\", \"test\"]:\n", " split_path = os.path.join(thermal_path, split)\n", " print(f\"\\n--- {split.upper()} ---\")\n", " if os.path.exists(split_path):\n", " print(\"Classes:\", os.listdir(split_path))\n", " else:\n", " print(\"❌ Split not found\")\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "WFpVI-6dtK6I", "outputId": "a1f6ab86-cf2a-4f8d-bc15-8169c7438b69" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using device: cuda\n", "Class mapping: {'Bird-drop': 0, 'Clean': 1, 'Dusty': 2, 'Electrical-damage': 3, 'Physical-Damage': 4, 'Snow-Covered': 5}\n", "Train samples: 929\n", "Val samples: 550\n", "Test samples: 95\n" ] } ], "source": [ "# STEP 5: DataLoader & Transforms\n", "\n", "import torch\n", "from torchvision import datasets, transforms\n", "from torch.utils.data import DataLoader\n", "\n", "DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "print(\"Using device:\", DEVICE)\n", "\n", "IMG_SIZE = 224\n", "BATCH_SIZE = 32\n", "\n", "# Transform (handles thermal / grayscale / RGB)\n", "train_transform = transforms.Compose([\n", " transforms.Resize((IMG_SIZE, IMG_SIZE)),\n", " transforms.Grayscale(num_output_channels=3),\n", " transforms.RandomHorizontalFlip(),\n", " transforms.ToTensor(),\n", " transforms.Normalize(\n", " mean=[0.485, 0.456, 0.406],\n", " std=[0.229, 0.224, 0.225]\n", " )\n", "])\n", "\n", "val_transform = transforms.Compose([\n", " transforms.Resize((IMG_SIZE, IMG_SIZE)),\n", " transforms.Grayscale(num_output_channels=3),\n", " transforms.ToTensor(),\n", " transforms.Normalize(\n", " mean=[0.485, 0.456, 0.406],\n", " std=[0.229, 0.224, 0.225]\n", " )\n", "])\n", "\n", "DATASET_PATH = \"/kaggle/input/pv-panel-defect-dataset\"\n", "\n", "train_data = datasets.ImageFolder(\n", " root=f\"{DATASET_PATH}/train\",\n", " transform=train_transform\n", ")\n", "\n", "val_data = datasets.ImageFolder(\n", " root=f\"{DATASET_PATH}/val\",\n", " transform=val_transform\n", ")\n", "\n", "test_data = datasets.ImageFolder(\n", " root=f\"{DATASET_PATH}/test\",\n", " transform=val_transform\n", ")\n", "\n", "train_loader = DataLoader(train_data, batch_size=BATCH_SIZE, shuffle=True)\n", "val_loader = DataLoader(val_data, batch_size=BATCH_SIZE, shuffle=False)\n", "test_loader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False)\n", "\n", "print(\"Class mapping:\", train_data.class_to_idx)\n", "print(\"Train samples:\", len(train_data))\n", "print(\"Val samples:\", len(val_data))\n", "print(\"Test samples:\", len(test_data))\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "VBpWsVAPvQOm" }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 9, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "R90aPddvtg3z", "outputId": "4b65372d-de96-4dc3-a733-327ddbafe519" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Epoch [1/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:26<00:00, 1.13it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 46.1716, Train Acc: 49.52%\n", "Val Loss: 23.4326, Val Acc: 72.36%\n", "\n", "Epoch [2/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:24<00:00, 1.24it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 31.0843, Train Acc: 76.43%\n", "Val Loss: 13.2697, Val Acc: 82.00%\n", "\n", "Epoch [3/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.25it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 20.2007, Train Acc: 84.82%\n", "Val Loss: 8.2944, Val Acc: 86.00%\n", "\n", "Epoch [4/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:24<00:00, 1.21it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 15.8832, Train Acc: 86.65%\n", "Val Loss: 5.8530, Val Acc: 89.45%\n", "\n", "Epoch [5/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.25it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 11.4642, Train Acc: 91.07%\n", "Val Loss: 4.3735, Val Acc: 92.91%\n", "\n", "Epoch [6/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:24<00:00, 1.25it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 9.1711, Train Acc: 95.16%\n", "Val Loss: 3.6670, Val Acc: 94.00%\n", "\n", "Epoch [7/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.26it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 7.1015, Train Acc: 95.59%\n", "Val Loss: 3.0334, Val Acc: 95.27%\n", "\n", "Epoch [8/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.26it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 5.9129, Train Acc: 96.77%\n", "Val Loss: 2.7183, Val Acc: 95.82%\n", "\n", "Epoch [9/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:24<00:00, 1.25it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 4.7660, Train Acc: 98.17%\n", "Val Loss: 2.6371, Val Acc: 96.18%\n", "\n", "Epoch [10/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.26it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 4.2479, Train Acc: 97.42%\n", "Val Loss: 2.5740, Val Acc: 96.18%\n", "\n", "Epoch [11/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.26it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 3.4081, Train Acc: 99.03%\n", "Val Loss: 2.5000, Val Acc: 95.64%\n", "\n", "Epoch [12/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.25it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 4.4313, Train Acc: 98.28%\n", "Val Loss: 2.3392, Val Acc: 96.36%\n", "\n", "Epoch [13/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:30<00:00, 1.02s/it]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 3.8637, Train Acc: 98.17%\n", "Val Loss: 2.3700, Val Acc: 96.18%\n", "\n", "Epoch [14/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.26it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 3.9151, Train Acc: 98.92%\n", "Val Loss: 2.4089, Val Acc: 96.36%\n", "\n", "Epoch [15/15]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 30/30 [00:23<00:00, 1.25it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Train Loss: 3.1275, Train Acc: 98.82%\n", "Val Loss: 2.2238, Val Acc: 95.82%\n" ] } ], "source": [ "# STEP 6: Build EfficientNet-B0 model & training loop\n", "\n", "import torch\n", "import torch.nn as nn\n", "import torch.optim as optim\n", "from torchvision import models\n", "from tqdm import tqdm\n", "\n", "NUM_CLASSES = 6\n", "EPOCHS = 15\n", "LR = 1e-4\n", "\n", "# Load pretrained EfficientNet-B0\n", "model = models.efficientnet_b0(pretrained=True)\n", "\n", "# Replace classifier\n", "in_features = model.classifier[1].in_features\n", "model.classifier[1] = nn.Linear(in_features, NUM_CLASSES)\n", "\n", "model = model.to(DEVICE)\n", "\n", "# Loss & optimizer\n", "criterion = nn.CrossEntropyLoss()\n", "optimizer = optim.Adam(model.parameters(), lr=LR)\n", "\n", "# Training & validation loop\n", "for epoch in range(EPOCHS):\n", " print(f\"\\nEpoch [{epoch+1}/{EPOCHS}]\")\n", "\n", " # ---- Training ----\n", " model.train()\n", " train_loss = 0\n", " correct = 0\n", " total = 0\n", "\n", " for images, labels in tqdm(train_loader):\n", " images, labels = images.to(DEVICE), labels.to(DEVICE)\n", "\n", " optimizer.zero_grad()\n", " outputs = model(images)\n", " loss = criterion(outputs, labels)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " train_loss += loss.item()\n", " _, predicted = torch.max(outputs, 1)\n", " total += labels.size(0)\n", " correct += (predicted == labels).sum().item()\n", "\n", " train_acc = 100 * correct / total\n", " print(f\"Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%\")\n", "\n", " # ---- Validation ----\n", " model.eval()\n", " val_loss = 0\n", " correct = 0\n", " total = 0\n", "\n", " with torch.no_grad():\n", " for images, labels in val_loader:\n", " images, labels = images.to(DEVICE), labels.to(DEVICE)\n", " outputs = model(images)\n", " loss = criterion(outputs, labels)\n", "\n", " val_loss += loss.item()\n", " _, predicted = torch.max(outputs, 1)\n", " total += labels.size(0)\n", " correct += (predicted == labels).sum().item()\n", "\n", " val_acc = 100 * correct / total\n", " print(f\"Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%\")\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 954 }, "id": "5XUQH8-ExIx6", "outputId": "e57ad2b3-8825-4f83-d3dc-f537c844b7c6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Classification Report:\n", "\n", " precision recall f1-score support\n", "\n", " Bird-drop 0.94 1.00 0.97 17\n", " Clean 0.84 0.89 0.86 18\n", " Dusty 0.93 0.81 0.87 16\n", "Electrical-damage 0.92 0.92 0.92 13\n", " Physical-Damage 1.00 1.00 1.00 15\n", " Snow-Covered 1.00 1.00 1.00 16\n", "\n", " accuracy 0.94 95\n", " macro avg 0.94 0.94 0.94 95\n", " weighted avg 0.94 0.94 0.94 95\n", "\n" ] }, { "data": { "image/png": "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aGgIfX19fPzxx6hdu3aZ4jp16hTWrFmDWbNmyae13LJlCzw9PTFjxgwsXLiwTMcjImIFnoioHHXt2hXx8fHo3bs3Dh06hFGjRuGbb75BSkoKlixZgpUrV8r33bhxI+bMmYNLly5h3LhxOHXqFKZMmYJdu3apNCZzc3McOHAAenp6mDRpErZt24bg4GD4+fkVi71WrVrYvHkzRo0ahdWrV6Nt27Y4deoUjI2NSz1+hw4dEBoaCnNzc8ycOROLFy9G8+bNERkZWebktzxMnToV48ePx9GjRzF27FhER0fjjz/+QM2aNRX209LSwrZt26CpqYnhw4ejX79+OHPmTJnO9eLFCwwePBju7u6YNm2afH2bNm0wduxYLFmyBOfPn1fJdRFR5SGRleUuISIiIiIiEhQr8EREREREIsIEnoiIiIhIRJjAExERERGJCBN4IiIiIiIRYQJPRERERCQiTOCJiIiIiESECTwRERERkYjwSaykdnTdvxI6hArjyaVVQodARESVhI6AWWV55g65Mer3bykr8EREREREIsIKPBERERGJm6Ry1aSZwBMRERGRuEkkQkfwQVWurytERERERCLHCjwRERERiVsla6GpXFdLRERERCRyrMATERERkbixB56IiIiIiNQVK/BEREREJG7sgSciIiIiInXFCjwRERERiVsl64FnAk9ERERE4sYWGiIiIiIiUleswBMRERGRuFWyFhpW4ImIiIiIRIQVeCIiIiISN/bAExERERGRumIFnoiIiIjEjT3wRERERESkrliBJyIiIiJxYw88ERERERGpK1bgiYiIiEjc2ANPRERERCQiEo3yW8ooPDwcfn5+qF69OiQSCQ4ePFhsn4SEBHTt2hXGxsbQ19dH06ZNkZqaqvQ5mMATEREREalIdnY2GjZsiNWrV5e4PSkpCa1bt4azszPCwsIQHx+PGTNmQEdHR+lzsIWGiIiIiMRNjW5i9fX1ha+vb6nbp02bhs6dO2PhwoXydfb29mU6h/pcLRERERGRmsnLy8Pz588Vlry8vHc6VlFREf744w/UqVMH3t7esLS0xMcff1xim83bMIEnIiIiInHTkJTbEhwcDGNjY4UlODj4ncLMyMhAVlYWFixYAB8fHxw7dgw9evRAz549cebMGaWPwxYaIiIiIqJSTJkyBUFBQQrrpFLpOx2rqKgIANCtWzd8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v//eLQfgKvZFRBoKFfAiIhYUHR1NRkYG3t7eBAYG4un5//8d+Pr6ul1bXl7ODTfcwOuvv17nda688sp/9P52u/2Cv6e8vByA999/n9atW7ud8/Hx+Uc5RETMSAW8iIgF+fr6Eh4efl7Xdu3albfeeouAgAD8/f3PeM3VV1/N9u3bueWWWwCorq7mq6++omvXrme8PjIyEqfTyZYtW1wtNP/rz98A1NTUuNY6duyIj48PJSUlZ925j4iIIDs7223t888///u/pIiIiegmVhEROafhw4fTsmVLhgwZQl5eHvv27WPz5s0kJiZy8OBBACZPnswzzzzD2rVr+eGHH5gwYcI5Z7i3bduW+Ph4Ro0axdq1a12vuXLlSgBCQkKw2WysW7eOI0eOUF5ejsPhIDk5mUceeYTly5ezd+9e8vPzmTdvHsuXLwdg/Pjx7Nmzh0cffZTCwkLeeOMNXn755Uv9EYmIXFYq4EVE5JyaNGnCJ598QnBwMMOGDSMiIoLRo0dz6tQp1478lClTGDFiBPHx8fTs2ROHw8Htt99+ztfNyMjgjjvuYMKECXTo0IGxY8dy4sQJAFq3bs306dOZOnUqrVq1YtKkSQCkpaXx1FNPMWvWLCIiIoiJieH9998nNDQUgODgYFavXs3atWvp3LkzmZmZzJw58xJ+OiIil5+t9mx3GImIiIiISL2jHXgRERERERNRAS8iIiIiYiIq4EVERERETEQFvIiIiIiIiaiAFxERERExERXwIiIiIiImogJeRERERMREVMCLiIiIiJiICngRERERERNRAS8iIiIiYiIq4EVERERETEQFvIiIiIiIifwfGoZpHdZPaM4AAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# STEP 7: Test Evaluation Metrics\n", "\n", "import numpy as np\n", "from sklearn.metrics import classification_report, confusion_matrix\n", "import seaborn as sns\n", "import matplotlib.pyplot as plt\n", "\n", "model.eval()\n", "\n", "all_preds = []\n", "all_labels = []\n", "\n", "with torch.no_grad():\n", " for images, labels in test_loader:\n", " images = images.to(DEVICE)\n", " outputs = model(images)\n", " _, preds = torch.max(outputs, 1)\n", "\n", " all_preds.extend(preds.cpu().numpy())\n", " all_labels.extend(labels.numpy())\n", "\n", "# Classification report\n", "print(\"\\nClassification Report:\\n\")\n", "print(classification_report(all_labels, all_preds, target_names=train_data.classes))\n", "\n", "# Confusion Matrix\n", "cm = confusion_matrix(all_labels, all_preds)\n", "\n", "plt.figure(figsize=(8,6))\n", "sns.heatmap(cm, annot=True, fmt=\"d\",\n", " xticklabels=train_data.classes,\n", " yticklabels=train_data.classes,\n", " cmap=\"Blues\")\n", "plt.xlabel(\"Predicted\")\n", "plt.ylabel(\"Actual\")\n", "plt.title(\"Confusion Matrix\")\n", "plt.show()\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "KMyAqFlhyVK7", "outputId": "c1d68f2a-d819-4cca-abbd-5143d875db73" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/693.4 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[91m╸\u001b[0m \u001b[32m686.1/693.4 kB\u001b[0m \u001b[31m23.4 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m693.4/693.4 kB\u001b[0m \u001b[31m16.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25h\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/133.1 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m133.1/133.1 kB\u001b[0m \u001b[31m13.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25h" ] } ], "source": [ "# FIX: Install missing ONNX dependencies required by latest PyTorch\n", "\n", "!pip install -q onnxscript\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "r-kjCoenyb79", "outputId": "4acc6c11-5789-482d-e296-cfdf8fb72736" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipython-input-3311461802.py:12: UserWarning: # 'dynamic_axes' is not recommended when dynamo=True, and may lead to 'torch._dynamo.exc.UserError: Constraints violated.' Supply the 'dynamic_shapes' argument instead if export is unsuccessful.\n", " torch.onnx.export(\n", "W1218 21:15:42.026000 174 torch/onnx/_internal/exporter/_compat.py:114] Setting ONNX exporter to use operator set version 18 because the requested opset_version 11 is a lower version than we have implementations for. Automatic version conversion will be performed, which may not be successful at converting to the requested version. If version conversion is unsuccessful, the opset version of the exported model will be kept at 18. Please consider setting opset_version >=18 to leverage latest ONNX features\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "[torch.onnx] Obtain model graph for `EfficientNet([...]` with `torch.export.export(..., strict=False)`...\n", "[torch.onnx] Obtain model graph for `EfficientNet([...]` with `torch.export.export(..., strict=False)`... ✅\n", "[torch.onnx] Run decomposition...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:onnxscript.version_converter:The model version conversion is not supported by the onnxscript version converter and fallback is enabled. The model will be converted using the onnx C API (target version: 11).\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "[torch.onnx] Run decomposition... ✅\n", "[torch.onnx] Translate the graph into ONNX...\n", "[torch.onnx] Translate the graph into ONNX... ✅\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:onnxscript.version_converter:Failed to convert the model to the target version 11 using the ONNX C API. The model was not modified\n", "Traceback (most recent call last):\n", " File \"/usr/local/lib/python3.12/dist-packages/onnxscript/version_converter/__init__.py\", line 127, in call\n", " converted_proto = _c_api_utils.call_onnx_api(\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/local/lib/python3.12/dist-packages/onnxscript/version_converter/_c_api_utils.py\", line 65, in call_onnx_api\n", " result = func(proto)\n", " ^^^^^^^^^^^\n", " File \"/usr/local/lib/python3.12/dist-packages/onnxscript/version_converter/__init__.py\", line 122, in _partial_convert_version\n", " return onnx.version_converter.convert_version(\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/local/lib/python3.12/dist-packages/onnx/version_converter.py\", line 39, in convert_version\n", " converted_model_str = C.convert_version(model_str, target_version)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", "RuntimeError: /github/workspace/onnx/version_converter/adapters/axes_input_to_attribute.h:65: adapt: Assertion `node->hasAttribute(kaxes)` failed: No initializer or constant input to node found\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Applied 98 of general pattern rewrite rules.\n", "✅ ONNX model exported to: /content/solar_panel_defect_model.onnx\n" ] } ], "source": [ "# STEP 8A: Export PyTorch model to ONNX\n", "\n", "import torch\n", "\n", "ONNX_PATH = \"/content/solar_panel_defect_model.onnx\"\n", "\n", "# Dummy input (batch size 1, 3-channel, 224x224)\n", "dummy_input = torch.randn(1, 3, 224, 224).to(DEVICE)\n", "\n", "model.eval()\n", "\n", "torch.onnx.export(\n", " model,\n", " dummy_input,\n", " ONNX_PATH,\n", " export_params=True,\n", " opset_version=11,\n", " do_constant_folding=True,\n", " input_names=[\"input_image\"],\n", " output_names=[\"class_scores\"],\n", " dynamic_axes={\n", " \"input_image\": {0: \"batch_size\"},\n", " \"class_scores\": {0: \"batch_size\"}\n", " }\n", ")\n", "\n", "print(\"✅ ONNX model exported to:\", ONNX_PATH)\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "WPPp6_uVyroE", "outputId": "1542d5a3-f5ac-4138-b4b5-633b9e9032f5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ONNX output shape: (1, 6)\n", "Sample output: [[ 993.0995 199.42247 -608.0232 -2058.3643 -4874.3457 -1554.5675 ]]\n" ] } ], "source": [ "# STEP 8B: Verify ONNX model inference\n", "\n", "import onnxruntime as ort\n", "import numpy as np\n", "\n", "# Load ONNX model\n", "ort_session = ort.InferenceSession(ONNX_PATH)\n", "\n", "# Prepare dummy input\n", "dummy_np = np.random.randn(1, 3, 224, 224).astype(np.float32)\n", "\n", "# Run inference\n", "outputs = ort_session.run(\n", " None,\n", " {\"input_image\": dummy_np}\n", ")\n", "\n", "print(\"ONNX output shape:\", outputs[0].shape)\n", "print(\"Sample output:\", outputs[0])\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "id": "Tj-Ynteqzf23" }, "outputs": [], "source": [ "# STEP G1: Install Gradio\n", "\n", "!pip install -q gradio onnxruntime pillow\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "PQzRiyU7z6WY", "outputId": "837b66a5-ad05-44cc-9e02-e5ff0db1eade" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✅ ONNX model loaded\n", "✅ Inference pipeline ready\n" ] } ], "source": [ "# STEP G2: Load ONNX model & preprocessing function\n", "\n", "import onnxruntime as ort\n", "import numpy as np\n", "from PIL import Image\n", "import torch\n", "from torchvision import transforms\n", "\n", "# Path to ONNX model\n", "ONNX_PATH = \"/content/solar_panel_defect_model.onnx\"\n", "\n", "# Load ONNX runtime session\n", "ort_session = ort.InferenceSession(ONNX_PATH, providers=[\"CPUExecutionProvider\"])\n", "\n", "print(\"✅ ONNX model loaded\")\n", "\n", "# Class labels (VERY IMPORTANT: order must match training)\n", "CLASS_NAMES = [\n", " 'Bird-drop',\n", " 'Clean',\n", " 'Dusty',\n", " 'Electrical-damage',\n", " 'Physical-Damage',\n", " 'Snow-Covered'\n", "]\n", "\n", "# Same preprocessing as training\n", "IMG_SIZE = 224\n", "\n", "preprocess = transforms.Compose([\n", " transforms.Resize((IMG_SIZE, IMG_SIZE)),\n", " transforms.Grayscale(num_output_channels=3),\n", " transforms.ToTensor(),\n", " transforms.Normalize(\n", " mean=[0.485, 0.456, 0.406],\n", " std=[0.229, 0.224, 0.225]\n", " )\n", "])\n", "\n", "def predict_image(pil_image):\n", " \"\"\"\n", " Input: PIL Image\n", " Output: (predicted_class, confidence, full_probs_dict)\n", " \"\"\"\n", " img = preprocess(pil_image).unsqueeze(0).numpy().astype(np.float32)\n", "\n", " # ONNX inference\n", " outputs = ort_session.run(\n", " None,\n", " {\"input_image\": img}\n", " )[0]\n", "\n", " # Softmax\n", " exp_scores = np.exp(outputs)\n", " probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)\n", "\n", " probs = probs[0]\n", " pred_idx = int(np.argmax(probs))\n", "\n", " predicted_class = CLASS_NAMES[pred_idx]\n", " confidence = float(probs[pred_idx])\n", "\n", " prob_dict = {CLASS_NAMES[i]: float(probs[i]) for i in range(len(CLASS_NAMES))}\n", "\n", " return predicted_class, confidence, prob_dict\n", "\n", "\n", "print(\"✅ Inference pipeline ready\")\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 646 }, "id": "IHyiWnj_0EFQ", "outputId": "0a33d28f-97e8-4d98-d582-261a875c92f4" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "It looks like you are running Gradio on a hosted Jupyter notebook, which requires `share=True`. Automatically setting `share=True` (you can turn this off by setting `share=False` in `launch()` explicitly).\n", "\n", "Colab notebook detected. This cell will run indefinitely so that you can see errors and logs. To turn off, set debug=False in launch().\n", "* Running on public URL: https://d55396fb96ea5bc795.gradio.live\n", "\n", "This share link expires in 1 week. For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)\n" ] }, { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# STEP G3: Gradio Interface\n", "\n", "import gradio as gr\n", "\n", "def gradio_predict(image):\n", " pred_class, confidence, prob_dict = predict_image(image)\n", "\n", " confidence_percent = f\"{confidence * 100:.2f}%\"\n", "\n", " return pred_class, confidence_percent, prob_dict\n", "\n", "\n", "iface = gr.Interface(\n", " fn=gradio_predict,\n", " inputs=gr.Image(type=\"pil\", label=\"Upload Solar Panel Image (Thermal / IR / RGB)\"),\n", " outputs=[\n", " gr.Textbox(label=\"Predicted Defect Class\"),\n", " gr.Textbox(label=\"Confidence\"),\n", " gr.Label(label=\"All Class Probabilities\")\n", " ],\n", " title=\"AI-Powered Solar Panel Defect Detection\",\n", " description=(\n", " \"Upload any solar panel image (thermal, infrared, or RGB). \"\n", " \"The AI model classifies defects such as soiling, electrical damage, \"\n", " \"physical damage, snow coverage, or clean panels.\"\n", " ),\n", " examples=None\n", ")\n", "\n", "iface.launch(debug=True)\n" ] } ], "metadata": { "accelerator": "GPU", "colab": { "gpuType": "T4", "provenance": [] }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 }