{"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":4535,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":3327,"modelId":986}],"dockerImageVersionId":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nfrom pathlib import Path\nfrom tqdm import tqdm\n\nimport lightgbm as lgb\nfrom lightgbm import LGBMRegressor\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.model_selection import GridSearchCV, KFold\nfrom sklearn.metrics import make_scorer\nfrom sklearn.multioutput import MultiOutputRegressor\n\nimport torch\nfrom torch.utils.data import DataLoader\nimport torchvision.transforms as T\nfrom PIL import Image\nfrom transformers import AutoModel","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T15:56:34.084395Z","iopub.execute_input":"2026-01-04T15:56:34.084804Z","iopub.status.idle":"2026-01-04T15:56:34.090129Z","shell.execute_reply.started":"2026-01-04T15:56:34.084774Z","shell.execute_reply":"2026-01-04T15:56:34.089231Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TARGETS = ['Dry_Green_g', 'Dry_Dead_g', 'Dry_Clover_g', 'GDM_g', 'Dry_Total_g']\nWEIGHTS = np.array([0.1, 0.1, 0.1, 0.2, 0.5])\n\ndef weighted_r2_metric(y_true, y_pred):\n    ss_res = np.sum(WEIGHTS * np.sum((y_true - y_pred)**2, axis=0))\n    global_mean = np.average(np.mean(y_true, axis=0), weights=WEIGHTS)\n    ss_tot = np.sum(WEIGHTS * np.sum((y_true - global_mean)**2, axis=0))\n    return 1 - (ss_res / ss_tot)\n\nweighted_scorer = make_scorer(weighted_r2_metric, greater_is_better=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T16:00:10.385461Z","iopub.execute_input":"2026-01-04T16:00:10.385841Z","iopub.status.idle":"2026-01-04T16:00:10.392034Z","shell.execute_reply.started":"2026-01-04T16:00:10.385811Z","shell.execute_reply":"2026-01-04T16:00:10.391135Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MEAN = [0.485, 0.456, 0.406]\nSTD  = [0.229, 0.224, 0.225]\n\n@torch.no_grad()\ndef extract_dense_features(df, model, device, base_path):\n    \"\"\"TTA 提取 dense 特征 -> DataFrame\"\"\"\n    tta_transforms = [\n        T.Compose([T.Resize(256), T.CenterCrop(224), T.ToTensor(), T.Normalize(MEAN, STD)]),\n        T.Compose([T.Resize(256), T.CenterCrop(224), T.RandomHorizontalFlip(p=1.0), T.ToTensor(), T.Normalize(MEAN, STD)])\n    ]\n    unique_paths = df['image_path'].unique()\n    all_feats = []\n    for path in tqdm(unique_paths, desc=\"GPU Feature Extraction\"):\n        img = Image.open(base_path / path).convert(\"RGB\")\n        tta_results = [\n            model(aug(img).unsqueeze(0).to(device)).last_hidden_state[:, 1:, :].mean(dim=1).cpu().numpy()\n            for aug in tta_transforms\n        ]\n        all_feats.append(np.mean(tta_results, axis=0))\n    feat_matrix = np.vstack(all_feats)\n    feat_cols = [f\"feat_{i}\" for i in range(feat_matrix.shape[1])]\n    return pd.DataFrame(feat_matrix, columns=feat_cols), unique_paths","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T15:57:03.008227Z","iopub.execute_input":"2026-01-04T15:57:03.008629Z","iopub.status.idle":"2026-01-04T15:57:03.019186Z","shell.execute_reply.started":"2026-01-04T15:57:03.008585Z","shell.execute_reply":"2026-01-04T15:57:03.018318Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nbase_path = Path(\"/kaggle/input/csiro-biomass\")\nmodel_dir = os.path.abspath(\"/kaggle/input/dinov2/pytorch/large/1\")\n\nmodel = AutoModel.from_pretrained(\n    model_dir, local_files_only=True, trust_remote_code=True\n).to(device).eval()\n\ntrain_df = pd.read_csv(base_path / \"train.csv\")\ntrain_p = train_df.pivot_table(\n    index=\"image_path\", columns=\"target_name\", values=\"target\"\n).reset_index()\nX_train_df, _ = extract_dense_features(train_p, model, device, base_path)\nY_train_log = np.log1p(train_p[TARGETS].values)\n\nscaler = StandardScaler()\nX_train_scaled = pd.DataFrame(\n    scaler.fit_transform(X_train_df), columns=X_train_df.columns\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T16:00:14.816069Z","iopub.execute_input":"2026-01-04T16:00:14.816366Z","iopub.status.idle":"2026-01-04T16:01:11.548161Z","shell.execute_reply.started":"2026-01-04T16:00:14.816340Z","shell.execute_reply":"2026-01-04T16:01:11.547404Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"param_grid = {\n    \"estimator__learning_rate\": [0.05, 0.1],\n    \"estimator__num_leaves\": [31, 63],\n    \"estimator__n_estimators\": [100, 200],\n    \"estimator__random_state\": [42]\n}\nbase_lgbm = MultiOutputRegressor(LGBMRegressor(verbosity=-1))\ngrid_search = GridSearchCV(\n    base_lgbm,\n    param_grid,\n    cv=KFold(n_splits=5, shuffle=True, random_state=42),\n    scoring=weighted_scorer,\n    n_jobs=-1,\n    verbose=0\n)\ngrid_search.fit(X_train_scaled, Y_train_log)\n\nprint(\"\\n\" + \"=\"*50)\nprint(f\"Best LGBM Params: {grid_search.best_params_}\")\nprint(f\"Best CV Weighted R2: {grid_search.best_score_:.4f}\")\nprint(\"=\"*50)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T16:03:49.707507Z","iopub.execute_input":"2026-01-04T16:03:49.708355Z","iopub.status.idle":"2026-01-04T16:10:01.138979Z","shell.execute_reply.started":"2026-01-04T16:03:49.708325Z","shell.execute_reply":"2026-01-04T16:10:01.138220Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.read_csv(base_path / \"test.csv\")\ntest_uniq = pd.DataFrame({\"image_path\": test_df[\"image_path\"].unique()})\nX_test_df, test_ids = extract_dense_features(test_uniq, model, device, base_path)\nX_test_scaled = pd.DataFrame(scaler.transform(X_test_df), columns=X_test_df.columns)\n\ntest_preds_log = grid_search.best_estimator_.predict(X_test_scaled)\navg_preds = np.maximum(np.expm1(test_preds_log), 0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T16:10:15.822071Z","iopub.execute_input":"2026-01-04T16:10:15.822383Z","iopub.status.idle":"2026-01-04T16:10:16.026208Z","shell.execute_reply.started":"2026-01-04T16:10:15.822354Z","shell.execute_reply":"2026-01-04T16:10:16.025362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"final_rows = []\nfor i, path in enumerate(test_ids):\n    image_id = Path(path).stem\n    for j, target_name in enumerate(TARGETS):\n        final_rows.append(\n            {\"sample_id\": f\"{image_id}__{target_name}\", \"target\": avg_preds[i, j]}\n        )\npd.DataFrame(final_rows).to_csv(\"submission.csv\", index=False)\nprint(\"\\n🚀 LGBM submission saved to submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-04T16:10:18.640093Z","iopub.execute_input":"2026-01-04T16:10:18.640960Z","iopub.status.idle":"2026-01-04T16:10:18.652286Z","shell.execute_reply.started":"2026-01-04T16:10:18.640930Z","shell.execute_reply":"2026-01-04T16:10:18.651469Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}