{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"},{"sourceId":9885587,"sourceType":"datasetVersion","datasetId":6070567}],"dockerImageVersionId":30787,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nimport timm\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import cohen_kappa_score\nfrom torch.cuda.amp import autocast, GradScaler\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Custom Preprocessing Class\nclass AdvancedPreprocessing:\n    def __init__(self, image_size=512):\n        self.image_size = image_size\n        self.cache = {}\n\n    @torch.no_grad()\n    def preprocess_image(self, image_path):\n        if image_path in self.cache:\n            return self.cache[image_path]\n        \n        img = cv2.imread(image_path)\n        if img is None:\n            raise ValueError(f\"Could not read image at {image_path}\")\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        \n        gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n        _, mask = cv2.threshold(gray, 5, 255, cv2.THRESH_BINARY)\n        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n        \n        if contours:\n            largest_contour = max(contours, key=cv2.contourArea)\n            mask = np.zeros_like(gray)\n            cv2.drawContours(mask, [largest_contour], -1, 255, -1)\n        \n        img = cv2.bitwise_and(img, img, mask=mask)\n        img = cv2.resize(img, (self.image_size, self.image_size))\n        \n        self.cache[image_path] = img\n        return img\n\n# EfficientNet Model with Pre-trained Weights\nclass EfficientNetWithMixup(nn.Module):\n    def __init__(self, num_classes=5):\n        super().__init__()\n        self.model = timm.create_model('efficientnet_b0', pretrained=False)\n        self.model.load_state_dict(torch.load('/kaggle/input/efficientnet-b0/efficientnet_b0_ra-3dd342df.pth'))\n        \n        in_features = self.model.classifier.in_features\n        self.model.classifier = nn.Sequential(\n            nn.Linear(in_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(512, num_classes)\n        )\n        \n    def forward(self, x):\n        return self.model(x)\n\n# Dataset Class\nclass RetinopathyDataset(Dataset):\n    def __init__(self, df, image_dir, transform=None, preprocessing=None):\n        self.df = df\n        self.image_dir = image_dir\n        self.transform = transform\n        self.preprocessing = preprocessing\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        image_path = f\"{self.image_dir}/{self.df.iloc[idx]['id_code']}.png\"\n        image = self.preprocessing.preprocess_image(image_path)\n        \n        if self.transform:\n            image = self.transform(image=image)['image']\n        \n        label = self.df.iloc[idx]['diagnosis'] if 'diagnosis' in self.df.columns else -1\n        return image, label\n\n# Mixup Data Augmentation Function\ndef mixup_data(x, y, alpha=0.2, device='cuda'):\n    if alpha > 0:\n        lam = np.random.beta(alpha, alpha)\n    else:\n        lam = 1\n    batch_size = x.size()[0]\n    index = torch.randperm(batch_size).to(device)\n    mixed_x = lam * x + (1 - lam) * x[index]\n    y_a, y_b = y, y[index]\n    return mixed_x, y_a, y_b, lam\n\n# Mixup Loss Function\ndef mixup_criterion(criterion, pred, y_a, y_b, lam):\n    return lam * criterion(pred, y_a) + (1 - lam) * criterion(pred, y_b)\n\n# Prediction CSV Generation\ndef predict_and_generate_csv(model, test_loader, device, output_csv_path):\n    model.eval()\n    predictions = []\n    image_ids = []\n    with torch.no_grad():\n        for inputs, ids in test_loader:\n            inputs = inputs.to(device)\n            outputs = model(inputs)\n            preds = torch.argmax(outputs, dim=1)\n            predictions.extend(preds.cpu().numpy())\n            image_ids.extend(ids)\n    \n    output_df = pd.DataFrame({'id_code': image_ids, 'diagnosis': predictions})\n    output_df.to_csv(output_csv_path, index=False)\n    submission_df = pd.read_csv(\"submission.csv\")\n    print(submission_df.head())\n    print(submission_df.columns)\n    print(\"Unique diagnoses:\", submission_df['diagnosis'].unique())\n    print(\"Number of rows:\", len(submission_df))\n    print(\"Missing values:\", submission_df.isnull().sum())\n    print(f\"Predictions saved to {output_csv_path}\")\n\n# Training Loop for Each Fold\ndef train_fold(fold, model, train_loader, valid_loader, device, criterion, optimizer, scheduler, num_epochs=15):\n    scaler = GradScaler()\n    best_score = 0\n    \n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0\n        for inputs, targets in train_loader:\n            inputs, targets = inputs.to(device), targets.to(device)\n            inputs_mixed, targets_a, targets_b, lam = mixup_data(inputs, targets, alpha=0.2, device=device)\n            \n            optimizer.zero_grad()\n            with autocast():\n                outputs = model(inputs_mixed)\n                loss = mixup_criterion(criterion, outputs, targets_a, targets_b, lam)\n            \n            scaler.scale(loss).backward()\n            scaler.step(optimizer)\n            scaler.update()\n            train_loss += loss.item()\n        \n        model.eval()\n        valid_preds = []\n        valid_targets = []\n        with torch.no_grad(), autocast():\n            for inputs, targets in valid_loader:\n                inputs, targets = inputs.to(device), targets.to(device)\n                outputs = model(inputs)\n                preds = torch.argmax(outputs, dim=1)\n                valid_preds.extend(preds.cpu().numpy())\n                valid_targets.extend(targets.cpu().numpy())\n        \n        valid_score = cohen_kappa_score(valid_targets, valid_preds, weights='quadratic')\n        if valid_score > best_score:\n            best_score = valid_score\n            torch.save(model.state_dict(), f'best_model_fold_{fold}.pth')\n            \n        scheduler.step(valid_score)\n        print(f'Epoch {epoch+1}/{num_epochs}, Valid Kappa Score: {valid_score:.4f}, Best Score: {best_score:.4f}')\n    \n    return best_score\n\n# Main Function for Cross-Validation and Test Prediction\ndef main():\n    config = {\n        'SEED': 42,\n        'IMAGE_SIZE': 384,\n        'BATCH_SIZE': 32,\n        'NUM_EPOCHS': 15,\n        'N_FOLDS': 5,\n        'LEARNING_RATE': 2e-4,\n        'DATA_PATH': '/kaggle/input/aptos2019-blindness-detection',\n    }\n    \n    torch.manual_seed(config['SEED'])\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    train_df = pd.read_csv(f\"{config['DATA_PATH']}/train.csv\")\n    test_df = pd.read_csv(f\"{config['DATA_PATH']}/test.csv\")\n    preprocessing = AdvancedPreprocessing(image_size=config['IMAGE_SIZE'])\n    \n    transforms = A.Compose([\n        A.RandomRotate90(),\n        A.Flip(),\n        A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.1, rotate_limit=45),\n        A.Normalize(),\n        ToTensorV2()\n    ])\n    \n    skf = StratifiedKFold(n_splits=config['N_FOLDS'], shuffle=True, random_state=config['SEED'])\n    best_scores = []\n    for fold, (train_idx, valid_idx) in enumerate(skf.split(train_df, train_df['diagnosis']), 1):\n        train_dataset = RetinopathyDataset(train_df.iloc[train_idx], f\"{config['DATA_PATH']}/train_images\", transforms, preprocessing)\n        valid_dataset = RetinopathyDataset(train_df.iloc[valid_idx], f\"{config['DATA_PATH']}/train_images\", transforms, preprocessing)\n        train_loader = DataLoader(train_dataset, batch_size=config['BATCH_SIZE'], shuffle=True, num_workers=4)\n        valid_loader = DataLoader(valid_dataset, batch_size=config['BATCH_SIZE'], shuffle=False, num_workers=4)\n        \n        model = EfficientNetWithMixup(num_classes=5).to(device)\n        criterion = nn.CrossEntropyLoss()\n        optimizer = torch.optim.AdamW(model.parameters(), lr=config['LEARNING_RATE'])\n        scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max')\n        \n        best_score = train_fold(fold, model, train_loader, valid_loader, device, criterion, optimizer, scheduler, config['NUM_EPOCHS'])\n        best_scores.append(best_score)\n        \n    test_dataset = RetinopathyDataset(test_df, f\"{config['DATA_PATH']}/test_images\", transforms, preprocessing)\n    test_loader = DataLoader(test_dataset, batch_size=config['BATCH_SIZE'], shuffle=False)\n    \n    model.load_state_dict(torch.load(f'best_model_fold_{np.argmax(best_scores) + 1}.pth'))\n    predict_and_generate_csv(model, test_loader, device, \"submission.csv\")\n\nif __name__ == \"__main__\":\n    main()\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:13:35.878327Z","iopub.execute_input":"2024-11-12T06:13:35.878713Z"}},"outputs":[],"execution_count":null}]}