{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Install required packages\n!pip install efficientnet-pytorch  # Install EfficientNet for PyTorch\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport cv2\nfrom PIL import Image\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader, WeightedRandomSampler\nimport torchvision.transforms as transforms\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, cohen_kappa_score, confusion_matrix\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\n\n# Try to import EfficientNet from efficientnet_pytorch, fallback to torchvision if unavailable\ntry:\n    from efficientnet_pytorch import EfficientNet\n    print(\"Using efficientnet-pytorch package\")\n    USE_EFFICIENTNET_PYTORCH = True\nexcept ModuleNotFoundError:\n    print(\"efficientnet-pytorch not found, falling back to torchvision.models.efficientnet\")\n    from torchvision.models import efficientnet_b3, EfficientNet_B3_Weights\n    USE_EFFICIENTNET_PYTORCH = False\n\n# Paths\ndata_path = \"/kaggle/input/aptos2019-blindness-detection/\"\ntrain_csv = os.path.join(data_path, \"train.csv\")\ntest_csv = os.path.join(data_path, \"test.csv\")\ntrain_images_dir = os.path.join(data_path, \"train_images\")\ntest_images_dir = os.path.join(data_path, \"test_images\")\n\n# Load and Split Data\ntrain_df_full = pd.read_csv(train_csv)\ntest_df = pd.read_csv(test_csv)\ntrain_df, val_df = train_test_split(train_df_full, test_size=0.15, random_state=42, stratify=train_df_full['diagnosis'])\n\n# Device Configuration\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device} ({torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'})\")\n\n# Constants\nIMG_SIZE = 320  # EfficientNet-B3 optimal input size\nBATCH_SIZE = 16\nEPOCHS = 50\nNUM_CLASSES = 5\nCLASS_NAMES = [\"No DR\", \"Mild\", \"Moderate\", \"Severe\", \"Proliferative DR\"]\n\n# Advanced Preprocessing\ndef preprocess_image(image_path):\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    \n    # Circular crop\n    height, width = img.shape[:2]\n    center = (width // 2, height // 2)\n    radius = min(center)\n    mask = np.zeros((height, width), dtype=np.uint8)\n    cv2.circle(mask, center, radius, 255, -1)\n    img = cv2.bitwise_and(img, img, mask=mask)\n    \n    # Contrast enhancement\n    img = cv2.convertScaleAbs(img, alpha=1.5, beta=10)\n    \n    # ROI extraction\n    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n    _, thresh = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)\n    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n    if contours:\n        contour = max(contours, key=cv2.contourArea)\n        x, y, w, h = cv2.boundingRect(contour)\n        img = img[y:y+h, x:x+w]\n    \n    return Image.fromarray(img)\n\n# Dataset Class\nclass APTOSDataset(Dataset):\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.img_dir, self.df.iloc[idx, 0] + \".png\")\n        img = preprocess_image(img_path)\n        label = self.df.iloc[idx, 1]\n        \n        if self.transform:\n            img = self.transform(img)\n        return img, label\n\n# Data Augmentation\ntrain_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomVerticalFlip(p=0.5),\n    transforms.RandomRotation(30),\n    transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3),\n    transforms.RandomAffine(degrees=0, translate=(0.1, 0.1), scale=(0.8, 1.2)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\nval_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# Handle Class Imbalance\nclass_counts = train_df['diagnosis'].value_counts().sort_index()\nclass_weights = 1. / torch.tensor([class_counts[i] for i in range(NUM_CLASSES)], dtype=torch.float).to(device)\nsampler_weights = [1. / class_counts[label] for label in train_df['diagnosis']]\nsampler = WeightedRandomSampler(sampler_weights, len(sampler_weights))\n\n# Datasets and Loaders\ntrain_dataset = APTOSDataset(train_df, train_images_dir, transform=train_transform)\nval_dataset = APTOSDataset(val_df, train_images_dir, transform=val_transform)\ntest_dataset = APTOSDataset(test_df, test_images_dir, transform=val_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, sampler=sampler, num_workers=4, pin_memory=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\n\n# Model Definition\nclass APTOSModel(nn.Module):\n    def __init__(self, num_classes=NUM_CLASSES):\n        super(APTOSModel, self).__init__()\n        if USE_EFFICIENTNET_PYTORCH:\n            self.effnet = EfficientNet.from_pretrained('efficientnet-b3')\n            self.effnet._fc = nn.Linear(self.effnet._fc.in_features, num_classes)\n        else:\n            self.effnet = efficientnet_b3(weights=EfficientNet_B3_Weights.IMAGENET1K_V1)\n            self.effnet.classifier = nn.Linear(self.effnet.classifier[1].in_features, num_classes)\n        self.dropout = nn.Dropout(0.5)\n\n    def forward(self, x):\n        x = self.effnet(x)\n        x = self.dropout(x)\n        return x\n\n# Ensemble of Two Models\nmodels_list = [\n    APTOSModel(num_classes=NUM_CLASSES).to(device),\n    APTOSModel(num_classes=NUM_CLASSES).to(device)\n]\n\n# Training Function\ndef train_model(models, train_loader, val_loader, epochs=EPOCHS):\n    criterion = nn.CrossEntropyLoss(weight=class_weights)\n    optimizers = [optim.Adam(model.parameters(), lr=3e-4) for model in models]\n    schedulers = [optim.lr_scheduler.CosineAnnealingLR(opt, T_max=epochs) for opt in optimizers]\n    \n    best_acc = 0.0\n    train_losses, val_accs = [], []\n\n    for epoch in range(epochs):\n        for model, optimizer in zip(models, optimizers):\n            model.train()\n            train_loss = 0\n            for images, labels in tqdm(train_loader, desc=f\"Epoch {epoch+1}/{epochs}\"):\n                images, labels = images.to(device), labels.to(device)\n                optimizer.zero_grad()\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                loss.backward()\n                optimizer.step()\n                train_loss += loss.item()\n            train_losses.append(train_loss / len(train_loader))\n            schedulers[models.index(model)].step()\n\n        # Validation with Ensemble\n        for model in models:\n            model.eval()\n        val_preds, val_labels = [], []\n        with torch.no_grad():\n            for images, labels in val_loader:\n                images, labels = images.to(device), labels.to(device)\n                ensemble_outputs = torch.zeros(images.size(0), NUM_CLASSES).to(device)\n                for model in models:\n                    outputs = model(images)\n                    ensemble_outputs += outputs / len(models)\n                preds = torch.argmax(ensemble_outputs, dim=1)\n                val_preds.extend(preds.cpu().numpy())\n                val_labels.extend(labels.cpu().numpy())\n\n        val_acc = accuracy_score(val_labels, val_preds)\n        val_qwk = cohen_kappa_score(val_labels, val_preds, weights=\"quadratic\")\n        val_accs.append(val_acc)\n\n        print(f\"Epoch {epoch+1}/{epochs} - Train Loss: {train_losses[-1]:.4f}, Val Acc: {val_acc:.4f}, Val QWK: {val_qwk:.4f}\")\n\n        if val_acc > best_acc:\n            best_acc = val_acc\n            for i, model in enumerate(models):\n                torch.save(model.state_dict(), f\"best_model_{i}.pth\")\n            print(f\"New best accuracy: {best_acc:.4f}\")\n\n        if val_acc >= 0.95:\n            print(\"Target 95% accuracy achieved!\")\n            break\n\n    return train_losses, val_accs, val_preds, val_labels\n\n# Visualization Functions\ndef plot_training_curves(train_losses, val_accs):\n    plt.figure(figsize=(12, 5))\n    plt.subplot(1, 2, 1)\n    plt.plot(train_losses, label=\"Train Loss\")\n    plt.title(\"Training Loss\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Loss\")\n    plt.legend()\n\n    plt.subplot(1, 2, 2)\n    plt.plot(val_accs, label=\"Val Accuracy\")\n    plt.title(\"Validation Accuracy\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Accuracy\")\n    plt.legend()\n    plt.tight_layout()\n    plt.show()\n\ndef plot_confusion_matrix(y_true, y_pred):\n    cm = confusion_matrix(y_true, y_pred)\n    plt.figure(figsize=(8, 6))\n    sns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Blues\", xticklabels=CLASS_NAMES, yticklabels=CLASS_NAMES)\n    plt.title(\"Confusion Matrix\")\n    plt.xlabel(\"Predicted\")\n    plt.ylabel(\"True\")\n    plt.show()\n\n# Main Execution\ntrain_losses, val_accs, val_preds, val_labels = train_model(models_list, train_loader, val_loader)\n\n# Visualizations\nplot_training_curves(train_losses, val_accs)\nplot_confusion_matrix(val_labels, val_preds)\n\n# Test Predictions with Ensemble\nfor i, model in enumerate(models_list):\n    model.load_state_dict(torch.load(f\"best_model_{i}.pth\"))\n    model.eval()\n\ntest_preds = []\nwith torch.no_grad():\n    for images, _ in test_loader:\n        images = images.to(device)\n        ensemble_outputs = torch.zeros(images.size(0), NUM_CLASSES).to(device)\n        for model in models_list:\n            outputs = model(images)\n            ensemble_outputs += outputs / len(models_list)\n        preds = torch.argmax(ensemble_outputs, dim=1)\n        test_preds.extend(preds.cpu().numpy())\n\n# Submission\nsubmission = pd.DataFrame({\"id_code\": test_df[\"id_code\"], \"diagnosis\": test_preds})\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"Submission file created: submission.csv\")\n\n# Final Validation Accuracy\nfinal_acc = accuracy_score(val_labels, val_preds)\nprint(f\"Final Validation Accuracy: {final_acc:.4f}\")\nif final_acc >= 0.95:\n    print(\"Successfully achieved 95% accuracy!\")\nelse:\n    print(f\"Best accuracy achieved: {final_acc:.4f}. Further tuning may be needed.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-09T11:34:13.221919Z","iopub.execute_input":"2025-03-09T11:34:13.222243Z","iopub.status.idle":"2025-03-09T20:32:11.309507Z","shell.execute_reply.started":"2025-03-09T11:34:13.222222Z","shell.execute_reply":"2025-03-09T20:32:11.307736Z"}},"outputs":[],"execution_count":null}]}