{"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":"gpu","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd \nfrom PIL import Image\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:05:22.328368Z","iopub.execute_input":"2025-10-29T05:05:22.328678Z","iopub.status.idle":"2025-10-29T05:05:35.735980Z","shell.execute_reply.started":"2025-10-29T05:05:22.328654Z","shell.execute_reply":"2025-10-29T05:05:35.735146Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Read Data","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '/kaggle/input/csiro-biomass/'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:05:35.737149Z","iopub.execute_input":"2025-10-29T05:05:35.737738Z","iopub.status.idle":"2025-10-29T05:05:35.741319Z","shell.execute_reply.started":"2025-10-29T05:05:35.737701Z","shell.execute_reply":"2025-10-29T05:05:35.740683Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_targets = pd.read_csv(BASE_PATH + 'train.csv' )[['image_path', 'target_name', 'target']]\n\nn_images = train_targets['image_path'].nunique()\n\ntrain_targets = train_targets.groupby('image_path').agg({\n    'target_name': list,\n    'target': list\n})\n\ntargets = np.vstack(train_targets['target'].values)\n\ncls = []\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:05:35.742128Z","iopub.execute_input":"2025-10-29T05:05:35.742416Z","iopub.status.idle":"2025-10-29T05:05:35.816737Z","shell.execute_reply.started":"2025-10-29T05:05:35.742363Z","shell.execute_reply":"2025-10-29T05:05:35.815947Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset using tf.data (memory-friendly)","metadata":{}},{"cell_type":"code","source":"images_ds = tf.keras.utils.image_dataset_from_directory(\n    BASE_PATH + 'train',\n    labels=None, \n    image_size=(1000, 2000),\n    batch_size=None, \n    shuffle=False \n)\n\n\nassert len(images_ds) == n_images, 'Number of images not match'\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:05:35.818242Z","iopub.execute_input":"2025-10-29T05:05:35.818464Z","iopub.status.idle":"2025-10-29T05:05:37.449018Z","shell.execute_reply.started":"2025-10-29T05:05:35.818447Z","shell.execute_reply":"2025-10-29T05:05:37.448294Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targets_ds = tf.data.Dataset.from_tensor_slices(targets)\n\ndataset = tf.data.Dataset.zip((images_ds, targets_ds)).shuffle(100, seed=42)\n\nval_size = 0.2\n\ntrain_size = n_images - int(val_size * n_images)\nval_size = int(val_size * n_images)\n\n\ntrain_ds = dataset.skip(val_size).repeat().batch(8).prefetch(buffer_size=tf.data.AUTOTUNE)\nval_ds = dataset.take(val_size).batch(8)\n\nsteps_per_epoch  = train_size // 8 # 8 = batch size\nvalidation_steps = val_size // 8  # 8 = batch size\n\n\n\n\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:05:37.449725Z","iopub.execute_input":"2025-10-29T05:05:37.449940Z","iopub.status.idle":"2025-10-29T05:05:38.913331Z","shell.execute_reply.started":"2025-10-29T05:05:37.449924Z","shell.execute_reply":"2025-10-29T05:05:38.912788Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Simple CNN model with TensorFlow","metadata":{}},{"cell_type":"code","source":"tf.random.set_seed(42)\n\ndata_augmentation = tf.keras.Sequential([\n    tf.keras.layers.RandomFlip(\"horizontal_and_vertical\"),  # Randomly flip images horizontally and vertically\n    tf.keras.layers.RandomRotation(0.2),                    # Randomly rotate images within ±20%\n    tf.keras.layers.RandomZoom(0.1),                        # Randomly zoom in or out up to 20%\n    tf.keras.layers.RandomContrast(0.15),                    # Randomly adjust image contrast by ±20%\n    tf.keras.layers.RandomBrightness(factor=0.2),           # Randomly adjust image brightness by ±20%\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:05:38.914070Z","iopub.execute_input":"2025-10-29T05:05:38.914321Z","iopub.status.idle":"2025-10-29T05:05:38.939021Z","shell.execute_reply.started":"2025-10-29T05:05:38.914297Z","shell.execute_reply":"2025-10-29T05:05:38.938187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tf.random.set_seed(42)\n\ninputs = tf.keras.layers.Input(shape=[1000, 2000, 3])\n\nx = data_augmentation(inputs)\n# ===\n\nx = tf.keras.layers.Conv2D(\n    filters=32,\n    kernel_size=3,\n    padding='same',\n    activation='relu',\n    kernel_initializer='he_normal')(x)\n\nx = tf.keras.layers.BatchNormalization()(x)   \nx = tf.keras.layers.MaxPooling2D()(x)\nx = tf.keras.layers.Dropout(0.1)(x)\n# ===\n\n\nx = tf.keras.layers.Conv2D(\n    filters=64,\n    kernel_size=3,\n    padding='same',\n    activation='relu',\n    kernel_initializer='he_normal')(x)\n\nx = tf.keras.layers.BatchNormalization()(x)   \nx = tf.keras.layers.MaxPooling2D()(x)\nx = tf.keras.layers.Dropout(0.125)(x)\n# ===\n\n\nx = tf.keras.layers.Conv2D(\n    filters=128,\n    kernel_size=3,\n    padding='same',\n    activation='relu',\n    kernel_initializer='he_normal')(x)\n\nx = tf.keras.layers.BatchNormalization()(x)   \nx = tf.keras.layers.MaxPooling2D()(x)\nx = tf.keras.layers.Dropout(0.15)(x)\n# ===\n\n\nga = tf.keras.layers.GlobalAveragePooling2D()(x)\ngm = tf.keras.layers.GlobalMaxPooling2D()(x)\n\nmerged = tf.keras.layers.Concatenate()([ga, gm])\n\nx = tf.keras.layers.Dense(512, activation='relu')(merged)\nx = tf.keras.layers.Dropout(0.1)(x)\n\noutputs = tf.keras.layers.Dense(5)(x)\n\nmodel = tf.keras.Model(inputs=inputs, outputs=outputs)\n\nmodel.summary()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:27:34.572722Z","iopub.execute_input":"2025-10-29T05:27:34.573025Z","iopub.status.idle":"2025-10-29T05:27:34.714460Z","shell.execute_reply.started":"2025-10-29T05:27:34.573005Z","shell.execute_reply":"2025-10-29T05:27:34.713908Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer=tf.keras.optimizers.Adam(1e-4),\n    loss='mse',\n    metrics=[\n        tf.keras.metrics.RootMeanSquaredError(name='rmse')\n    ]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:27:39.193601Z","iopub.execute_input":"2025-10-29T05:27:39.193894Z","iopub.status.idle":"2025-10-29T05:27:39.210062Z","shell.execute_reply.started":"2025-10-29T05:27:39.193873Z","shell.execute_reply":"2025-10-29T05:27:39.209376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"checkpoint_cb = tf.keras.callbacks.ModelCheckpoint(\n    filepath='best_model.keras',     # saves only the best model\n    monitor='val_loss',\n    save_best_only=True,\n    mode='min',\n    verbose=1\n)\n\n# Stop training early if no improvement in val_loss\nearlystop_cb = tf.keras.callbacks.EarlyStopping(\n    monitor='val_loss',\n    patience=8,         # number of epochs to wait before stopping\n    restore_best_weights=True,\n    verbose=0\n)\n\nreduce_lr_cb = tf.keras.callbacks.ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=0.5,         # reduce LR by half\n    patience=3,\n    min_lr=1e-6,\n    verbose=0\n)\n\n\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=1,\n    steps_per_epoch = steps_per_epoch,\n    validation_steps = validation_steps,\n    callbacks=[checkpoint_cb, earlystop_cb, reduce_lr_cb]\n)\n\n\n#loss: 319.4160 - mae: 12.2534 - rmse: 17.7127 - val_loss: 318.9702 - val_mae: 11.5043 - val_rmse: 17.8597 - learning_rate: 5.0000e-04\n\n# 35/35 ━━━━━━━━━━━━━━━━━━━━ 33s 944ms/step - loss: 414.6152 - mae: 14.1044 - rmse: 20.3407 - val_loss: 863.7164 - val_mae: 17.3120 - val_rmse: 29.3891 - learning_rate: 5.0000e-05\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:28:06.332117Z","iopub.execute_input":"2025-10-29T05:28:06.332684Z","iopub.status.idle":"2025-10-29T05:36:34.742115Z","shell.execute_reply.started":"2025-10-29T05:28:06.332660Z","shell.execute_reply":"2025-10-29T05:36:34.741264Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"model = tf.keras.models.load_model('/kaggle/working/best_model.keras')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:36:37.655928Z","iopub.execute_input":"2025-10-29T05:36:37.656588Z","iopub.status.idle":"2025-10-29T05:36:37.851470Z","shell.execute_reply.started":"2025-10-29T05:36:37.656560Z","shell.execute_reply":"2025-10-29T05:36:37.850884Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_ds = tf.keras.utils.image_dataset_from_directory(\n    directory = '/kaggle/input/csiro-biomass/test',\n    labels=None, \n    image_size=(1000, 2000),\n    batch_size=16, \n    shuffle=False \n)\n\ntest_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\n\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:36:39.904850Z","iopub.execute_input":"2025-10-29T05:36:39.905549Z","iopub.status.idle":"2025-10-29T05:36:39.939304Z","shell.execute_reply.started":"2025-10-29T05:36:39.905525Z","shell.execute_reply":"2025-10-29T05:36:39.938587Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pred = model.predict(test_ds).ravel()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:36:42.892088Z","iopub.execute_input":"2025-10-29T05:36:42.892613Z","iopub.status.idle":"2025-10-29T05:36:43.169178Z","shell.execute_reply.started":"2025-10-29T05:36:42.892588Z","shell.execute_reply":"2025-10-29T05:36:43.168651Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    'sample_id':test_df['sample_id'],\n    'target': y_pred\n})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:36:47.756889Z","iopub.execute_input":"2025-10-29T05:36:47.757534Z","iopub.status.idle":"2025-10-29T05:36:47.761494Z","shell.execute_reply.started":"2025-10-29T05:36:47.757511Z","shell.execute_reply":"2025-10-29T05:36:47.760723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:36:52.961879Z","iopub.execute_input":"2025-10-29T05:36:52.962612Z","iopub.status.idle":"2025-10-29T05:36:52.969784Z","shell.execute_reply.started":"2025-10-29T05:36:52.962587Z","shell.execute_reply":"2025-10-29T05:36:52.969073Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-29T05:36:57.110408Z","iopub.execute_input":"2025-10-29T05:36:57.110959Z","iopub.status.idle":"2025-10-29T05:36:57.115722Z","shell.execute_reply.started":"2025-10-29T05:36:57.110937Z","shell.execute_reply":"2025-10-29T05:36:57.114999Z"}},"outputs":[],"execution_count":null}]}