{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Modification B: label smoothing + CutMix, ResNeXt50, 384 px, 8 epochs, fold 0\n\nBuilt on my label smoothing notebook (`train-label-smoothing-384`), with one addition: **CutMix**. The rest of the script is explained section by section in `train-faithful-512-15ep`.\n\n**What changed:** With probability 0.5 for each batch, a random rectangle from another image in the same batch is pasted onto each image, and the loss is a mix of the two labels weighted by the pasted area. CutMix is meant to act as a regulariser, which suits the noisy labels in this competition.\n\n**Results (fold 0):**\n\n| Epoch | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |\n|---|---|---|---|---|---|---|---|---|\n| Accuracy | 0.8389 | 0.8638 | 0.8686 | 0.8737 | 0.8700 | 0.8653 | **0.8743** | 0.8743 |\n\n| Version | Validation | Private LB | Public LB |\n|---|---|---|---|\n| Baseline (384 px, no TTA) | 0.87388 | 0.8742 | 0.8706 |\n| Label smoothing (no TTA) | 0.87986 | 0.8779 | 0.8792 |\n| Label smoothing + flip TTA | not measured | 0.8816 | 0.8876 |\n| **Label smoothing + CutMix + flip TTA** | **0.87425** | **0.8742** | **0.8742** |\n\n**Result: No improvement.** CutMix reached about the same validation accuracy as the baseline and clearly below label smoothing alone (0.87986). On the leaderboard, the fair comparison is against label smoothing with TTA (0.8816 / 0.8876), and CutMix was lower on both splits. It was also about 4% slower per epoch (about 807 s against 766 s).\n\n**Interpretation (a guess, not tested):** CutMix makes each training step harder, and 8 epochs may be too few for it to pay off. I kept the run as a documented negative result. The saved weights are from epoch 7, because the code only saves when the accuracy strictly improves and epoch 8 tied it.\n\nThe full log and output files are in the committed run of this notebook. Later saves only changed text.\n\n**Related notebooks:**\n- Baseline run (384 px, 8 epochs): https://www.kaggle.com/code/ananyaamadhursharma/cassava-leaf-disease-resnext50\n- Label smoothing run: https://www.kaggle.com/code/ananyaamadhursharma/train-label-smoothing-384\n- Faithful 512 px / 15 epoch run: https://www.kaggle.com/code/ananyaamadhursharma/train-faithful-512-15ep","metadata":{}},{"cell_type":"code","source":"# ====================================================\n# directory settings\n# ====================================================\n\nimport os\n\nOUTPUT_DIR = './'\nif not os.path.exists(OUTPUT_DIR):\n    os.makedirs(OUTPUT_DIR)\n\nTRAIN_PATH = '../input/cassava-leaf-disease-classification/train_images'\nTEST_PATH = '../input/cassava-leaf-disease-classification/test_images'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:20.980548Z","iopub.execute_input":"2026-10-14T22:13:20.980862Z","iopub.status.idle":"2026-10-14T22:13:20.987347Z","shell.execute_reply.started":"2026-10-14T22:13:20.980714Z","shell.execute_reply":"2026-10-14T22:13:20.986394Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# CFG\n# ====================================================\n\nclass CFG:\n    print_freq=100\n    num_workers = 4\n    model_name = 'resnext50_32x4d'\n    size = 384\n    epochs = 8\n    factor = 0.2\n    patience = 5\n    eps = 1e-6\n    lr = 1e-4\n    min_lr = 1e-6\n    batch_size = 16\n    weight_decay = 1e-6\n    gradient_accumulation_steps = 1\n    max_grad_norm = 1000\n    seed = 42\n    target_size = 5\n    target_col = 'label'\n    n_fold = 5\n    trn_fold = [0]\n    cutmix_prob = 0.5","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:20.989704Z","iopub.execute_input":"2026-10-14T22:13:20.989928Z","iopub.status.idle":"2026-10-14T22:13:21.010549Z","shell.execute_reply.started":"2026-10-14T22:13:20.989908Z","shell.execute_reply":"2026-10-14T22:13:21.008893Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# libraries\n# ====================================================\n\nimport sys\nsys.path.append('../input/pytorch-image-models/pytorch-image-models-master')\nimport os\nimport math\nimport time\nimport random\nimport shutil\nfrom pathlib import Path\nfrom contextlib import contextmanager\nfrom collections import defaultdict, Counter\nimport scipy as sp\nimport numpy as np\nimport pandas as pd\nfrom sklearn import preprocessing\nfrom sklearn.metrics import accuracy_score\nfrom tqdm.auto import tqdm\nfrom functools import partial\nimport cv2\nfrom PIL import Image\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.optim import Adam, SGD\nimport torchvision.models as models\nfrom torch.nn.parameter import Parameter\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom albumentations import (Compose, Normalize, Resize, RandomResizedCrop, HorizontalFlip, VerticalFlip, ShiftScaleRotate, Transpose)\nfrom albumentations.pytorch import ToTensorV2\nfrom albumentations import ImageOnlyTransform\nimport timm\nimport warnings \nwarnings.filterwarnings('ignore')\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nfrom matplotlib import pyplot as plt\nimport joblib","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:21.012032Z","iopub.execute_input":"2026-10-14T22:13:21.012406Z","iopub.status.idle":"2026-10-14T22:13:28.166783Z","shell.execute_reply.started":"2026-10-14T22:13:21.012372Z","shell.execute_reply":"2026-10-14T22:13:28.164622Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# utils\n# ====================================================\n\ndef get_score(y_true, y_pred):\n    return accuracy_score(y_true, y_pred)\n\n@contextmanager\ndef timer(name):\n    t0 = time.time()\n    LOGGER.info(f'[{name}] start')\n    yield\n    LOGGER.info(f'[{name}] done in {time.time() - t0:.0f} s.')\n\ndef init_logger(log_file=OUTPUT_DIR+'train.log'):\n    from logging import getLogger, INFO, FileHandler,  Formatter,  StreamHandler\n    logger = getLogger(__name__)\n    logger.setLevel(INFO)\n    handler1 = StreamHandler()\n    handler1.setFormatter(Formatter(\"%(message)s\"))\n    handler2 = FileHandler(filename=log_file)\n    handler2.setFormatter(Formatter(\"%(message)s\"))\n    logger.addHandler(handler1)\n    logger.addHandler(handler2)\n    return logger\n\nLOGGER = init_logger()\n\ndef seed_torch(seed=42):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n\nseed_torch(seed=CFG.seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:28.169048Z","iopub.execute_input":"2026-10-14T22:13:28.169391Z","iopub.status.idle":"2026-10-14T22:13:28.192421Z","shell.execute_reply.started":"2026-10-14T22:13:28.169353Z","shell.execute_reply":"2026-10-14T22:13:28.190956Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# dataset\n# ====================================================\n\nclass TrainDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.file_names = df['image_id'].values\n        self.labels = df['label'].values\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        file_name = self.file_names[idx]\n        file_path = f'{TRAIN_PATH}/{file_name}'\n        image = cv2.imread(file_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        if self.transform:\n            augmented = self.transform(image=image)\n            image = augmented['image']\n        label = torch.tensor(self.labels[idx]).long()\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:28.194023Z","iopub.execute_input":"2026-10-14T22:13:28.194347Z","iopub.status.idle":"2026-10-14T22:13:28.219798Z","shell.execute_reply.started":"2026-10-14T22:13:28.194313Z","shell.execute_reply":"2026-10-14T22:13:28.218446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# transformations\n# ====================================================\n\ndef get_transforms(*, data):\n    \n    if data == 'train':\n        return Compose([\n            RandomResizedCrop(CFG.size, CFG.size),\n            Transpose(p=0.5),\n            HorizontalFlip(p=0.5),\n            VerticalFlip(p=0.5),\n            ShiftScaleRotate(p=0.5),\n            Normalize(\n                mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225],\n            ),\n            ToTensorV2(),\n        ])\n    \n    elif data == 'valid':\n       return Compose([\n           Resize(CFG.size, CFG.size),\n           Normalize(\n               mean=[0.485, 0.456, 0.406],\n               std=[0.229, 0.224, 0.225],\n           ),\n           ToTensorV2(),\n       ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:28.221485Z","iopub.execute_input":"2026-10-14T22:13:28.221816Z","iopub.status.idle":"2026-10-14T22:13:28.236394Z","shell.execute_reply.started":"2026-10-14T22:13:28.221776Z","shell.execute_reply":"2026-10-14T22:13:28.23471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# MODEL INITIALIZATION - LOAD PRETRAINED WEIGHTS LOCALLY\n# ====================================================\n\nimport os\nimport torch\nimport torch.nn as nn\n\nPRETRAINED_PATH = (\n    \"/kaggle/input/datasets/ananyaamadhursharma/\"\n    \"resnext50-pretrained-weights/pretrained_weights/\"\n    \"resnext50_32x4d_ra-d733960d.pth\"\n)\n\nclass CustomResNext(nn.Module):\n\n    def __init__(\n        self,\n        model_name=\"resnext50_32x4d\",\n        pretrained=False\n    ):\n        super().__init__()\n\n        # Create the model without downloading anything.\n        self.model = timm.create_model(\n            model_name,\n            pretrained=False\n        )\n\n        # Load pretrained ImageNet weights from Kaggle.\n        if pretrained:\n\n            if not os.path.isfile(PRETRAINED_PATH):\n                raise FileNotFoundError(\n                    \"Weights file not found at: \"\n                    + PRETRAINED_PATH\n                )\n\n            checkpoint = torch.load(\n                PRETRAINED_PATH,\n                map_location=\"cpu\"\n            )\n\n            # Handle common checkpoint formats.\n            if isinstance(checkpoint, dict):\n                for key in (\"state_dict\", \"model_state_dict\"):\n                    if key in checkpoint and isinstance(\n                        checkpoint[key], dict\n                    ):\n                        checkpoint = checkpoint[key]\n                        break\n\n            # Remove the prefix if saved using DataParallel.\n            if (\n                isinstance(checkpoint, dict)\n                and len(checkpoint) > 0\n                and all(\n                    key.startswith(\"module.\")\n                    for key in checkpoint.keys()\n                )\n            ):\n                checkpoint = {\n                    key[len(\"module.\"):]: value\n                    for key, value in checkpoint.items()\n                }\n\n            # Load the pretrained weights.\n            self.model.load_state_dict(\n                checkpoint,\n                strict=True\n            )\n\n            print(\"Pretrained ResNeXt50 weights loaded successfully.\")\n\n        # Replace the 1000-class ImageNet head with 5 classes.\n        n_features = self.model.fc.in_features\n\n        self.model.fc = nn.Linear(\n            n_features,\n            CFG.target_size\n        )\n\n    def forward(self, x):\n        return self.model(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:28.23954Z","iopub.execute_input":"2026-10-14T22:13:28.239863Z","iopub.status.idle":"2026-10-14T22:13:28.259502Z","shell.execute_reply.started":"2026-10-14T22:13:28.239826Z","shell.execute_reply":"2026-10-14T22:13:28.25784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(\"Checking pretrained weights...\")\n\nprint(\"File exists:\", os.path.isfile(PRETRAINED_PATH))\nprint(\"Path:\", PRETRAINED_PATH)\n\nif os.path.isfile(PRETRAINED_PATH):\n    print(\n        \"File size (MB):\",\n        round(os.path.getsize(PRETRAINED_PATH) / (1024 * 1024), 2)\n    )\nelse:\n    print(\"\\nSearching attached Kaggle inputs...\")\n\n    for root, dirs, files in os.walk(\"/kaggle/input\"):\n        for filename in files:\n            if filename == \"resnext50_32x4d_ra-d733960d.pth\":\n                print(\"Found file:\", os.path.join(root, filename))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:28.261733Z","iopub.execute_input":"2026-10-14T22:13:28.262049Z","iopub.status.idle":"2026-10-14T22:13:28.300991Z","shell.execute_reply.started":"2026-10-14T22:13:28.262013Z","shell.execute_reply":"2026-10-14T22:13:28.298917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# TEST MODEL INITIALIZATION\n# ====================================================\n\nassert os.path.isfile(PRETRAINED_PATH), (\n    \"Pretrained weights file is missing.\"\n)\n\ntest_model = CustomResNext(\n    CFG.model_name,\n    pretrained=True\n).to(device)\n\ntest_model.eval()\n\nwith torch.no_grad():\n    test_input = torch.randn(\n        1, 3, CFG.size, CFG.size\n    ).to(device)\n\n    test_output = test_model(test_input)\n\nprint(\"Device:\", device)\nprint(\"Input shape:\", tuple(test_input.shape))\nprint(\"Output shape:\", tuple(test_output.shape))\nprint(\"Expected output shape:\", (1, CFG.target_size))\n\nassert test_output.shape == (1, CFG.target_size)\n\nprint(\"MODEL TEST PASSED\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:28.302365Z","iopub.execute_input":"2026-10-14T22:13:28.302574Z","iopub.status.idle":"2026-10-14T22:13:30.025808Z","shell.execute_reply.started":"2026-10-14T22:13:28.302553Z","shell.execute_reply":"2026-10-14T22:13:30.024627Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# helper functions\n# ====================================================\n\nclass AverageMeter(object):\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n\n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count\n\n\ndef asMinutes(s):\n    m = math.floor(s / 60)\n    s -= m * 60\n    return '%dm %ds' % (m, s)\n\n\ndef timeSince(since, percent):\n    now = time.time()\n    s = now - since\n    es = s / (percent)\n    rs = es - s\n    return '%s (remain %s)' % (asMinutes(s), asMinutes(rs))\n\n# ====================================================\n# CutMix helper: random rectangle to paste\n# ====================================================\ndef rand_bbox(size, lam):\n    H, W = size[2], size[3]\n    cut_rat = np.sqrt(1. - lam)\n    cut_w, cut_h = int(W * cut_rat), int(H * cut_rat)\n    cx, cy = np.random.randint(W), np.random.randint(H)\n    x1 = int(np.clip(cx - cut_w // 2, 0, W))\n    x2 = int(np.clip(cx + cut_w // 2, 0, W))\n    y1 = int(np.clip(cy - cut_h // 2, 0, H))\n    y2 = int(np.clip(cy + cut_h // 2, 0, H))\n    return x1, y1, x2, y2\n\n\ndef train_fn(train_loader, model, criterion, optimizer, epoch, scheduler, device):\n    batch_time = AverageMeter()\n    data_time = AverageMeter()\n    losses = AverageMeter()\n    scores = AverageMeter()\n    model.train()\n    start = end = time.time()\n    global_step = 0\n    for step, (images, labels) in enumerate(train_loader):\n        data_time.update(time.time() - end)\n        images = images.to(device)\n        labels = labels.to(device)\n        batch_size = labels.size(0)\n        if np.random.rand() < CFG.cutmix_prob:\n            lam = np.random.beta(1.0, 1.0)\n            rand_index = torch.randperm(images.size(0)).to(device)\n            target_a, target_b = labels, labels[rand_index]\n            x1, y1, x2, y2 = rand_bbox(images.size(), lam)\n            images[:, :, y1:y2, x1:x2] = images[rand_index, :, y1:y2, x1:x2]\n            lam = 1 - ((x2 - x1) * (y2 - y1) / (images.size(-1) * images.size(-2)))\n            y_preds = model(images)\n            loss = lam * criterion(y_preds, target_a) + (1 - lam) * criterion(y_preds, target_b)\n        else:\n            y_preds = model(images)\n            loss = criterion(y_preds, labels)\n        losses.update(loss.item(), batch_size)\n        if CFG.gradient_accumulation_steps > 1:\n            loss = loss / CFG.gradient_accumulation_steps\n        else:\n            loss.backward()\n        grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), CFG.max_grad_norm)\n        if (step + 1) % CFG.gradient_accumulation_steps == 0:\n            optimizer.step()\n            optimizer.zero_grad()\n            global_step += 1\n        batch_time.update(time.time() - end)\n        end = time.time()\n        if step % CFG.print_freq == 0 or step == (len(train_loader)-1):\n            print('Epoch: [{0}][{1}/{2}] '\n                  'Data {data_time.val:.3f} ({data_time.avg:.3f}) '\n                  'Elapsed {remain:s} '\n                  'Loss: {loss.val:.4f}({loss.avg:.4f}) '\n                  'Grad: {grad_norm:.4f}  '\n                  .format(\n                   epoch+1, step, len(train_loader), batch_time=batch_time,\n                   data_time=data_time, loss=losses,\n                   remain=timeSince(start, float(step+1)/len(train_loader)),\n                   grad_norm=grad_norm,\n                   ))\n    return losses.avg\n\ndef valid_fn(valid_loader, model, criterion, device):\n    batch_time = AverageMeter()\n    data_time = AverageMeter()\n    losses = AverageMeter()\n    scores = AverageMeter()\n    model.eval()\n    preds = []\n    start = end = time.time()\n    for step, (images, labels) in enumerate(valid_loader):\n        data_time.update(time.time() - end)\n        images = images.to(device)\n        labels = labels.to(device)\n        batch_size = labels.size(0)\n        with torch.no_grad():\n            y_preds = model(images)\n        loss = criterion(y_preds, labels)\n        losses.update(loss.item(), batch_size)\n        preds.append(y_preds.softmax(1).to('cpu').numpy())\n        if CFG.gradient_accumulation_steps > 1:\n            loss = loss / CFG.gradient_accumulation_steps\n        batch_time.update(time.time() - end)\n        end = time.time()\n        if step % CFG.print_freq == 0 or step == (len(valid_loader)-1):\n            print('EVAL: [{0}/{1}] '\n                  'Data {data_time.val:.3f} ({data_time.avg:.3f}) '\n                  'Elapsed {remain:s} '\n                  'Loss: {loss.val:.4f}({loss.avg:.4f}) '\n                  .format(\n                   step, len(valid_loader), batch_time=batch_time,\n                   data_time=data_time, loss=losses,\n                   remain=timeSince(start, float(step+1)/len(valid_loader)),\n                   ))\n    predictions = np.concatenate(preds)\n    return losses.avg, predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:30.027014Z","iopub.execute_input":"2026-10-14T22:13:30.027237Z","iopub.status.idle":"2026-10-14T22:13:30.053708Z","shell.execute_reply.started":"2026-10-14T22:13:30.027209Z","shell.execute_reply":"2026-10-14T22:13:30.052856Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Label smoothing loss (written by hand because the committed\n# Kaggle environment has an old PyTorch without label_smoothing=)\n# ====================================================\nclass LabelSmoothingCrossEntropy(nn.Module):\n    def __init__(self, smoothing=0.1):\n        super().__init__()\n        self.smoothing = smoothing\n\n    def forward(self, logits, target):\n        log_probs = F.log_softmax(logits, dim=-1)\n        nll = -log_probs.gather(dim=-1, index=target.unsqueeze(1)).squeeze(1)\n        smooth = -log_probs.mean(dim=-1)\n        loss = (1.0 - self.smoothing) * nll + self.smoothing * smooth\n        return loss.mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:30.054956Z","iopub.execute_input":"2026-10-14T22:13:30.055234Z","iopub.status.idle":"2026-10-14T22:13:30.08473Z","shell.execute_reply.started":"2026-10-14T22:13:30.055205Z","shell.execute_reply":"2026-10-14T22:13:30.083819Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# train loop\n# ====================================================\n\ndef train_loop(folds, fold):\n\n    LOGGER.info(f\"========== fold: {fold} training ==========\")\n\n    trn_idx = folds[folds['fold'] != fold].index\n    val_idx = folds[folds['fold'] == fold].index\n\n    train_folds = folds.loc[trn_idx].reset_index(drop=True)\n    valid_folds = folds.loc[val_idx].reset_index(drop=True)\n\n    train_dataset = TrainDataset(train_folds, transform=get_transforms(data='train'))\n    valid_dataset = TrainDataset(valid_folds, transform=get_transforms(data='valid'))\n\n    train_loader = DataLoader(train_dataset, batch_size=CFG.batch_size, \n                              shuffle=True, num_workers=CFG.num_workers, pin_memory=True, drop_last=True)\n    valid_loader = DataLoader(valid_dataset, batch_size=CFG.batch_size, \n                              shuffle=False, num_workers=CFG.num_workers, pin_memory=True, drop_last=False)\n    \n    model = CustomResNext(CFG.model_name, pretrained=True)\n    model.to(device)\n\n    optimizer = Adam(model.parameters(), lr=CFG.lr, weight_decay=CFG.weight_decay, amsgrad=False)\n    scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=CFG.factor, patience=CFG.patience, verbose=True, eps=CFG.eps)\n  \n    criterion = LabelSmoothingCrossEntropy(smoothing=0.1)\n\n    best_score = 0.\n    best_loss = np.inf\n    \n    for epoch in range(CFG.epochs):\n        start_time = time.time()\n        avg_loss = train_fn(train_loader, model, criterion, optimizer, epoch, scheduler, device)\n        avg_val_loss, preds = valid_fn(valid_loader, model, criterion, device)\n        valid_labels = valid_folds[CFG.target_col].values\n        scheduler.step(avg_val_loss)\n        score = get_score(valid_labels, preds.argmax(1))\n        elapsed = time.time() - start_time\n        LOGGER.info(f'Epoch {epoch+1} - avg_train_loss: {avg_loss:.4f}  avg_val_loss: {avg_val_loss:.4f}  time: {elapsed:.0f}s')\n        LOGGER.info(f'Epoch {epoch+1} - Accuracy: {score}')\n        if score > best_score:\n            best_score = score\n            LOGGER.info(f'Epoch {epoch+1} - Save Best Score: {best_score:.4f} Model')\n            torch.save({'model': model.state_dict(), 'preds': preds}, OUTPUT_DIR+f'{CFG.model_name}_fold{fold}_best.pth')\n    \n    check_point = torch.load(OUTPUT_DIR+f'{CFG.model_name}_fold{fold}_best.pth')\n    valid_folds[[str(c) for c in range(5)]] = check_point['preds']\n    valid_folds['preds'] = check_point['preds'].argmax(1)\n\n    return valid_folds","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:30.085875Z","iopub.execute_input":"2026-10-14T22:13:30.086088Z","iopub.status.idle":"2026-10-14T22:13:30.117222Z","shell.execute_reply.started":"2026-10-14T22:13:30.086065Z","shell.execute_reply":"2026-10-14T22:13:30.116077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# ====================================================\n# main function\n# ====================================================\n\ndef main():\n\n    def get_result(result_df):\n        preds = result_df['preds'].values\n        labels = result_df[CFG.target_col].values\n        score = get_score(labels, preds)\n        LOGGER.info(f'Score: {score:<.5f}')\n\n    oof_df = pd.DataFrame()\n\n    for fold in CFG.trn_fold:\n        _oof_df = train_loop(folds, fold)\n        oof_df = pd.concat([oof_df, _oof_df])\n\n        LOGGER.info(f\"========== fold: {fold} result ==========\")\n        get_result(_oof_df)\n\n    LOGGER.info(f\"========== CV ==========\")\n    get_result(oof_df)\n\n    oof_df.to_csv(OUTPUT_DIR + 'oof_df.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:30.118646Z","iopub.execute_input":"2026-10-14T22:13:30.118993Z","iopub.status.idle":"2026-10-14T22:13:30.149285Z","shell.execute_reply.started":"2026-10-14T22:13:30.118962Z","shell.execute_reply":"2026-10-14T22:13:30.147739Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load training data\ntrain = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\n# Split into folds for cross validation - we used the same split for all the models we trained!\nfolds = train.merge(\n    pd.read_csv(\"../input/cassava-leaf-disease-resnext/validation_data.csv\")[[\"image_id\", \"fold\"]], on=\"image_id\")\n\nif __name__ == '__main__':\n    main()","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"execution":{"iopub.status.busy":"2026-10-14T22:13:30.15123Z","iopub.execute_input":"2026-10-14T22:13:30.151581Z","iopub.status.idle":"2026-10-14T22:13:44.048692Z","shell.execute_reply.started":"2026-10-14T22:13:30.151546Z","shell.execute_reply":"2026-10-14T22:13:44.047488Z"}},"outputs":[],"execution_count":null}]}