{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"},{"sourceId":72271,"sourceType":"modelInstanceVersion","modelInstanceId":60373,"modelId":82844}],"dockerImageVersionId":30732,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-07-04T11:27:12.071131Z","iopub.execute_input":"2024-07-04T11:27:12.071529Z","iopub.status.idle":"2024-07-04T11:27:12.436972Z","shell.execute_reply.started":"2024-07-04T11:27:12.071492Z","shell.execute_reply":"2024-07-04T11:27:12.436060Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torchvision\n# resnet = torchvision.models.resnet18(pretrained=True)\n# torch.save(resnet, '/kaggle/working/resnet18_v1.pth')","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:12.438635Z","iopub.execute_input":"2024-07-04T11:27:12.438983Z","iopub.status.idle":"2024-07-04T11:27:17.345322Z","shell.execute_reply.started":"2024-07-04T11:27:12.438959Z","shell.execute_reply":"2024-07-04T11:27:17.344556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resnet18_model = torch.load('/kaggle/input/resnet18/pytorch/v1/1/resnet18_v1.pth')\nresnet18_model","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:17.346458Z","iopub.execute_input":"2024-07-04T11:27:17.346876Z","iopub.status.idle":"2024-07-04T11:27:17.983644Z","shell.execute_reply.started":"2024-07-04T11:27:17.346850Z","shell.execute_reply":"2024-07-04T11:27:17.982694Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Read the given data\n\nimport os\nimport albumentations # for augumentations\nimport matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\nimport torchvision\nfrom sklearn import metrics, model_selection\nimport cv2\n\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:17.984791Z","iopub.execute_input":"2024-07-04T11:27:17.985054Z","iopub.status.idle":"2024-07-04T11:27:18.851217Z","shell.execute_reply.started":"2024-07-04T11:27:17.985032Z","shell.execute_reply":"2024-07-04T11:27:18.850434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train_data = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\ndf_train_data","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.853752Z","iopub.execute_input":"2024-07-04T11:27:18.854161Z","iopub.status.idle":"2024-07-04T11:27:18.897766Z","shell.execute_reply.started":"2024-07-04T11:27:18.854136Z","shell.execute_reply":"2024-07-04T11:27:18.896788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train_data.label.value_counts()","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.898815Z","iopub.execute_input":"2024-07-04T11:27:18.899105Z","iopub.status.idle":"2024-07-04T11:27:18.911565Z","shell.execute_reply.started":"2024-07-04T11:27:18.899070Z","shell.execute_reply":"2024-07-04T11:27:18.910686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train, df_valid = model_selection.train_test_split(df_train_data, test_size=0.1, random_state=109, stratify=df_train_data[\"label\"].values)\n\ndf_train.reset_index(drop=True, inplace=True)\ndf_valid.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.912690Z","iopub.execute_input":"2024-07-04T11:27:18.912944Z","iopub.status.idle":"2024-07-04T11:27:18.930735Z","shell.execute_reply.started":"2024-07-04T11:27:18.912923Z","shell.execute_reply":"2024-07-04T11:27:18.929889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.shape, df_valid.shape","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.931914Z","iopub.execute_input":"2024-07-04T11:27:18.932632Z","iopub.status.idle":"2024-07-04T11:27:18.938264Z","shell.execute_reply.started":"2024-07-04T11:27:18.932600Z","shell.execute_reply":"2024-07-04T11:27:18.937317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = \"../input/cassava-leaf-disease-classification/train_images\"\ntrain_image_paths = [os.path.join(image_path, x) for x in df_train[\"image_id\"].values]\nvalid_image_paths = [os.path.join(image_path, x) for x in df_valid[\"image_id\"].values]\n\nlen(train_image_paths), len(valid_image_paths), train_image_paths[:3]","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.939235Z","iopub.execute_input":"2024-07-04T11:27:18.939498Z","iopub.status.idle":"2024-07-04T11:27:18.989958Z","shell.execute_reply.started":"2024-07-04T11:27:18.939476Z","shell.execute_reply":"2024-07-04T11:27:18.989081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_targets = df_train[\"label\"].values\nvalid_targets = df_valid[\"label\"].values\n\ntrain_targets","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.991008Z","iopub.execute_input":"2024-07-04T11:27:18.991270Z","iopub.status.idle":"2024-07-04T11:27:18.997317Z","shell.execute_reply.started":"2024-07-04T11:27:18.991248Z","shell.execute_reply":"2024-07-04T11:27:18.996346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class LeafImageDataset:\n    def __init__(self, image_paths, targets, augumentations=None):\n        self.image_paths = image_paths\n        self.targets = targets\n        self.augumentations = augumentations\n        \n    def __len__(self):\n        return len(self.image_paths)\n    \n    def __getitem__(self, idx):\n        target = self.targets[idx]\n        image = cv2.imread(self.image_paths[idx])\n        image=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        if self.augumentations is not None:\n            augumented = self.augumentations(image=image)\n            image = augumented[\"image\"]\n#             mask = augumented[\"mask\"]\n        image= np.transpose(image,(2,0,1)).astype(np.float32)\n        return {\n            \"image\" : torch.tensor(image),\n#             \"mask\" : torch.tensor(mask),\n            \"target\" : torch.tensor(target)\n        }","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:18.998540Z","iopub.execute_input":"2024-07-04T11:27:18.998877Z","iopub.status.idle":"2024-07-04T11:27:19.007284Z","shell.execute_reply.started":"2024-07-04T11:27:18.998854Z","shell.execute_reply":"2024-07-04T11:27:19.006359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = LeafImageDataset(train_image_paths, train_targets)\ntrain_dataset[0]\nvalid_dataset = LeafImageDataset(valid_image_paths, valid_targets)","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.008376Z","iopub.execute_input":"2024-07-04T11:27:19.008700Z","iopub.status.idle":"2024-07-04T11:27:19.073591Z","shell.execute_reply.started":"2024-07-04T11:27:19.008677Z","shell.execute_reply":"2024-07-04T11:27:19.072857Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_image(image_dict):\n    img_tensor = image_dict[\"image\"]\n    target = image_dict[\"target\"]\n    print(target.item())\n    plt.figure(figsize=(5,10))\n    image = img_tensor.permute(1,2,0)/255\n    plt.imshow(image)\n\nplot_image(train_dataset[10])","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.074705Z","iopub.execute_input":"2024-07-04T11:27:19.075205Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_aug = albumentations.Compose(\n[\n    albumentations.RandomResizedCrop(256, 256),\n    albumentations.Transpose(p=0.5),\n    albumentations.HorizontalFlip(p=0.5),\n    albumentations.VerticalFlip(p=0.5)\n])\n\n\nvalid_aug = albumentations.Compose(\n[\n    albumentations.CenterCrop(256, 256, p=1.0),\n    albumentations.Resize(256, 256),\n    albumentations.Transpose(p=0.5),\n    albumentations.HorizontalFlip(p=0.5),\n    albumentations.VerticalFlip(p=0.5)\n])\n\n\ntrain_dataset = LeafImageDataset(train_image_paths, train_targets, augumentations=train_aug)\nvalid_dataset = LeafImageDataset(valid_image_paths, valid_targets, augumentations=valid_aug)\ntrain_dataset[10]\n","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.497962Z","iopub.execute_input":"2024-07-04T11:27:19.498624Z","iopub.status.idle":"2024-07-04T11:27:19.558675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_image(train_dataset[10])","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.559756Z","iopub.execute_input":"2024-07-04T11:27:19.560037Z","iopub.status.idle":"2024-07-04T11:27:19.902452Z","shell.execute_reply.started":"2024-07-04T11:27:19.560014Z","shell.execute_reply":"2024-07-04T11:27:19.901616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"?torch.utils.data.DataLoader","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.903482Z","iopub.execute_input":"2024-07-04T11:27:19.903737Z","iopub.status.idle":"2024-07-04T11:27:19.977513Z","shell.execute_reply.started":"2024-07-04T11:27:19.903715Z","shell.execute_reply":"2024-07-04T11:27:19.976727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Dataloader\ntrain_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64,num_workers=2 )\nvalid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=64,num_workers=2 )\n","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.978492Z","iopub.execute_input":"2024-07-04T11:27:19.978760Z","iopub.status.idle":"2024-07-04T11:27:19.983699Z","shell.execute_reply.started":"2024-07-04T11:27:19.978737Z","shell.execute_reply":"2024-07-04T11:27:19.982768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for data in train_loader:\n    print(data[\"image\"], data[\"target\"])\n    break","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:19.984709Z","iopub.execute_input":"2024-07-04T11:27:19.984962Z","iopub.status.idle":"2024-07-04T11:27:22.215900Z","shell.execute_reply.started":"2024-07-04T11:27:19.984941Z","shell.execute_reply":"2024-07-04T11:27:22.214980Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# torchvision.models.resnet18(pretrained=False)","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:22.217469Z","iopub.execute_input":"2024-07-04T11:27:22.218419Z","iopub.status.idle":"2024-07-04T11:27:22.223062Z","shell.execute_reply.started":"2024-07-04T11:27:22.218352Z","shell.execute_reply":"2024-07-04T11:27:22.222103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class LeafModel(nn.Module):\n    def __init__(self, num_classes, pretrained=True):\n        super().__init__()\n#         self.convnet = torchvision.models.resnet18(pretrained=pretrained)\n        self.convnet = resnet18_model\n        self.convnet.fc = nn.Linear(512, num_classes)\n        self.step_scheduler_after = \"epoch\"\n        \n    def loss(self, outputs, targets):\n        if targets is None:\n            return None\n        return nn.CrossEntropyLoss()(outputs, targets)\n    \n    def monitor_metrics(self, outputs, targets):\n        outputs = torch.argmax(outputs, dim=1).cpu().detach().numpy()\n        targets = targets.cpu().detach().numpy()\n        acc = metrics.accuracy_score(targets, outputs)\n        return {\n            \"accuracy\" : acc\n        }\n        \n    def fetch_optimizer(self):\n        opt = torch.optim.Adam(self.parameters(), lr=1e-3)\n        return opt\n    \n    def fetch_scheduler(self, optimizer):\n        sch = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.7)\n        return sch\n        \n    def forward(self, img, targets=None):\n        outputs = self.convnet(img)\n        loss = None\n        metrics = {}\n        if targets is not None:\n            loss = self.loss(outputs, targets)\n            metrics = self.monitor_metrics(outputs, targets)\n        return outputs, loss, metrics\n       \nnum_classes=df_train_data[\"label\"].nunique()\nmodel = LeafModel(num_classes=num_classes, pretrained=True)\nmodel\n        ","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:22.224217Z","iopub.execute_input":"2024-07-04T11:27:22.224522Z","iopub.status.idle":"2024-07-04T11:27:22.246514Z","shell.execute_reply.started":"2024-07-04T11:27:22.224500Z","shell.execute_reply":"2024-07-04T11:27:22.245579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.train","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:22.247482Z","iopub.execute_input":"2024-07-04T11:27:22.247732Z","iopub.status.idle":"2024-07-04T11:27:22.254621Z","shell.execute_reply.started":"2024-07-04T11:27:22.247710Z","shell.execute_reply":"2024-07-04T11:27:22.253656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# trial\nimg_ = train_dataset[10][\"image\"].unsqueeze(0)\ny_ = train_dataset[10][\"target\"].unsqueeze(0)\nmodel(img_, y_)","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:22.255757Z","iopub.execute_input":"2024-07-04T11:27:22.256062Z","iopub.status.idle":"2024-07-04T11:27:22.450246Z","shell.execute_reply.started":"2024-07-04T11:27:22.256030Z","shell.execute_reply":"2024-07-04T11:27:22.449323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class EarlyStopper:\n    def __init__(self, patience=1, min_delta=0):\n        self.patience = patience\n        self.min_delta = min_delta\n        self.counter = 0\n        self.min_validation_loss = float('inf')\n\n    def early_stop(self, validation_loss):\n        if validation_loss < self.min_validation_loss:\n            self.min_validation_loss = validation_loss\n            self.counter = 0\n        elif validation_loss > (self.min_validation_loss + self.min_delta):\n            self.counter += 1\n            if self.counter >= self.patience:\n                return True\n        return False\n\nearly_stopper = EarlyStopper(patience=3, min_delta=10)\n# for epoch in np.arange(n_epochs):\n#     train_loss = train_one_epoch(model, train_loader)\n#     validation_loss = validate_one_epoch(model, validation_loader)\n#     if early_stopper.early_stop(validation_loss):             \n#         break","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:22.451840Z","iopub.execute_input":"2024-07-04T11:27:22.452144Z","iopub.status.idle":"2024-07-04T11:27:22.459070Z","shell.execute_reply.started":"2024-07-04T11:27:22.452117Z","shell.execute_reply":"2024-07-04T11:27:22.457951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nclass EarlyStopping:\n    def __init__(self, patience=5, min_delta=0):\n        self.patience = patience\n        self.min_delta = min_delta\n        self.counter = 0\n        self.best_loss = None\n        self.early_stop = False\n        \n    def __call__(self, val_loss):\n        if self.best_loss is None:\n            self.best_loss = val_loss\n        elif val_loss > self.best_loss - self.min_delta:\n            self.counter += 1\n            if self.counter >= self.patience:\n                self.early_stop = True\n        else:\n            self.best_loss = val_loss\n            self.counter = 0\n        \n\n\ndef train(model, train_dataset, valid_dataset, device, num_epochs=1, patience=3):\n    model.to(device)\n    optimizer = model.fetch_optimizer()\n    scheduler = model.fetch_scheduler(optimizer)\n    early_stopping = EarlyStopping(patience=patience)\n    \n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0\n        for batch in train_dataset:\n            optimizer.zero_grad()\n            image = batch[\"image\"].to(device)\n            target = batch[\"target\"].to(device)\n            output, loss, acc = model(image, target)\n            loss.backward()\n            optimizer.step()\n\n            \n            train_loss += loss.item()\n        train_loss = train_loss / len(train_dataset)\n        \n        model.eval()\n        valid_loss = 0\n        with torch.no_grad():\n            for batch_v in valid_dataset:\n                image = batch_v[\"image\"].to(device)\n                target = batch_v[\"target\"].to(device)\n                output, loss, acc = model(image, target)\n                valid_loss += loss.item()\n        valid_loss = valid_loss / len(valid_dataset)\n\n        if model.step_scheduler_after == \"epoch\":\n            scheduler.step()\n            \n        print(\n          f\"Epoch : {epoch+1} / {num_epochs} ..\",\n          f\"Train loss : {train_loss:.3f} \",\n          f\"Validation loss : {valid_loss:.3f} \"\n        )\n        # Early stopping check\n        early_stopping(valid_loss)\n        if early_stopping.early_stop:\n            print(\"Early stopping triggered\")\n            break\n        \n        # Optionally step the scheduler if it's set to step per batch\n#         if model.step_scheduler_after == \"batch\":\n#             for _ in range(len(train_dataset)):\n#                 scheduler.step()\n\ntrain(model, train_loader, valid_loader, device=device, num_epochs=10)","metadata":{"execution":{"iopub.status.busy":"2024-07-04T11:27:22.460469Z","iopub.execute_input":"2024-07-04T11:27:22.460992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df_data = pd.read_csv(\"../input/cassava-leaf-disease-classification/sample_submission.csv\")\nimage_path = \"../input/cassava-leaf-disease-classification/test_images/\"\ntest_image_paths = [\n    os.path.join(image_path, x) for x in test_df_data[\"image_id\"].values\n]\n\n\ntest_aug = albumentations.Compose(\n[\n    albumentations.CenterCrop(256, 256, p=1.0),\n    albumentations.Resize(256, 256),\n    albumentations.Transpose(p=0.5),\n    albumentations.HorizontalFlip(p=0.5),\n    albumentations.VerticalFlip(p=0.5)\n])\n\ntest_targets = test_df_data[\"label\"].values\n\ntest_dataset = LeafImageDataset(test_image_paths, test_targets, augumentations=valid_aug)\n\ntest_loader = torch.utils.data.DataLoader(test_dataset)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df_data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndef test(model, test_loader, device):\n    model.to(device)\n    model.eval()\n    test_loss = 0\n    correct = 0\n    total = 0\n    final_pred = None\n    with torch.no_grad():\n        for batch in test_loader:\n            image = batch[\"image\"].to(device)\n            target = batch[\"target\"].to(device)\n            output, loss, _ = model(image, target)\n            test_loss += loss.item()\n            pred = torch.argmax(output, dim=1)\n            if final_pred is None:\n                final_pred = pred.item()\n            else:\n                final_pred = np.vstack((final_pred, pred.item()))\n            correct += (pred == target).sum().item()\n            total += target.size(0)\n    \n    test_loss /= len(test_loader)\n    accuracy = correct / total\n    \n    print(f\"Test loss: {test_loss:.3f}, Test accuracy: {accuracy:.3f}\")\n    \n    return final_pred, test_loss, accuracy\n\nfinal_preds, test_loss, test_accuracy = test(model, test_loader, device=device)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_preds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df_data[\"label\"] = final_preds\ntest_df_data.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}