{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":6125,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":4596,"modelId":2797},{"sourceId":6127,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":4598,"modelId":2797}],"dockerImageVersionId":30761,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nos.environ[\"KERAS_BACKEND\"] = \"jax\"\nos.environ[\"MKL_NUM_THREADS\"] = \"4\"\nos.environ[\"NUMEXPR_NUM_THREADS\"] = \"4\"\nos.environ[\"OMP_NUM_THREADS\"] = \"4\"\nos.environ[\"OMP_SCHEDULE\"] = \"STATIC\"\n\nimport gc\nimport sys\n\nimport math\nimport random","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:28.820327Z","iopub.execute_input":"2024-08-22T12:12:28.821339Z","iopub.status.idle":"2024-08-22T12:12:28.828620Z","shell.execute_reply.started":"2024-08-22T12:12:28.821272Z","shell.execute_reply":"2024-08-22T12:12:28.827440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import keras_cv\nimport keras\nimport keras.backend as K\nimport tensorflow as tf\nimport tensorflow_io as tfio\n\nimport numpy as np \nimport pandas as pd\n\nfrom glob import glob\nfrom tqdm import tqdm\n\nimport librosa\nimport IPython.display as ipd\nimport librosa.display as lid\n\nimport matplotlib.pyplot as plt\nimport matplotlib as mpl\n\ncmap = mpl.cm.get_cmap('coolwarm')","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:28.836165Z","iopub.execute_input":"2024-08-22T12:12:28.837496Z","iopub.status.idle":"2024-08-22T12:12:28.854599Z","shell.execute_reply.started":"2024-08-22T12:12:28.837412Z","shell.execute_reply":"2024-08-22T12:12:28.853332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df = pd.read_csv('/kaggle/input/birdclef-2024/train_metadata.csv')\nmeta_df.head(10)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:28.857134Z","iopub.execute_input":"2024-08-22T12:12:28.858045Z","iopub.status.idle":"2024-08-22T12:12:29.192717Z","shell.execute_reply.started":"2024-08-22T12:12:28.857983Z","shell.execute_reply":"2024-08-22T12:12:29.191503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df.shape","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.205459Z","iopub.execute_input":"2024-08-22T12:12:29.206282Z","iopub.status.idle":"2024-08-22T12:12:29.213903Z","shell.execute_reply.started":"2024-08-22T12:12:29.206225Z","shell.execute_reply":"2024-08-22T12:12:29.212738Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df.info()","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.216182Z","iopub.execute_input":"2024-08-22T12:12:29.216695Z","iopub.status.idle":"2024-08-22T12:12:29.315813Z","shell.execute_reply.started":"2024-08-22T12:12:29.216639Z","shell.execute_reply":"2024-08-22T12:12:29.314505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df['secondary_labels'].value_counts().head()","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.318803Z","iopub.execute_input":"2024-08-22T12:12:29.319873Z","iopub.status.idle":"2024-08-22T12:12:29.346971Z","shell.execute_reply.started":"2024-08-22T12:12:29.319816Z","shell.execute_reply":"2024-08-22T12:12:29.345759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_df['rating'].value_counts().head()","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.348512Z","iopub.execute_input":"2024-08-22T12:12:29.349547Z","iopub.status.idle":"2024-08-22T12:12:29.373823Z","shell.execute_reply.started":"2024-08-22T12:12:29.349486Z","shell.execute_reply":"2024-08-22T12:12:29.372525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(meta_df['scientific_name'].unique()))","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.375404Z","iopub.execute_input":"2024-08-22T12:12:29.376543Z","iopub.status.idle":"2024-08-22T12:12:29.398019Z","shell.execute_reply.started":"2024-08-22T12:12:29.376475Z","shell.execute_reply":"2024-08-22T12:12:29.396588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"versions = {\n    \"TensorFlow\": tf.__version__,\n    \"Keras\": keras.__version__,\n    \"KerasCV\": keras_cv.__version__\n}\n\nfor library, version in versions.items():\n    print(f\"{library}: {version}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.400088Z","iopub.execute_input":"2024-08-22T12:12:29.401324Z","iopub.status.idle":"2024-08-22T12:12:29.415895Z","shell.execute_reply.started":"2024-08-22T12:12:29.401255Z","shell.execute_reply":"2024-08-22T12:12:29.414637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configuration","metadata":{}},{"cell_type":"code","source":"class CFG:\n    def __init__(self):\n        self.seed = 42\n        \n        # Input image size and batch size\n        self.img_size = (128, 384)\n        self.batch_size = 64\n        \n        # Audio duration, sample rate, and length\n        self.duration = 15  # in seconds\n        self.sample_rate = 32000\n        self.audio_len = self.duration * self.sample_rate\n        \n        # STFT parameters\n        self.nfft = 2028\n        self.window = 2048\n        self.hop_length = self.calculate_hop_length()\n        self.fmin = 20\n        self.fmax = 16000\n        \n        # Training parameters\n        self.epochs = 10\n        self.preset = 'efficientnetv2_b2_imagenet'\n        \n        # Data augmentation\n        self.augment = True\n        \n        # Class Labels for BirdCLEF 2024\n        self.class_names = self.load_class_names('/kaggle/input/birdclef-2024/train_audio/')\n        self.num_classes = len(self.class_names)\n        self.class_labels = list(range(self.num_classes))\n        self.label2name = {i: name for i, name in enumerate(self.class_names)}\n        self.name2label = {v: k for k, v in self.label2name.items()}\n    \n    def calculate_hop_length(self):\n        return self.audio_len // (self.img_size[1] - 1)\n    \n    def load_class_names(self, path):\n        if os.path.exists(path):\n            return sorted(os.listdir(path))\n        else:\n            return []\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.417876Z","iopub.execute_input":"2024-08-22T12:12:29.418775Z","iopub.status.idle":"2024-08-22T12:12:29.451874Z","shell.execute_reply.started":"2024-08-22T12:12:29.418716Z","shell.execute_reply":"2024-08-22T12:12:29.450103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CFG:\n    seed = 42\n    \n    # Input image size and batch size\n    img_size = [128, 384]\n    batch_size = 64\n    \n    # Audio duration, sample rate, and length\n    duration = 15 # second\n    sample_rate = 32000\n    audio_len = duration*sample_rate\n\n        # STFT parameters\n    nfft = 2028\n    window = 2048\n    hop_length = audio_len // (img_size[1] - 1)\n    fmin = 20\n    fmax = 16000\n    \n    # Number of epochs, model name\n    epochs = 10\n    preset = 'efficientnetv2_b2_imagenet'\n    \n    # Data augmentation parameters\n    augment=True\n\n        # Class Labels for BirdCLEF 24\n    class_names = sorted(os.listdir('/kaggle/input/birdclef-2024/train_audio/'))\n    num_classes = len(class_names)\n    class_labels = list(range(num_classes))\n    label2name = dict(zip(class_labels, class_names))\n    name2label = {v:k for k,v in label2name.items()}\n    ","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.458446Z","iopub.execute_input":"2024-08-22T12:12:29.459284Z","iopub.status.idle":"2024-08-22T12:12:29.486832Z","shell.execute_reply.started":"2024-08-22T12:12:29.459217Z","shell.execute_reply":"2024-08-22T12:12:29.485521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ReProductibility","metadata":{}},{"cell_type":"code","source":"# Set random seed for TensorFlow\ntf.random.set_seed(CFG.seed)\n\n# Set random seed for NumPy\nnp.random.seed(CFG.seed)\n\n# Set random seed for Python's random module\nrandom.seed(CFG.seed)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.492366Z","iopub.execute_input":"2024-08-22T12:12:29.499768Z","iopub.status.idle":"2024-08-22T12:12:29.519305Z","shell.execute_reply.started":"2024-08-22T12:12:29.499695Z","shell.execute_reply":"2024-08-22T12:12:29.518090Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Path","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '/kaggle/input/birdclef-2024'","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.520831Z","iopub.execute_input":"2024-08-22T12:12:29.527877Z","iopub.status.idle":"2024-08-22T12:12:29.534156Z","shell.execute_reply.started":"2024-08-22T12:12:29.527804Z","shell.execute_reply":"2024-08-22T12:12:29.532897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Meta Data","metadata":{}},{"cell_type":"code","source":"# Load the metadata\ndf = pd.read_csv(os.path.join(BASE_PATH, 'train_metadata.csv'))\n\n# Construct file paths\ndf['filepath'] = df['filename'].apply(lambda fname: os.path.join(BASE_PATH, 'train_audio', fname))\n\n# Map primary labels to target values\ndf['target'] = df['primary_label'].map(CFG.name2label)\n\n# Extract the filename from the filepath\ndf['filename'] = df['filepath'].apply(lambda path: os.path.basename(path))\n\n# Extract xc_id by removing the file extension\ndf['xc_id'] = df['filename'].apply(lambda fname: os.path.splitext(fname)[0])\n\n# Display the first two rows\ndf.head(5)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:29.537929Z","iopub.execute_input":"2024-08-22T12:12:29.540002Z","iopub.status.idle":"2024-08-22T12:12:30.090523Z","shell.execute_reply.started":"2024-08-22T12:12:29.539935Z","shell.execute_reply":"2024-08-22T12:12:30.089299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# EDA","metadata":{}},{"cell_type":"code","source":"def load_audio(filepath, target_sr=CFG.sample_rate):\n    # Load the audio file with the target sample rate for consistency\n    audio, sr = librosa.load(filepath, sr=target_sr)\n    return audio, sr\n\ndef get_spectrogram(audio):\n    # Generate a Mel spectrogram with more tuned parameters for better representation\n    spec = librosa.feature.melspectrogram(\n        y=audio,\n        sr=CFG.sample_rate,\n        n_mels=256,\n        n_fft=CFG.nfft,\n        hop_length=CFG.hop_length,\n        fmax=CFG.fmax,\n        fmin=CFG.fmin,\n        power=2.0  # Using squared magnitude for more robust features\n    )\n    # Convert to decibel scale\n    spec_db = librosa.power_to_db(spec, ref=np.max)\n    \n    # Standard normalization for better model input\n    normalized_spec = (spec_db - np.min(spec_db)) / (np.max(spec_db) - np.min(spec_db))\n    \n    return normalized_spec\n\ndef display_audio(row):\n    caption = f'Id: {row.filename} | Name: {row.common_name} | Sci.Name: {row.scientific_name} | Rating: {row.rating}'\n    \n    # Load the audio\n    audio, sr = load_audio(row.filepath)\n    \n    # Ensure the audio length is consistent\n    if len(audio) > CFG.audio_len:\n        audio = audio[:CFG.audio_len]\n    else:\n        # Pad with zeros if the audio is shorter\n        padding = CFG.audio_len - len(audio)\n        audio = np.pad(audio, (0, padding), 'constant')\n    \n    # Generate the spectrogram\n    spec = get_spectrogram(audio)\n    \n    # Display the audio\n    print(\"# Audio:\")\n    display(ipd.Audio(audio, rate=CFG.sample_rate))\n    \n    # Visualization\n    print(\"# Visualization:\")\n    fig, ax = plt.subplots(2, 1, figsize=(12, 6), sharex=True, tight_layout=True)\n    fig.suptitle(caption)\n    \n    # Waveform plot\n    lid.waveshow(audio, sr=CFG.sample_rate, ax=ax[0], color='b')\n    ax[0].set_title('Waveform')\n    \n    # Mel Spectrogram plot\n    lid.specshow(spec, sr=CFG.sample_rate, hop_length=CFG.hop_length, x_axis='time', y_axis='mel', fmin=CFG.fmin, fmax=CFG.fmax, cmap='viridis', ax=ax[1])\n    ax[1].set_title('Mel Spectrogram')\n    \n    plt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:30.098205Z","iopub.execute_input":"2024-08-22T12:12:30.101816Z","iopub.status.idle":"2024-08-22T12:12:30.125699Z","shell.execute_reply.started":"2024-08-22T12:12:30.101743Z","shell.execute_reply":"2024-08-22T12:12:30.124279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Sampleling","metadata":{}},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[5]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:30.133507Z","iopub.execute_input":"2024-08-22T12:12:30.137962Z","iopub.status.idle":"2024-08-22T12:12:32.836909Z","shell.execute_reply.started":"2024-08-22T12:12:30.137888Z","shell.execute_reply":"2024-08-22T12:12:32.835534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[9]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:32.838669Z","iopub.execute_input":"2024-08-22T12:12:32.839560Z","iopub.status.idle":"2024-08-22T12:12:35.477851Z","shell.execute_reply.started":"2024-08-22T12:12:32.839492Z","shell.execute_reply":"2024-08-22T12:12:35.476529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[14]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:35.480034Z","iopub.execute_input":"2024-08-22T12:12:35.485446Z","iopub.status.idle":"2024-08-22T12:12:37.791949Z","shell.execute_reply.started":"2024-08-22T12:12:35.485358Z","shell.execute_reply":"2024-08-22T12:12:37.790536Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[16]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:37.793939Z","iopub.execute_input":"2024-08-22T12:12:37.794742Z","iopub.status.idle":"2024-08-22T12:12:40.518235Z","shell.execute_reply.started":"2024-08-22T12:12:37.794678Z","shell.execute_reply":"2024-08-22T12:12:40.516913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[38]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:40.520329Z","iopub.execute_input":"2024-08-22T12:12:40.521461Z","iopub.status.idle":"2024-08-22T12:12:42.771864Z","shell.execute_reply.started":"2024-08-22T12:12:40.521368Z","shell.execute_reply":"2024-08-22T12:12:42.770516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[42]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:42.773502Z","iopub.execute_input":"2024-08-22T12:12:42.774294Z","iopub.status.idle":"2024-08-22T12:12:45.587486Z","shell.execute_reply.started":"2024-08-22T12:12:42.774234Z","shell.execute_reply":"2024-08-22T12:12:45.585932Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Select a specific row using `.loc[]` for clarity\nrow = df.iloc[50]\n\n# Display the selected audio and its visualization\ndisplay_audio(row)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:45.589827Z","iopub.execute_input":"2024-08-22T12:12:45.597844Z","iopub.status.idle":"2024-08-22T12:12:48.034327Z","shell.execute_reply.started":"2024-08-22T12:12:45.597766Z","shell.execute_reply":"2024-08-22T12:12:48.032568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Split","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedShuffleSplit\n\n# Use StratifiedShuffleSplit for better class balance in train/validation split\nsplitter = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=CFG.seed)\n\n# Perform the split while maintaining class distribution (if your target is imbalanced)\nfor train_idx, valid_idx in splitter.split(df, df['target']):\n    train_df = df.iloc[train_idx]\n    valid_df = df.iloc[valid_idx]\n\n# Print the number of samples in each set\nprint(f\"Num Train: {len(train_df)} | Num Valid: {len(valid_df)}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:48.036583Z","iopub.execute_input":"2024-08-22T12:12:48.071529Z","iopub.status.idle":"2024-08-22T12:12:48.177111Z","shell.execute_reply.started":"2024-08-22T12:12:48.071442Z","shell.execute_reply":"2024-08-22T12:12:48.175176Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Loader","metadata":{}},{"cell_type":"code","source":"# Decodes Audio\ndef build_decoder(with_labels=True, dim=1024):\n    def get_audio(filepath):\n        file_bytes = tf.io.read_file(filepath)\n        audio = tfio.audio.decode_vorbis(file_bytes)  # decode .ogg file\n        audio = tf.cast(audio, tf.float32)\n        if tf.shape(audio)[1] > 1:  # stereo -> mono\n            audio = audio[..., 0:1]\n        audio = tf.squeeze(audio, axis=-1)\n        return audio\n\n    def crop_or_pad(audio, target_len, pad_mode=\"constant\"):\n        audio_len = tf.shape(audio)[0]\n        diff_len = abs(\n            target_len - audio_len\n        )  # find difference between target and audio length\n        if audio_len < target_len:  # do padding if audio length is shorter\n            pad1 = tf.random.uniform([], maxval=diff_len, dtype=tf.int32)\n            pad2 = diff_len - pad1\n            audio = tf.pad(audio, paddings=[[pad1, pad2]], mode=pad_mode)\n        elif audio_len > target_len:  # do cropping if audio length is larger\n            idx = tf.random.uniform([], maxval=diff_len, dtype=tf.int32)\n            audio = audio[idx : (idx + target_len)]\n        return tf.reshape(audio, [target_len])\n\n    def apply_preproc(spec):\n        # Standardize\n        mean = tf.math.reduce_mean(spec)\n        std = tf.math.reduce_std(spec)\n        spec = tf.where(tf.math.equal(std, 0), spec - mean, (spec - mean) / std)\n\n        # Normalize using Min-Max\n        min_val = tf.math.reduce_min(spec)\n        max_val = tf.math.reduce_max(spec)\n        spec = tf.where(\n            tf.math.equal(max_val - min_val, 0),\n            spec - min_val,\n            (spec - min_val) / (max_val - min_val),\n        )\n        return spec\n \n    def get_target(target):\n        target = tf.reshape(target, [1])\n        target = tf.cast(tf.one_hot(target, CFG.num_classes), tf.float32)\n        target = tf.reshape(target, [CFG.num_classes])\n        return target\n\n    def decode(path):\n        # Load audio file\n        audio = get_audio(path)\n        # Crop or pad audio to keep a fixed length\n        audio = crop_or_pad(audio, dim)\n        # Audio to Spectrogram\n        spec = keras.layers.MelSpectrogram(\n            num_mel_bins=CFG.img_size[0],\n            fft_length=CFG.nfft,\n            sequence_stride=CFG.hop_length,\n            sampling_rate=CFG.sample_rate,\n        )(audio)\n        # Apply normalization and standardization\n        spec = apply_preproc(spec)\n        # Spectrogram to 3 channel image (for imagenet)\n        spec = tf.tile(spec[..., None], [1, 1, 3])\n        spec = tf.reshape(spec, [*CFG.img_size, 3])\n        return spec\n\n    def decode_with_labels(path, label):\n        label = get_target(label)\n        return decode(path), label\n\n    return decode_with_labels if with_labels else decode","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:48.180863Z","iopub.execute_input":"2024-08-22T12:12:48.183871Z","iopub.status.idle":"2024-08-22T12:12:48.237835Z","shell.execute_reply.started":"2024-08-22T12:12:48.183783Z","shell.execute_reply":"2024-08-22T12:12:48.236399Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Augmenters","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nimport keras_cv\n\ndef build_augmenter():\n    augmenters = [\n        keras_cv.layers.MixUp(alpha=0.3),  # Slightly lower alpha for more subtle MixUp\n        keras_cv.layers.RandomCutout(height_factor=(0.9, 1.0),  # Time-masking\n                                     width_factor=(0.05, 0.1)), \n        keras_cv.layers.RandomCutout(height_factor=(0.05, 0.1),  # Frequency-masking\n                                     width_factor=(0.9, 1.0)),\n        keras_cv.layers.RandomFlip(mode=\"horizontal\"),  # Simulates random inversion in time\n        keras_cv.layers.RandomRotation(factor=0.02),  # Small random rotation for better generalization\n    ]\n    \n    def augment(img, label):\n        data = {\"images\": img, \"labels\": label}\n        for augmenter in augmenters:\n            if tf.random.uniform([]) < 0.5:  # Increased probability of applying augmentation\n                data = augmenter(data, training=True)\n        return data[\"images\"], data[\"labels\"]\n    \n    return augment\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:48.240492Z","iopub.execute_input":"2024-08-22T12:12:48.242678Z","iopub.status.idle":"2024-08-22T12:12:48.276275Z","shell.execute_reply.started":"2024-08-22T12:12:48.242580Z","shell.execute_reply":"2024-08-22T12:12:48.275193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Pipeline","metadata":{}},{"cell_type":"code","source":"def build_dataset(paths, labels=None, batch_size=32, \n                  decode_fn=None, augment_fn=None, cache=True,\n                  augment=False, shuffle=2048):\n\n    if decode_fn is None:\n        decode_fn = build_decoder(labels is not None, dim=CFG.audio_len)\n\n    if augment_fn is None:\n        augment_fn = build_augmenter()\n        \n    AUTO = tf.data.experimental.AUTOTUNE\n    slices = (paths,) if labels is None else (paths, labels)\n    ds = tf.data.Dataset.from_tensor_slices(slices)\n    ds = ds.map(decode_fn, num_parallel_calls=AUTO)\n    ds = ds.cache() if cache else ds\n    if shuffle:\n        opt = tf.data.Options()\n        ds = ds.shuffle(shuffle, seed=CFG.seed)\n        opt.experimental_deterministic = False\n        ds = ds.with_options(opt)\n    ds = ds.batch(batch_size, drop_remainder=True)\n    ds = ds.map(augment_fn, num_parallel_calls=AUTO) if augment else ds\n    ds = ds.prefetch(AUTO)\n    return ds","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:48.281086Z","iopub.execute_input":"2024-08-22T12:12:48.281587Z","iopub.status.idle":"2024-08-22T12:12:48.307908Z","shell.execute_reply.started":"2024-08-22T12:12:48.281534Z","shell.execute_reply":"2024-08-22T12:12:48.306538Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build Train and Valid Dataloaders","metadata":{}},{"cell_type":"code","source":"# Training Dataset Setup\ntrain_paths = train_df['filepath'].values\ntrain_labels = train_df['target'].values\ntrain_ds = build_dataset(\n    paths=train_paths,\n    labels=train_labels,\n    batch_size=CFG.batch_size,\n    shuffle=True,\n    augment=CFG.augment\n)\n\n# Validation Dataset Setup\nvalid_paths = valid_df['filepath'].values\nvalid_labels = valid_df['target'].values\nvalid_ds = build_dataset(\n    paths=valid_paths,\n    labels=valid_labels,\n    batch_size=CFG.batch_size,\n    shuffle=False,\n    augment=False\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:48.309607Z","iopub.execute_input":"2024-08-22T12:12:48.310976Z","iopub.status.idle":"2024-08-22T12:12:52.216361Z","shell.execute_reply.started":"2024-08-22T12:12:48.310916Z","shell.execute_reply":"2024-08-22T12:12:52.215129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualization ","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nimport librosa.display as lid\n\ndef plot_batch(batch, rows=3, cols=3, label2name=None):\n    \"\"\"\n    Plot a batch of spectrograms with optional labels.\n\n    Parameters:\n    - batch (tuple): A tuple of (spectrograms, targets) or just (spectrograms).\n    - rows (int): Number of rows in the plot grid.\n    - cols (int): Number of columns in the plot grid.\n    - label2name (dict): Mapping from label index to label name.\n\n    Returns:\n    - None: Displays the plot.\n    \"\"\"\n    if isinstance(batch, (tuple, list)):\n        specs, targets = batch\n    else:\n        specs = batch\n        targets = None\n\n    plt.figure(figsize=(cols * 5, rows * 3))\n    for idx in range(rows * cols):\n        ax = plt.subplot(rows, cols, idx + 1)\n        lid.specshow(\n            np.array(specs[idx, ..., 0]),\n            sr=CFG.sample_rate,\n            hop_length=CFG.hop_length,\n            n_fft=CFG.nfft,\n            x_axis=\"time\",\n            y_axis=\"mel\",\n            cmap=\"coolwarm\"\n        )\n\n        if targets is not None:\n            label = targets[idx].numpy().argmax()\n            label_name = label2name.get(label, f\"Label {label}\")\n            ax.set_title(label_name, fontsize=12)\n        ax.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:52.221796Z","iopub.execute_input":"2024-08-22T12:12:52.222414Z","iopub.status.idle":"2024-08-22T12:12:52.245011Z","shell.execute_reply.started":"2024-08-22T12:12:52.222356Z","shell.execute_reply":"2024-08-22T12:12:52.243617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sample a batch from the training dataset\nsample_ds = train_ds.take(100)\n\n# Get a single batch from the dataset\nbatch = next(iter(sample_ds))\n\n# Plot the batch\nplot_batch(batch, rows=3, cols=3, label2name=CFG.label2name)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:12:52.246729Z","iopub.execute_input":"2024-08-22T12:12:52.247492Z","iopub.status.idle":"2024-08-22T12:13:15.581869Z","shell.execute_reply.started":"2024-08-22T12:12:52.247416Z","shell.execute_reply":"2024-08-22T12:13:15.580592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Modeling","metadata":{}},{"cell_type":"code","source":"# Define input layer\ninput_shape = (None, None, 3)\ninp = tf.keras.layers.Input(shape=input_shape)\n\n# Load pretrained EfficientNetV2 backbone\nbackbone = keras_cv.models.EfficientNetV2Backbone.from_preset(CFG.preset)\n\n# Create ImageClassifier using the backbone\nclassifier = keras_cv.models.ImageClassifier(\n    backbone=backbone,\n    num_classes=CFG.num_classes,\n    name=\"image_classifier\"\n)\n\n# Build the model\nout = classifier(inp)\nmodel = tf.keras.models.Model(inputs=inp, outputs=out)\n\n# Compile the model with optimizer, loss function, and metrics\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(),\n    loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.02),\n    metrics=[\n        tf.keras.metrics.AUC(name='auc'),\n        tf.keras.metrics.CategoricalAccuracy(name='accuracy')\n    ]\n)\n\n# Display model summary\nmodel.summary()\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:13:15.587726Z","iopub.execute_input":"2024-08-22T12:13:15.588187Z","iopub.status.idle":"2024-08-22T12:13:23.243314Z","shell.execute_reply.started":"2024-08-22T12:13:15.588141Z","shell.execute_reply":"2024-08-22T12:13:23.241962Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# LR Schedule","metadata":{}},{"cell_type":"code","source":"def get_lr_callback(batch_size=8, mode='cos', epochs=10, plot=False):\n    \"\"\"\n    Create a learning rate scheduler callback.\n\n    Parameters:\n    - batch_size (int): The batch size used in training.\n    - mode (str): The scheduling mode ('cos', 'exp', 'step').\n    - epochs (int): Total number of epochs for training.\n    - plot (bool): Whether to plot the learning rate schedule.\n\n    Returns:\n    - tf.keras.callbacks.LearningRateScheduler: Learning rate scheduler callback.\n    \"\"\"\n    lr_start, lr_max, lr_min = 5e-5, 8e-6 * batch_size, 1e-5\n    lr_ramp_ep, lr_sus_ep, lr_decay = 3, 0, 0.75\n\n    def lrfn(epoch):  # Learning rate update function\n        if epoch < lr_ramp_ep:\n            lr = (lr_max - lr_start) / lr_ramp_ep * epoch + lr_start\n        elif epoch < lr_ramp_ep + lr_sus_ep:\n            lr = lr_max\n        elif mode == 'exp':\n            lr = (lr_max - lr_min) * lr_decay**(epoch - lr_ramp_ep - lr_sus_ep) + lr_min\n        elif mode == 'step':\n            lr = lr_max * lr_decay**((epoch - lr_ramp_ep - lr_sus_ep) // 2)\n        elif mode == 'cos':\n            decay_total_epochs = epochs - lr_ramp_ep - lr_sus_ep\n            decay_epoch_index = epoch - lr_ramp_ep - lr_sus_ep\n            phase = math.pi * decay_epoch_index / decay_total_epochs\n            lr = (lr_max - lr_min) * 0.5 * (1 + math.cos(phase)) + lr_min\n        else:\n            raise ValueError(f\"Unknown mode: {mode}\")\n        return lr\n\n    if plot:  # Plot learning rate schedule if plot is True\n        plt.figure(figsize=(10, 5))\n        lr_schedule = [lrfn(epoch) for epoch in range(epochs)]\n        plt.plot(range(epochs), lr_schedule, marker='o')\n        plt.xlabel('Epoch')\n        plt.ylabel('Learning Rate')\n        plt.title('Learning Rate Schedule')\n        plt.grid(True)\n        plt.show()\n\n    return tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)\n\n# Example usage\n# lr_callback = get_lr_callback(batch_size=32, mode='cos', epochs=20, plot=True)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:13:23.244994Z","iopub.execute_input":"2024-08-22T12:13:23.245374Z","iopub.status.idle":"2024-08-22T12:13:23.259830Z","shell.execute_reply.started":"2024-08-22T12:13:23.245333Z","shell.execute_reply":"2024-08-22T12:13:23.258561Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lr_cb = get_lr_callback(CFG.batch_size, plot=True)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:13:23.261918Z","iopub.execute_input":"2024-08-22T12:13:23.262377Z","iopub.status.idle":"2024-08-22T12:13:23.509245Z","shell.execute_reply.started":"2024-08-22T12:13:23.262333Z","shell.execute_reply":"2024-08-22T12:13:23.507886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Checkpoint","metadata":{}},{"cell_type":"code","source":"ckpt_cb = keras.callbacks.ModelCheckpoint(\"best_model.weights.h5\",\n                                         monitor='val_auc',\n                                         save_best_only=True,\n                                         save_weights_only=True,\n                                         mode='max')","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:13:23.510710Z","iopub.execute_input":"2024-08-22T12:13:23.511100Z","iopub.status.idle":"2024-08-22T12:13:23.516973Z","shell.execute_reply.started":"2024-08-22T12:13:23.511057Z","shell.execute_reply":"2024-08-22T12:13:23.515763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(\n    train_ds, \n    validation_data=valid_ds, \n    epochs=CFG.epochs,\n    callbacks=[lr_cb, ckpt_cb], \n    verbose=1\n)","metadata":{"execution":{"iopub.status.busy":"2024-08-22T12:13:23.518364Z","iopub.execute_input":"2024-08-22T12:13:23.518818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get the AUC scores from the validation history\nval_auc = history.history[\"val_auc\"]\n\n# Find the index of the maximum AUC score\nbest_epoch = np.argmax(val_auc)\n\n# Get the best AUC score\nbest_score = val_auc[best_epoch]\n\n# Print the results\nprint('>>> Best AUC:', best_score)\nprint('>>> Best Epoch:', best_epoch + 1)  # Adding 1 to convert from 0-based index to 1-based epoch number\n","metadata":{},"execution_count":null,"outputs":[]}]}