{"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":"gpu","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":290775987,"sourceType":"kernelVersion"}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 1️⃣ Setup & Load Artifacts","metadata":{}},{"cell_type":"code","source":"import os\nos.listdir('/kaggle/input/csiro-embbedings-2-0')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:21.925304Z","iopub.execute_input":"2026-01-08T15:45:21.925666Z","iopub.status.idle":"2026-01-08T15:45:21.940473Z","shell.execute_reply.started":"2026-01-08T15:45:21.925632Z","shell.execute_reply":"2026-01-08T15:45:21.939863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport random\n\nSEED = 42\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n# Load embeddings\nsiglip = np.load(\"/kaggle/input/csiro-embbedings-2-0/train_siglip.npy\")\ndino   = np.load(\"/kaggle/input/csiro-embbedings-2-0/train_dino.npy\")\nids    = np.load(\"/kaggle/input/csiro-embbedings-2-0/train_image_ids.npy\")\n\n# Concatenate visual backbone\nX = np.concatenate([siglip, dino], axis=1)\nX = torch.tensor(X, dtype=torch.float32).to(DEVICE)\n\nprint(\"Backbone shape:\", X.shape)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:21.941443Z","iopub.execute_input":"2026-01-08T15:45:21.941711Z","iopub.status.idle":"2026-01-08T15:45:29.780708Z","shell.execute_reply.started":"2026-01-08T15:45:21.941681Z","shell.execute_reply":"2026-01-08T15:45:29.779966Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2️⃣ Load Targets (ONCE)","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/csiro-biomass/train.csv\")\ntrain[\"image_id\"] = train[\"sample_id\"].str.split(\"__\").str[0]\n\nwide = train.pivot_table(\n    index=\"image_id\",\n    columns=\"target_name\",\n    values=\"target\"\n)\n\nwide[\"GDM\"]   = wide[\"Dry_Green_g\"] + wide[\"Dry_Clover_g\"]\nwide[\"Total\"] = wide[\"GDM\"] + wide[\"Dry_Dead_g\"]\n\nwide = wide.loc[ids]\n\ntargets = {\n    \"total\": torch.tensor(wide[\"Total\"].values, dtype=torch.float32).to(DEVICE),\n    \"ratio\": torch.tensor(\n        np.stack([\n            wide[\"Dry_Green_g\"] / wide[\"Total\"],\n            wide[\"Dry_Dead_g\"]  / wide[\"Total\"],\n            wide[\"Dry_Clover_g\"]/ wide[\"Total\"]\n        ], axis=1),\n        dtype=torch.float32\n    ).to(DEVICE)\n}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:29.781749Z","iopub.execute_input":"2026-01-08T15:45:29.782226Z","iopub.status.idle":"2026-01-08T15:45:29.868175Z","shell.execute_reply.started":"2026-01-08T15:45:29.782200Z","shell.execute_reply":"2026-01-08T15:45:29.867408Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3️⃣ Pair Sampler (USED BY ALL MVPs)","metadata":{}},{"cell_type":"code","source":"def sample_pairs(n, batch=1024):\n    i = torch.randint(0, n, (batch,))\n    j = torch.randint(0, n, (batch,))\n    return i, j\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:29.869151Z","iopub.execute_input":"2026-01-08T15:45:29.869457Z","iopub.status.idle":"2026-01-08T15:45:29.873235Z","shell.execute_reply.started":"2026-01-08T15:45:29.869405Z","shell.execute_reply":"2026-01-08T15:45:29.872714Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4️⃣ MVP Backbone (SHARED)","metadata":{}},{"cell_type":"code","source":"class MVP(nn.Module):\n    def __init__(self, in_dim, out_dim):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(in_dim * 4, 256),\n            nn.ReLU(),\n            nn.Linear(256, out_dim)\n        )\n\n    def forward(self, a, b):\n        x = torch.cat([a, b, torch.abs(a - b), a * b], dim=1)\n        return self.net(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:29.873931Z","iopub.execute_input":"2026-01-08T15:45:29.874162Z","iopub.status.idle":"2026-01-08T15:45:29.891164Z","shell.execute_reply.started":"2026-01-08T15:45:29.874131Z","shell.execute_reply":"2026-01-08T15:45:29.890237Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🧩 MVP-1 — BIOMASS MAGNITUDE","metadata":{}},{"cell_type":"code","source":"mvp_biomass = MVP(X.shape[1], 64).to(DEVICE)\nopt = torch.optim.AdamW(mvp_biomass.parameters(), lr=1e-3)\nloss_fn = nn.L1Loss()\n\nfor _ in range(3000):\n    i, j = sample_pairs(len(X))\n    pred = mvp_biomass(X[i], X[j])\n    target = torch.abs(targets[\"total\"][i] - targets[\"total\"][j]).unsqueeze(1)\n    loss = loss_fn(pred.mean(dim=1, keepdim=True), target)\n    opt.zero_grad()\n    loss.backward()\n    opt.step()\n\ntorch.save(mvp_biomass.state_dict(), \"mvp_biomass.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:29.894394Z","iopub.execute_input":"2026-01-08T15:45:29.894920Z","iopub.status.idle":"2026-01-08T15:45:42.639534Z","shell.execute_reply.started":"2026-01-08T15:45:29.894887Z","shell.execute_reply":"2026-01-08T15:45:42.638883Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🌱 MVP-2 — COMPOSITION RATIOS","metadata":{}},{"cell_type":"code","source":"mvp_ratio = MVP(X.shape[1], 64).to(DEVICE)\nopt = torch.optim.AdamW(mvp_ratio.parameters(), lr=1e-3)\nloss_fn = nn.L1Loss()\n\nfor _ in range(3000):\n    i, j = sample_pairs(len(X))\n    pred = mvp_ratio(X[i], X[j])\n    target = torch.abs(targets[\"ratio\"][i] - targets[\"ratio\"][j]).mean(dim=1, keepdim=True)\n    loss = loss_fn(pred.mean(dim=1, keepdim=True), target)\n    opt.zero_grad()\n    loss.backward()\n    opt.step()\n\ntorch.save(mvp_ratio.state_dict(), \"mvp_ratio.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:42.640429Z","iopub.execute_input":"2026-01-08T15:45:42.640839Z","iopub.status.idle":"2026-01-08T15:45:51.284086Z","shell.execute_reply.started":"2026-01-08T15:45:42.640814Z","shell.execute_reply":"2026-01-08T15:45:51.283485Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 🟫 MVP-3 — RANKING MVP","metadata":{}},{"cell_type":"code","source":"mvp_rank = MVP(X.shape[1], 32).to(DEVICE)\nopt = torch.optim.AdamW(mvp_rank.parameters(), lr=1e-3)\nloss_fn = nn.BCEWithLogitsLoss()\n\nfor _ in range(3000):\n    i, j = sample_pairs(len(X))\n    pred = mvp_rank(X[i], X[j]).mean(dim=1)\n    target = (targets[\"total\"][i] > targets[\"total\"][j]).float()\n    loss = loss_fn(pred, target)\n    opt.zero_grad()\n    loss.backward()\n    opt.step()\n\ntorch.save(mvp_rank.state_dict(), \"mvp_rank.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:51.284972Z","iopub.execute_input":"2026-01-08T15:45:51.285251Z","iopub.status.idle":"2026-01-08T15:45:59.893137Z","shell.execute_reply.started":"2026-01-08T15:45:51.285216Z","shell.execute_reply":"2026-01-08T15:45:59.892352Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 5️⃣ Extract FINAL MVP EMBEDDINGS (CRITICAL)","metadata":{}},{"cell_type":"code","source":"def extract(mvp, X, dim):\n    with torch.no_grad():\n        out = mvp(X, X)\n    return out.cpu().numpy()\n\nnp.save(\"train_mvp_biomass.npy\", extract(mvp_biomass, X, 64))\nnp.save(\"train_mvp_ratio.npy\",   extract(mvp_ratio,   X, 64))\nnp.save(\"train_mvp_rank.npy\",    extract(mvp_rank,    X, 32))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:59.894111Z","iopub.execute_input":"2026-01-08T15:45:59.894451Z","iopub.status.idle":"2026-01-08T15:45:59.903939Z","shell.execute_reply.started":"2026-01-08T15:45:59.894402Z","shell.execute_reply":"2026-01-08T15:45:59.902784Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ✅ SANITY CHECK (MANDATORY)","metadata":{}},{"cell_type":"code","source":"print(\"Biomass:\", np.load(\"train_mvp_biomass.npy\").shape)\nprint(\"Ratio  :\", np.load(\"train_mvp_ratio.npy\").shape)\nprint(\"Rank   :\", np.load(\"train_mvp_rank.npy\").shape)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T15:45:59.905096Z","iopub.execute_input":"2026-01-08T15:45:59.905403Z","iopub.status.idle":"2026-01-08T15:45:59.917297Z","shell.execute_reply.started":"2026-01-08T15:45:59.905371Z","shell.execute_reply":"2026-01-08T15:45:59.916692Z"}},"outputs":[],"execution_count":null}]}