{"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":[{"sourceId":112509,"databundleVersionId":14254895,"isSourceIdPinned":false,"sourceType":"competition"}],"dockerImageVersionId":31236,"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 matplotlib.pyplot as plt\nimport seaborn as sns\nimport warnings\nwarnings.filterwarnings(action = 'ignore')\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import transforms, datasets\n\nimport os\nfrom PIL import Image\n\ntrain = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\ntest = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\nsubmission = pd.read_csv(\"/kaggle/input/csiro-biomass/sample_submission.csv\")\n\ntrain[\"sample_id\"] = train[\"sample_id\"].str.split(\"__\", expand = True)[0]\ntrain = train.pivot(index = train.columns[:-2].tolist(), columns = \"target_name\", values = \"target\")\ntrain.columns.name = None\ntrain.reset_index(inplace = True)\n\ntest[\"sample_id\"] = test[\"sample_id\"].str.split(\"__\", expand = True)[0]\ntest = test.drop_duplicates('sample_id').reset_index(drop = True)\n# test = test.pivot(index = test.columns[:-1].tolist(), columns = \"target_name\", values = \"target\")\n\nBASE_PATH = \"/kaggle/input/csiro-biomass/\"\nBATCH_SIZE = 8\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ntransform = transforms.Compose([\n    transforms.Resize((128,256)),\n    transforms.ToTensor()]\n)\n\nclass Dataset_v1(Dataset):\n    def __init__(self, df, transform = None, mode = \"train\"):\n        self.df = df\n        self.transform = transform\n        self.mode = mode\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        \n        RELATIVE_PATH = self.df.image_path.iloc[idx]\n        img_path = os.path.join(BASE_PATH, RELATIVE_PATH)\n\n        with Image.open(img_path) as img:\n                image = img.convert(\"RGB\")\n\n        image = self.transform(image)\n\n        if self.mode == 'test':\n            # 테스트 모드일 때는 (이미지, 이미지_ID) 반환\n            img_id = self.df.sample_id.iloc[idx]\n            return image, img_id\n\n        else:\n            labels = self.df.iloc[idx][['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g']]\n            labels = torch.tensor(labels, dtype = torch.float32)\n\n            return image, labels\n\npin = (DEVICE.type == \"cuda\")\n\ntrain_data = Dataset_v1(df = train, transform = transform)\ntrain_loader = DataLoader(dataset = train_data, batch_size = 16, shuffle = True, drop_last = False, num_workers = 0, pin_memory=pin)\n\ntest_data = Dataset_v1(df = test, transform = transform, mode = 'test')\ntest_loader = DataLoader(dataset = test_data, batch_size = 5, shuffle = False, drop_last = False, num_workers = 0, pin_memory=pin)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-11T01:52:07.660504Z","iopub.execute_input":"2026-01-11T01:52:07.660773Z","iopub.status.idle":"2026-01-11T01:52:20.898363Z","shell.execute_reply.started":"2026-01-11T01:52:07.660746Z","shell.execute_reply":"2026-01-11T01:52:20.897456Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self):\n        super(CNN, self).__init__()\n        self.conv1 = nn.Sequential(\n            nn.Conv2d(in_channels = 3, out_channels = 64, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(64),\n            nn.ReLU())\n        self.conv2 = nn.Sequential(\n            nn.Conv2d(in_channels = 64, out_channels = 64, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.AvgPool2d(2,2))\n\n        self.conv3 = nn.Sequential(\n            nn.Conv2d(in_channels = 64, out_channels = 128, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(128),\n            nn.ReLU())\n        self.conv4 = nn.Sequential(\n            nn.Conv2d(in_channels = 128, out_channels = 128, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.AvgPool2d(2,2))\n\n        self.conv5 = nn.Sequential(\n            nn.Conv2d(in_channels = 128, out_channels = 256, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(256),\n            nn.ReLU())\n        self.conv6 = nn.Sequential(\n            nn.Conv2d(in_channels = 256, out_channels = 256, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(256),\n            nn.ReLU(),\n            nn.AvgPool2d(2,2))\n\n        self.conv7 = nn.Sequential(\n            nn.Conv2d(in_channels = 256, out_channels = 512, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(512),\n            nn.ReLU())\n        self.conv8 = nn.Sequential(\n            nn.Conv2d(in_channels = 512, out_channels = 512, kernel_size = 3, padding = 1),\n            nn.BatchNorm2d(512),\n            nn.ReLU(),\n            nn.AvgPool2d(2,2))\n\n        self.gap = nn.AdaptiveAvgPool2d(1)\n\n        self.fc1 = nn.Sequential(\n            nn.BatchNorm1d(512),\n            nn.ReLU(),\n            nn.Linear(512, 3))\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.conv2(x)\n        x = self.conv3(x)\n        x = self.conv4(x)\n        x = self.conv5(x)\n        x = self.conv6(x)\n        x = self.conv7(x)\n        x = self.conv8(x)\n        x = self.gap(x)\n        x = x.view(x.size(0), -1)\n        x = self.fc1(x)\n\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-11T01:52:20.900687Z","iopub.execute_input":"2026-01-11T01:52:20.900970Z","iopub.status.idle":"2026-01-11T01:52:20.913606Z","shell.execute_reply.started":"2026-01-11T01:52:20.900945Z","shell.execute_reply":"2026-01-11T01:52:20.912617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CNN().to(DEVICE)\noptimizer = torch.optim.Adam(model.parameters(), lr = 1e-5)\nloss_function = nn.L1Loss()\n\ndef Train(model = model, train_loader = train_loader, optimizer = optimizer, log_interval = 10):\n    model.train()\n    for batch_idx, (image, label) in enumerate(train_loader):\n        optimizer.zero_grad()\n        image = image.to(DEVICE)\n        label = label.to(DEVICE)\n        output = model(image)\n        loss = loss_function(output, label)\n        loss.backward()\n        optimizer.step()\n\n        if batch_idx % log_interval == 0:\n            print(f\"EPOCH: {EPOCH} [{batch_idx * len(image)} / {len(train_loader.dataset)}] {100. * batch_idx / len(train_loader):.4f}%\\tTrain Loss:{loss.item():.6f}\")\n\n# def Test(model = model, test_loader = test_loader):\n#     model.eval()\n#     predictions = []\n#     sample_ids = []\n#     with torch.no_grad():\n#         for image, sample_id in test_loader:\n#             image = image.to(DEVICE)\n#             # sample_id = sample_id.to(DEVICE)\n#             output = model(image)\n#             pred_3 = torch.clamp(output, min = 0).cpu().numpy()\n#             clover, dead, green = pred_3[:, 0], pred_3[:, 1], pred_3[:, 2]\n#             total = clover + dead + green\n#             gdm = clover + green\n#             pred_5 = np.column_stack([clover, dead, green, total, gdm])\n#             predictions.extend(pred_5.flatten())\n            \n#     return predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-11T01:52:20.914563Z","iopub.execute_input":"2026-01-11T01:52:20.914829Z","iopub.status.idle":"2026-01-11T01:52:21.011572Z","shell.execute_reply.started":"2026-01-11T01:52:20.914805Z","shell.execute_reply":"2026-01-11T01:52:21.010522Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def Test(model, test_loader):\n    model.eval()\n    pred_dict = {}\n    with torch.no_grad():\n        for image, img_ids in test_loader:\n            image = image.to(DEVICE, non_blocking = True)\n            output = model(image)\n            # preds = torch.clamp(output, min=0).cpu().numpy()\n            preds = torch.clamp(output, min=0)\n            preds = torch.nan_to_num(preds, nan=0.0, posinf=0.0, neginf=0.0).cpu().numpy()\n\n            \n            for i, s_id in enumerate(img_ids):\n                clover, dead, green = preds[i]\n                # 각 타겟별로 딕셔너리에 저장\n                pred_dict[f\"{s_id}__Dry_Clover_g\"] = clover\n                pred_dict[f\"{s_id}__Dry_Dead_g\"] = dead\n                pred_dict[f\"{s_id}__Dry_Green_g\"] = green\n                pred_dict[f\"{s_id}__Dry_Total_g\"] = clover + dead + green\n                pred_dict[f\"{s_id}__GDM_g\"] = clover + green\n    return pred_dict","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-11T01:52:21.013015Z","iopub.execute_input":"2026-01-11T01:52:21.013454Z","iopub.status.idle":"2026-01-11T01:52:21.021373Z","shell.execute_reply.started":"2026-01-11T01:52:21.013416Z","shell.execute_reply":"2026-01-11T01:52:21.020341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 10\nfor EPOCH in range(1, EPOCHS + 1):\n    Train(model = model, train_loader = train_loader, optimizer = optimizer, log_interval = 5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-11T01:52:21.022564Z","iopub.execute_input":"2026-01-11T01:52:21.022961Z","iopub.status.idle":"2026-01-11T01:56:07.896386Z","shell.execute_reply.started":"2026-01-11T01:52:21.022933Z","shell.execute_reply":"2026-01-11T01:56:07.895453Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. 예측 실행 (딕셔너리 결과물 획득)\nresults = Test(model, test_loader)\n\n# 2. 원본 제출 양식을 '기준 틀'로 새로 로드 (매우 중요: 행 순서 보장)\nsubmission = pd.read_csv(\"/kaggle/input/csiro-biomass/sample_submission.csv\")\n\n# 3. [핵심 수정] map 함수를 사용하여 원본 ID 순서에 맞춰 예측값 주입\n# 리스트로 붙이는 방식과 달리, ID 이름표를 보고 값을 찾아가므로 절대 꼬이지 않음\nsubmission['target'] = submission['sample_id'].map(results)\n\n# 4. [에러 방지] 혹시라도 매핑 안 된 행(NaN)이 있다면 0.0으로 강제 보충\n# 단 한 줄이라도 NaN이 있으면 캐글은 Scoring Error를 냅니다.\nsubmission['target'] = submission['target'].fillna(0.0)\n\n# 5. 데이터 타입 및 컬럼 순서 고정\nsubmission = submission[['sample_id', 'target']]\nsubmission[\"target\"] = (submission[\"target\"].replace([np.inf, -np.inf], 0.0).fillna(0.0).astype(float))\n\n# 6. 최종 저장\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-11T01:59:58.771523Z","iopub.execute_input":"2026-01-11T01:59:58.772442Z","iopub.status.idle":"2026-01-11T01:59:59.029383Z","shell.execute_reply.started":"2026-01-11T01:59:58.772407Z","shell.execute_reply":"2026-01-11T01:59:59.028472Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"assert os.path.exists(\"submission.csv\")\nassert submission.shape[1] == 2\nassert submission['target'].isnull().sum() == 0","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}