{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Inference with my own trained weights: faithful 512 px + label smoothing 384 px ensemble + flip TTA\n\nThis notebook averages **two ResNeXt50 models that I trained myself**, both fold 0:\n1. The **faithful** model: 512 px, 15 epochs, plain cross-entropy (the winners' settings);\n2. The **label smoothing** model: 384 px, 8 epochs, label smoothing 0.1 (Modification A).\n\nIt also uses flip test-time augmentation (TTA). It does not use any of the winners' released weights.\n\n**Result:** Private score **0.8862** and Public score **0.8896**.\n\n| Submission | Private LB | Public LB |\n|---|---|---|\n| Faithful 512 px, no TTA | 0.8785 | 0.8790 |\n| Label smoothing 384 px + flip TTA | 0.8816 | 0.8876 |\n| Baseline + label smoothing (both 384 px), ensemble + TTA | 0.8857 | 0.8851 |\n| **Faithful 512 px + label smoothing 384 px, ensemble + TTA (this notebook)** | **0.8862** | **0.8896** |\n| Winners' full ensemble (`Final Version Inference`) | 0.9134 | 0.9143 |\n\nThis is my best score from my own trained weights. The private score is only 0.0005 above my earlier ensemble, which is within noise, so I do not claim a clear gain from this combination. These are single submissions, not repeated experiments.\n\n**How it works:** The two models were trained at different image sizes, so each one is fed images at its own size (512 or 384), and a separate data loader is built for each size. For every test image there are four views (original, horizontal flip, vertical flip, transpose) and two models, so eight sets of class probabilities. They are averaged with equal weight, and the class with the highest average is submitted. Feeding both models the same size would have hurt the one trained at the other size.\n\n**Rest of the code:** The same as `inference-own-resnext-384`, which explains it step by step.\n\n**Related notebooks:**\n- Training notebooks: https://www.kaggle.com/code/ananyaamadhursharma/train-faithful-512-15ep, https://www.kaggle.com/code/ananyaamadhursharma/train-label-smoothing-384\n- Earlier ensemble of two 384 px models: https://www.kaggle.com/code/ananyaamadhursharma/inference-own-resnext-384-ensemble-tta\n- Full winning ensemble: https://www.kaggle.com/code/ananyaamadhursharma/final-version-inference","metadata":{}},{"cell_type":"code","source":"import os\n\nprint(\"Input folders:\", os.listdir(\"/kaggle/input\"))\nprint()\nfor root, dirs, files in os.walk(\"/kaggle/input\"):\n    for f in files:\n        if f.endswith(\".pth\"):\n            print(\"WEIGHTS:\", os.path.join(root, f))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-10-18T13:08:16.539432Z","iopub.execute_input":"2026-10-18T13:08:16.539845Z","iopub.status.idle":"2026-10-18T13:08:39.526275Z","shell.execute_reply.started":"2026-10-18T13:08:16.539806Z","shell.execute_reply":"2026-10-18T13:08:39.525148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Ensemble: faithful ResNeXt50 (512 px) + label smoothing ResNeXt50 (384 px) + flip TTA\n# Each model gets its own image size.\n# ====================================================\nimport os, sys\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader, Dataset\nfrom albumentations import Compose, Normalize, Resize\nfrom albumentations.pytorch import ToTensorV2\n\nsys.path.append('../input/pytorch-image-models/pytorch-image-models-master')\nimport timm\n\n# ---------------- settings: (weights path, image size it was trained at) ----------------\nMODELS = [\n    (\"/kaggle/input/datasets/ananyaamadhursharma/cassava-resnext-fold0-512-faithful/resnext50_32x4d_fold0_best.pth\", 512),\n    (\"/kaggle/input/datasets/ananyaamadhursharma/cassava-resnext-fold0-384-labelsmooth/resnext50_32x4d_fold0_best.pth\", 384),\n]\nUSE_TTA = True\n\ncandidates = [\n    \"../input/cassava-leaf-disease-classification/test_images/\",\n    \"/kaggle/input/cassava-leaf-disease-classification/test_images/\",\n    \"/kaggle/input/competitions/cassava-leaf-disease-classification/test_images/\",\n]\nTEST_DIR = next(p for p in candidates if os.path.exists(p))\nprint(\"Using test folder:\", TEST_DIR)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(\"Device:\", device)\n\nclass CustomResNext(nn.Module):\n    def __init__(self, model_name='resnext50_32x4d', pretrained=False):\n        super().__init__()\n        self.model = timm.create_model(model_name, pretrained=pretrained)\n        n_features = self.model.fc.in_features\n        self.model.fc = nn.Linear(n_features, 5)\n\n    def forward(self, x):\n        return self.model(x)\n\nclass TestDataset(Dataset):\n    def __init__(self, files, transform=None):\n        self.files = files\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.files)\n\n    def __getitem__(self, idx):\n        image = cv2.imread(self.files[idx])\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        return self.transform(image=image)['image']\n\ndef make_transform(size):\n    return Compose([\n        Resize(size, size),\n        Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n        ToTensorV2(),\n    ])\n\n# ---------------- load models ----------------\nloaded = []   # list of (model, size)\nfor path, size in MODELS:\n    m = CustomResNext(pretrained=False)\n    state = torch.load(path, map_location=\"cpu\", weights_only=False)\n    m.load_state_dict(state['model'])\n    m.to(device)\n    m.eval()\n    loaded.append((m, size))\n    print(\"Loaded:\", path.split('/')[-3], \"| size\", size)\nprint(\"Models loaded:\", len(loaded))\n\n# ---------------- one loader per image size (same file order) ----------------\nimage_ids = sorted(os.listdir(TEST_DIR))\nfiles = [TEST_DIR + i for i in image_ids]\nsizes = sorted(set(s for _, s in loaded))\nloaders = [DataLoader(TestDataset(files, make_transform(s)), batch_size=16,\n                      shuffle=False, num_workers=2, pin_memory=True) for s in sizes]\n\ndef predict(batches):\n    # batches: dict {size: tensor}\n    probs = []\n    for m, s in loaded:\n        x = batches[s].to(device)\n        views = [x]\n        if USE_TTA:\n            views += [x.flip(3), x.flip(2), x.transpose(2, 3)]\n        for v in views:\n            probs.append(m(v).softmax(1))\n    return torch.stack(probs).mean(0)\n\nall_probs = []\nwith torch.no_grad():\n    for batch_tuple in zip(*loaders):\n        batches = dict(zip(sizes, batch_tuple))\n        all_probs.append(predict(batches).cpu().numpy())\nall_probs = np.concatenate(all_probs)\n\nsubmission = pd.DataFrame({\"image_id\": image_ids, \"label\": all_probs.argmax(1)})\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"TTA used:\", USE_TTA, \"| models:\", len(loaded), \"| sizes:\", sizes)\nprint(submission.head())\nprint(\"Rows:\", len(submission))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T13:10:03.256425Z","iopub.execute_input":"2026-10-18T13:10:03.25728Z","iopub.status.idle":"2026-10-18T13:11:16.239024Z","shell.execute_reply.started":"2026-10-18T13:10:03.257243Z","shell.execute_reply":"2026-10-18T13:11:16.237917Z"}},"outputs":[],"execution_count":null}]}