{"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":3951115,"sourceType":"datasetVersion","datasetId":1027206},{"sourceId":14420663,"sourceType":"datasetVersion","datasetId":9210449},{"sourceId":14452012,"sourceType":"datasetVersion","datasetId":9226367}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# !pip install -q timm\n# !pip install -q pretrainedmodels","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-output":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:00.696766Z","iopub.execute_input":"2026-01-09T17:59:00.696999Z","iopub.status.idle":"2026-01-09T17:59:09.722533Z","shell.execute_reply.started":"2026-01-09T17:59:00.696982Z","shell.execute_reply":"2026-01-09T17:59:09.721972Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nsys.path.append('../input/timm-pytorch-image-models/pytorch-image-models-master')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport copy\nimport time\nimport random\nimport pickle\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nimport torchvision\nfrom torchvision import models\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.cuda import amp\nfrom tqdm.notebook import tqdm\n\nfrom sklearn.model_selection import StratifiedShuffleSplit\nfrom sklearn.model_selection import train_test_split, StratifiedKFold\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.utils import class_weight\n\nfrom tqdm.notebook import tqdm\nfrom collections import defaultdict\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nimport timm\n# import pretrainedmodels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:09.723811Z","iopub.execute_input":"2026-01-09T17:59:09.724052Z","iopub.status.idle":"2026-01-09T17:59:26.800620Z","shell.execute_reply.started":"2026-01-09T17:59:09.724030Z","shell.execute_reply":"2026-01-09T17:59:26.800126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ROOT_DIR = \"/kaggle/input/cassava-yourself\"\nTRAIN_DIR = \"/kaggle/input/cassava-yourself/train_images\"\nTEST_DIR = \"/kaggle/input/cassava-leaf-disease-classification/test_images\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.801271Z","iopub.execute_input":"2026-01-09T17:59:26.801433Z","iopub.status.idle":"2026-01-09T17:59:26.803940Z","shell.execute_reply.started":"2026-01-09T17:59:26.801418Z","shell.execute_reply":"2026-01-09T17:59:26.803541Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CFG:\n    model_name = 'tf_efficientnet_b4_ns'\n    img_size = 512\n    scheduler = 'CosineAnnealingWarmRestarts'\n    T_max = 10\n    T_0 = 10\n    lr = 1e-4\n    min_lr = 1e-6\n    batch_size = 16\n    weight_decay = 1e-6\n    seed = 42\n    num_classes = 5\n    num_epochs = 10\n    n_fold = 5\n    NUM_FOLDS_TO_RUN = [2,]\n    smoothing = 0.2\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.804861Z","iopub.execute_input":"2026-01-09T17:59:26.805025Z","iopub.status.idle":"2026-01-09T17:59:26.876448Z","shell.execute_reply.started":"2026-01-09T17:59:26.805010Z","shell.execute_reply":"2026-01-09T17:59:26.876021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def set_seed(seed = 42):\n    '''Sets the seed of the entire notebook so results are the same every time we run.\n    This is for REPRODUCIBILITY.'''\n    np.random.seed(seed)\n    random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    # When running on the CuDNN backend, two further options must be set\n    torch.backends.cudnn.deterministic = True\n    # Set a fixed value for the hash seed\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    \nset_seed(CFG.seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.877024Z","iopub.execute_input":"2026-01-09T17:59:26.877161Z","iopub.status.idle":"2026-01-09T17:59:26.895303Z","shell.execute_reply.started":"2026-01-09T17:59:26.877147Z","shell.execute_reply":"2026-01-09T17:59:26.894904Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(f\"{ROOT_DIR}/train.csv\")\n\n# skf = StratifiedKFold(n_splits=CFG.n_fold)\n# for fold, ( _, val_) in enumerate(skf.split(X=df, y=df.label)):\n#     df.loc[val_ , \"kfold\"] = int(fold)\n    \n# df['kfold'] = df['kfold'].astype(int)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.895866Z","iopub.execute_input":"2026-01-09T17:59:26.896010Z","iopub.status.idle":"2026-01-09T17:59:26.938777Z","shell.execute_reply.started":"2026-01-09T17:59:26.895995Z","shell.execute_reply":"2026-01-09T17:59:26.938376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CassavaLeafDataset(nn.Module):\n    def __init__(self, root_dir, df, transforms=None):\n        self.root_dir = root_dir\n        self.df = df\n        self.transforms = transforms\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        img_path = os.path.join(self.root_dir, self.df.iloc[index, 0])\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        label = self.df.iloc[index, 1]\n        \n        if self.transforms:\n            img = self.transforms(image=img)[\"image\"]\n            \n        return img, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.939354Z","iopub.execute_input":"2026-01-09T17:59:26.939500Z","iopub.status.idle":"2026-01-09T17:59:26.943078Z","shell.execute_reply.started":"2026-01-09T17:59:26.939486Z","shell.execute_reply":"2026-01-09T17:59:26.942708Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_transforms = {\n    \"train\": A.Compose([\n        A.RandomResizedCrop(\n            size=(CFG.img_size, CFG.img_size),\n            scale=(0.8, 1.0),\n            ratio=(0.75, 1.33),\n            p=1.0\n        ),\n        A.Transpose(p=0.5),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.ShiftScaleRotate(p=0.5),  # OK for now\n        A.HueSaturationValue(\n            hue_shift_limit=0.2,\n            sat_shift_limit=0.2,\n            val_shift_limit=0.2,\n            p=0.5\n        ),\n        A.RandomBrightnessContrast(\n            brightness_limit=(-0.1, 0.1),\n            contrast_limit=(-0.1, 0.1),\n            p=0.5\n        ),\n        A.Normalize(\n            mean=[0.485, 0.456, 0.406],\n            std=[0.229, 0.224, 0.225],\n            max_pixel_value=255.0,\n            p=1.0\n        ),\n        A.CoarseDropout(p=0.5),\n        ToTensorV2()\n    ], p=1.0),\n\n    \"valid\": A.Compose([\n        A.CenterCrop(CFG.img_size, CFG.img_size, p=1.),\n        A.Resize(CFG.img_size, CFG.img_size),\n        A.Normalize(\n                mean=[0.485, 0.456, 0.406], \n                std=[0.229, 0.224, 0.225], \n                max_pixel_value=255.0, \n                p=1.0\n            ),\n        ToTensorV2()], p=1.)\n}\n\nTTA_TRANSFORMS = [\n    A.Compose([\n        A.Resize(CFG.img_size, CFG.img_size),\n        A.Normalize(mean=[0.485,0.456,0.406],\n                    std=[0.229,0.224,0.225]),\n        ToTensorV2()\n    ]),\n    A.Compose([\n        A.HorizontalFlip(p=1.0),\n        A.Resize(CFG.img_size, CFG.img_size),\n        A.Normalize(mean=[0.485,0.456,0.406],\n                    std=[0.229,0.224,0.225]),\n        ToTensorV2()\n    ]),\n    A.Compose([\n        A.VerticalFlip(p=1.0),\n        A.Resize(CFG.img_size, CFG.img_size),\n        A.Normalize(mean=[0.485,0.456,0.406],\n                    std=[0.229,0.224,0.225]),\n        ToTensorV2()\n    ]),\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.943609Z","iopub.execute_input":"2026-01-09T17:59:26.943754Z","iopub.status.idle":"2026-01-09T17:59:26.961793Z","shell.execute_reply.started":"2026-01-09T17:59:26.943740Z","shell.execute_reply":"2026-01-09T17:59:26.961382Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Use custom model","metadata":{}},{"cell_type":"code","source":"class StochasticDepth(nn.Module):\n    def __init__(self, drop_rate):\n        super().__init__()\n        self.drop_rate = drop_rate\n\n    def forward(self, x):\n        if not self.training or self.drop_rate == 0.0:\n            return x\n        keep_prob = 1.0 - self.drop_rate\n        shape = (x.shape[0],) + (1,) * (x.ndim - 1)\n        rand = keep_prob + torch.rand(shape, device=x.device)\n        mask = torch.floor(rand)\n        return x / keep_prob * mask\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.962352Z","iopub.execute_input":"2026-01-09T17:59:26.962497Z","iopub.status.idle":"2026-01-09T17:59:26.966539Z","shell.execute_reply.started":"2026-01-09T17:59:26.962483Z","shell.execute_reply":"2026-01-09T17:59:26.966124Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class GeM(nn.Module):\n    def __init__(self, p=3.0, eps=1e-6):\n        super().__init__()\n        self.p = nn.Parameter(torch.ones(1) * p)\n        self.eps = eps\n\n    def forward(self, x):\n        p = self.p.clamp(min=0.1)\n        x = x.clamp(min=self.eps)\n        x = x.pow(p)\n        x = F.adaptive_avg_pool2d(x, 1)\n        return x.pow(1.0 / p).flatten(1)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.967999Z","iopub.execute_input":"2026-01-09T17:59:26.968161Z","iopub.status.idle":"2026-01-09T17:59:26.981581Z","shell.execute_reply.started":"2026-01-09T17:59:26.968146Z","shell.execute_reply":"2026-01-09T17:59:26.981177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SEBlock(nn.Module):\n    def __init__(self, channels, reduction=16, drop_rate=0.2):\n        super().__init__()\n        self.fc1 = nn.Linear(channels, channels // reduction)\n        self.fc2 = nn.Linear(channels // reduction, channels)\n        self.sd = StochasticDepth(drop_rate)\n\n    def forward(self, x):\n        se = F.relu(self.fc1(x))\n        se = torch.sigmoid(self.fc2(se))\n        x = x * se\n        return self.sd(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.982106Z","iopub.execute_input":"2026-01-09T17:59:26.982269Z","iopub.status.idle":"2026-01-09T17:59:26.991721Z","shell.execute_reply.started":"2026-01-09T17:59:26.982256Z","shell.execute_reply":"2026-01-09T17:59:26.991334Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**backbone ensemble models**","metadata":{}},{"cell_type":"code","source":"class EfficientNetV2L_Backbone(nn.Module):\n    def __init__(self):\n        super().__init__()\n        m = timm.create_model(\n            \"tf_efficientnetv2_l.in21k_ft_in1k\",\n            pretrained=False,\n            features_only=True\n        )\n        self.features = m\n        self.gem = GeM()\n\n    def forward(self, x):\n        x = self.features(x)[-1]\n        return self.gem(x)\n\nclass ConvNeXtB_Backbone(nn.Module):\n    def __init__(self):\n        super().__init__()\n        m = timm.create_model(\n            \"convnext_base.fb_in22k\",\n            pretrained=False,\n            features_only=True\n        )\n        self.features = m\n        self.gem = GeM()\n\n    def forward(self, x):\n        x = self.features(x)[-1]\n        return self.gem(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:26.992282Z","iopub.execute_input":"2026-01-09T17:59:26.992434Z","iopub.status.idle":"2026-01-09T17:59:27.002410Z","shell.execute_reply.started":"2026-01-09T17:59:26.992419Z","shell.execute_reply":"2026-01-09T17:59:27.001997Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ClassificationHead(nn.Module):\n    def __init__(self, in_dim, num_classes):\n        super().__init__()\n        self.head = nn.Sequential(\n            nn.BatchNorm1d(in_dim),\n            nn.Linear(in_dim, 1024),\n            nn.ReLU(inplace=True),\n            nn.BatchNorm1d(1024),\n            SEBlock(1024, drop_rate=0.2),\n            nn.Dropout(0.5),\n            nn.Linear(1024, num_classes)\n        )\n\n    def forward(self, x):\n        return self.head(x)\n\nclass EnsembleModel(nn.Module):\n    def __init__(self, num_classes):\n        super().__init__()\n\n        self.convnext = ConvNeXtB_Backbone()        # → 1024\n        self.effv2    = EfficientNetV2L_Backbone()  # → 640\n\n        self.head_cnx = ClassificationHead(1024, num_classes)\n        self.head_eff = ClassificationHead(640, num_classes)\n\n    def forward(self, x):\n        f1 = self.convnext(x)   # [B, 1024]\n        f2 = self.effv2(x)      # [B, 640]\n\n        logit1 = self.head_cnx(f1)\n        logit2 = self.head_eff(f2)\n\n        return (logit1 + logit2) / 2\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:27.002967Z","iopub.execute_input":"2026-01-09T17:59:27.003107Z","iopub.status.idle":"2026-01-09T17:59:27.013608Z","shell.execute_reply.started":"2026-01-09T17:59:27.003093Z","shell.execute_reply":"2026-01-09T17:59:27.013188Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Adding MixUp&CutMix","metadata":{}},{"cell_type":"markdown","source":"## Adding minimal EMA wrapper","metadata":{}},{"cell_type":"code","source":"def freeze_effv2_l(model, freeze_ratio=0.7):\n    blocks = model.features.blocks\n    n_blocks = len(blocks)\n    n_freeze = int(n_blocks * freeze_ratio)\n\n    for i in range(n_freeze):\n        for p in blocks[i].parameters():\n            p.requires_grad = False\n\n    print(f\"EffNetV2-L: froze {n_freeze}/{n_blocks} blocks\")\n\ndef freeze_convnext_ratio(model, freeze_ratio=0.25):\n    children = list(model.features.children())\n    n_freeze = int(len(children) * freeze_ratio)\n\n    for i in range(n_freeze):\n        for p in children[i].parameters():\n            p.requires_grad = False\n\n    print(f\"ConvNeXt-B: froze {n_freeze}/{len(children)} feature children\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:27.043880Z","iopub.execute_input":"2026-01-09T17:59:27.044028Z","iopub.status.idle":"2026-01-09T17:59:27.057226Z","shell.execute_reply.started":"2026-01-09T17:59:27.044015Z","shell.execute_reply":"2026-01-09T17:59:27.056532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = EnsembleModel(num_classes=CFG.num_classes).to(CFG.device)\n\nfreeze_effv2_l(model.effv2, freeze_ratio=0.5)\n# freeze_convnext_ratio(model.convnext)\n\n\n# ===== AMP (same as timm usage) =====\nscaler = torch.amp.GradScaler(\"cuda\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T17:59:27.057764Z","iopub.execute_input":"2026-01-09T17:59:27.057914Z","iopub.status.idle":"2026-01-09T17:59:40.915223Z","shell.execute_reply.started":"2026-01-09T17:59:27.057894Z","shell.execute_reply":"2026-01-09T17:59:40.914701Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\n\ndef build_test_df(TEST_DIR):\n    image_ids = sorted([\n        f for f in os.listdir(TEST_DIR)\n        if f.lower().endswith((\".jpg\", \".png\", \".jpeg\"))\n    ])\n    return pd.DataFrame({\n        \"image_id\": image_ids,\n        \"label\": 0   # dummy label, never used\n    })\n\ntest_df = build_test_df(TEST_DIR)\nprint(test_df.head(), len(test_df))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@torch.no_grad()\ndef prediction(\n    model,\n    TEST_DIR,\n    df_test,\n    device,\n    batch_size,\n    num_workers=4\n):\n    model.eval()\n\n    all_probs = []\n    image_ids = df_test[\"image_id\"].values\n\n    # ======================================\n    # Loop over TTA transforms\n    # ======================================\n    for tta_idx, tta_tfms in enumerate(TTA_TRANSFORMS):\n        print(f\"TTA {tta_idx+1}/{len(TTA_TRANSFORMS)}\")\n\n        test_dataset = CassavaLeafDataset(\n            TEST_DIR,\n            df_test,\n            transforms=tta_tfms\n        )\n\n        test_loader = DataLoader(\n            test_dataset,\n            batch_size=batch_size,\n            shuffle=False,\n            num_workers=num_workers,\n            pin_memory=True\n        )\n\n        probs_tta = []\n\n        pbar = tqdm(test_loader, desc=f\"TTA-{tta_idx+1}\", leave=False)\n\n        for inputs, _ in pbar:  # labels are dummy / ignored\n            inputs = inputs.to(device)\n\n            with torch.amp.autocast(device_type=\"cuda\"):\n\n\n                outputs = model(inputs)\n                probs = torch.softmax(outputs, dim=1)\n\n            probs_tta.append(probs.cpu())\n\n        probs_tta = torch.cat(probs_tta, dim=0)\n        all_probs.append(probs_tta)\n\n    # ======================================\n    # Average TTA predictions\n    # ======================================\n    mean_probs = torch.stack(all_probs).mean(dim=0)\n\n    y_pred = mean_probs.argmax(dim=1).numpy()\n\n    return image_ids, y_pred\n","metadata":{"trusted":true,"execution":{"execution_failed":"2026-01-09T18:04:41.888Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ckpt_path = \"/kaggle/input/pytorch-cassava-model-base-ver3/best_model.pth\"  # CHANGE\nmodel.load_state_dict(torch.load(ckpt_path, map_location=CFG.device))","metadata":{"trusted":true,"execution":{"execution_failed":"2026-01-09T18:04:41.888Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_ids, y_pred = prediction(\n    model=model,\n    TEST_DIR=TEST_DIR,\n    df_test=test_df,\n    device=CFG.device,\n    batch_size=CFG.batch_size\n)\n\nsubmission = pd.DataFrame({\n    \"image_id\": image_ids,\n    \"label\": y_pred\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()\n","metadata":{"trusted":true,"execution":{"execution_failed":"2026-01-09T18:04:41.888Z"}},"outputs":[],"execution_count":null}]}