{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":14514638,"sourceType":"datasetVersion","datasetId":9270464}],"dockerImageVersionId":31260,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =====================================================\n# 🔥 ONE-CELL ENSEMBLE + PREDICT (4-FOLD RESNET)\n# =====================================================\n\nimport os, gc\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\n\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport cv2\n\nimport timm\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import accuracy_score\n\n# =====================\n# CONFIG\n# =====================\nSEED = 42\nN_FOLDS = 4\nNUM_CLASSES = 5\nBATCH_SIZE = 64\nNUM_WORKERS = 4\nDEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n\nDATA_DIR = \"/kaggle/input/cassava-leaf-disease-classification\"\nTRAIN_CSV = f\"{DATA_DIR}/train.csv\"\nTEST_CSV  = f\"{DATA_DIR}/sample_submission.csv\"\nTRAIN_IMG_DIR = f\"{DATA_DIR}/train_images\"\nTEST_IMG_DIR  = f\"{DATA_DIR}/test_images\"\n\nCKPT_DIR = \"/kaggle/input/resnet\"   # resnet_fold{i}_best.pth\nIMG_SIZE = 384\n\n# =====================\n# LOAD DATA\n# =====================\ntrain_df = pd.read_csv(TRAIN_CSV)\ntest_df  = pd.read_csv(TEST_CSV)\nlabels = train_df.label.values\n\n# =====================\n# AUGMENTATION (NO RANDOM!)\n# =====================\nvalid_aug = A.Compose([\n    A.Resize(IMG_SIZE, IMG_SIZE),\n    A.Normalize(),\n    ToTensorV2()\n])\n\n# =====================\n# DATASET\n# =====================\nclass CassavaDataset(Dataset):\n    def __init__(self, df, img_dir, aug, is_test=False):\n        self.df = df.reset_index(drop=True)\n        self.img_dir = img_dir\n        self.aug = aug\n        self.is_test = is_test\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = f\"{self.img_dir}/{row.image_id}\"\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = self.aug(image=img)['image']\n\n        if self.is_test:\n            return img\n        return img, row.label\n\n# =====================\n# INIT STORAGE\n# =====================\noof_pred  = np.zeros((len(train_df), NUM_CLASSES), dtype=np.float32)\ntest_pred = np.zeros((len(test_df), NUM_CLASSES), dtype=np.float32)\n\n# =====================\n# STRATIFIED K-FOLD\n# =====================\nskf = StratifiedKFold(N_FOLDS, shuffle=True, random_state=SEED)\n\n# =====================\n# LOOP OVER FOLDS\n# =====================\nfor fold, (_, val_idx) in enumerate(skf.split(train_df, train_df.label)):\n    print(f\"\\n=== LOAD MODEL FOLD {fold} ===\")\n\n    # ---------- LOAD MODEL ----------\n    model = timm.create_model(\n        'resnet50d',\n        pretrained=False,\n        num_classes=NUM_CLASSES\n    ).to(DEVICE)\n\n    ckpt_path = f\"{CKPT_DIR}/resnet_fold{fold}_best.pth\"\n    model.load_state_dict(torch.load(ckpt_path, map_location=DEVICE))\n    model.eval()\n\n    # ---------- VALID OOF ----------\n    val_df = train_df.iloc[val_idx]\n    val_ds = CassavaDataset(val_df, TRAIN_IMG_DIR, valid_aug)\n    val_loader = DataLoader(\n        val_ds,\n        batch_size=BATCH_SIZE,\n        shuffle=False,\n        num_workers=NUM_WORKERS,\n        pin_memory=True\n    )\n\n    ptr = 0\n    with torch.no_grad():\n        for imgs, _ in tqdm(val_loader, leave=False):\n            imgs = imgs.to(DEVICE)\n            probs = torch.softmax(model(imgs), dim=1).cpu().numpy()\n            bs = probs.shape[0]\n            oof_pred[val_idx[ptr:ptr+bs]] += probs\n            ptr += bs\n\n    # ---------- TEST PRED ----------\n    test_ds = CassavaDataset(test_df, TEST_IMG_DIR, valid_aug, is_test=True)\n    test_loader = DataLoader(\n        test_ds,\n        batch_size=BATCH_SIZE,\n        shuffle=False,\n        num_workers=NUM_WORKERS,\n        pin_memory=True\n    )\n\n    fold_test_pred = []\n    with torch.no_grad():\n        for imgs in tqdm(test_loader, leave=False):\n            imgs = imgs.to(DEVICE)\n            probs = torch.softmax(model(imgs), dim=1).cpu().numpy()\n            fold_test_pred.append(probs)\n\n    test_pred += np.vstack(fold_test_pred)\n\n    del model\n    gc.collect()\n    torch.cuda.empty_cache()\n\n# =====================\n# AVERAGE\n# =====================\noof_pred  /= N_FOLDS\ntest_pred /= N_FOLDS\n\n# =====================\n# OOF ACC\n# =====================\noof_label = oof_pred.argmax(axis=1)\nacc = accuracy_score(labels, oof_label)\nprint(\"\\n🔥 FINAL OOF ACC:\", acc)\n\n# =====================\n# SUBMISSION\n# =====================\ntest_df['label'] = test_pred.argmax(axis=1)\ntest_df.to_csv(\"submission.csv\", index=False)\nprint(\"✅ Saved submission.csv\")\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-16T07:14:23.213919Z","iopub.execute_input":"2026-01-16T07:14:23.214134Z","iopub.status.idle":"2026-01-16T07:17:57.055952Z","shell.execute_reply.started":"2026-01-16T07:14:23.214104Z","shell.execute_reply":"2026-01-16T07:17:57.055194Z"}},"outputs":[],"execution_count":null}]}