{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":10873118,"sourceType":"datasetVersion","datasetId":6755607}],"dockerImageVersionId":30919,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nfrom PIL import Image\nimport torchvision\nfrom torch.utils.data import Dataset, DataLoader\nimport torch\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nimport torch.nn as nn\nfrom sklearn.model_selection import StratifiedKFold\n\nfrom sklearn.metrics import recall_score","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:04.046844Z","iopub.execute_input":"2025-03-07T08:04:04.047280Z","iopub.status.idle":"2025-03-07T08:04:46.679086Z","shell.execute_reply.started":"2025-03-07T08:04:04.047234Z","shell.execute_reply":"2025-03-07T08:04:46.678444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_dir = '/kaggle/input/cassava-leaf-disease-classification'\npd_train = pd.read_csv(os.path.join(data_dir, 'train.csv'))\n\nX = pd_train\ny = pd_train['label']\n\nskf = StratifiedKFold(n_splits=6, random_state=42, shuffle=True)\n\npd_train['fold'] = -1\n\nfor i, (trn_idx, vid_idx) in enumerate(skf.split(X, y)):\n    pd_train.loc[vid_idx, 'fold'] = i\n\npd_train.to_csv(os.path.join('/kaggle/working/', 'pd_folds.csv'), index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:46.680072Z","iopub.execute_input":"2025-03-07T08:04:46.680505Z","iopub.status.idle":"2025-03-07T08:04:46.756710Z","shell.execute_reply.started":"2025-03-07T08:04:46.680472Z","shell.execute_reply":"2025-03-07T08:04:46.756113Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CassavaDataset(Dataset):\n    def __init__(self, csv, img_height, img_width, transform):\n        self.csv = csv.reset_index()\n        self.img_ids = csv['image_id'].values\n        self.img_height = img_height\n        self.img_width = img_width\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.csv)\n\n    def __getitem__(self, index):\n        img_id = self.img_ids[index]\n        img = Image.open(data_dir + f'/train_images/{img_id}')\n        img = np.array(img)\n        \n        if self.transform is not None:\n            img = self.transform(image=img)['image']\n\n        label = self.csv.iloc[index].label\n\n        return img, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:46.758077Z","iopub.execute_input":"2025-03-07T08:04:46.758298Z","iopub.status.idle":"2025-03-07T08:04:46.763007Z","shell.execute_reply.started":"2025-03-07T08:04:46.758278Z","shell.execute_reply":"2025-03-07T08:04:46.762231Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_augmentation = A.Compose(\n    [\n        A.Rotate(20),\n        A.Normalize(mean=0.5, std=1),\n        ToTensorV2()\n    ]\n)\n\nvalid_augmentation = A.Compose(\n    [\n        A.Normalize(mean=0.5, std=1),\n        ToTensorV2()\n    ]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:46.764111Z","iopub.execute_input":"2025-03-07T08:04:46.764412Z","iopub.status.idle":"2025-03-07T08:04:46.784529Z","shell.execute_reply.started":"2025-03-07T08:04:46.764392Z","shell.execute_reply":"2025-03-07T08:04:46.783759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trn_idx = pd_train.loc[pd_train['fold'].isin([0, 1, 2, 3, 4])].index\nvld_idx = pd_train.loc[pd_train['fold'].isin([5])].index\n\ntrn_dataset = CassavaDataset(csv=pd_train.loc[trn_idx][:], img_height=600, img_width=800, transform=train_augmentation)\nvld_dataset = CassavaDataset(csv=pd_train.loc[vld_idx][:], img_height=600, img_width=800, transform=valid_augmentation)\n\ntrn_loader = DataLoader(trn_dataset, shuffle=True, num_workers=4, batch_size=16)\nvld_loader = DataLoader(vld_dataset, shuffle=True, num_workers=4, batch_size=16)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:46.785426Z","iopub.execute_input":"2025-03-07T08:04:46.785716Z","iopub.status.idle":"2025-03-07T08:04:46.819462Z","shell.execute_reply.started":"2025-03-07T08:04:46.785689Z","shell.execute_reply":"2025-03-07T08:04:46.818857Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# -*- coding: utf-8 -*-\nfrom __future__ import print_function, division, absolute_import\nimport torchvision.models as models\nimport torch\nimport torch.utils.model_zoo as model_zoo\nimport torch.nn.functional as F\nimport types\nimport re\n\n#################################################################\n# You can find the definitions of those models here:\n# https://github.com/pytorch/vision/blob/master/torchvision/models\n#\n# To fit the API, we usually added/redefined some methods and\n# renamed some attributs (see below for each models).\n#\n# However, you usually do not need to see the original model\n# definition from torchvision. Just use `print(model)` to see\n# the modules and see bellow the `model.features` and\n# `model.classifier` definitions.\n#################################################################\n\n__all__ = [\n    'alexnet',\n    'densenet121', 'densenet169', 'densenet201', 'densenet161',\n    'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152',\n    'inceptionv3',\n    'squeezenet1_0', 'squeezenet1_1',\n    'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn',\n    'vgg19_bn', 'vgg19'\n]\n\nmodel_urls = {\n    'alexnet': 'https://download.pytorch.org/models/alexnet-owt-4df8aa71.pth',\n    'densenet121': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet121-fbdb23505.pth',\n    'densenet169': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet169-f470b90a4.pth',\n    'densenet201': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet201-5750cbb1e.pth',\n    'densenet161': 'http://data.lip6.fr/cadene/pretrainedmodels/densenet161-347e6b360.pth',\n    'inceptionv3': 'https://download.pytorch.org/models/inception_v3_google-1a9a5a14.pth',\n    'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',\n    'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',\n    'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',\n    'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',\n    'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',\n    'squeezenet1_0': 'https://download.pytorch.org/models/squeezenet1_0-a815701f.pth',\n    'squeezenet1_1': 'https://download.pytorch.org/models/squeezenet1_1-f364aa15.pth',\n    'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth',\n    'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth',\n    'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth',\n    'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth',\n    'vgg11_bn': 'https://download.pytorch.org/models/vgg11_bn-6002323d.pth',\n    'vgg13_bn': 'https://download.pytorch.org/models/vgg13_bn-abd245e5.pth',\n    'vgg16_bn': 'https://download.pytorch.org/models/vgg16_bn-6c64b313.pth',\n    'vgg19_bn': 'https://download.pytorch.org/models/vgg19_bn-c79401a0.pth',\n    # 'vgg16_caffe': 'https://s3-us-west-2.amazonaws.com/jcjohns-models/vgg16-00b39a1b.pth',\n    # 'vgg19_caffe': 'https://s3-us-west-2.amazonaws.com/jcjohns-models/vgg19-d01eb7cb.pth'\n}\n\ninput_sizes = {}\nmeans = {}\nstds = {}\n\nfor model_name in __all__:\n    input_sizes[model_name] = [3, 224, 224]\n    means[model_name] = [0.485, 0.456, 0.406]\n    stds[model_name] = [0.229, 0.224, 0.225]\n\nfor model_name in ['inceptionv3']:\n    input_sizes[model_name] = [3, 299, 299]\n    means[model_name] = [0.5, 0.5, 0.5]\n    stds[model_name] = [0.5, 0.5, 0.5]\n\npretrained_settings = {}\n\nfor model_name in __all__:\n    pretrained_settings[model_name] = {\n        'imagenet': {\n            'url': model_urls[model_name],\n            'input_space': 'RGB',\n            'input_size': input_sizes[model_name],\n            'input_range': [0, 1],\n            'mean': means[model_name],\n            'std': stds[model_name],\n            'num_classes': 1000\n        }\n    }\n\n# for model_name in ['vgg16', 'vgg19']:\n#     pretrained_settings[model_name]['imagenet_caffe'] = {\n#         'url': model_urls[model_name + '_caffe'],\n#         'input_space': 'BGR',\n#         'input_size': input_sizes[model_name],\n#         'input_range': [0, 255],\n#         'mean': [103.939, 116.779, 123.68],\n#         'std': [1., 1., 1.],\n#         'num_classes': 1000\n#     }\n\ndef update_state_dict(state_dict):\n    # '.'s are no longer allowed in module names, but pervious _DenseLayer\n    # has keys 'norm.1', 'relu.1', 'conv.1', 'norm.2', 'relu.2', 'conv.2'.\n    # They are also in the checkpoints in model_urls. This pattern is used\n    # to find such keys.\n    pattern = re.compile(\n        r'^(.*denselayer\\d+\\.(?:norm|relu|conv))\\.((?:[12])\\.(?:weight|bias|running_mean|running_var))$')\n    for key in list(state_dict.keys()):\n        res = pattern.match(key)\n        if res:\n            new_key = res.group(1) + res.group(2)\n            state_dict[new_key] = state_dict[key]\n            del state_dict[key]\n    return state_dict\n\ndef load_pretrained(model, num_classes, settings):\n    assert num_classes == settings['num_classes'], \\\n        \"num_classes should be {}, but is {}\".format(settings['num_classes'], num_classes)\n\n    state_dict = torch.load('/kaggle/input/bengali/resnet34-333f7ec4.pth', weights_only=True)\n    state_dict = update_state_dict(state_dict)\n    model.load_state_dict(state_dict)\n    model.input_space = settings['input_space']\n    model.input_size = settings['input_size']\n    model.input_range = settings['input_range']\n    model.mean = settings['mean']\n    model.std = settings['std']\n    return model\n\n#################################################################\n# AlexNet\n\ndef modify_alexnet(model):\n    # Modify attributs\n    model._features = model.features\n    del model.features\n    model.dropout0 = model.classifier[0]\n    model.linear0 = model.classifier[1]\n    model.relu0 = model.classifier[2]\n    model.dropout1 = model.classifier[3]\n    model.linear1 = model.classifier[4]\n    model.relu1 = model.classifier[5]\n    model.last_linear = model.classifier[6]\n    del model.classifier\n\n    def features(self, input):\n        x = self._features(input)\n        x = x.view(x.size(0), 256 * 6 * 6)\n        x = self.dropout0(x)\n        x = self.linear0(x)\n        x = self.relu0(x)\n        x = self.dropout1(x)\n        x = self.linear1(x)\n        return x\n\n    def logits(self, features):\n        x = self.relu1(features)\n        x = self.last_linear(x)\n        return x\n\n    def forward(self, input):\n        x = self.features(input)\n        x = self.logits(x)\n        return x\n\n    # Modify methods\n    model.features = types.MethodType(features, model)\n    model.logits = types.MethodType(logits, model)\n    model.forward = types.MethodType(forward, model)\n    return model\n\ndef alexnet(num_classes=1000, pretrained='imagenet'):\n    r\"\"\"AlexNet model architecture from the\n    `\"One weird trick...\" <https://arxiv.org/abs/1404.5997>`_ paper.\n    \"\"\"\n    # https://github.com/pytorch/vision/blob/master/torchvision/models/alexnet.py\n    model = models.alexnet(pretrained=False)\n    if pretrained is not None:\n        settings = pretrained_settings['alexnet'][pretrained]\n        model = load_pretrained(model, num_classes, settings)\n    model = modify_alexnet(model)\n    return model\n\n###############################################################\n# DenseNets\n\ndef modify_densenets(model):\n    # Modify attributs\n    model.last_linear = model.classifier\n    del model.classifier\n\n    def logits(self, features):\n        x = F.relu(features, inplace=True)\n        x = F.avg_pool2d(x, kernel_size=7, stride=1)\n        x = x.view(x.size(0), -1)\n        x = self.last_linear(x)\n        return x\n\n    def forward(self, input):\n        x = self.features(input)\n        x = self.logits(x)\n        return x\n\n    # Modify methods\n    model.logits = types.MethodType(logits, model)\n    model.forward = types.MethodType(forward, model)\n    return model\n\n###############################################################\n# ResNets\n\ndef modify_resnets(model):\n    # Modify attributs\n    model.last_linear = model.fc\n    model.fc = None\n\n    def features(self, input):\n        x = self.conv1(input)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool(x)\n\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        return x\n\n    def logits(self, features):\n        x = self.avgpool(features)\n        x = x.view(x.size(0), -1)\n        x = self.last_linear(x)\n        return x\n\n    def forward(self, input):\n        x = self.features(input)\n        x = self.logits(x)\n        return x\n\n    # Modify methods\n    model.features = types.MethodType(features, model)\n    model.logits = types.MethodType(logits, model)\n    model.forward = types.MethodType(forward, model)\n    return model\n\ndef resnet18(num_classes=1000, pretrained='imagenet'):\n    \"\"\"Constructs a ResNet-18 model.\n    \"\"\"\n    model = models.resnet18(pretrained=False)\n    if pretrained is not None:\n        settings = pretrained_settings['resnet18'][pretrained]\n        model = load_pretrained(model, num_classes, settings)\n    model = modify_resnets(model)\n    return model\n\ndef resnet34(num_classes=1000, pretrained='imagenet'):\n    \"\"\"Constructs a ResNet-34 model.\n    \"\"\"\n    model = models.resnet34(pretrained=False)\n    if pretrained is not None:\n        settings = pretrained_settings['resnet34'][pretrained]\n        model = load_pretrained(model, num_classes, settings)\n    model = modify_resnets(model)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:46.820149Z","iopub.execute_input":"2025-03-07T08:04:46.820389Z","iopub.status.idle":"2025-03-07T08:04:46.839227Z","shell.execute_reply.started":"2025-03-07T08:04:46.820361Z","shell.execute_reply":"2025-03-07T08:04:46.838579Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_name = 'resnet34'\nmodel = resnet34(pretrained='imagenet')\nin_features = model.last_linear.in_features\nmodel.last_linear = nn.Linear(in_features, 5)\n\nmodel.cuda()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:46.840116Z","iopub.execute_input":"2025-03-07T08:04:46.840430Z","iopub.status.idle":"2025-03-07T08:04:48.376840Z","shell.execute_reply.started":"2025-03-07T08:04:46.840402Z","shell.execute_reply":"2025-03-07T08:04:48.376163Z"},"scrolled":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\nloss_fn = nn.CrossEntropyLoss()\nscheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', verbose=True, patience=7, factor=0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:48.378798Z","iopub.execute_input":"2025-03-07T08:04:48.379023Z","iopub.status.idle":"2025-03-07T08:04:48.387184Z","shell.execute_reply.started":"2025-03-07T08:04:48.379002Z","shell.execute_reply":"2025-03-07T08:04:48.386421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_score = -1\nfor epoch in range(1, 10):\n    train_loss = []\n    model.train()\n    \n    for inputs, targets in trn_loader:\n        inputs = inputs.cuda()\n        targets = targets.cuda()\n        logits = model(inputs)\n        \n        loss = loss_fn(logits, targets)\n        \n        loss.backward()\n        \n        optimizer.step()\n        optimizer.zero_grad()\n        train_loss.append(loss.item())\n    \n    val_loss = []\n    val_true = []\n    val_pred = []\n    \n    model.eval()\n    \n    with torch.no_grad():\n        for inputs, targets in vld_loader:\n            inputs = inputs.cuda()\n            targets = targets.cuda()\n            \n            logits = model(inputs)\n\n            loss = loss_fn(logits, targets)\n            val_loss.append(loss.item())\n    \n            logits = logits.cpu().argmax(dim=1).data.numpy()\n            \n            val_true.append(targets.cpu().numpy())\n            val_pred.append(logits)\n    \n    val_true = np.concatenate(val_true)\n    val_pred = np.concatenate(val_pred)\n    \n    val_loss = np.mean(val_loss)\n    train_loss = np.mean(train_loss)\n\n    final_score = recall_score(val_true, val_pred, average='macro')\n\n    if final_score > best_score:\n        best_score = final_score\n\n        state_dict = model.cpu().state_dict()\n        model = model.cuda()\n        torch.save(state_dict, os.path.join('/kaggle/working/', \"model.pt\"))\n\n    print(f'epoch {epoch}')\n    print(f'train_loss: {train_loss:.5f}; val_loss: {val_loss:.5f}; score: {final_score:.5f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T08:04:48.388244Z","iopub.execute_input":"2025-03-07T08:04:48.388508Z","iopub.status.idle":"2025-03-07T10:32:19.279381Z","shell.execute_reply.started":"2025-03-07T08:04:48.388478Z","shell.execute_reply":"2025-03-07T10:32:19.278287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pd_test = pd.DataFrame()\npd_test['image_id'] = [f for f in os.listdir(data_dir + f'/test_images/') if os.path.isfile(os.path.join(data_dir + f'/test_images/', f))]\n\npd_submit = pd.DataFrame()\npd_submit['image_id'] = pd_test['image_id']\npd_submit['label'] = -1\n\npd_submit","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T10:32:19.280694Z","iopub.execute_input":"2025-03-07T10:32:19.281080Z","iopub.status.idle":"2025-03-07T10:32:19.312756Z","shell.execute_reply.started":"2025-03-07T10:32:19.281038Z","shell.execute_reply":"2025-03-07T10:32:19.311895Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.load_state_dict(torch.load(os.path.join('/kaggle/working/', \"model.pt\"), weights_only=True))\nwith torch.no_grad():\n    for index, image_id in pd_test.itertuples():\n        img = Image.open(data_dir + f'/test_images/{image_id}')\n        img = np.array(img)\n        \n        img = valid_augmentation(image=img)['image']\n\n        inputs = img.unsqueeze(dim=0).cuda()\n        \n        logits = model(inputs)\n        label = logits.cpu().argmax(dim=1).data.numpy()\n\n        pd_submit.loc[index, 'label'] = label\n\npd_submit.to_csv(os.path.join('/kaggle/working/', 'submission.csv'), index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-07T10:32:19.313652Z","iopub.execute_input":"2025-03-07T10:32:19.313960Z","iopub.status.idle":"2025-03-07T10:32:19.584204Z","shell.execute_reply.started":"2025-03-07T10:32:19.313939Z","shell.execute_reply":"2025-03-07T10:32:19.583580Z"}},"outputs":[],"execution_count":null}]}