{"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":[{"sourceType":"competition","sourceId":70203,"databundleVersionId":8068726}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport math\nimport random\nimport pathlib\nimport numpy as np\nimport pandas as pd\nimport soundfile as sf  \nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras import mixed_precision\nfrom sklearn.model_selection import train_test_split\n\n# ==========================================\n# 1. HARDWARE MICRO-OPTIMIZATIONS\n# ==========================================\n# Enable Automatic Mixed Precision (AMP) to unleash GPU Tensor Cores\npolicy = mixed_precision.Policy('mixed_float16')\nmixed_precision.set_global_policy(policy)\n\n# Global reproducibility anchors\nSEED = 42\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntf.random.set_seed(SEED)\n\n# ==========================================\n# 2. GLOBAL CONFIGURATION\n# ==========================================\nDATA_DIR = pathlib.Path(\"/kaggle/input/competitions/birdclef-2024\")\nMETA_PATH = DATA_DIR / \"train_metadata.csv\"\nTRAIN_AUDIO_DIR = DATA_DIR / \"train_audio\"\n\nTARGET_SR = 32000  \nCLIP_SECONDS = 5.0  # Upgraded to 5s to capture complete structural bird calls\nTARGET_SAMPLES = int(TARGET_SR * CLIP_SECONDS)  # 160,000 raw audio samples\n\n# Spectrogram Resolutions\nN_FFT = 2048\nHOP_LENGTH = 512\nN_MELS = 128  \nTIME_STEPS = 1 + (TARGET_SAMPLES - N_FFT) // HOP_LENGTH\n\nBATCH_SIZE = 64  # Bumped to 64 because Mixed Precision massively frees up VRAM\nEPOCHS = 35\n\n# ==========================================\n# 3. METADATA PROCESSING & ANALYSIS\n# ==========================================\nprint(\"Parsing and matching dataset metadata...\")\ndf = pd.read_csv(META_PATH)\n\ndef resolve_audio_path(row):\n    fname = str(row[\"filename\"])\n    label = str(row[\"primary_label\"])\n    candidates = [TRAIN_AUDIO_DIR / label / fname, TRAIN_AUDIO_DIR / fname]\n    for p in candidates:\n        if p.exists(): return str(p)\n    matches = list(TRAIN_AUDIO_DIR.rglob(fname))\n    if matches: return str(matches[0])\n    return None\n\ndf[\"filepath\"] = df.apply(resolve_audio_path, axis=1)\ndf = df[df[\"filepath\"].notna()].reset_index(drop=True)\n\nlabels = sorted(df[\"primary_label\"].unique().tolist())\nlabel2id = {name: i for i, name in enumerate(labels)}\ndf[\"label\"] = df[\"primary_label\"].map(label2id).astype(int)\nNUM_CLASSES = len(labels)\n\n# Stratified data distribution split\ntrain_df, val_df = train_test_split(df, test_size=0.15, random_state=SEED, stratify=df[\"label\"])\ntrain_df, val_df = train_df.reset_index(drop=True), val_df.reset_index(drop=True)\n\n# Balanced Class Weighting Calculations\ncounts = train_df[\"label\"].value_counts().sort_index().reindex(range(NUM_CLASSES), fill_value=1)\nclass_weight = {i: len(train_df) / (NUM_CLASSES * counts[i]) for i in range(NUM_CLASSES)}\n\n# ==========================================\n# 4. HIGH-PERFORMANCE AUDIO I/O LAYER\n# ==========================================\ndef load_audio_py(path_bytes, training):\n    path = path_bytes.decode(\"utf-8\")\n    info = sf.info(path)\n    total_frames = info.frames\n    channels = info.channels\n\n    if total_frames <= TARGET_SAMPLES:\n        y, _ = sf.read(path, dtype='float32')\n        if channels > 1: y = np.mean(y, axis=1)\n        y = np.pad(y, (0, TARGET_SAMPLES - len(y)))\n    else:\n        if training:\n            # Random cropping over the 5-second boundary to explore the full audio file\n            start_frame = np.random.randint(0, total_frames - TARGET_SAMPLES + 1)\n        else:\n            # Balanced fallback window for validation consistency\n            start_frame = 0\n        y, _ = sf.read(path, start=start_frame, frames=TARGET_SAMPLES, dtype='float32')\n        if channels > 1: y = np.mean(y, axis=1)\n        if len(y) < TARGET_SAMPLES: y = np.pad(y, (0, TARGET_SAMPLES - len(y)))\n        else: y = y[:TARGET_SAMPLES]\n    return y\n\n# ==========================================\n# 5. GPU SPECTROGRAM TRANSFORMATION + AUGMENTATION\n# ==========================================\n@tf.function\ndef raw_to_mel_spectrogram_tf(wav, label, training=True):\n    if training:\n        if tf.random.uniform([]) < 0.5:\n            wav = wav * tf.random.uniform([], 0.8, 1.2)\n        if tf.random.uniform([]) < 0.3:\n            max_shift = TARGET_SR // 2\n            shift = tf.random.uniform([], -max_shift, max_shift, dtype=tf.int32)\n            wav = tf.roll(wav, shift=shift, axis=0)\n\n    # 1. Short-Time Fourier Transform\n    stft = tf.signal.stft(wav, frame_length=N_FFT, frame_step=HOP_LENGTH, fft_length=N_FFT)\n    spectrogram = tf.abs(stft)\n\n    # 2. Linear to Mel Space Mapping Matrix\n    num_spectrogram_bins = stft.shape[-1]\n    linear_to_mel_weight_matrix = tf.signal.linear_to_mel_weight_matrix(\n        num_mel_bins=N_MELS,\n        num_spectrogram_bins=num_spectrogram_bins,\n        sample_rate=TARGET_SR,\n        lower_edge_hertz=80.0,\n        upper_edge_hertz=16000.0\n    )\n    mel_spectrogram = tf.tensordot(spectrogram, linear_to_mel_weight_matrix, 1)\n    mel_spectrogram.set_shape(spectrogram.shape[:-1].concatenate(linear_to_mel_weight_matrix.shape[-1:]))\n\n    # 3. Logarithmic Compression\n    log_mel_spectrogram = tf.math.log(mel_spectrogram + 1e-6)\n\n    # 4. Standardized Rescaling\n    mean = tf.reduce_mean(log_mel_spectrogram)\n    std = tf.math.reduce_std(log_mel_spectrogram)\n    normalized_spec = (log_mel_spectrogram - mean) / (std + 1e-6)\n    normalized_spec = tf.expand_dims(normalized_spec, axis=-1)\n\n    # 5. Native SpecAugment Engine\n    if training:\n        # Dynamic Time Masking\n        if tf.random.uniform([]) < 0.5:\n            t_width = tf.random.uniform([], 8, 24, dtype=tf.int32)\n            t_start = tf.random.uniform([], 0, TIME_STEPS - t_width, dtype=tf.int32)\n            mask = tf.concat([tf.ones([t_start, N_MELS, 1]), \n                              tf.zeros([t_width, N_MELS, 1]), \n                              tf.ones([TIME_STEPS - t_start - t_width, N_MELS, 1])], axis=0)\n            normalized_spec = normalized_spec * mask\n\n        # Dynamic Frequency Masking\n        if tf.random.uniform([]) < 0.5:\n            f_width = tf.random.uniform([], 8, 24, dtype=tf.int32)\n            f_start = tf.random.uniform([], 0, N_MELS - f_width, dtype=tf.int32)\n            mask = tf.concat([tf.ones([TIME_STEPS, f_start, 1]), \n                              tf.zeros([TIME_STEPS, f_width, 1]), \n                              tf.ones([TIME_STEPS, N_MELS - f_start - f_width, 1])], axis=1)\n            normalized_spec = normalized_spec * mask\n\n    return normalized_spec, label\n\n# ==========================================\n# 6. NON-DETERMINISTIC ASYNC TF.DATA ENGINE\n# ==========================================\ndef make_example(path, label, training=False):\n    def _load(p): return load_audio_py(p.numpy(), training)\n    wav = tf.py_function(func=_load, inp=[path], Tout=tf.float32)\n    wav.set_shape([TARGET_SAMPLES])\n    return wav, label\n\ndef build_dataset(frame, training=False):\n    paths = frame[\"filepath\"].values.astype(str)\n    labels = frame[\"label\"].values.astype(np.int32)\n    ds = tf.data.Dataset.from_tensor_slices((paths, labels))\n\n    # [MICRO-OPTIMIZATION]: Drop sequential execution requirements for processing boost\n    if training:\n        options = tf.data.Options()\n        options.experimental_deterministic = False\n        ds = ds.with_options(options)\n        ds = ds.shuffle(4096, seed=SEED, reshuffle_each_iteration=True)\n\n    ds = ds.map(lambda p, l: make_example(p, l, training=training), num_parallel_calls=tf.data.AUTOTUNE)\n    ds = ds.map(lambda w, l: raw_to_mel_spectrogram_tf(w, l, training=training), num_parallel_calls=tf.data.AUTOTUNE)\n\n    if training: ds = ds.repeat()\n    ds = ds.batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\n    return ds\n\nprint(\"Constructing high-throughput data streams...\")\ntrain_ds = build_dataset(train_df, training=True)\nval_ds = build_dataset(val_df, training=False)\n\nsteps_per_epoch = math.ceil(len(train_df) / BATCH_SIZE)\nval_steps = math.ceil(len(val_df) / BATCH_SIZE)\n\n# ==========================================\n# 7. HIGH-CAPACITY MODEL + XLA ENGINE\n# ==========================================\nprint(\"Assembling EfficientNetB2 Graph...\")\ninputs = keras.Input(shape=(TIME_STEPS, N_MELS, 1))\n\n# Expand grayscale to pseudo-RGB\nx = layers.Concatenate(axis=-1)([inputs, inputs, inputs])\n\nbackbone = keras.applications.EfficientNetB2(\n    include_top=False,\n    weights=\"imagenet\",\n    input_tensor=x\n)\n\n# Fully unfettered backbone optimization\nbackbone.trainable = True\n\nx = backbone.output\navg_pool = layers.GlobalAveragePooling2D()(x)\nmax_pool = layers.GlobalMaxPooling2D()(x)\nx = layers.Concatenate()([avg_pool, max_pool])\n\nx = layers.Dense(256, activation=\"relu\")(x)\nx = layers.BatchNormalization()(x)\nx = layers.Dropout(0.4)(x)\n\n# [MICRO-OPTIMIZATION]: Force float32 activation out explicitly for Mixed Precision stability\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\", dtype=\"float32\")(x)\n\nmodel = keras.Model(inputs, outputs)\n\n# Continuous Cosine Annealing Learning Rate Decay Schedule\nlearning_rate_schedule = keras.optimizers.schedules.CosineDecay(\n    initial_learning_rate=4e-4,\n    decay_steps=steps_per_epoch * EPOCHS,\n    alpha=1e-6\n)\n\nmodel.compile(\n    optimizer=keras.optimizers.Adam(learning_rate=learning_rate_schedule, clipnorm=1.0),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"],\n    # [MICRO-OPTIMIZATION]: Enable compilation fusion to collapse layers down for rapid GPU loops\n    jit_compile=True\n)\n\n# ==========================================\n# 8. TRAINING EXECUTION ENGINE\n# ==========================================\ncallbacks = [\n    keras.callbacks.EarlyStopping(monitor=\"val_loss\", patience=5, restore_best_weights=True, verbose=1),\n    keras.callbacks.ModelCheckpoint(\"/kaggle/working/mel_2d_cnn_best.keras\", monitor=\"val_accuracy\", save_best_only=True, verbose=1)\n]\n\nprint(\"Optimization handshake complete. Executing pipeline...\")\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    class_weight=class_weight,\n    callbacks=callbacks\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-17T00:23:31.481875Z","iopub.execute_input":"2026-05-17T00:23:31.482837Z","iopub.status.idle":"2026-05-17T02:17:52.289166Z","shell.execute_reply.started":"2026-05-17T00:23:31.482800Z","shell.execute_reply":"2026-05-17T02:17:52.288264Z"}},"outputs":[],"execution_count":null}]}