{"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":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input/csiro-biomass'):\n    for filename in filenames:\n        os.path.join(dirname, filename)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-19T20:15:16.596162Z","iopub.execute_input":"2025-12-19T20:15:16.596465Z","iopub.status.idle":"2025-12-19T20:15:16.789946Z","shell.execute_reply.started":"2025-12-19T20:15:16.596440Z","shell.execute_reply":"2025-12-19T20:15:16.789321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\nDATA_ROOT = \"/kaggle/input/csiro-biomass\"\n\ntrain_df = pd.read_csv(f\"{DATA_ROOT}/train.csv\")\ntest_df  = pd.read_csv(f\"{DATA_ROOT}/test.csv\")\n\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T20:15:16.791353Z","iopub.execute_input":"2025-12-19T20:15:16.791780Z","iopub.status.idle":"2025-12-19T20:15:16.811724Z","shell.execute_reply.started":"2025-12-19T20:15:16.791728Z","shell.execute_reply":"2025-12-19T20:15:16.811145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nos.environ[\"PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION\"] = \"python\"\nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"2\"\nfrom tensorflow.keras import mixed_precision\nmixed_precision.set_global_policy('mixed_float16')\n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import mean_squared_error\nfrom tensorflow.keras.applications import EfficientNetB0\nfrom tensorflow.keras.layers import Input, Dense, Dropout, GlobalAveragePooling2D, Concatenate\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import Callback\n\nDATA_ROOT = \"/kaggle/input/csiro-biomass\"\nIMG_SIZE = 224\nBATCH_SIZE = 32\nEPOCHS = 60\nAUTOTUNE = tf.data.AUTOTUNE\n\nTARGET_COLS = [\n    \"Dry_Clover_g\",\n    \"Dry_Dead_g\",\n    \"Dry_Green_g\",\n    \"Dry_Total_g\",\n    \"GDM_g\"\n]\n\ntrain_df = pd.read_csv(f\"{DATA_ROOT}/train.csv\")\n\npivot_df = train_df.pivot_table(\n    index=\"image_path\",\n    columns=\"target_name\",\n    values=\"target\",\n    aggfunc=\"mean\"\n).reset_index()\n\nnum_df = (\n    train_df\n    .groupby(\"image_path\")[[\"Pre_GSHH_NDVI\", \"Height_Ave_cm\"]]\n    .mean()\n    .reset_index()\n)\n\npivot_df = pivot_df.merge(num_df, on=\"image_path\").dropna()\n\nX_img = (DATA_ROOT + \"/\" + pivot_df[\"image_path\"]).values\nX_num = pivot_df[[\"Pre_GSHH_NDVI\", \"Height_Ave_cm\"]].values\ny = np.log1p(pivot_df[TARGET_COLS].values)\n\nXimg_tr, Ximg_val, Xnum_tr, Xnum_val, y_tr, y_val = train_test_split(\n    X_img, X_num, y, test_size=0.2, random_state=42\n)\n\ndef load_train(img_path, num_feats, label):\n    img = tf.io.read_file(img_path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, (IMG_SIZE, IMG_SIZE))\n    img = tf.image.random_flip_left_right(img)\n    img = tf.image.random_brightness(img, 0.1)\n    img = tf.image.random_contrast(img, 0.9, 1.1)\n    img = tf.cast(img, tf.float32) / 255.0\n    return {\"image\": img, \"numeric\": num_feats}, label\n\ndef load_val(img_path, num_feats, label):\n    img = tf.io.read_file(img_path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, (IMG_SIZE, IMG_SIZE))\n    img = tf.cast(img, tf.float32) / 255.0\n    return {\"image\": img, \"numeric\": num_feats}, label\n\ntrain_ds = tf.data.Dataset.from_tensor_slices((Ximg_tr, Xnum_tr, y_tr))\ntrain_ds = train_ds.map(load_train, num_parallel_calls=AUTOTUNE)\ntrain_ds = train_ds.shuffle(1024).batch(BATCH_SIZE).repeat().prefetch(AUTOTUNE)\n\nval_ds = tf.data.Dataset.from_tensor_slices((Ximg_val, Xnum_val, y_val))\nval_ds = val_ds.map(load_val, num_parallel_calls=AUTOTUNE)\nval_ds = val_ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n\nimg_input = Input(shape=(IMG_SIZE, IMG_SIZE, 3), name=\"image\")\n\ncnn = EfficientNetB0(\n    include_top=False,\n    weights=None,\n    input_tensor=img_input\n)\n\ncnn.trainable = True\n\nx_img = GlobalAveragePooling2D()(cnn.output)\nx_img = Dense(256, activation=\"relu\")(x_img)\nx_img = Dropout(0.4)(x_img)\n\nnum_input = Input(shape=(2,), name=\"numeric\")\nx_num = Dense(64, activation=\"relu\")(num_input)\nx_num = Dense(32, activation=\"relu\")(x_num)\nx_num = Dense(16, activation=\"relu\")(x_num)\n\nx = Concatenate()([x_img, x_num])\nx = Dense(128, activation=\"relu\")(x)\nx = Dropout(0.3)(x)\n\noutputs = Dense(5, activation=\"linear\")(x)\n\nmodel = Model(inputs=[img_input, num_input], outputs=outputs)\n\ndef biomass_loss(y_true, y_pred):\n    mse = tf.reduce_mean(tf.square(y_true - y_pred))\n    constraint = tf.reduce_mean(\n        tf.square(y_pred[:, 3] - (y_pred[:, 0] + y_pred[:, 1] + y_pred[:, 2]))\n    )\n    return mse + 0.5 * constraint\n\noptimizer = Adam(1e-5)\n\nmodel.compile(\n    optimizer=optimizer,\n    loss=biomass_loss,\n    metrics=[\"mae\"]\n)\n\nclass LRLogger(Callback):\n    def on_train_begin(self, logs=None):\n        self.lrs = []\n    def on_epoch_end(self, epoch, logs=None):\n        lr = self.model.optimizer.learning_rate\n        self.lrs.append(float(tf.keras.backend.get_value(lr)))\n\nsteps_per_epoch = len(Ximg_tr) // BATCH_SIZE\nval_steps = len(Ximg_val) // BATCH_SIZE\n\nlr_logger = LRLogger()\n\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    steps_per_epoch=steps_per_epoch,\n    validation_steps=val_steps,\n    epochs=EPOCHS,\n    callbacks=[lr_logger]\n)\n\nepochs_range = range(1, EPOCHS + 1)\n\nplt.figure()\nplt.plot(epochs_range, history.history[\"loss\"])\nplt.plot(epochs_range, history.history[\"val_loss\"])\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend([\"Train\", \"Validation\"])\nplt.title(\"Loss Curve\")\nplt.show()\n\nplt.figure()\nplt.plot(epochs_range, history.history[\"mae\"])\nplt.plot(epochs_range, history.history[\"val_mae\"])\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"MAE\")\nplt.legend([\"Train\", \"Validation\"])\nplt.title(\"MAE Curve\")\nplt.show()\n\nplt.figure()\nplt.plot(epochs_range, lr_logger.lrs)\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Learning Rate\")\nplt.title(\"Learning Rate\")\nplt.show()\n\ntest_df = pd.read_csv(f\"{DATA_ROOT}/test.csv\")\ntest_df = test_df.drop_duplicates(subset=\"image_path\").reset_index(drop=True)\n\nX_test_img = (DATA_ROOT + \"/\" + test_df[\"image_path\"]).values\nnum_means = pivot_df[[\"Pre_GSHH_NDVI\", \"Height_Ave_cm\"]].mean().values\nX_test_num = np.tile(num_means, (len(test_df), 1))\n\ndef load_test(img_path, num_feats):\n    img = tf.io.read_file(img_path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, (IMG_SIZE, IMG_SIZE))\n    img = tf.cast(img, tf.float32) / 255.0\n    return {\"image\": img, \"numeric\": num_feats}\n\ntest_ds = tf.data.Dataset.from_tensor_slices((X_test_img, X_test_num))\ntest_ds = test_ds.map(load_test, num_parallel_calls=AUTOTUNE)\ntest_ds = test_ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n\ntest_preds = model.predict(test_ds)\ntest_preds = np.expm1(test_preds)\ntest_preds = np.maximum(test_preds, 0)\ntest_preds[:, 3] = test_preds[:, 0] + test_preds[:, 1] + test_preds[:, 2]\n\nrows = []\nfor img_path, p in zip(X_test_img, test_preds):\n    image_id = os.path.basename(img_path).replace(\".jpg\", \"\")\n    for name, val in zip(TARGET_COLS, p):\n        rows.append([f\"{image_id}__{name}\", float(val)])\n\nsubmission = pd.DataFrame(rows, columns=[\"sample_id\", \"target\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T20:15:16.812673Z","iopub.execute_input":"2025-12-19T20:15:16.813006Z","iopub.status.idle":"2025-12-19T20:24:22.906154Z","shell.execute_reply.started":"2025-12-19T20:15:16.812974Z","shell.execute_reply":"2025-12-19T20:24:22.905546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(\"submission.csv saved\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T20:24:22.907643Z","iopub.execute_input":"2025-12-19T20:24:22.907915Z","iopub.status.idle":"2025-12-19T20:24:22.919304Z","shell.execute_reply.started":"2025-12-19T20:24:22.907892Z","shell.execute_reply":"2025-12-19T20:24:22.918641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(submission)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-19T20:24:22.920108Z","iopub.execute_input":"2025-12-19T20:24:22.920461Z","iopub.status.idle":"2025-12-19T20:24:22.933382Z","shell.execute_reply.started":"2025-12-19T20:24:22.920430Z","shell.execute_reply":"2025-12-19T20:24:22.932659Z"}},"outputs":[],"execution_count":null}]}