{"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":[{"sourceType":"competition","sourceId":13836,"databundleVersionId":1718836,"isSourceIdPinned":false}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader, WeightedRandomSampler\nimport cv2\nimport albumentations as A\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import StratifiedKFold\nimport timm\nfrom tqdm import tqdm\nimport torch.nn as nn\nfrom torch.amp import autocast, GradScaler","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:45:09.232266Z","iopub.execute_input":"2026-05-20T16:45:09.232823Z","iopub.status.idle":"2026-05-20T16:45:09.238470Z","shell.execute_reply.started":"2026-05-20T16:45:09.232787Z","shell.execute_reply":"2026-05-20T16:45:09.237628Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trn_pth = '/kaggle/input/competitions/cassava-leaf-disease-classification/train_images'\ntrn_df = '/kaggle/input/competitions/cassava-leaf-disease-classification/train.csv'\ntst_pth = '/kaggle/input/competitions/cassava-leaf-disease-classification/test_images'\ntst_df = '/kaggle/input/competitions/cassava-leaf-disease-classification/sample_submission.csv'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:15.961562Z","iopub.execute_input":"2026-05-20T16:42:15.962093Z","iopub.status.idle":"2026-05-20T16:42:15.965949Z","shell.execute_reply.started":"2026-05-20T16:42:15.962066Z","shell.execute_reply":"2026-05-20T16:42:15.965200Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trn_df = pd.read_csv(trn_df)\ntst_df = pd.read_csv(tst_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:15.966956Z","iopub.execute_input":"2026-05-20T16:42:15.967291Z","iopub.status.idle":"2026-05-20T16:42:16.013704Z","shell.execute_reply.started":"2026-05-20T16:42:15.967257Z","shell.execute_reply":"2026-05-20T16:42:16.013048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# print(trn_df.info)\n# print(trn_df.info())\n# print(trn_df['label'].value_counts())\n# print(tst_df.info)\n# print(tst_df.info())\n# df['col'].insull().sum().sum() info()에서 null 보이면","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:16.015282Z","iopub.execute_input":"2026-05-20T16:42:16.015609Z","iopub.status.idle":"2026-05-20T16:42:16.019141Z","shell.execute_reply.started":"2026-05-20T16:42:16.015585Z","shell.execute_reply":"2026-05-20T16:42:16.018340Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class trn_ds(Dataset):\n\n    def __init__(self, df, img_pth, transform = None):\n        self.df = df.reset_index(drop = True)\n        self.pth = img_pth\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        image_id = row['image_id']\n        label = row['label']\n        image = cv2.imread(os.path.join(self.pth, image_id))\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        if self.transform:\n            albu = self.transform(image = image)\n            image = albu['image']\n\n        return image, label\n\n\nclass tst_ds(Dataset):\n\n    def __init__(self, df, img_pth, transform = None):\n        self.df = df.reset_index(drop = True)\n        self.pth = img_pth\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        image_id = row['image_id']\n        image = cv2.imread(os.path.join(self.pth, image_id))\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        if self.transform:\n            albu = self.transform(image = image)\n            image = albu['image']\n\n        return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:16.020094Z","iopub.execute_input":"2026-05-20T16:42:16.020425Z","iopub.status.idle":"2026-05-20T16:42:16.033932Z","shell.execute_reply.started":"2026-05-20T16:42:16.020388Z","shell.execute_reply":"2026-05-20T16:42:16.033289Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trn_form = A.Compose([\n    A.Resize(380, 380),  \n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.RandomRotate90(p=0.5),\n\n    A.RandomBrightnessContrast(p=0.3),\n    A.HueSaturationValue(p=0.3),\n    A.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225]),\n    A.pytorch.ToTensorV2()\n])\n\ntst_form = A.Compose([\n    A.Resize(380, 380),\n    A.Normalize(mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]),\n    A.pytorch.ToTensorV2()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:16.035107Z","iopub.execute_input":"2026-05-20T16:42:16.035370Z","iopub.status.idle":"2026-05-20T16:42:16.050733Z","shell.execute_reply.started":"2026-05-20T16:42:16.035348Z","shell.execute_reply":"2026-05-20T16:42:16.050025Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#trn = trn_ds(trn_df, trn_pth, trn_form)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:16.051582Z","iopub.execute_input":"2026-05-20T16:42:16.051928Z","iopub.status.idle":"2026-05-20T16:42:16.055709Z","shell.execute_reply.started":"2026-05-20T16:42:16.051905Z","shell.execute_reply":"2026-05-20T16:42:16.055026Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# mean=[0.485, 0.456, 0.406]\n# std=[0.229, 0.224, 0.225]\n\n# for image, label in trn:\n#     image = image.permute(1,2,0).numpy()\n#     image = image * std + mean # numpy broadcasting\n#     plt.imshow(image) #imshow hwc / rgb / float 0~1 or int 0~255\n#     plt.show()\n#     break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:42:16.056517Z","iopub.execute_input":"2026-05-20T16:42:16.057066Z","iopub.status.idle":"2026-05-20T16:42:16.069738Z","shell.execute_reply.started":"2026-05-20T16:42:16.057044Z","shell.execute_reply":"2026-05-20T16:42:16.069003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"skf = StratifiedKFold(n_splits = 3, shuffle = True, random_state = 42)\n\nfor fold, (trn_idx, val_idx) in enumerate(skf.split(trn_df, trn_df['label'])):\n    \n    trn = trn_df.iloc[trn_idx]\n    val = trn_df.iloc[val_idx]\n\n    trn_dataset = trn_ds(trn, trn_pth, trn_form)\n    val_dataset = trn_ds(val, trn_pth, tst_form)\n\n    trn_dl = DataLoader(trn_dataset, batch_size = 8, shuffle = True, num_workers = 2)\n    val_dl = DataLoader(val_dataset, batch_size = 8, num_workers = 2)\n\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    model = timm.create_model('efficientnet_b4', pretrained=True, num_classes=5)\n    model.to(device)\n    criterion = nn.CrossEntropyLoss(label_smoothing=0.1)\n    optim = torch.optim.AdamW(model.parameters(), lr=2e-4, weight_decay=1e-4)\n    scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(\n        optim, T_0=5, T_mult=1, eta_min=1e-5\n    )\n\n    steps = 4\n    scaler = GradScaler()\n    \n    best_acc = 0\n    \n    for epoch in range(10):\n\n        model.train()\n        optim.zero_grad()\n        for step, (images, labels) in enumerate(tqdm(trn_dl)):\n            images = images.to(device)\n            labels = labels.to(device)\n            \n            with autocast(device.type):\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                loss = loss / steps\n                \n            scaler.scale(loss).backward()\n\n            if (step + 1) % steps == 0:\n                scaler.step(optim)\n                scaler.update()\n                optim.zero_grad()\n                \n        scheduler.step()\n\n        correct, total = 0, 0\n        model.eval()\n        with torch.no_grad():\n            for images, labels in tqdm(val_dl):\n                images = images.to(device)\n                labels = labels.to(device)\n    \n                outputs = model(images)\n                _, preds = outputs.max(1) # 1차원 따라 max / max return - max value와 그 index \n                correct += (preds == labels).sum().item() #item() - scalar 값 가져오기\n                total += labels.size(0) \n\n        print(f'Epoch {epoch+1}, Val Acc: {correct/total:.4f}')\n        if best_acc <= correct/total: \n            best_acc = correct/total\n            torch.save(model.state_dict(), f'/kaggle/working/model{fold}.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-20T16:45:47.334463Z","iopub.execute_input":"2026-05-20T16:45:47.335210Z","iopub.status.idle":"2026-05-20T16:52:38.359397Z","shell.execute_reply.started":"2026-05-20T16:45:47.335173Z","shell.execute_reply":"2026-05-20T16:52:38.358243Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}