{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":45867,"databundleVersionId":6924515,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pickle\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T15:24:51.987561Z","iopub.execute_input":"2025-04-03T15:24:51.987825Z","iopub.status.idle":"2025-04-03T15:25:09.195604Z","shell.execute_reply.started":"2025-04-03T15:24:51.987799Z","shell.execute_reply":"2025-04-03T15:25:09.194488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------\n# 1. Configuration\n# ----------------------------\nclass Config:\n    is_submission = False  # Set True later if preparing submission\n    SEED = 42\n    # Update these paths based on your Kaggle dataset structure\n    train_csv_path = \"/kaggle/input/UBC-OCEAN/train.csv\"\n    train_thumbnail_paths = \"/kaggle/input/UBC-OCEAN/train_thumbnails\"\n    batch_size = 8\n    learning_rate = 1e-3\n    epochs = 50  # Adjust epochs as needed\n    image_size = (256, 256)  # Using 256x256 thumbnails\n\nconfig = Config()\n\n# Set seeds for reproducibility\ntf.random.set_seed(config.SEED)\nnp.random.seed(config.SEED)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:03:48.772552Z","iopub.execute_input":"2025-04-03T16:03:48.772941Z","iopub.status.idle":"2025-04-03T16:03:48.810365Z","shell.execute_reply.started":"2025-04-03T16:03:48.772914Z","shell.execute_reply":"2025-04-03T16:03:48.80907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------\n# 2. Load and Preprocess the Data\n# ----------------------------\n# Read the CSV metadata\ndf = pd.read_csv(config.train_csv_path)\n\n# Filter the data if necessary (e.g., use only thumbnails)\nif \"is_tma\" in df.columns:\n    df = df[df[\"is_tma\"] == False]\n\n# One-hot encode the labels\nlabels_one_hot = pd.get_dummies(df['label'], prefix=\"label\")\ntrain_df = pd.concat([df[\"image_id\"], labels_one_hot], axis=1)\n# Construct the full image path for each thumbnail\ntrain_df[\"image_thumbnail_path\"] = train_df[\"image_id\"].apply(\n    lambda x: os.path.join(config.train_thumbnail_paths, f\"{x}_thumbnail.png\")\n)\n\n# Extract image paths and label arrays\nimage_paths = train_df[\"image_thumbnail_path\"].values\nlabel_values = train_df[[col for col in train_df.columns if col.startswith(\"label_\")]].values\n\n# Create mappings for label names (helpful during inference)\nlabel_names = [col.replace(\"label_\", \"\") for col in train_df.columns if col.startswith(\"label_\")]\nname_to_id = {name: idx for idx, name in enumerate(label_names)}\nid_to_name = {idx: name for name, idx in name_to_id.items()}\n\n# Save the mapping for later use (inference/submission)\nwith open(\"id_to_name.pkl\", \"wb\") as f:\n    pickle.dump(id_to_name, f)\n\n# Calculate class weights to balance the training process\nclass_counts = np.sum(label_values, axis=0)\ntotal_samples = np.sum(class_counts)\nclass_weights = {i: (total_samples - count) / total_samples for i, count in enumerate(class_counts)}\nprint(\"Class weights:\", class_weights)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:03:51.591642Z","iopub.execute_input":"2025-04-03T16:03:51.592029Z","iopub.status.idle":"2025-04-03T16:03:51.617009Z","shell.execute_reply.started":"2025-04-03T16:03:51.592001Z","shell.execute_reply":"2025-04-03T16:03:51.615933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------\n# 3. Create tf.data.Dataset Pipeline\n# ----------------------------\ndef read_image(path):\n    # Read and decode the PNG image file\n    image = tf.io.read_file(path)\n    image = tf.io.decode_png(image, channels=3)\n    # Resize and normalize the image\n    image = tf.image.resize(image, config.image_size)\n    image = tf.cast(image, tf.float32) / 255.0\n    # Optionally, standardize (zero mean, unit variance)\n    image = tf.image.per_image_standardization(image)\n    return image\n\n# Create datasets for images and labels\nx_ds = tf.data.Dataset.from_tensor_slices(image_paths)\nx_ds = x_ds.map(lambda path: read_image(path), num_parallel_calls=tf.data.AUTOTUNE)\ny_ds = tf.data.Dataset.from_tensor_slices(label_values)\n\n# Combine images and labels\ndataset = tf.data.Dataset.zip((x_ds, y_ds))\ndataset = dataset.shuffle(buffer_size=len(image_paths), seed=config.SEED)\n\n# Split dataset into training and validation sets (e.g., 90/10 split)\nval_size = int(0.1 * len(image_paths))\ntrain_ds = dataset.skip(val_size).batch(config.batch_size).prefetch(tf.data.AUTOTUNE)\nval_ds = dataset.take(val_size).batch(config.batch_size).prefetch(tf.data.AUTOTUNE)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:03:54.514225Z","iopub.execute_input":"2025-04-03T16:03:54.514573Z","iopub.status.idle":"2025-04-03T16:03:54.633904Z","shell.execute_reply.started":"2025-04-03T16:03:54.514548Z","shell.execute_reply":"2025-04-03T16:03:54.632734Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------\n# 4. Build the Model using EfficientNetB3\n# ----------------------------\n# Load the pre-trained EfficientNetB3 model without the top classifier\nbase_model = EfficientNetB3(weights=\"imagenet\", include_top=False,\n                            input_shape=(config.image_size[0], config.image_size[1], 3))\nbase_model.trainable = False  # Freeze the base model to use as a feature extractor\n\n# Build the custom classification head on top of EfficientNetB3\ninputs = tf.keras.Input(shape=(config.image_size[0], config.image_size[1], 3))\nx = base_model(inputs, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.BatchNormalization()(x)\nx = layers.Dense(256, activation='relu')(x)\nx = layers.Dropout(0.3)(x)\n# Final layer: number of classes equals the number of one-hot encoded columns\noutputs = layers.Dense(label_values.shape[1], activation='softmax')(x)\n\nmodel = tf.keras.Model(inputs, outputs)\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:03:57.110185Z","iopub.execute_input":"2025-04-03T16:03:57.110571Z","iopub.status.idle":"2025-04-03T16:03:59.730927Z","shell.execute_reply.started":"2025-04-03T16:03:57.11054Z","shell.execute_reply":"2025-04-03T16:03:59.730109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ----------------------------\n# 5. Compile and Train the Model\n# ----------------------------\nmodel.compile(optimizer=Adam(learning_rate=config.learning_rate),\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])\n\n# Optional callbacks: Save the best model and stop early if needed\ncheckpoint = ModelCheckpoint(\"efficientnetb3_best.keras\", monitor='val_accuracy', \n                             save_best_only=True, mode='max')\nearlystop = EarlyStopping(monitor='val_accuracy', patience=5, restore_best_weights=True)\n\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=config.epochs,\n    class_weight=class_weights,\n    callbacks=[checkpoint, earlystop]\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:26:36.286069Z","iopub.execute_input":"2025-04-03T16:26:36.286545Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Plot Accuracy and Loss curves\nplt.figure(figsize=(12, 5))\n\n# Plot training & validation accuracy values\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.title('Accuracy Over Epochs')\nplt.legend()\n\n# Plot training & validation loss values\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.title('Loss Over Epochs')\nplt.legend()\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:03:25.260977Z","iopub.status.idle":"2025-04-03T16:03:25.261353Z","shell.execute_reply":"2025-04-03T16:03:25.261173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Save the final model weights for future use\nmodel.save_weights(\"efficientnetb3_final.weights.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-03T16:03:25.262531Z","iopub.status.idle":"2025-04-03T16:03:25.263018Z","shell.execute_reply":"2025-04-03T16:03:25.262801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}