{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"isSourceIdPinned":false,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Definición del Problema\n\nEn esta competencia, tu tarea consiste en utilizar imágenes de pastizales para predecir cinco componentes clave de la biomasa, fundamentales para la gestión del pastoreo y la alimentación:\n\n- Dry green vegetation (excluding clover)\n- Dry dead material\n- Dry clover biomass\n- Green dry matter (GDM)\n- Total dry biomass\n\nPredecir estas cantidades con precisión ayudará a los agricultores e investigadores a monitorear el crecimiento de los pastos, optimizar la disponibilidad de alimento y mejorar la sostenibilidad de los sistemas ganaderos.\n\nEs un problema de regresión a partir del análisis de imágenes.","metadata":{}},{"cell_type":"markdown","source":"# Importación de Librerias","metadata":{}},{"cell_type":"code","source":"import os\n\nimport numpy as np  \nimport pandas as pd \n\n# Manipulación de imágenes\nimport cv2                        \nfrom PIL import Image\nimport glob\n\n# Creación de gráficas\nimport matplotlib.pyplot as plt \nimport seaborn as sns          \n\n# Redes neuronales\nimport tensorflow as tf\nfrom tensorflow.keras.utils import Sequence\nfrom tensorflow.keras import layers, models, applications, callbacks\n\n# Estandarización y evaluación\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import r2_score, mean_squared_error","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:49.030065Z","iopub.execute_input":"2025-12-08T05:49:49.030372Z","iopub.status.idle":"2025-12-08T05:49:49.035216Z","shell.execute_reply.started":"2025-12-08T05:49:49.030349Z","shell.execute_reply":"2025-12-08T05:49:49.034346Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Importación de Datos","metadata":{}},{"cell_type":"code","source":"PATH_DATA = '/kaggle/input/csiro-biomass'\nPATH_TRAIN_CSV = os.path.join(PATH_DATA, 'train.csv')\nPATH_TRAIN_IMG = os.path.join(PATH_DATA, 'train')\nPATH_TEST_CSV = os.path.join(PATH_DATA, 'test.csv')\nPATH_TEST_IMG = os.path.join(PATH_DATA, 'test')\n\ndf = pd.read_csv(PATH_TRAIN_CSV)\nprint(f\"Dataset size: {df.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:51.013105Z","iopub.execute_input":"2025-12-08T05:49:51.013569Z","iopub.status.idle":"2025-12-08T05:49:51.025444Z","shell.execute_reply.started":"2025-12-08T05:49:51.013544Z","shell.execute_reply":"2025-12-08T05:49:51.024626Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Exploración de Datos","metadata":{}},{"cell_type":"code","source":"# Primeras n columnas del data frame\ndisplay(df.head(5))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:53.218923Z","iopub.execute_input":"2025-12-08T05:49:53.219460Z","iopub.status.idle":"2025-12-08T05:49:53.233369Z","shell.execute_reply.started":"2025-12-08T05:49:53.219436Z","shell.execute_reply":"2025-12-08T05:49:53.232649Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:54.202884Z","iopub.execute_input":"2025-12-08T05:49:54.203171Z","iopub.status.idle":"2025-12-08T05:49:54.210133Z","shell.execute_reply.started":"2025-12-08T05:49:54.203150Z","shell.execute_reply":"2025-12-08T05:49:54.209504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Datos numéricos\ndf.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:54.434913Z","iopub.execute_input":"2025-12-08T05:49:54.435138Z","iopub.status.idle":"2025-12-08T05:49:54.450288Z","shell.execute_reply.started":"2025-12-08T05:49:54.435121Z","shell.execute_reply":"2025-12-08T05:49:54.449703Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Datos categóricos\ndf.describe(include='object')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:56.366561Z","iopub.execute_input":"2025-12-08T05:49:56.367160Z","iopub.status.idle":"2025-12-08T05:49:56.383904Z","shell.execute_reply.started":"2025-12-08T05:49:56.367133Z","shell.execute_reply":"2025-12-08T05:49:56.383349Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Distribución de las especies\nplt.figure(figsize=(10, 6))\nsns.countplot(data=df, x=\"Species\", order=df[\"Species\"].value_counts().index)\n\nplt.title(\"Species Distribution\")\nplt.xlabel(\"Species\")\nplt.ylabel(\"Count\")\nplt.xticks(rotation=45, ha=\"right\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:56.988403Z","iopub.execute_input":"2025-12-08T05:49:56.988680Z","iopub.status.idle":"2025-12-08T05:49:57.432962Z","shell.execute_reply.started":"2025-12-08T05:49:56.988657Z","shell.execute_reply":"2025-12-08T05:49:57.432366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Explorar imágenes\ndef show_images(df, n, n_cols, img_root):\n    df = df.sort_values('target').reset_index(drop=True)\n    indices = np.linspace(0, len(df)-1, n, dtype=int)\n\n    images = df.iloc[indices]\n\n    n_rows = (n + 1) // n_cols\n\n    plt.figure(figsize=(n, n_cols * n_rows))\n\n    for i, (_, row) in enumerate(images.iterrows()):\n        plt.subplot(n_rows, n_cols, i+1)\n\n        img = Image.open(os.path.join(img_root, row['image_path']))\n\n        plt.imshow(img)\n        plt.title(f\"{row['sample_id']} - {row['target']:.2f}\")\n        plt.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\nshow_images(df, 8, 2, PATH_DATA)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:49:59.817839Z","iopub.execute_input":"2025-12-08T05:49:59.818450Z","iopub.status.idle":"2025-12-08T05:50:02.168478Z","shell.execute_reply.started":"2025-12-08T05:49:59.818424Z","shell.execute_reply":"2025-12-08T05:50:02.167514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Limpieza de Datos","metadata":{}},{"cell_type":"code","source":"# Cambia el data frame a formato wide\n# A cada imágen le corresponde solo una fila\ndf_wide = df.pivot_table(\n    index=['image_path', 'Sampling_Date', 'State', 'Species', 'Pre_GSHH_NDVI', 'Height_Ave_cm'], \n    columns='target_name', \n    values='target'\n).reset_index()\n\ndf_wide.columns.name = None\n\nprint(f\"Original shape: {df.shape}\")\nprint(f\"New shape: {df_wide.shape}\")      \ndf_wide.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:15.515420Z","iopub.execute_input":"2025-12-08T05:50:15.516095Z","iopub.status.idle":"2025-12-08T05:50:15.541222Z","shell.execute_reply.started":"2025-12-08T05:50:15.516071Z","shell.execute_reply":"2025-12-08T05:50:15.540574Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Definición del Modelo","metadata":{"_kg_hide-input":true}},{"cell_type":"code","source":"# Configuración\nBATCH_SIZE = 16\nHEIGHT, WIDTH = 256, 512\nIMG_SIZE = (WIDTH, HEIGHT)\n\n# Las 5 columnas a predecir\nTARGET_COLS = ['Dry_Green_g', 'Dry_Dead_g', 'Dry_Clover_g', 'GDM_g', 'Dry_Total_g']\n\n# Split de datos\ntrain_df, val_df = train_test_split(df_wide, test_size=0.2, random_state=42)\n\nprint(f\"Train samples: {len(train_df)}\")\nprint(f\"Val samples: {len(val_df)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:18.937575Z","iopub.execute_input":"2025-12-08T05:50:18.938280Z","iopub.status.idle":"2025-12-08T05:50:18.945949Z","shell.execute_reply.started":"2025-12-08T05:50:18.938254Z","shell.execute_reply":"2025-12-08T05:50:18.945226Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Métrica personalizada (Weighted R2)\ndef calculate_weighted_r2(y_true, y_pred):\n    # Pesos de la competencia\n    col_weights = np.array([0.1, 0.1, 0.1, 0.2, 0.5])\n    \n    y_true_flat = y_true.ravel()\n    y_pred_flat = y_pred.ravel()\n    weights_flat = np.tile(col_weights, y_true.shape[0])\n    \n    weighted_mean = np.sum(weights_flat * y_true_flat) / np.sum(weights_flat)\n    ss_res = np.sum(weights_flat * (y_true_flat - y_pred_flat)**2)\n    ss_tot = np.sum(weights_flat * (y_true_flat - weighted_mean)**2)\n    \n    if ss_tot == 0: return 0.0\n    return 1 - (ss_res / ss_tot)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:21.267408Z","iopub.execute_input":"2025-12-08T05:50:21.268104Z","iopub.status.idle":"2025-12-08T05:50:21.272742Z","shell.execute_reply.started":"2025-12-08T05:50:21.268078Z","shell.execute_reply":"2025-12-08T05:50:21.272080Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MetricCallback(callbacks.Callback):\n    def __init__(self, val_gen):\n        super().__init__()\n        self.val_gen = val_gen\n\n    def on_epoch_end(self, epoch, logs=None):\n        y_true_all, y_pred_all = [], []\n        # Recorrer validación\n        for i in range(len(self.val_gen)):\n            X_b, y_b = self.val_gen[i]\n            preds = self.model.predict(X_b, verbose=0)\n            y_true_all.append(y_b)\n            y_pred_all.append(preds)\n            \n        y_true_all = np.vstack(y_true_all)\n        y_pred_all = np.vstack(y_pred_all)\n        y_pred_all = np.clip(y_pred_all, 0, None) # Clip post-predicción\n        \n        score = calculate_weighted_r2(y_true_all, y_pred_all)\n        logs['val_weighted_r2'] = score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:22.091061Z","iopub.execute_input":"2025-12-08T05:50:22.091751Z","iopub.status.idle":"2025-12-08T05:50:22.096881Z","shell.execute_reply.started":"2025-12-08T05:50:22.091728Z","shell.execute_reply":"2025-12-08T05:50:22.096076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generador de imágenes\nclass BiomassImageGenerator(Sequence):\n    def __init__(self, df, root_dir, target_cols, batch_size=16, img_size=(512,256), augment=False, **kwargs):\n        super().__init__(**kwargs)\n        self.df = df\n        self.root_dir = root_dir\n        self.target_cols = target_cols\n        self.batch_size = batch_size\n        self.img_size = img_size # (Width, Height)\n        self.augment = augment\n        self.indices = np.arange(len(self.df))\n        if augment: np.random.shuffle(self.indices)\n\n    def __len__(self):\n        return int(np.ceil(len(self.df) / self.batch_size))\n\n    def __getitem__(self, index):\n        batch_indices = self.indices[index*self.batch_size : (index+1)*self.batch_size]\n        batch_df = self.df.iloc[batch_indices]\n\n        # Inicializar arreglos\n        X = np.zeros((len(batch_df), self.img_size[1], self.img_size[0], 3), dtype=np.float32)\n        y = batch_df[self.target_cols].values.astype(np.float32)\n\n        for i, (idx, row) in enumerate(batch_df.iterrows()):\n            full_path = os.path.join(self.root_dir, row['image_path'])\n            \n            img = cv2.imread(full_path)\n            if img is not None:\n                img = cv2.resize(img, self.img_size)\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                \n                # Data Augmentation simple\n                if self.augment:\n                    if np.random.rand() > 0.5: img = cv2.flip(img, 1) # Horizontal\n                    if np.random.rand() > 0.5: img = cv2.flip(img, 0) # Vertical\n                \n                X[i] = img / 255.0\n            \n        return X, y # <--- Retorna SOLO Imagen y Targets\n\n    def on_epoch_end(self):\n        if self.augment: np.random.shuffle(self.indices)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:24.039482Z","iopub.execute_input":"2025-12-08T05:50:24.039757Z","iopub.status.idle":"2025-12-08T05:50:24.048014Z","shell.execute_reply.started":"2025-12-08T05:50:24.039737Z","shell.execute_reply":"2025-12-08T05:50:24.047275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Arquitectura del modelo\ndef create_model(input_shape):\n    inputs = layers.Input(shape=input_shape)\n    \n    # Transfer learning EfficientNetB0\n    base = applications.EfficientNetB0(include_top=False, weights='imagenet', input_tensor=inputs)\n    base.trainable = False \n    \n    # Head\n    x = layers.GlobalAveragePooling2D()(base.output)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.Dropout(0.3)(x)\n    x = layers.Dense(128, activation='relu')(x)\n    \n    # Salida: 5 Neuronas (una para cada target)\n    outputs = layers.Dense(5, activation='linear')(x)\n    \n    model = models.Model(inputs, outputs)\n    model.compile(optimizer='adam', loss='mse', metrics=['mae'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:26.207504Z","iopub.execute_input":"2025-12-08T05:50:26.208030Z","iopub.status.idle":"2025-12-08T05:50:26.213246Z","shell.execute_reply.started":"2025-12-08T05:50:26.208005Z","shell.execute_reply":"2025-12-08T05:50:26.212504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Instanciar generadores\ntrain_gen = BiomassImageGenerator(train_df, PATH_DATA, TARGET_COLS, batch_size=BATCH_SIZE, augment=True)\nval_gen = BiomassImageGenerator(val_df, PATH_DATA, TARGET_COLS, batch_size=BATCH_SIZE, augment=False)\n\n# Crear Modelo\nmodel = create_model((HEIGHT, WIDTH, 3))\n\n# Callbacks\nmetric_cb = MetricCallback(val_gen)\nchk_pt = callbacks.ModelCheckpoint('best_model.keras', save_best_only=True, monitor='val_weighted_r2', mode='max')\nearly = callbacks.EarlyStopping(monitor='val_loss', patience=6, restore_best_weights=True)\n\n# Entrenar\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=20,\n    callbacks=[metric_cb, chk_pt, early]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:50:27.955696Z","iopub.execute_input":"2025-12-08T05:50:27.956385Z","iopub.status.idle":"2025-12-08T05:54:37.256352Z","shell.execute_reply.started":"2025-12-08T05:50:27.956358Z","shell.execute_reply":"2025-12-08T05:54:37.255537Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Resultados de Prueba","metadata":{}},{"cell_type":"code","source":"# --- GENERACIÓN DE SUBMISSION ---\nprint(\"Generando submission\")\n\n# Cargar test.csv\ndf_test = pd.read_csv(PATH_TEST_CSV)\n\n# Obtener imágenes únicas para no predecir repetidos\nunique_paths = df_test['image_path'].unique()\n\npred_map = {}\n\n# Iterar y predecir\nfor path in unique_paths:\n    # Construir ruta completa\n    full_path = os.path.join(PATH_DATA, path)\n    \n    img = cv2.imread(full_path)\n    if img is None:\n        # Fallback (negro) si no existe imagen\n        img = np.zeros((HEIGHT, WIDTH, 3), dtype=np.float32)\n    else:\n        img = cv2.resize(img, (WIDTH, HEIGHT))\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = img.astype('float32') / 255.0\n    \n    img_batch = np.expand_dims(img, axis=0)\n    \n    # Predecir: Devuelve array de forma (1, 5)\n    preds = model.predict(img_batch, verbose=0)[0]\n    preds = np.clip(preds, 0, None) # Evitar negativos\n    \n    # Guardar en diccionario\n    pred_map[path] = preds\n\n# Construir la lista de resultados\nsubmission_rows = []\n\nfor idx, row in df_test.iterrows():\n    img_path = row['image_path']   # Ej: test/ID123.jpg\n    target_name = row['target_name'] # Ej: Dry_Green_g\n    sample_id = row['sample_id']     # Ej: ID123__Dry_Green_g\n    \n    vector_preds = pred_map.get(img_path, [0,0,0,0,0])\n    \n\n    if target_name in TARGET_COLS:\n        col_idx = TARGET_COLS.index(target_name)\n        val = vector_preds[col_idx]\n    else:\n        val = 0.0\n        \n    submission_rows.append({\n        'sample_id': sample_id,\n        'target': float(val)\n    })\n\n# Guardar CSV\nsubmission_df = pd.DataFrame(submission_rows)\nsubmission_df.to_csv('submission.csv', index=False)\n\nprint(\"Submission generada\\n\")\nprint(submission_df.head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T05:54:53.740853Z","iopub.execute_input":"2025-12-08T05:54:53.741608Z","iopub.status.idle":"2025-12-08T05:54:58.683014Z","shell.execute_reply.started":"2025-12-08T05:54:53.741586Z","shell.execute_reply":"2025-12-08T05:54:58.682296Z"}},"outputs":[],"execution_count":null}]}