{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"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\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","trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:32:38.380924Z","iopub.execute_input":"2025-11-30T05:32:38.381592Z","iopub.status.idle":"2025-11-30T05:32:39.907641Z","shell.execute_reply.started":"2025-11-30T05:32:38.381565Z","shell.execute_reply":"2025-11-30T05:32:39.907042Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nimport timm\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom PIL import Image\nfrom sklearn.model_selection import StratifiedGroupKFold\nfrom sklearn.preprocessing import LabelEncoder","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:32:39.909448Z","iopub.execute_input":"2025-11-30T05:32:39.909772Z","iopub.status.idle":"2025-11-30T05:33:25.129924Z","shell.execute_reply.started":"2025-11-30T05:32:39.909755Z","shell.execute_reply":"2025-11-30T05:33:25.129075Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#パラメータまとめ\nclass CFG:\n    model_name = 'tf_efficientnetv2_l'\n    img_size = 384\n    n_classes = 5 #予測するターゲット数\n    batch_size = 8\n    epochs = 10\n    learning_rate = 1e-4\n    seed = 42\n    target_cols = [\n        'Dry_Green_g', 'Dry_Dead_g', 'Dry_Clover_g', 'GDM_g', 'Dry_Total_g'\n    ]\n    loss_weights = torch.tensor([0.1, 0.1, 0.1, 0.2, 0.5]).cuda()\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n#シード値を固定\ndef seed_everything(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\nseed_everything(CFG.seed)\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.130887Z","iopub.execute_input":"2025-11-30T05:33:25.131376Z","iopub.status.idle":"2025-11-30T05:33:25.392473Z","shell.execute_reply.started":"2025-11-30T05:33:25.131356Z","shell.execute_reply":"2025-11-30T05:33:25.391684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/csiro-biomass/train.csv')\ntrain_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.393446Z","iopub.execute_input":"2025-11-30T05:33:25.393649Z","iopub.status.idle":"2025-11-30T05:33:25.436193Z","shell.execute_reply.started":"2025-11-30T05:33:25.393633Z","shell.execute_reply":"2025-11-30T05:33:25.435451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df['image_id'] = train_df['sample_id'].apply(lambda x: x.split('__')[0])\n\n#メタデータの使い方は要検討!!\ntrain_pivot = train_df.pivot(\n    index=['image_id', 'image_path', 'Sampling_Date', 'State'], #残したい列\n    columns='target_name',\n    values='target'\n).reset_index()\ntrain_pivot = train_pivot[['image_path'] + CFG.target_cols + ['Sampling_Date', 'State']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.437145Z","iopub.execute_input":"2025-11-30T05:33:25.437380Z","iopub.status.idle":"2025-11-30T05:33:25.464624Z","shell.execute_reply.started":"2025-11-30T05:33:25.437355Z","shell.execute_reply":"2025-11-30T05:33:25.464026Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_pivot","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.465351Z","iopub.execute_input":"2025-11-30T05:33:25.465553Z","iopub.status.idle":"2025-11-30T05:33:25.478541Z","shell.execute_reply.started":"2025-11-30T05:33:25.465536Z","shell.execute_reply":"2025-11-30T05:33:25.477676Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_pivot['Sampling_Date']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.480986Z","iopub.execute_input":"2025-11-30T05:33:25.481301Z","iopub.status.idle":"2025-11-30T05:33:25.490354Z","shell.execute_reply.started":"2025-11-30T05:33:25.481277Z","shell.execute_reply":"2025-11-30T05:33:25.489730Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class PastureDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.image_paths = df['image_path'].values\n        self.labels = df[CFG.target_cols].values if CFG.target_cols[0] in df.columns else None\n        self.transform = transform\n    def __len__(self):\n        return len(self.df)\n    def __getitem__(self, idx):\n        path = '/kaggle/input/csiro-biomass/' + self.image_paths[idx]\n        image = Image.open(path).convert('RGB')\n        image = np.array(image)\n        if self.transform:\n            augmented = self.transform(image=image)\n            image = augmented['image']\n        if self.labels is not None:\n            label = torch.tensor(self.labels[idx]).float()\n            return image, label\n        else:\n            return iamge\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.491141Z","iopub.execute_input":"2025-11-30T05:33:25.491393Z","iopub.status.idle":"2025-11-30T05:33:25.502368Z","shell.execute_reply.started":"2025-11-30T05:33:25.491365Z","shell.execute_reply":"2025-11-30T05:33:25.501705Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_transforms(data='train'):\n    if data == 'train':\n    #ここでdata_augmentation \n        return A.Compose([\n            A.Resize(CFG.img_size, CFG.img_size),\n            A.HorizontalFlip(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.Rotate(limit=30, p=0.5),\n            A.Normalize(\n                mean=[0.485, 0.456, 0.406], # ImageNetの平均\n                std=[0.229, 0.224, 0.225],  # ImageNetの標準偏差\n            ),\n            ToTensorV2()\n        ])\n    elif data == 'valid':\n        return A.Compose([\n            A.Resize(CFG.img_size, CFG.img_size),\n            A.Normalize(\n                mean=[0.485, 0.456, 0.406], # ImageNetの平均\n                std=[0.229, 0.224, 0.225],  # ImageNetの標準偏差\n            ),\n            ToTensorV2()\n        ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.503138Z","iopub.execute_input":"2025-11-30T05:33:25.503388Z","iopub.status.idle":"2025-11-30T05:33:25.512468Z","shell.execute_reply.started":"2025-11-30T05:33:25.503371Z","shell.execute_reply":"2025-11-30T05:33:25.511737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class PastureModel(nn.Module):\n    def __init__(self, model_name, pretrained=True):\n        super().__init__()\n        self.model = timm.create_model(\n            model_name,\n            pretrained=pretrained,\n            num_classes=CFG.n_classes\n        )\n    def forward(self, x):\n        return self.model(x)\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.513211Z","iopub.execute_input":"2025-11-30T05:33:25.513587Z","iopub.status.idle":"2025-11-30T05:33:25.525777Z","shell.execute_reply.started":"2025-11-30T05:33:25.513566Z","shell.execute_reply":"2025-11-30T05:33:25.525132Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#損失関数\nclass WeightedMSELoss(nn.Module):\n    def __init__(self, weights):\n        super().__init__()\n        self.weights = weights # shape: [5]\n\n    def forward(self, inputs, targets):\n        loss = (inputs - targets) ** 2\n        loss = loss * self.weights\n        return loss.mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.526504Z","iopub.execute_input":"2025-11-30T05:33:25.526829Z","iopub.status.idle":"2025-11-30T05:33:25.538161Z","shell.execute_reply.started":"2025-11-30T05:33:25.526813Z","shell.execute_reply":"2025-11-30T05:33:25.537440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_one_epoch(model, optimizer, scheduler, dataloader, device, epoch):\n    model.train()\n    dataset_size = 0\n    running_loss = 0.0\n    criterion = WeightedMSELoss(CFG.loss_weights)\n    for images, labels in dataloader:\n        images = images.to(device)\n        labels = labels.to(device)\n        batch_size = images.size(0)\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += (loss.item() * batch_size)\n        dataset_size += batch_size\n    return running_loss / dataset_size\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.539001Z","iopub.execute_input":"2025-11-30T05:33:25.539297Z","iopub.status.idle":"2025-11-30T05:33:25.545594Z","shell.execute_reply.started":"2025-11-30T05:33:25.539273Z","shell.execute_reply":"2025-11-30T05:33:25.544851Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_score(y_true, y_pred):\n    weights = np.array([0.1, 0.1, 0.1, 0.2, 0.5])\n    weights_broadcast = np.broadcast_to(weights, y_true.shape)\n    y_weighted_mean = np.average(y_true, weights=weights_broadcast)\n    rss = np.sum(weights_broadcast * (y_true - y_pred) ** 2)\n    tss = np.sum(weights_broadcast * (y_true - y_weighted_mean) ** 2)\n    r2 = 1 - (rss / tss)\n    return r2\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.546380Z","iopub.execute_input":"2025-11-30T05:33:25.546715Z","iopub.status.idle":"2025-11-30T05:33:25.558555Z","shell.execute_reply.started":"2025-11-30T05:33:25.546692Z","shell.execute_reply":"2025-11-30T05:33:25.557901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def valid_one_epoch(model, dataloader, device):\n    model.eval()\n    preds_all = []\n    labels_all = []\n\n    with torch.no_grad():\n        for images, labels in dataloader:\n            images = images.to(device)\n            labels = labels.to(device)\n            outputs = model(images)\n            preds_all.append(outputs.cpu().numpy())\n            labels_all.append(labels.cpu().numpy())\n    preds_all = np.concatenate(preds_all, axis=0)\n    labels_all = np.concatenate(labels_all, axis=0)\n    preds_all = np.clip(preds_all, 0, None)\n    score = get_score(labels_all, preds_all)\n    return score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.559329Z","iopub.execute_input":"2025-11-30T05:33:25.559977Z","iopub.status.idle":"2025-11-30T05:33:25.570520Z","shell.execute_reply.started":"2025-11-30T05:33:25.559950Z","shell.execute_reply":"2025-11-30T05:33:25.569784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = LabelEncoder()\ntrain_pivot['state_label'] = le.fit_transform(train_pivot['State'])\nsgkf = StratifiedGroupKFold(n_splits=5)\ngroups = train_pivot['Sampling_Date'].values\ny_stratify = train_pivot['state_label'].values\n\nfor fold, (train_idx, valid_idx) in enumerate(sgkf.split(train_pivot, y=y_stratify, groups=groups)):\n    train_dates = set(train_pivot.iloc[train_idx]['Sampling_Date'])\n    valid_dates = set(train_pivot.iloc[valid_idx]['Sampling_Date'])\n    intersect = train_dates.intersection(valid_dates)\n    print(f\"Fold {fold+1}:\")\n    print(f\"  Train State分布: {train_pivot.iloc[train_idx]['State'].value_counts().to_dict()}\")\n    print(f\"  Valid State分布: {train_pivot.iloc[valid_idx]['State'].value_counts().to_dict()}\")\n    print(f\"  日付のLeak有無: {'LEAKあり!!' if intersect else 'OK (Leakなし)'}\")\n    \n    train_ds = PastureDataset(train_pivot.iloc[train_idx], transform=get_transforms('train'))\n    valid_ds = PastureDataset(train_pivot.iloc[valid_idx], transform=get_transforms('valid'))\n    train_loader = DataLoader(train_ds, batch_size=CFG.batch_size, shuffle=True, num_workers=2)\n    valid_loader = DataLoader(valid_ds, batch_size=CFG.batch_size, shuffle=False, num_workers=2)\n    \n    model = PastureModel(CFG.model_name).to(CFG.device)\n    optimizer = optim.AdamW(model.parameters(), lr=CFG.learning_rate)\n\n    best_score = -np.inf\n    for epoch in range(CFG.epochs):\n        train_loss = train_one_epoch(model, optimizer, None, train_loader, CFG.device, epoch)\n        val_score = valid_one_epoch(model, valid_loader, CFG.device)\n        print(f'Fold {fold+1}|Epoch{epoch+1}|Train Loss:{train_loss:.4f}|Val R2 Score:{val_score:.4f}')\n\n        if val_score > best_score:\n            best_score = val_score\n            torch.save(model.state_dict(), f'best_model_fold{fold+1}.pth')\n    print(f'Fold{fold+1} Best R2 Score: {best_score:.4f}')\n    break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T05:33:25.571294Z","iopub.execute_input":"2025-11-30T05:33:25.571761Z","execution_failed":"2025-11-30T05:38:26.461Z"}},"outputs":[],"execution_count":null}]}