{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":106372,"databundleVersionId":12864984,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install efficientnet_pytorch","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:35:32.229969Z","iopub.execute_input":"2025-06-28T12:35:32.230211Z","iopub.status.idle":"2025-06-28T12:37:03.251952Z","shell.execute_reply.started":"2025-06-28T12:35:32.230185Z","shell.execute_reply":"2025-06-28T12:37:03.250962Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Implement","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom sklearn.model_selection import train_test_split\nimport torchvision.transforms as transforms\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.datasets.folder import default_loader\nfrom sklearn.preprocessing import LabelEncoder\nfrom tqdm import tqdm\nimport random\nimport joblib\nfrom efficientnet_pytorch import EfficientNet\n\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') #GPU 할당\n\n#하이퍼 파라미터 튜닝\nCFG = {\n    'IMG_SIZE':128, #이미지 사이즈\n    'EPOCHS':20, #에포크\n    'LEARNING_RATE':1e-4, #학습률\n    'BATCH_SIZE':32, #배치사이즈\n    'NUM_WORKERS': 1,\n    'SEED':42\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:37:03.259652Z","iopub.execute_input":"2025-06-28T12:37:03.259990Z","iopub.status.idle":"2025-06-28T12:37:14.026400Z","shell.execute_reply.started":"2025-06-28T12:37:03.259956Z","shell.execute_reply":"2025-06-28T12:37:14.025552Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Seed 고정\ndef seed_everything(seed):\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    torch.backends.cudnn.benchmark = True\n\nseed_everything(CFG['SEED'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:37:14.027152Z","iopub.execute_input":"2025-06-28T12:37:14.027486Z","iopub.status.idle":"2025-06-28T12:37:14.042084Z","shell.execute_reply.started":"2025-06-28T12:37:14.027467Z","shell.execute_reply":"2025-06-28T12:37:14.041394Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Load","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/2025-bamboo-summer-competiton-dl-pr/train.csv\")\ntest = pd.read_csv(\"/kaggle/input/2025-bamboo-summer-competiton-dl-pr/test.csv\")\n\n# 라벨 인코딩\nle = LabelEncoder()\ntrain[\"label_encoded\"] = le.fit_transform(train[\"label\"])\n\ntrain.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:37:14.061580Z","iopub.execute_input":"2025-06-28T12:37:14.061859Z","iopub.status.idle":"2025-06-28T12:37:14.135402Z","shell.execute_reply.started":"2025-06-28T12:37:14.061832Z","shell.execute_reply":"2025-06-28T12:37:14.134832Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## CustomDataset","metadata":{}},{"cell_type":"code","source":"class ButterflyDataset(Dataset):\n    def __init__(self, dataframe, image_dir, transform=None, train=True):\n        self.df = dataframe.reset_index(drop=True)\n        self.image_dir = image_dir\n        self.transform = transform\n        self.train = train\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = os.path.join(self.image_dir, row[\"filename\"])\n        image = default_loader(img_path)\n        if self.transform:\n            image = self.transform(image)\n\n        if self.train:\n            label = row[\"label_encoded\"]\n            return image, label\n        else:\n            return image, row[\"filename\"]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:37:14.043171Z","iopub.execute_input":"2025-06-28T12:37:14.043584Z","iopub.status.idle":"2025-06-28T12:37:14.060997Z","shell.execute_reply.started":"2025-06-28T12:37:14.043561Z","shell.execute_reply":"2025-06-28T12:37:14.060466Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocessing","metadata":{}},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((CFG['IMG_SIZE'], CFG['IMG_SIZE'])),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n])\ntest_transform = transforms.Compose([\n    transforms.Resize((CFG['IMG_SIZE'], CFG['IMG_SIZE'])),\n    transforms.ToTensor(),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:37:14.135935Z","iopub.execute_input":"2025-06-28T12:37:14.136126Z","iopub.status.idle":"2025-06-28T12:37:14.140313Z","shell.execute_reply.started":"2025-06-28T12:37:14.136111Z","shell.execute_reply":"2025-06-28T12:37:14.139568Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Train","metadata":{}},{"cell_type":"code","source":"# train/val 8:2로 stratify 분할\ntrain_df, val_df = train_test_split(\n    train,\n    test_size=0.2,\n    stratify=train[\"label_encoded\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:37:14.141027Z","iopub.execute_input":"2025-06-28T12:37:14.141217Z","iopub.status.idle":"2025-06-28T12:37:14.177757Z","shell.execute_reply.started":"2025-06-28T12:37:14.141202Z","shell.execute_reply":"2025-06-28T12:37:14.177192Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_classes = len(le.classes_)\nmodel = EfficientNet.from_pretrained('efficientnet-b0', num_classes=num_classes)\nmodel = model.to(device)\n\ntrain_dataset = ButterflyDataset(train_df, image_dir=os.path.join(\"/kaggle/input/2025-bamboo-summer-competiton-dl-pr/train\"), transform=train_transform)\nval_dataset = ButterflyDataset(val_df, image_dir=os.path.join(\"/kaggle/input/2025-bamboo-summer-competiton-dl-pr/train\"), transform=train_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=CFG['BATCH_SIZE'], shuffle=True, num_workers=CFG['NUM_WORKERS'])\nval_loader = DataLoader(val_dataset, batch_size=CFG['BATCH_SIZE'], shuffle=False, num_workers=CFG['NUM_WORKERS'])\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)\n\nfor epoch in range(CFG['EPOCHS']):\n    model.train()\n    running_loss = 0.0\n    for images, labels in tqdm(train_loader, desc=f\"[Train] Epoch {epoch+1}/{CFG['EPOCHS']}\"):\n        images, labels = images.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n\n    avg_train_loss = running_loss / len(train_loader)\n\n    model.eval()\n    val_loss = 0.0\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for images, labels in tqdm(val_loader, desc=f\"[Val] Epoch {epoch+1}/{CFG['EPOCHS']}\"):\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            val_loss += loss.item()\n\n            preds = torch.argmax(outputs, dim=1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n    avg_val_loss = val_loss / len(val_loader)\n    val_acc = correct / total\n\n    print(f\"Epoch {epoch+1} | Train Loss: {avg_train_loss:.4f} | Val Loss: {avg_val_loss:.4f} | Val Acc: {val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:41:23.369311Z","iopub.execute_input":"2025-06-28T12:41:23.369883Z","iopub.status.idle":"2025-06-28T12:47:06.582306Z","shell.execute_reply.started":"2025-06-28T12:41:23.369853Z","shell.execute_reply":"2025-06-28T12:47:06.581328Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"code","source":"test_dataset = ButterflyDataset(test, image_dir=os.path.join(\"/kaggle/input/2025-bamboo-summer-competiton-dl-pr/test\"), transform=test_transform, train=False)\ntest_loader = DataLoader(test_dataset, batch_size=CFG['BATCH_SIZE'], shuffle=False, num_workers=CFG['NUM_WORKERS'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:47:06.584132Z","iopub.execute_input":"2025-06-28T12:47:06.584376Z","iopub.status.idle":"2025-06-28T12:47:06.589360Z","shell.execute_reply.started":"2025-06-28T12:47:06.584353Z","shell.execute_reply":"2025-06-28T12:47:06.588810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\npredictions = []\nfilenames = []\n\nwith torch.no_grad():\n    for images, fnames in tqdm(test_loader, desc=\"Predicting\"):\n        images = images.to(device)\n        outputs = model(images)\n        probs = torch.softmax(outputs, dim=1)\n        preds = torch.argmax(probs, dim=1).cpu().numpy()\n        labels = le.inverse_transform(preds)\n        predictions.extend(labels)\n        filenames.extend(fnames)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:47:06.590060Z","iopub.execute_input":"2025-06-28T12:47:06.590284Z","iopub.status.idle":"2025-06-28T12:47:17.698426Z","shell.execute_reply.started":"2025-06-28T12:47:06.590268Z","shell.execute_reply":"2025-06-28T12:47:17.697431Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submit","metadata":{}},{"cell_type":"code","source":"submission = pd.read_csv('/kaggle/input/2025-bamboo-summer-competiton-dl-pr/sample_submission.csv')\nsubmission['label'] = predictions\nsubmission.to_csv(os.path.join(\"baseline_submission.csv\"), index=False)\nprint(\"✅ submission.csv 생성 완료!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-28T12:47:17.700211Z","iopub.execute_input":"2025-06-28T12:47:17.700445Z","iopub.status.idle":"2025-06-28T12:47:17.725404Z","shell.execute_reply.started":"2025-06-28T12:47:17.700422Z","shell.execute_reply":"2025-06-28T12:47:17.724825Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}