{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"This notebook provides a simple 6‑cell starter baseline for the CSIRO Biomass competition.\nIt includes data loading, preprocessing, a multi‑target regression model, training loop, and submission generation.\nDesigned for educational purposes — not guaranteed to reach medal level.\nParticipants are encouraged to adjust paths, parameters, augmentations, and train their own weights to improve performance.","metadata":{}},{"cell_type":"code","source":"# =========================\n# Cell 1 — Imports & Config\n# =========================\nimport os, random\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader\nimport timm\nimport cv2\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nCFG = {\n    \"input_dir\": \"/kaggle/input/csiro-biomass\",  # adjust if needed\n    \"model_name\": \"tf_efficientnet_b4_ns\",\n    \"img_size\": 384,\n    \"batch_size\": 32,\n    \"epochs\": 3,          # short run for starter\n    \"lr\": 2e-4,\n    \"device\": \"cuda\" if torch.cuda.is_available() else \"cpu\",\n    \"seed\": 42,\n    \"targets_order\": [\"Dry_Green_g\",\"Dry_Dead_g\",\"Dry_Clover_g\",\"GDM_g\",\"Dry_Total_g\"]\n}\n\ndef seed_everything(seed=CFG[\"seed\"]):\n    random.seed(seed); np.random.seed(seed)\n    torch.manual_seed(seed)\n    if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)\nseed_everything()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Cell 2 — Load Data\n# =========================\ntrain = pd.read_csv(os.path.join(CFG[\"input_dir\"], \"train.csv\"))\ntest  = pd.read_csv(os.path.join(CFG[\"input_dir\"], \"test.csv\"))\n\n# absolute paths\ntrain[\"image_path\"] = train[\"image_path\"].apply(lambda p: os.path.join(CFG[\"input_dir\"], p))\ntest[\"image_path\"]  = test[\"image_path\"].apply(lambda p: os.path.join(CFG[\"input_dir\"], p))\n\n# pivot train to wide format: one row per image with 5 targets\nwide = train.pivot_table(index=\"image_path\",\n                         columns=\"target_name\",\n                         values=\"target\",\n                         aggfunc=\"mean\").reset_index()\n\nfor t in CFG[\"targets_order\"]:\n    if t not in wide.columns:\n        wide[t] = np.nan\nwide = wide[[\"image_path\"] + CFG[\"targets_order\"]]\nwide.fillna(0.0, inplace=True)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Cell 3 — Dataset & Transforms\n# =========================\ntrain_tf = A.Compose([\n    A.Resize(CFG[\"img_size\"], CFG[\"img_size\"]),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n    A.Normalize(),\n    ToTensorV2()\n])\n\nvalid_tf = A.Compose([\n    A.Resize(CFG[\"img_size\"], CFG[\"img_size\"]),\n    A.Normalize(),\n    ToTensorV2()\n])\n\nclass BiomassTrainDS(Dataset):\n    def __init__(self, df, transforms):\n        self.df = df.reset_index(drop=True)\n        self.transforms = transforms\n        self.target_cols = CFG[\"targets_order\"]\n    def __len__(self): return len(self.df)\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img = cv2.imread(row[\"image_path\"])\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = self.transforms(image=img)[\"image\"]\n        y = row[self.target_cols].values.astype(np.float32)\n        return img, torch.tensor(y)\n\nclass BiomassTestDS(Dataset):\n    def __init__(self, df_unique_images, transforms):\n        self.df = df_unique_images.reset_index(drop=True)\n        self.transforms = transforms\n    def __len__(self): return len(self.df)\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img = cv2.imread(row[\"image_path\"])\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = self.transforms(image=img)[\"image\"]\n        return img, row[\"image_path\"]\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Cell 4 — Model\n# =========================\nclass BiomassModel(nn.Module):\n    def __init__(self, model_name, n_outputs=5):\n        super().__init__()\n        self.backbone = timm.create_model(model_name, pretrained=True, num_classes=0)\n        self.head = nn.Sequential(\n            nn.Dropout(0.25),\n            nn.Linear(self.backbone.num_features, n_outputs)\n        )\n    def forward(self, x):\n        f = self.backbone(x)\n        return self.head(f)\n\nmodel = BiomassModel(CFG[\"model_name\"], n_outputs=len(CFG[\"targets_order\"])).to(CFG[\"device\"])\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Cell 5 — Training Loop (Skeleton)\n# =========================\noptimizer = torch.optim.AdamW(model.parameters(), lr=CFG[\"lr\"])\ncriterion = nn.SmoothL1Loss()\n\nmask = np.random.rand(len(wide)) < 0.9\ntr_df = wide[mask].copy()\nva_df = wide[~mask].copy()\n\ntr_loader = DataLoader(BiomassTrainDS(tr_df, train_tf),\n                       batch_size=CFG[\"batch_size\"], shuffle=True, num_workers=2, pin_memory=True)\nva_loader = DataLoader(BiomassTrainDS(va_df, valid_tf),\n                       batch_size=CFG[\"batch_size\"], shuffle=False, num_workers=2, pin_memory=True)\n\nfor ep in range(CFG[\"epochs\"]):\n    model.train()\n    tr_losses = []\n    for imgs, targets in tr_loader:\n        imgs = imgs.to(CFG[\"device\"])\n        targets = targets.to(CFG[\"device\"])\n        optimizer.zero_grad()\n        preds = model(imgs)\n        loss = criterion(preds, targets)\n        loss.backward()\n        optimizer.step()\n        tr_losses.append(loss.item())\n    model.eval()\n    va_losses = []\n    with torch.no_grad():\n        for imgs, targets in va_loader:\n            imgs = imgs.to(CFG[\"device\"])\n            targets = targets.to(CFG[\"device\"])\n            preds = model(imgs)\n            loss = criterion(preds, targets)\n            va_losses.append(loss.item())\n    print(f\"Epoch {ep+1}/{CFG['epochs']} | train {np.mean(tr_losses):.4f} | valid {np.mean(va_losses):.4f}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Cell 6 — Prediction & Submission\n# =========================\nunique_test_imgs = test[[\"image_path\"]].drop_duplicates().reset_index(drop=True)\ntest_loader = DataLoader(BiomassTestDS(unique_test_imgs, valid_tf),\n                         batch_size=CFG[\"batch_size\"], shuffle=False, num_workers=2, pin_memory=True)\n\nmodel.eval()\nimage_to_pred = {}\nwith torch.no_grad():\n    for imgs, paths in test_loader:\n        imgs = imgs.to(CFG[\"device\"])\n        outputs = model(imgs).cpu().numpy()\n        for p, out in zip(paths, outputs):\n            image_to_pred[p] = out\n\npred_vals = []\nfor _, row in test.iterrows():\n    img_path = row[\"image_path\"]\n    tname = row[\"target_name\"]\n    idx = CFG[\"targets_order\"].index(tname)\n    val = float(image_to_pred.get(img_path, np.zeros(len(CFG[\"targets_order\"])))[idx])\n    pred_vals.append(val)\n\nsubmission = pd.DataFrame({\n    \"sample_id\": test[\"sample_id\"],\n    \"target\": pred_vals\n})\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"✅ submission.csv saved\")\n\n\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}