{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =========================================\n# Step 1: Import libraries and check GPU\n# =========================================\n\nimport os\nimport glob\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image, ImageFile\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import models, transforms\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report, confusion_matrix, accuracy_score\n\nImageFile.LOAD_TRUNCATED_IMAGES = True\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Using device:\", device)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:39:33.431114Z","iopub.execute_input":"2026-09-21T18:39:33.431497Z","iopub.status.idle":"2026-09-21T18:39:33.437746Z","shell.execute_reply.started":"2026-09-21T18:39:33.431468Z","shell.execute_reply":"2026-09-21T18:39:33.436715Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nbase_path = \"/kaggle/input/competitions/galaxy-zoo-the-galaxy-challenge\"\n\nprint(\"Does base path exist?\", os.path.exists(base_path))\n\nif os.path.exists(base_path):\n    print(\"\\nFiles found:\")\n    for f in os.listdir(base_path):\n        print(f)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:39:38.996539Z","iopub.execute_input":"2026-09-21T18:39:38.996819Z","iopub.status.idle":"2026-09-21T18:39:39.012337Z","shell.execute_reply.started":"2026-09-21T18:39:38.996796Z","shell.execute_reply":"2026-09-21T18:39:39.011345Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================================\n# Step 2: Extract Galaxy Zoo dataset\n# =========================================\n\nimport zipfile\nimport glob\nimport os\n\nbase_path = \"/kaggle/input/competitions/galaxy-zoo-the-galaxy-challenge\"\n\nsolution_zip = os.path.join(\n    base_path,\n    \"training_solutions_rev1.zip\"\n)\n\nimage_zip = os.path.join(\n    base_path,\n    \"images_training_rev1.zip\"\n)\n\n# -----------------------------\n# Extract labels\n# -----------------------------\n\nsolution_dir = \"/kaggle/working/galaxy_zoo_solutions\"\n\nos.makedirs(solution_dir, exist_ok=True)\n\nwith zipfile.ZipFile(solution_zip, \"r\") as zip_ref:\n    zip_ref.extractall(solution_dir)\n\n# -----------------------------\n# Extract images\n# -----------------------------\n\nimage_dir = \"/kaggle/working/galaxy_zoo_images\"\n\nos.makedirs(image_dir, exist_ok=True)\n\nwith zipfile.ZipFile(image_zip, \"r\") as zip_ref:\n    zip_ref.extractall(image_dir)\n\n# -----------------------------\n# Find CSV\n# -----------------------------\n\ncsv_files = glob.glob(\n    solution_dir + \"/**/*.csv\",\n    recursive=True\n)\n\nlabel_csv = csv_files[0]\n\n# -----------------------------\n# Find images\n# -----------------------------\n\nimage_files = glob.glob(\n    image_dir + \"/**/*.jpg\",\n    recursive=True\n)\n\nprint(\"CSV:\", label_csv)\nprint(\"Total images found:\", len(image_files))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:39:41.800938Z","iopub.execute_input":"2026-09-21T18:39:41.801659Z","iopub.status.idle":"2026-09-21T18:39:58.225612Z","shell.execute_reply.started":"2026-09-21T18:39:41.801629Z","shell.execute_reply":"2026-09-21T18:39:58.224919Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_df = pd.read_csv(label_csv)\nprint(\"Labels shape:\", labels_df.shape)\nprint(labels_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:02.65348Z","iopub.execute_input":"2026-09-21T18:40:02.654464Z","iopub.status.idle":"2026-09-21T18:40:02.995491Z","shell.execute_reply.started":"2026-09-21T18:40:02.654418Z","shell.execute_reply":"2026-09-21T18:40:02.99448Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"new_data = labels_df[(labels_df[\"Class1.1\"] >= 0.8) | (labels_df[\"Class1.2\"] >= 0.8)].reset_index(drop=True)\n\nconditions = [new_data[\"Class1.1\"] >= 0.8, new_data[\"Class1.2\"] >= 0.8]\nchoices = [\"elliptical\", \"spiral\"]\n\nnew_data[\"morphology_label\"] = np.select(conditions, choices, default=\"uncertain\")\nnew_data = new_data[new_data[\"morphology_label\"] != \"uncertain\"].reset_index(drop=True)\n\nprint(\"Dataset shape:\", new_data.shape)\nprint(new_data[\"morphology_label\"].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:06.063194Z","iopub.execute_input":"2026-09-21T18:40:06.063633Z","iopub.status.idle":"2026-09-21T18:40:06.106753Z","shell.execute_reply.started":"2026-09-21T18:40:06.063602Z","shell.execute_reply":"2026-09-21T18:40:06.105848Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_map = {os.path.splitext(os.path.basename(f))[0]: f for f in image_files}\nnew_data[\"image_id\"] = new_data[\"GalaxyID\"].astype(str)\nnew_data[\"file_path\"] = new_data[\"image_id\"].map(image_map)\nnew_data = new_data.dropna(subset=[\"file_path\"]).reset_index(drop=True)\n\nprint(\"Final dataset shape:\", new_data.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:08.889083Z","iopub.execute_input":"2026-09-21T18:40:08.889518Z","iopub.status.idle":"2026-09-21T18:40:09.037075Z","shell.execute_reply.started":"2026-09-21T18:40:08.889482Z","shell.execute_reply":"2026-09-21T18:40:09.036116Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================================\n# Step 6: Balance the dataset\n# =========================================\n\nmin_count = new_data[\"morphology_label\"].value_counts().min()\n\nbalanced_data = (\n    new_data\n    .groupby(\"morphology_label\", group_keys=False)\n    .apply(lambda x: x.sample(min_count, random_state=42))\n    .reset_index(drop=True)\n)\n\nprint(\"Balanced dataset:\")\nprint(balanced_data[\"morphology_label\"].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:11.55544Z","iopub.execute_input":"2026-09-21T18:40:11.555751Z","iopub.status.idle":"2026-09-21T18:40:11.586442Z","shell.execute_reply.started":"2026-09-21T18:40:11.555724Z","shell.execute_reply":"2026-09-21T18:40:11.585488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df, test_df = train_test_split(\n    balanced_data,\n    test_size=0.2,\n    stratify=balanced_data[\"morphology_label\"],\n    random_state=42\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:26.782548Z","iopub.execute_input":"2026-09-21T18:40:26.782831Z","iopub.status.idle":"2026-09-21T18:40:26.813005Z","shell.execute_reply.started":"2026-09-21T18:40:26.782806Z","shell.execute_reply":"2026-09-21T18:40:26.812362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((256,256)),\n    transforms.RandomResizedCrop(224),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(180),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])\n])\n\ntest_transform = transforms.Compose([\n    transforms.Resize((256,256)),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])\n])\n\nclass GalaxyZooDataset(Dataset):\n    def __init__(self, df, label_to_idx, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.label_to_idx = label_to_idx\n        self.transform = transform\n    def __len__(self):\n        return len(self.df)\n    def __getitem__(self, idx):\n        image = Image.open(self.df.loc[idx, \"file_path\"]).convert(\"RGB\")\n        label = self.label_to_idx[self.df.loc[idx, \"morphology_label\"]]\n        if self.transform:\n            image = self.transform(image)\n        return image, label\n\nclass_names = sorted(balanced_data[\"morphology_label\"].unique())\nlabel_to_idx = {label: idx for idx, label in enumerate(class_names)}\n\ntrain_dataset = GalaxyZooDataset(train_df, label_to_idx, transform=train_transform)\ntest_dataset = GalaxyZooDataset(test_df, label_to_idx, transform=test_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2, pin_memory=True)\ntest_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=2, pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:29.340757Z","iopub.execute_input":"2026-09-21T18:40:29.341472Z","iopub.status.idle":"2026-09-21T18:40:29.355843Z","shell.execute_reply.started":"2026-09-21T18:40:29.341446Z","shell.execute_reply":"2026-09-21T18:40:29.354885Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================================\n# Step 9: EfficientNet-B0 model setup\n# =========================================\n\nmodel = models.efficientnet_b0(\n    weights=models.EfficientNet_B0_Weights.DEFAULT\n)\n\n# Replace the final classification layer\nmodel.classifier[1] = nn.Linear(\n    model.classifier[1].in_features,\n    len(class_names)\n)\n\nmodel = model.to(device)\n\ncriterion = nn.CrossEntropyLoss()\n\noptimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=1e-4\n)\n\n# Mixed precision scaler\nscaler = torch.cuda.amp.GradScaler()\n\nprint(\"EfficientNet-B0 model ready.\")\nprint(\"Number of classes:\", len(class_names))\nprint(\"Model device:\", next(model.parameters()).device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:33.104657Z","iopub.execute_input":"2026-09-21T18:40:33.105262Z","iopub.status.idle":"2026-09-21T18:40:33.86093Z","shell.execute_reply.started":"2026-09-21T18:40:33.105225Z","shell.execute_reply":"2026-09-21T18:40:33.859593Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================================\n# Step 10: Train EfficientNet-B0\n# =========================================\n\nepochs = 40\n\nfor epoch in range(epochs):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for images, labels in train_loader:\n        images = images.to(device, non_blocking=True)\n        labels = labels.to(device, non_blocking=True)\n\n        optimizer.zero_grad()\n\n        with torch.cuda.amp.autocast():\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n\n        running_loss += loss.item()\n\n        _, preds = torch.max(outputs, 1)\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n\n    train_loss = running_loss / len(train_loader)\n    train_acc = correct / total\n\n    print(\n        f\"Epoch [{epoch+1}/{epochs}] | \"\n        f\"Loss: {train_loss:.4f} | \"\n        f\"Train Accuracy: {train_acc:.4f}\"\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T18:40:39.411773Z","iopub.execute_input":"2026-09-21T18:40:39.412402Z","iopub.status.idle":"2026-09-21T19:11:50.106044Z","shell.execute_reply.started":"2026-09-21T18:40:39.412372Z","shell.execute_reply":"2026-09-21T19:11:50.10511Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================================\n# Step 11: Evaluate EfficientNet-B0\n# =========================================\n\nmodel.eval()\n\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n    for images, labels in test_loader:\n        images = images.to(device, non_blocking=True)\n\n        outputs = model(images)\n        preds = torch.argmax(outputs, dim=1).cpu().numpy()\n\n        all_preds.extend(preds)\n        all_labels.extend(labels.numpy())\n\naccuracy = accuracy_score(all_labels, all_preds)\n\nprint(\"EfficientNet-B0 Accuracy:\", accuracy)\n\nprint(\"\\nEfficientNet-B0 Classification Report:\")\nprint(classification_report(\n    all_labels,\n    all_preds,\n    target_names=class_names\n))\n\nprint(\"EfficientNet-B0 Confusion Matrix:\")\nprint(confusion_matrix(all_labels, all_preds))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T19:47:42.237755Z","iopub.execute_input":"2026-09-21T19:47:42.238392Z","iopub.status.idle":"2026-09-21T19:47:50.929522Z","shell.execute_reply.started":"2026-09-21T19:47:42.238352Z","shell.execute_reply":"2026-09-21T19:47:50.928667Z"}},"outputs":[],"execution_count":null}]}