{"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,"sourceType":"competition"},{"sourceId":6125,"sourceType":"modelInstanceVersion","modelInstanceId":4596,"modelId":2797},{"sourceId":6128,"sourceType":"modelInstanceVersion","modelInstanceId":4596,"modelId":2797}],"dockerImageVersionId":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2026-01-18T16:16:39.537276Z","iopub.execute_input":"2026-01-18T16:16:39.538071Z","iopub.status.idle":"2026-01-18T16:16:39.726103Z","shell.execute_reply.started":"2026-01-18T16:16:39.538041Z","shell.execute_reply":"2026-01-18T16:16:39.725411Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CSIRO - Image2Biomass Prediction","metadata":{}},{"cell_type":"markdown","source":"The goal of this notebooks is to predict pasture biomass from images and groung truth measurements from testing dataset only. \nTesting dataset is made only by images whereas for training dataset you can also leverage numerical data (like Pre_GSHH_NDVI \tHeight_Ave_cm)  and categorical data (like State, Species and Month).","metadata":{}},{"cell_type":"markdown","source":"Even if the test dataset consists only of images, you can still use the numeric and categorical variables present in the training dataset.\n\n","metadata":{}},{"cell_type":"markdown","source":"To this end you can develop two different models: a Teacher model that leverage both images and numerical and categorical features and a Student model that leverages only images.","metadata":{}},{"cell_type":"markdown","source":"The question is: how can you develop such a model? The key idea is to train the Teacher on images and numerical and categorical data; then you train the Student only on images but the LOSS that the Student needs to minimize is also linked to Teacher predictions:","metadata":{}},{"cell_type":"markdown","source":"Loss = α * MSE(y_student, y_true) + (1-α) * MSE(y_student, y_teacher)","metadata":{}},{"cell_type":"markdown","source":"![image.png](attachment:5b5cfe5e-57a6-4ffc-b456-bd387b9969a7.png)","metadata":{},"attachments":{"5b5cfe5e-57a6-4ffc-b456-bd387b9969a7.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAABM8AAAEhCAYAAABoYYebAAAABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAZ25vbWUtc2NyZWVuc2hvdO8Dvz4AAAAndEVYdENyZWF0aW9uIFRpbWUAc2FiIDE3IGdlbiAyMDI2LCAxODo1NzowMq+B8y8AACAASURBVHic7N13VBXX3sbx76FXAQFBpIodRbBrLBhbNGqaSTQx3ZJ+b+preu+5yU1yTe5NL3aTGKMxxq6xd1BRVGyooIIU6e28f6AoIgIKzEGfz1qudZiZM/uZYTZyfuw9YzKbzWZERERERERERESkHCujA4iIiIiIiIiIiFgqFc9EREREREREREQqoOKZiIiIiIiIiIhIBVQ8ExERERERERERqYCKZyIiIiIiIiIiIhVQ8UxERERERERERKQCKp6JiIiIiIiIiIhUQMUzERERERERERGRCqh4JiIiIiIiIiIiUgEVz0RERERERERERCqg4pmIiIiIiIiIiEgFVDwTERERERERERGpgIpnIiIiIiIiIiIiFVDxTEREREREREREpAIqnomIiIiIiIiIiFRAxTMREREREREREZEKqHgmIiIiIiIiIiJSARXPREREREREREREKqDimYiIiIiIiIiISAVUPBMREREREREREamAimciIiIiIiIiIiIVsKmLRp6YOI/o+MS6aEpqQfvQxnz8yBCjY4iIiIiIiIiI1Lk6KZ5Fxyfi16Z7XTQltSA6do3REUREREREREREDKFpmyIiIiIiIiIiIhVQ8UxERERERERERKQCKp6JiIiIiIiIiIhUQMUzERERERERERGRCqh4JiIiIiIiIiIiUgEVz0RERERERERERCqg4pmIiIiIiIiIiEgFVDwTERERERERERGpgIpnIiIiIiIiIiIiFVDxTEREREREREREpAL1pngW8/tEJo8No7io0OgoIiIiIiIiIiJylag3xTMREREREREREZG6puKZiIiIiIiIiIhIBWyMDnA5ZjzejfY3Pc7RmBUc270BG3snWvQdRXDnwWyY8iYn4rfi6OZNhxFPE9ChPwBFBXnE/D6Rgxvmk5N2DDsnNwI69KfDbc9iY+dAUUE+W375iIPr52E2F+HTqhuObt4k7VzD0NdmA2A2m9m18Ad2L5tGdmoSLt4BtB0yjpBuw0qzJe+LZsvP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"}}},{"cell_type":"markdown","source":"If alpha is equal to 1 Student does not take into account Teacher prediction. If alpha > 0 and alpha < 1 weights of the students are updated with the gradients in order to take into account also teacher predictions.","metadata":{}},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nfrom tensorflow.keras.metrics import R2Score\nfrom tensorflow.keras import layers, models\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.preprocessing import StandardScaler\nfrom tensorflow.keras.callbacks import EarlyStopping\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T16:25:34.366573Z","iopub.execute_input":"2026-01-18T16:25:34.366928Z","iopub.status.idle":"2026-01-18T16:25:48.907585Z","shell.execute_reply.started":"2026-01-18T16:25:34.366902Z","shell.execute_reply":"2026-01-18T16:25:48.906790Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"GPUs:\", tf.config.list_physical_devices('GPU'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-18T16:25:48.908930Z","iopub.execute_input":"2026-01-18T16:25:48.909469Z","iopub.status.idle":"2026-01-18T16:25:49.493753Z","shell.execute_reply.started":"2026-01-18T16:25:48.909444Z","shell.execute_reply":"2026-01-18T16:25:49.492880Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CONFIG(object):\n    TRAIN_DATA_PATH = '/kaggle/input/csiro-biomass/train.csv'\n    TEST_DATA_PATH = '/kaggle/input/csiro-biomass/test.csv'\n    TRAIN_IMG_PATH = r'/kaggle/input/csiro-biomass'\n    TEST_IMG_PATH = r'/kaggle/input/csiro-biomass'\n    IMG_SIZE = (224, 224)\n    IMG_SHAPE = (224,224,3)\n    BATCH_SIZE = 20\n    EPOCHS = 30\n    N_TARGETS = 5\n    TARGETS = ['Dry_Clover_g','Dry_Dead_g','Dry_Green_g','Dry_Total_g','GDM_g']\n    DATA_PATH = r'/kaggle/input/csiro-biomass'\n    MODEL_PATH_STUDENT = r'/kaggle/input/efficientnetv2/keras/efficientnetv2_b0_imagenet/3'\n    MODEL_PATH_TEACHER = r'/kaggle/input/efficientnetv2/keras/efficientnetv2_b0_imagenet/2'\n    NUM_FEATURES = ['Pre_GSHH_NDVI','Height_Ave_cm']\n    CAT_FEATURES = ['Month','State','Species']\n    N_NUM = len(NUM_FEATURES)    \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.897404Z","iopub.execute_input":"2026-01-17T18:05:18.897792Z","iopub.status.idle":"2026-01-17T18:05:18.910034Z","shell.execute_reply.started":"2026-01-17T18:05:18.897773Z","shell.execute_reply":"2026-01-17T18:05:18.909367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TransformTrainData(object):\n\n    month_mapping = {\n        1: 'January',\n        2 : 'February',\n        3 : 'March',\n        4: 'April',\n        5 : 'May',\n        6: 'June',\n        7: 'July',\n        8: 'August',\n        9: 'September',\n        10 : 'October',\n        11: 'November',\n        12:  'December'\n    }\n\n    num_features = ['Pre_GSHH_NDVI','Height_Ave_cm']\n    cat_features = ['Month','State','Species']\n\n    def __init__(self,df):\n        self.df = df\n        self.transform_data()\n        \n    def transform_data(self):\n        self.df['Sampling_Date'] = pd.to_datetime(self.df['Sampling_Date'], format = '%Y/%m/%d')\n        self.df['Month'] = self.df['Sampling_Date'].dt.month.map(TransformTrainData.month_mapping)\n        self.df_data_long = self.df.pivot(index = [\"image_path\",'State','Species','Month','Pre_GSHH_NDVI','Height_Ave_cm'],\n                columns = \"target_name\",\n                values = \"target\").reset_index()\n        return self.df_data_long        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.911382Z","iopub.execute_input":"2026-01-17T18:05:18.911609Z","iopub.status.idle":"2026-01-17T18:05:18.923846Z","shell.execute_reply.started":"2026-01-17T18:05:18.911591Z","shell.execute_reply":"2026-01-17T18:05:18.923277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data = pd.read_csv(CONFIG.TRAIN_DATA_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.925053Z","iopub.execute_input":"2026-01-17T18:05:18.925263Z","iopub.status.idle":"2026-01-17T18:05:18.947516Z","shell.execute_reply.started":"2026-01-17T18:05:18.925237Z","shell.execute_reply":"2026-01-17T18:05:18.946832Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_input = TransformTrainData(train_data).transform_data()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.948201Z","iopub.execute_input":"2026-01-17T18:05:18.948406Z","iopub.status.idle":"2026-01-17T18:05:18.969307Z","shell.execute_reply.started":"2026-01-17T18:05:18.948384Z","shell.execute_reply":"2026-01-17T18:05:18.968844Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_input.head(2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.970727Z","iopub.execute_input":"2026-01-17T18:05:18.970909Z","iopub.status.idle":"2026-01-17T18:05:18.980809Z","shell.execute_reply.started":"2026-01-17T18:05:18.970894Z","shell.execute_reply":"2026-01-17T18:05:18.980184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scaler = StandardScaler()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.981616Z","iopub.execute_input":"2026-01-17T18:05:18.981867Z","iopub.status.idle":"2026-01-17T18:05:18.993025Z","shell.execute_reply.started":"2026-01-17T18:05:18.981841Z","shell.execute_reply":"2026-01-17T18:05:18.992401Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_input[CONFIG.TARGETS] = scaler.fit_transform(data_input[CONFIG.TARGETS])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:18.993676Z","iopub.execute_input":"2026-01-17T18:05:18.993859Z","iopub.status.idle":"2026-01-17T18:05:19.008333Z","shell.execute_reply.started":"2026-01-17T18:05:18.993835Z","shell.execute_reply":"2026-01-17T18:05:19.007332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_input.head(2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.009310Z","iopub.execute_input":"2026-01-17T18:05:19.010029Z","iopub.status.idle":"2026-01-17T18:05:19.026745Z","shell.execute_reply.started":"2026-01-17T18:05:19.010003Z","shell.execute_reply":"2026-01-17T18:05:19.026072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CATEGORIES = {'Month':data_input['Month'].nunique(),\n              'State': data_input['State'].nunique(),\n              'Species': data_input['Species'].nunique()}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.027419Z","iopub.execute_input":"2026-01-17T18:05:19.027695Z","iopub.status.idle":"2026-01-17T18:05:19.038734Z","shell.execute_reply.started":"2026-01-17T18:05:19.027666Z","shell.execute_reply":"2026-01-17T18:05:19.038060Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Student(object):\n\n    def __init__(self,model_path):\n        self.model_path = model_path       \n        self.create_model()\n\n    def create_model(self):\n        base_model = tf.keras.models.load_model(self.model_path)\n        base_model.trainable = False\n        x = tf.keras.layers.GlobalAveragePooling2D()(base_model.output)\n        x = tf.keras.layers.Dense(256, activation=\"relu\")(x)\n        x = tf.keras.layers.Dropout(0.3)(x)\n        output = tf.keras.layers.Dense(CONFIG.N_TARGETS)(x)\n        self.model = tf.keras.Model(inputs=base_model.input, outputs=output)\n        self.model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),\n            loss=\"mse\",metrics=[R2Score()])\n        print(self.model.summary())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.039449Z","iopub.execute_input":"2026-01-17T18:05:19.039653Z","iopub.status.idle":"2026-01-17T18:05:19.048221Z","shell.execute_reply.started":"2026-01-17T18:05:19.039636Z","shell.execute_reply":"2026-01-17T18:05:19.047503Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Teacher(object):\n\n    def __init__(self, model_path):\n        self.model_path = model_path\n        self.create_model()\n\n    def create_model(self):\n        img_input = layers.Input(shape=CONFIG.IMG_SHAPE, name=\"image\")\n        base_cnn = tf.keras.models.load_model(self.model_path)\n        x_img = base_cnn(img_input, training=False)\n        x_img = layers.GlobalAveragePooling2D()(x_img)\n        x_img = layers.Dense(256, activation=\"relu\")(x_img)\n        \n        #numerical features\n        num_input = layers.Input(shape=(CONFIG.N_NUM,), name=\"numerical\")\n        x_num = layers.BatchNormalization()(num_input)\n        x_num = layers.Dense(128, activation=\"relu\")(x_num)\n\n\n        #categorical features\n        cat_inputs = []\n        cat_embeddings = []\n        \n        for name, vocab_size in CATEGORIES.items():\n            inp = layers.Input(shape=(1,), name=name)\n            emb = layers.Embedding(vocab_size, 16)(inp)\n            emb = layers.Flatten()(emb)\n            cat_inputs.append(inp)\n            cat_embeddings.append(emb)\n        \n        x_cat = layers.Concatenate()(cat_embeddings)\n        x_cat = layers.Dense(64, activation=\"relu\")(x_cat)\n        combined = layers.Concatenate()([x_img, x_num, x_cat])\n        combined = layers.Dense(256, activation=\"relu\")(combined)\n        combined = layers.Dropout(0.3)(combined)\n        outputs = layers.Dense(5, name=\"targets\")(combined)\n        self.model = models.Model(\n            inputs=[img_input, num_input] + cat_inputs,\n            outputs=outputs,name=\"teacher\")\n        self.model.compile(\n        optimizer=tf.keras.optimizers.Adam(1e-3),\n        loss=\"mse\",metrics=[R2Score()])\n        print(self.model.summary())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.050142Z","iopub.execute_input":"2026-01-17T18:05:19.050370Z","iopub.status.idle":"2026-01-17T18:05:19.062749Z","shell.execute_reply.started":"2026-01-17T18:05:19.050333Z","shell.execute_reply":"2026-01-17T18:05:19.062071Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class LoadData(object):\n\n    def __init__(self,data_long):\n        self.data_long = data_long\n\n    def load_image(self, path, target):\n        img = tf.io.read_file(path)\n        img = tf.image.decode_jpeg(img, channels=3)\n        img = tf.image.resize_with_pad(img, 256, 256)\n        img = tf.image.random_crop(img, size=CONFIG.IMG_SHAPE)\n        img = tf.cast(img, tf.float32)\n        #img = tf.image.resize(img, CONFIG.IMG_SIZE)\n        img = tf.keras.applications.resnet50.preprocess_input(img)\n        return img, target\n\n    def create_data_student(self):\n        image_paths = self.data_long[\"image_path\"].apply(lambda x: os.path.join(CONFIG.DATA_PATH, x)).values\n        X_train, X_val, y_train, y_val = train_test_split(\n            image_paths,\n            self.data_long[CONFIG.TARGETS],\n            test_size=0.2,\n            random_state=42\n        )\n        train_ds = tf.data.Dataset.from_tensor_slices((X_train, y_train))\n        train_ds = train_ds.map(self.load_image, num_parallel_calls=tf.data.AUTOTUNE)\n        train_ds = train_ds.batch(CONFIG.BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\n        \n        val_ds = tf.data.Dataset.from_tensor_slices((X_val, y_val))\n        val_ds = val_ds.map(self.load_image, num_parallel_calls=tf.data.AUTOTUNE)\n        val_ds = val_ds.batch(CONFIG.BATCH_SIZE).prefetch(tf.data.AUTOTUNE) \n        return train_ds ,val_ds\n\n\n    def create_data_teacher(self,cat_features):\n        X_num = self.data_long[['Pre_GSHH_NDVI','Height_Ave_cm']].values.astype(\"float32\")\n        X_cat = []\n        cat_encoders = {}\n        \n        for col in cat_features:\n            le = LabelEncoder()\n            encoded = le.fit_transform(self.data_long[col].astype(str))\n            X_cat.append(encoded.reshape(-1, 1))\n            cat_encoders[col] = le\n\n        image_paths = self.data_long[\"image_path\"].apply(lambda x: os.path.join(CONFIG.DATA_PATH, x)).values\n        im = []\n        for p in image_paths:\n            img = tf.io.read_file(p)\n            img = tf.image.decode_jpeg(img, channels=3)\n            img = tf.image.resize(img, CONFIG.IMG_SIZE)\n            img = tf.cast(img, tf.float32) / 255.0\n            im.append(img)\n        X_img = np.stack(im)\n\n        return X_num, X_cat, X_img\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.063538Z","iopub.execute_input":"2026-01-17T18:05:19.063743Z","iopub.status.idle":"2026-01-17T18:05:19.077384Z","shell.execute_reply.started":"2026-01-17T18:05:19.063726Z","shell.execute_reply":"2026-01-17T18:05:19.076829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds, val_ds = LoadData(data_input).create_data_student()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.078182Z","iopub.execute_input":"2026-01-17T18:05:19.078528Z","iopub.status.idle":"2026-01-17T18:05:19.348041Z","shell.execute_reply.started":"2026-01-17T18:05:19.078499Z","shell.execute_reply":"2026-01-17T18:05:19.347315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"student = Student(CONFIG.MODEL_PATH_STUDENT)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:19.349057Z","iopub.execute_input":"2026-01-17T18:05:19.349283Z","iopub.status.idle":"2026-01-17T18:05:29.503001Z","shell.execute_reply.started":"2026-01-17T18:05:19.349264Z","shell.execute_reply":"2026-01-17T18:05:29.502323Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"teacher = Teacher(CONFIG.MODEL_PATH_TEACHER)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:29.503908Z","iopub.execute_input":"2026-01-17T18:05:29.504164Z","iopub.status.idle":"2026-01-17T18:05:33.946146Z","shell.execute_reply.started":"2026-01-17T18:05:29.504144Z","shell.execute_reply":"2026-01-17T18:05:33.945580Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_num, X_cat, X_img = LoadData(data_input).create_data_teacher(CONFIG.CAT_FEATURES)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:05:33.946966Z","iopub.execute_input":"2026-01-17T18:05:33.947186Z","iopub.status.idle":"2026-01-17T18:06:08.868020Z","shell.execute_reply.started":"2026-01-17T18:05:33.947167Z","shell.execute_reply":"2026-01-17T18:06:08.867419Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"early_stop = EarlyStopping(\n    monitor=\"val_loss\",     \n    patience=5,             \n    restore_best_weights=True\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"teacher.model.fit(\n    [X_img, X_num] + X_cat,\n    data_input[CONFIG.TARGETS],\n    validation_split=0.2,\n    epochs=CONFIG.EPOCHS,\n    batch_size= CONFIG.BATCH_SIZE,\n    callbacks=[early_stop]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:06:08.869038Z","iopub.execute_input":"2026-01-17T18:06:08.869637Z","iopub.status.idle":"2026-01-17T18:08:44.306926Z","shell.execute_reply.started":"2026-01-17T18:06:08.869615Z","shell.execute_reply":"2026-01-17T18:08:44.306163Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_teacher =  teacher.model.predict([X_img, X_num] + X_cat)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:44.307967Z","iopub.execute_input":"2026-01-17T18:08:44.308361Z","iopub.status.idle":"2026-01-17T18:08:57.389770Z","shell.execute_reply.started":"2026-01-17T18:08:44.308313Z","shell.execute_reply":"2026-01-17T18:08:57.389167Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"alpha = 0.6","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.390584Z","iopub.execute_input":"2026-01-17T18:08:57.390797Z","iopub.status.idle":"2026-01-17T18:08:57.394191Z","shell.execute_reply.started":"2026-01-17T18:08:57.390777Z","shell.execute_reply":"2026-01-17T18:08:57.393566Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optimizer = tf.keras.optimizers.Adam(1e-4)\n\n@tf.function\ndef train_step(images, y_true, y_teacher):\n    with tf.GradientTape() as tape:\n        y_pred = student.model(images, training=True)\n        loss_true = tf.reduce_mean(tf.keras.losses.mse(y_true, y_pred))\n        loss_teacher = tf.reduce_mean(tf.keras.losses.mse(y_teacher, y_pred))\n        loss = alpha * loss_true + (1 - alpha) * loss_teacher\n\n    grads = tape.gradient(loss, student.model.trainable_variables)\n    optimizer.apply_gradients(zip(grads, student.model.trainable_variables))\n    return loss","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.395095Z","iopub.execute_input":"2026-01-17T18:08:57.395712Z","iopub.status.idle":"2026-01-17T18:08:57.410291Z","shell.execute_reply.started":"2026-01-17T18:08:57.395691Z","shell.execute_reply":"2026-01-17T18:08:57.409742Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_image(path):\n    img = tf.io.read_file(path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, CONFIG.IMG_SIZE)\n    img = tf.cast(img, tf.float32) / 255.0\n    return img","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.411810Z","iopub.execute_input":"2026-01-17T18:08:57.412061Z","iopub.status.idle":"2026-01-17T18:08:57.420237Z","shell.execute_reply.started":"2026-01-17T18:08:57.412043Z","shell.execute_reply":"2026-01-17T18:08:57.419726Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_paths = data_input[\"image_path\"].apply(\n    lambda x: os.path.join(CONFIG.DATA_PATH, x)\n).values","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.421694Z","iopub.execute_input":"2026-01-17T18:08:57.421945Z","iopub.status.idle":"2026-01-17T18:08:57.434288Z","shell.execute_reply.started":"2026-01-17T18:08:57.421928Z","shell.execute_reply":"2026-01-17T18:08:57.433693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_paths[:3]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.435125Z","iopub.execute_input":"2026-01-17T18:08:57.435485Z","iopub.status.idle":"2026-01-17T18:08:57.449216Z","shell.execute_reply.started":"2026-01-17T18:08:57.435457Z","shell.execute_reply":"2026-01-17T18:08:57.448588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = tf.data.Dataset.from_tensor_slices(image_paths)\ntrain = train.map(load_image, num_parallel_calls=tf.data.AUTOTUNE)\ntrain = train.batch(CONFIG.BATCH_SIZE).prefetch(tf.data.AUTOTUNE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.450041Z","iopub.execute_input":"2026-01-17T18:08:57.450289Z","iopub.status.idle":"2026-01-17T18:08:57.505998Z","shell.execute_reply.started":"2026-01-17T18:08:57.450271Z","shell.execute_reply":"2026-01-17T18:08:57.505419Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(20):\n    print(epoch)\n    for im, y, y_t in zip(train, data_input[CONFIG.TARGETS].values, y_teacher):\n        loss = train_step(im, y, y_t)\n    print(f\"Epoch {epoch+1}: loss={loss.numpy():.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:08:57.508588Z","iopub.execute_input":"2026-01-17T18:08:57.508802Z","iopub.status.idle":"2026-01-17T18:10:58.087460Z","shell.execute_reply.started":"2026-01-17T18:08:57.508784Z","shell.execute_reply":"2026-01-17T18:10:58.086653Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"test_data = pd.read_csv(CONFIG.TEST_DATA_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:10:58.088551Z","iopub.execute_input":"2026-01-17T18:10:58.088820Z","iopub.status.idle":"2026-01-17T18:10:58.096020Z","shell.execute_reply.started":"2026-01-17T18:10:58.088787Z","shell.execute_reply":"2026-01-17T18:10:58.095395Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_image_paths = test_data[\"image_path\"].drop_duplicates().apply(\n    lambda x: os.path.join(CONFIG.DATA_PATH, x)\n).values","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:10:58.097042Z","iopub.execute_input":"2026-01-17T18:10:58.097456Z","iopub.status.idle":"2026-01-17T18:10:58.104110Z","shell.execute_reply.started":"2026-01-17T18:10:58.097424Z","shell.execute_reply":"2026-01-17T18:10:58.103395Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = tf.data.Dataset.from_tensor_slices(test_image_paths)\ntest = test.map(load_image, num_parallel_calls=tf.data.AUTOTUNE)\ntest = test.batch(CONFIG.BATCH_SIZE).prefetch(tf.data.AUTOTUNE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:10:58.104933Z","iopub.execute_input":"2026-01-17T18:10:58.105189Z","iopub.status.idle":"2026-01-17T18:10:58.133368Z","shell.execute_reply.started":"2026-01-17T18:10:58.105169Z","shell.execute_reply":"2026-01-17T18:10:58.132603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = student.model.predict(test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:10:58.134241Z","iopub.execute_input":"2026-01-17T18:10:58.134485Z","iopub.status.idle":"2026-01-17T18:11:04.313213Z","shell.execute_reply.started":"2026-01-17T18:10:58.134455Z","shell.execute_reply":"2026-01-17T18:11:04.312650Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = scaler.inverse_transform(predictions)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:11:04.314172Z","iopub.execute_input":"2026-01-17T18:11:04.314475Z","iopub.status.idle":"2026-01-17T18:11:04.318685Z","shell.execute_reply.started":"2026-01-17T18:11:04.314446Z","shell.execute_reply":"2026-01-17T18:11:04.317836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_submission(predictions, test_images, test_df):\n    \"\"\"Create submission file in required format\"\"\"\n    \n    # Create prediction dataframe\n    pred_df = pd.DataFrame(predictions, columns=list(CONFIG.TARGETS))\n    pred_df['image_path'] = test_images['image_path'].unique()\n    \n    # Melt to long format\n    pred_long = pred_df.melt(\n        id_vars=['image_path'],\n        value_vars=list(CONFIG.TARGETS),\n        var_name='target_name',\n        value_name='target'\n    )\n    \n    # Create sample_id\n    pred_long['image_id'] = pred_long['image_path'].str.replace('test/', '').str.replace('.jpg', '')\n    pred_long['sample_id'] = pred_long['image_id'] + '__' + pred_long['target_name']\n    \n    # Select and sort columns\n    submission = pred_long[['sample_id', 'target']].copy()\n    \n    # Merge with test_df to ensure correct order\n    submission = test_df[['sample_id']].merge(submission, on='sample_id', how='left')\n        \n    return submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:11:04.319624Z","iopub.execute_input":"2026-01-17T18:11:04.319916Z","iopub.status.idle":"2026-01-17T18:11:04.335730Z","shell.execute_reply.started":"2026-01-17T18:11:04.319894Z","shell.execute_reply":"2026-01-17T18:11:04.335093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = create_submission(predictions, test_data, test_data)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:11:04.336578Z","iopub.execute_input":"2026-01-17T18:11:04.336966Z","iopub.status.idle":"2026-01-17T18:11:04.360445Z","shell.execute_reply.started":"2026-01-17T18:11:04.336931Z","shell.execute_reply":"2026-01-17T18:11:04.359803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:11:04.361204Z","iopub.execute_input":"2026-01-17T18:11:04.361506Z","iopub.status.idle":"2026-01-17T18:11:04.378081Z","shell.execute_reply.started":"2026-01-17T18:11:04.361487Z","shell.execute_reply":"2026-01-17T18:11:04.377314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-17T18:11:04.378870Z","iopub.execute_input":"2026-01-17T18:11:04.379095Z","iopub.status.idle":"2026-01-17T18:11:04.390152Z","shell.execute_reply.started":"2026-01-17T18:11:04.379069Z","shell.execute_reply":"2026-01-17T18:11:04.389556Z"}},"outputs":[],"execution_count":null}]}