{"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":91844,"databundleVersionId":11361821,"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\n! pip install pandarallel\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\n# for dirname, _, filenames in os.walk('/kaggle/input/birdclef-2025/train_audio'):\n    # for filename in filenames:\n        # print(os.path.join(dirname, filename))\n        # pass\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":"06865f8c-efd2-44a8-9292-7e9f22e0885a","_cell_guid":"c0492972-a264-4978-981e-127588cc3709","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:02:53.692418Z","iopub.execute_input":"2025-05-05T19:02:53.692743Z","iopub.status.idle":"2025-05-05T19:03:03.525153Z","shell.execute_reply.started":"2025-05-05T19:02:53.692707Z","shell.execute_reply":"2025-05-05T19:03:03.523578Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels = {'brtpar1', '1139490', 'compau', 'chbant1', 'yehcar1', 'yecspi2', 'watjac1', 'grasal4', 'grbhaw1', \n          'yebfly1', 'neocor', '81930', 'spbwoo1', '64862', 'grepot1', 'ruther1', 'banana', 'whttro1', \n          '1462711', '42087', '66531', 'soulap1', 'amakin1', '41970', '65373', '714022', 'bafibi1', 'blcant4', \n          'rutjac1', 'plbwoo1', 'anhing', 'yehbla2', '21211', 'recwoo1', 'blbgra1', 'creoro1', 'shtfly1', \n          'amekes', 'blchaw1', '21116', '566513', 'bugtan', 'strcuc1', '1564122', '1462737', 'purgal2', \n          'socfly1', 'gohman1', 'gycwor1', 'bubwre1', 'blhpar1', '65336', 'solsan', '134933', '24292', \n          '42113', 'plukit1', 'savhaw1', 'sobtyr1', 'chfmac1', 'yebsee1', '66016', 'blbwre1', 'mastit1', \n          'smbani', 'whfant1', 'strfly1', 'roahaw', 'rumfly1', '476537', 'butsal1', 'bucmot3', 'colcha1', \n          'bobfly1', '67082', 'rebbla1', 'pavpig2', '1192948', 'whbman1', 'verfly', 'eardov1', 'norscr1', \n          'rinkin1', '67252', 'greibi1', 'greegr', 'cattyr', 'laufal1', 'trokin', 'grekis', 'crebob1', \n          'bubcur1', 'fotfly', 'palhor2', '476538', '24322', 'tropar', 'whwswa1', 'yercac1', '517119', \n          '24272', 'cocher1', 'labter1', 'bicwre1', 'compot1', 'olipic1', 'blcjay1', 'colara1', 'spepar1', \n          'cregua1', 'cargra1', '22976', 'plctan1', '715170', 'leagre', '22973', 'bkmtou1', 'yelori1', \n          'trsowl', 'strher', 'ragmac1', 'yeofly1', '548639', 'tbsfin1', '135045', '65344', 'bkcdon', \n          'stbwoo2', 'piepuf1', '868458', '963335', 'blctit1', 'saffin', 'rtlhum', 'royfly1', '66893', \n          'rutpuf1', 'linwoo1', 'wbwwre1', 'srwswa1', '126247', 'gretin1', 'grnkin', 'littin1', 'secfly1', \n          '41778', '528041', 'bbwduc', 'greani1', 'rubsee1', 'orcpar', 'rosspo1', 'yebela1', '47067', \n          'crcwoo1', '65349', 'snoegr', 'gybmar', 'thbeup1', '66578', 'turvul', 'rugdov', 'baymac', \n          'speowl1', 'cocwoo1', 'cotfly1', 'y00678', '65419', 'bobher1', '52884', '41663', '22333', \n          'piwtyr1', '21038', '787625', 'rufmot1', '65962', 'paltan1', '48124', '555142', '65547', \n          'crbtan1', '1194042', 'ywcpar', 'shghum1', 'cinbec1', 'thlsch3', '1346504', '555086', 'sahpar1', \n          'grysee1', 'blkvul', '523060', 'strowl1', 'whbant1', 'whmtyr1', '65448', 'ampkin1', 'whtdov', \n          'yectyr1', '42007', '46010', 'pirfly1', 'woosto', 'babwar', '50186'}\nprint(len(labels))","metadata":{"_uuid":"7d1582c7-b934-4308-bef1-2cb926448b35","_cell_guid":"d12fcec0-c322-427d-94c5-a35fb6545838","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:03:03.526836Z","iopub.execute_input":"2025-05-05T19:03:03.527738Z","iopub.status.idle":"2025-05-05T19:03:03.540863Z","shell.execute_reply.started":"2025-05-05T19:03:03.527685Z","shell.execute_reply":"2025-05-05T19:03:03.538814Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport os\nfrom IPython.display import Audio\nfrom scipy.io import wavfile\nimport soundfile as sf\nimport tensorflow as tf\nimport tensorflow_hub as hub\nimport warnings\nwarnings.filterwarnings('ignore')\nfrom sklearn.preprocessing import OneHotEncoder\nfrom sklearn.model_selection import train_test_split\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom sklearn.metrics import classification_report\nimport scipy.signal\nfrom tqdm import tqdm\nflag = 0","metadata":{"_uuid":"f2cccdd0-2b59-4596-8461-962c18bb52b7","_cell_guid":"7e2e0e2d-61f6-4f04-926a-6d3213cde37c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:03:03.542421Z","iopub.execute_input":"2025-05-05T19:03:03.542986Z","iopub.status.idle":"2025-05-05T19:03:24.472911Z","shell.execute_reply.started":"2025-05-05T19:03:03.542951Z","shell.execute_reply":"2025-05-05T19:03:24.471753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def ensure_sample_rate(original_sample_rate, waveform, desired_sample_rate=22000):\n    if original_sample_rate != desired_sample_rate:\n        desired_length = int(\n            round(float(len(waveform))/original_sample_rate * desired_sample_rate))\n        waveform = scipy.signal.resample(waveform, desired_length)\n    return desired_sample_rate, waveform","metadata":{"_uuid":"50f45f5a-29ad-4b38-842e-763412cd51ea","_cell_guid":"6bfaabde-4d94-47b0-b0d5-ca75ba8e961e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:03:24.474345Z","iopub.execute_input":"2025-05-05T19:03:24.475174Z","iopub.status.idle":"2025-05-05T19:03:24.480906Z","shell.execute_reply.started":"2025-05-05T19:03:24.475122Z","shell.execute_reply":"2025-05-05T19:03:24.479583Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_audio(filename):\n    wav_data, sample_rate = sf.read(file=filename, dtype=np.int16)\n    if len(wav_data.shape) > 1:\n        wav_data = np.mean(wav_data, axis=1)\n    sample_rate, wav_data = ensure_sample_rate(sample_rate, wav_data)\n    return sample_rate, wav_data","metadata":{"_uuid":"815b5ee9-300a-46c7-a58f-b34ef3e1b410","_cell_guid":"864fe43d-6dc2-4b7f-9aaf-eae3ca7506c0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:03:24.482004Z","iopub.execute_input":"2025-05-05T19:03:24.482284Z","iopub.status.idle":"2025-05-05T19:03:24.504933Z","shell.execute_reply.started":"2025-05-05T19:03:24.482261Z","shell.execute_reply":"2025-05-05T19:03:24.503707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\n# Define the root directory containing the audio files\nroot_dir = '/kaggle/input/birdclef-2025/train_audio' # Adjust if your path is different\n\n# List to store the data for the DataFrame\naudio_data_list = []\n\n# Walk through the directory structure\nfor dirname, _, filenames in os.walk(root_dir):\n    # Skip the root directory itself if it doesn't contain class folders directly\n    # (Adjust this condition if your structure is different)\n    if dirname == root_dir:\n        continue\n\n    # Extract the class name (subdirectory name)\n    # os.path.basename gets the last part of the directory path\n    class_name = os.path.basename(dirname)\n\n    # Iterate through files in the current directory\n    for filename in filenames:\n        # Construct the full path to the audio file\n        full_path = os.path.join(dirname, filename)\n\n        # Append the file path and its class to the list\n        audio_data_list.append([full_path, class_name])\n        # You can remove the print statement if you don't need it anymore\n        # print(full_path) # Optional: print the path as it's processed\n\n# Create the Pandas DataFrame\naudio_dataframe = pd.DataFrame(audio_data_list, columns=[\"audio_path\", \"class\"])\n\n# Display the first few rows of the DataFrame (optional)\nprint(audio_dataframe.head())\n\n# Display the shape of the DataFrame (optional)\nprint(f\"\\nDataFrame shape: {audio_dataframe.shape}\")","metadata":{"_uuid":"4121086d-c342-4272-89c9-d3dc8774759d","_cell_guid":"1dca9713-a909-4e49-b6b0-82330ce3d220","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:03:24.508849Z","iopub.execute_input":"2025-05-05T19:03:24.509189Z","iopub.status.idle":"2025-05-05T19:04:06.694893Z","shell.execute_reply.started":"2025-05-05T19:03:24.509164Z","shell.execute_reply":"2025-05-05T19:04:06.693670Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = audio_dataframe.iloc[0]\n\nfor i in audio_dataframe['class'].unique():\n    df_sub = audio_dataframe[audio_dataframe['class'] == i]\n    num_rows = 2\n    # print(len(df_sub.index))\n    if len(df_sub.index) < num_rows:\n        num_rows = len(df_sub.index)\n    # print(num_rows)\n    df_sub = audio_dataframe[audio_dataframe['class'] == i].iloc[:num_rows]\n    # print(df_sub)\n    df = pd.concat([df,df_sub])\ndf['class']\n\n# flag = 0","metadata":{"_uuid":"e22d37fd-03e9-47dd-85d6-0df6a5124199","_cell_guid":"e34eed76-cb7b-440e-9833-13cee54e7eaa","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:04:06.697022Z","iopub.execute_input":"2025-05-05T19:04:06.697473Z","iopub.status.idle":"2025-05-05T19:04:08.072097Z","shell.execute_reply.started":"2025-05-05T19:04:06.697432Z","shell.execute_reply":"2025-05-05T19:04:08.071192Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport concurrent.futures\nfrom tqdm.auto import tqdm\nimport time # Just for mock function\nimport os # To get cpu count\nif flag == 0:\n    \n    # Get the list of paths to process\n    paths_to_process = df['audio_path'].tolist()\n\n    # Determine number of workers (e.g., number of CPU cores)\n    # Adjust based on memory constraints and task type (CPU vs I/O bound)\n    num_workers = os.cpu_count()\n    print(f\"Using {num_workers} workers.\")\n\n    results = [None] * len(paths_to_process) # Preallocate results list\n\n    print(\"Starting parallel processing with concurrent.futures...\")\n    # Use ProcessPoolExecutor for CPU-bound tasks\n    with concurrent.futures.ProcessPoolExecutor(max_workers=num_workers) as executor:\n        # Use executor.map to apply the function in parallel\n        # Wrap executor.map with tqdm for a progress bar\n        # executor.map preserves the order of the input iterable\n        future_to_path = {executor.submit(read_audio, path): i for i, path in enumerate(paths_to_process)}\n\n        for future in tqdm(concurrent.futures.as_completed(future_to_path), total=len(paths_to_process), desc=\"Reading audio files\"):\n            index = future_to_path[future]\n            try:\n                results[index] = future.result()\n            except Exception as exc:\n                print(f'Path at index {index} generated an exception: {exc}')\n                results[index] = None # Or some other error indicator\n    flag = 1\n    print(\"Finished parallel processing.\")\n\n    # Assign the results back to the DataFrame\n    df['audio_data'] = results\n    df.to_pickle('/kaggle/working/df.pk1')\n    print(df.head())","metadata":{"_uuid":"a8b30ee5-27f6-4929-8939-116228480d8e","_cell_guid":"977fe82e-9a6e-4262-858d-9bdf56e4bd1a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:04:08.073103Z","iopub.execute_input":"2025-05-05T19:04:08.073406Z","iopub.status.idle":"2025-05-05T19:06:57.503426Z","shell.execute_reply.started":"2025-05-05T19:04:08.073382Z","shell.execute_reply":"2025-05-05T19:06:57.501944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import re\n\ndef audio_data_unfucker(fucked_tuple: tuple) -> list:\n        assert(type(fucked_tuple[1]) != int)\n        return fucked_tuple[1]","metadata":{"_uuid":"c4191425-c246-40c9-9374-e5ba95ff31ef","_cell_guid":"a0870745-efe2-43a5-84ee-eb84e58b272d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:06:57.504821Z","iopub.execute_input":"2025-05-05T19:06:57.505578Z","iopub.status.idle":"2025-05-05T19:06:57.512838Z","shell.execute_reply.started":"2025-05-05T19:06:57.505527Z","shell.execute_reply":"2025-05-05T19:06:57.510902Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if flag == 1:\n    pass\nelse:\n    flag = 1\ndf = pd.read_pickle('/kaggle/working/df.pk1')\ndf = df.iloc[2:]\nprint(df.columns)\ndf['audio_data'] = df['audio_data'].apply(audio_data_unfucker)","metadata":{"_uuid":"e5124c66-5846-45c9-b0de-c024b252b875","_cell_guid":"4d65d6a6-763d-400d-b9ed-ba0683348fda","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:06:57.514246Z","iopub.execute_input":"2025-05-05T19:06:57.514696Z","iopub.status.idle":"2025-05-05T19:07:00.337993Z","shell.execute_reply.started":"2025-05-05T19:06:57.514664Z","shell.execute_reply":"2025-05-05T19:07:00.336826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"audio_data = df['audio_data'].copy()","metadata":{"_uuid":"3a81fb7f-2eed-4a4d-a30d-989cb47f221d","_cell_guid":"a645c135-06d9-4af0-abf6-31e6fdf726f5","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:07:00.339192Z","iopub.execute_input":"2025-05-05T19:07:00.339571Z","iopub.status.idle":"2025-05-05T19:07:00.344615Z","shell.execute_reply.started":"2025-05-05T19:07:00.339543Z","shell.execute_reply":"2025-05-05T19:07:00.343344Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"max_len = 1000  # Your desired fixed length\nprocessed_audio = []\nprocessed_labels = []\n\nprint(f\"Processing audio data with max_len = {max_len}\")\nprint(f\"Original DataFrame has {len(df)} rows.\")\n\n# Iterate through the DataFrame rows using itertuples (generally faster)\n# 'Index' is the DataFrame index, 'audio_data' and 'label' match column names\nfor row in tqdm(df.itertuples(), total=len(df), desc=\"Processing Audio\"):\n    # --- Get data for the current row ---\n    # Use getattr for robustness if column names might vary slightly\n    # print(row)\n    original_seq = getattr(row, 'audio_data')\n    original_label = getattr(row, '_3') # Fetch the label\n\n    # Ensure the sequence is a numpy array with the desired dtype\n    # This handles cases where 'audio_data' might contain lists\n    seq = np.array(original_seq, dtype=np.float32)\n    current_len = len(seq)\n\n    # --- Case 1: Sequence is shorter than max_len ---\n    if current_len < max_len:\n        padding_needed = max_len - current_len\n        # Use np.pad which can be cleaner for padding\n        padded_seq = np.pad(seq, (0, padding_needed), mode='constant', constant_values=0)\n        processed_audio.append(padded_seq)\n        processed_labels.append(original_label) # Append the original label\n\n    # --- Case 2: Sequence is exactly max_len ---\n    elif current_len == max_len:\n        processed_audio.append(seq) # No padding or truncation needed\n        processed_labels.append(original_label) # Append the original label\n\n    # --- Case 3: Sequence is longer than max_len ---\n    else:\n        # Calculate how many full chunks we can get\n        num_full_chunks = current_len // max_len\n\n        # Iterate through the sequence, extracting non-overlapping chunks\n        for i in range(num_full_chunks):\n            start_index = i * max_len\n            end_index = start_index + max_len\n            chunk = seq[start_index:end_index]\n            processed_audio.append(chunk)\n            # Append the SAME original label for EACH chunk\n            processed_labels.append(original_label)\n\n        # --- Handle the remainder (the part left over after full chunks) ---\n        remainder_len = current_len % max_len\n        if remainder_len > 0:\n            # Extract the remainder\n            remainder_start_index = num_full_chunks * max_len\n            remainder_chunk = seq[remainder_start_index:]\n\n            # Pad the remainder to max_len\n            padding_needed = max_len - remainder_len\n            padded_remainder = np.pad(remainder_chunk, (0, padding_needed), mode='constant', constant_values=0)\n\n            processed_audio.append(padded_remainder)\n            # Append the SAME original label for the padded remainder chunk\n            processed_labels.append(original_label)\n\n\n# --- Final Output ---\n# Convert lists to numpy arrays (common practice for ML/DL)\nfinal_audio_array = np.array(processed_audio)\n# Labels can be kept as a list or converted to numpy array/pandas Series\nfinal_labels = processed_labels # Or np.array(processed_labels)\n\nprint(f\"\\nFinished processing.\")\nprint(f\"Resulting audio array shape: {final_audio_array.shape}\")\n# Based on example: (1+1+2+1+1) = 6 samples -> (6, 100)\nprint(f\"Number of resulting labels: {len(final_labels)}\")\n# Based on example: 6 labels -> ['Short', 'Exact', 'Long', 'Long', 'Long', 'Short_2']\n# print(\"Example processed labels:\", final_labels)","metadata":{"_uuid":"532a3805-4956-4a69-8092-9cc4ecb70f22","_cell_guid":"88fd1701-e577-4f4d-819b-40827b85c463","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:07:00.345952Z","iopub.execute_input":"2025-05-05T19:07:00.346292Z","iopub.status.idle":"2025-05-05T19:07:04.981180Z","shell.execute_reply.started":"2025-05-05T19:07:00.346264Z","shell.execute_reply":"2025-05-05T19:07:04.979522Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\n\n\nprint(f\"Original audio array shape: {final_audio_array.shape}\")\nprint(f\"Original number of labels: {len(final_labels)}\")\n\n\n### VARIABLE TO CHANGE\nlimit_per_label = 1000\n\n# 1. Create a DataFrame with labels and original indices\n#    This avoids putting the large audio array into the DataFrame, saving memory.\nprint(\"\\nCreating temporary DataFrame with labels and indices...\")\nindices_df = pd.DataFrame({\n    'label': final_labels,\n    'original_index': np.arange(len(final_labels)) # Store 0, 1, 2, ... N-1\n})\n\n# 2. Group by label and select the first 'limit_per_label' indices for each group\nprint(f\"Grouping by label and selecting up to {limit_per_label} indices per group...\")\n# .head(n) conveniently takes min(n, group_size) automatically\nselected_indices_df = indices_df.groupby('label', observed=True).head(limit_per_label)\n# 'observed=True' can speed up grouping if labels are categorical\n\n# --- Alternative: Random Sampling (if you don't want the *first* 100) ---\n# def sample_or_head(group, n):\n#     group_size = len(group)\n#     # Sample if group is large enough, otherwise take all (head)\n#     return group.sample(n=min(group_size, n), random_state=42) # Use random_state for reproducibility\n#\n# selected_indices_df = indices_df.groupby('label', observed=True).apply(sample_or_head, n=limit_per_label).reset_index(drop=True)\n# print(f\"Grouping by label and randomly sampling up to {limit_per_label} indices per group...\")\n# --- End Alternative ---\n\n\n# 3. Get the selected original indices\nselected_indices = selected_indices_df['original_index'].values\n\n# Ensure the indices are sorted if you want the final array order to be somewhat grouped by label\n# This is optional, .head() preserves original relative order within groups.\n# selected_indices = np.sort(selected_indices)\n\nprint(f\"Total indices selected: {len(selected_indices)}\")\n\n# 4. Use the selected indices to filter your original arrays\nprint(\"Filtering original audio array and labels using selected indices...\")\nlimited_audio_array = final_audio_array[selected_indices]\n\n# Ensure final_labels is a numpy array if it isn't already for fancy indexing\nfinal_labels_array = np.array(final_labels)\nlimited_labels_array = final_labels_array[selected_indices]\n\n# --- Verification (Optional) ---\nprint(\"\\n--- Verification ---\")\nprint(f\"Limited audio array shape: {limited_audio_array.shape}\")\nprint(f\"Limited labels array length: {len(limited_labels_array)}\")\n\n# Check counts per label in the limited set\nunique_labels, counts = np.unique(limited_labels_array, return_counts=True)\nprint(\"Counts per label in the limited dataset:\")\nfor label, count in zip(unique_labels, counts):\n    print(f\"  Label '{label}': {count} samples\")\n    if count > limit_per_label:\n        print(f\"  WARNING: Label '{label}' has more than {limit_per_label} samples!\") # Should not happen with .head()\n\nprint(\"\\nDownsampling complete.\")\n\n# Now use 'limited_audio_array' and 'limited_labels_array' for further steps","metadata":{"_uuid":"992bd950-31c2-419f-b60e-7bedf893bf28","_cell_guid":"a2f4c61b-20fe-438a-9c86-e120ac966b44","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:07:04.982636Z","iopub.execute_input":"2025-05-05T19:07:04.982950Z","iopub.status.idle":"2025-05-05T19:07:05.700710Z","shell.execute_reply.started":"2025-05-05T19:07:04.982927Z","shell.execute_reply":"2025-05-05T19:07:05.693219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_yamnet = hub.load('https://tfhub.dev/google/yamnet/1')","metadata":{"_uuid":"47139e48-33e0-4a9d-897d-eec7987fad55","_cell_guid":"5d537e1d-6936-4c82-becb-f7edcbdfae58","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:07:05.702351Z","iopub.execute_input":"2025-05-05T19:07:05.702832Z","iopub.status.idle":"2025-05-05T19:07:12.259844Z","shell.execute_reply.started":"2025-05-05T19:07:05.702792Z","shell.execute_reply":"2025-05-05T19:07:12.258639Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@tf.function\ndef get_embedding(waveform):\n    waveform = waveform / tf.int16.max\n    _, embedding, _ = model_yamnet(waveform)\n    return embedding\n\naudio_embeddings = []\nfor wav in tqdm(limited_audio_array, desc=\"Generating Audio Embeddings\"):\n    emb = get_embedding(wav)\n    audio_embeddings.append(emb.numpy())","metadata":{"_uuid":"74922e51-60fc-4d4b-8d64-9282189624b9","_cell_guid":"072300bf-dab1-40f2-ba2d-90a0ef11906a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:07:12.260691Z","iopub.execute_input":"2025-05-05T19:07:12.260964Z","iopub.status.idle":"2025-05-05T19:32:08.882755Z","shell.execute_reply.started":"2025-05-05T19:07:12.260941Z","shell.execute_reply":"2025-05-05T19:32:08.881445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = np.array(audio_embeddings) # Shape will be (N, 1024)\nX_reshaped = np.squeeze(X, axis=1) # Specify axis=1 to remove the middle dimension\nprint(f\"Generated embeddings array X with shape: {X_reshaped.shape}\")\n\n# Ensure you have the corresponding labels (length N)\ny = limited_labels_array","metadata":{"_uuid":"6978e048-de39-46ec-9e1b-2b8d0a94c65e","_cell_guid":"072fdaf9-4b36-4b9a-ba89-3e5ed7d926d3","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:32:08.884107Z","iopub.execute_input":"2025-05-05T19:32:08.884497Z","iopub.status.idle":"2025-05-05T19:32:09.425010Z","shell.execute_reply.started":"2025-05-05T19:32:08.884455Z","shell.execute_reply":"2025-05-05T19:32:09.423837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n# --- Label Encoding ---\nprint(\"\\nEncoding labels...\")\n# 1. Encode string/object labels to integers (0, 1, 2, ...)\nlabel_encoder = LabelEncoder()\ny_integer_encoded = label_encoder.fit_transform(y)\n\n# 2. Determine the number of unique classes\nnum_classes = len(label_encoder.classes_)\nprint(f\"Found {num_classes} unique classes: {label_encoder.classes_}\")\n\n# 3. Convert integer labels to one-hot encoding\ny_one_hot = tf.keras.utils.to_categorical(y_integer_encoded, num_classes=num_classes)\nprint(f\"One-hot encoded labels shape: {y_one_hot.shape}\") # Should be (N, num_classes)\n\n# --- Train/Test Split ---\nprint(\"\\nSplitting data into training and testing sets...\")\nxtrain, xtest, ytrain_one_hot, ytest_one_hot = train_test_split(\n    X_reshaped,                      # Your embeddings array (N, 1024)\n    y_one_hot,              # Your one-hot encoded labels (N, num_classes)\n    test_size=0.2,          # Fraction for the test set\n    random_state=42,        # For reproducibility\n    stratify=y_integer_encoded # IMPORTANT: Stratify based on original integer labels\n                               # to ensure class balance in train/test sets\n)\n\nprint(f\"xtrain shape: {xtrain.shape}\") # (N * 0.8, 1024)\nprint(f\"ytrain_one_hot shape: {ytrain_one_hot.shape}\") # (N * 0.8, num_classes)\nprint(f\"xtest shape: {xtest.shape}\") # (N * 0.2, 1024)\nprint(f\"ytest_one_hot shape: {ytest_one_hot.shape}\") # (N * 0.2, num_classes)","metadata":{"_uuid":"96703e48-f9a8-4045-8758-44dcf03a392f","_cell_guid":"da422fe0-2261-4a53-8774-3d2cd857e466","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T19:32:09.426202Z","iopub.execute_input":"2025-05-05T19:32:09.426570Z","iopub.status.idle":"2025-05-05T19:32:10.599738Z","shell.execute_reply.started":"2025-05-05T19:32:09.426544Z","shell.execute_reply":"2025-05-05T19:32:10.598663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\nDefining and compiling the model...\")\n# --- Define Model ---\nmodel = models.Sequential([\n    # Input shape is now (1024,) after averaging embeddings\n    layers.Input(shape=(1024,)),\n    # Flatten layer is not needed if input is 1D before Dense\n    # layers.Flatten(), # Remove this\n    layers.Dense(200, activation='relu'),\n    layers.Dropout(0.1),\n    layers.Dense(200, activation='relu'),\n    layers.Dropout(0.1),\n    layers.Dense(200, activation='relu'),\n    layers.Dropout(0.1),\n    layers.Dense(200, activation='relu'),\n    # Output layer must have 'num_classes' units\n    layers.Dense(num_classes, activation='softmax')\n])\n\nmodel.summary()\n\n# --- Compile Model ---\n# Using categorical_crossentropy because ytrain is one-hot encoded\nmodel.compile(optimizer='adam',\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])\n\nprint(\"Model compiled successfully.\")","metadata":{"_uuid":"c5c31e39-febd-40c2-ae5e-f3263d92a9d0","_cell_guid":"c0d42a1c-377d-4870-a195-8ab8fb3b7380","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T20:45:36.818592Z","iopub.execute_input":"2025-05-05T20:45:36.819137Z","iopub.status.idle":"2025-05-05T20:45:36.938717Z","shell.execute_reply.started":"2025-05-05T20:45:36.819099Z","shell.execute_reply":"2025-05-05T20:45:36.937467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping\n\nearly_stopping = EarlyStopping(\n    monitor='val_loss',  # Metric to monitor (usually validation loss or accuracy)\n    patience=3,         # Number of epochs with no improvement after which training will be stopped\n    verbose=1,           # Set to 1 to print messages when stopping happens\n    mode='min',          # 'min' for loss/error metrics, 'max' for accuracy metrics\n    restore_best_weights=True  # Restore model weights from the epoch with the best value of the monitored quantity.\n)","metadata":{"_uuid":"c587b256-b5b9-4798-b931-57815075d174","_cell_guid":"626c0064-1aa5-4726-8e0a-2bef034c3163","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T20:45:37.134509Z","iopub.execute_input":"2025-05-05T20:45:37.134916Z","iopub.status.idle":"2025-05-05T20:45:37.141763Z","shell.execute_reply.started":"2025-05-05T20:45:37.134883Z","shell.execute_reply":"2025-05-05T20:45:37.140064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Train Model ---\nprint(\"\\nStarting model training...\")\nepochs = 100\nhistory = model.fit(\n    xtrain,\n    ytrain_one_hot,\n    epochs=epochs,\n    validation_split=0.1, # Optional: use part of training data for validation during training\n    callbacks=[early_stopping]\n    # Or use validation_data=(xtest, ytest_one_hot) - be careful not to \"tune\" on test set\n)\n\nprint(\"Model training finished.\")\n\n# --- Evaluate Model (Optional) ---\nprint(\"\\nEvaluating model on the test set...\")\nloss, accuracy = model.evaluate(xtest, ytest_one_hot, verbose=0)\nprint(f\"Test Loss: {loss:.4f}\")\nprint(f\"Test Accuracy: {accuracy:.4f}\")","metadata":{"_uuid":"06142eb2-30f2-4552-9f83-bd1ba457ffe4","_cell_guid":"e17912b0-2a76-4801-ab44-24280d799315","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-05-05T20:45:38.503775Z","iopub.execute_input":"2025-05-05T20:45:38.504237Z","iopub.status.idle":"2025-05-05T20:53:51.119087Z","shell.execute_reply.started":"2025-05-05T20:45:38.504203Z","shell.execute_reply":"2025-05-05T20:53:51.117684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training & validation accuracy values\nplt.figure(figsize=(14, 5))\n\n# ---- Accuracy ----\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Training Accuracy')\nif 'val_accuracy' in history.history:\n    plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.title('Model Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True)\n\n# ---- Loss ----\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Training Loss')\nif 'val_loss' in history.history:\n    plt.plot(history.history['val_loss'], label='Validation Loss')\nplt.title('Model Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:53:51.121623Z","iopub.execute_input":"2025-05-05T20:53:51.121940Z","iopub.status.idle":"2025-05-05T20:53:51.691523Z","shell.execute_reply.started":"2025-05-05T20:53:51.121915Z","shell.execute_reply":"2025-05-05T20:53:51.690273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ytest_pred_probs = model.predict(xtest)  # Probabilities (N, 206)\nytest_pred_labels = np.argmax(ytest_pred_probs, axis=1)  # Predicted class indices\nytest_true_labels = np.argmax(ytest_one_hot, axis=1)     # True class indices\n\nfrom sklearn.metrics import classification_report\n\nprint(\"\\nClassification Report:\")\nprint(classification_report(ytest_true_labels, ytest_pred_labels, target_names=label_encoder.classes_))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:53:51.693693Z","iopub.execute_input":"2025-05-05T20:53:51.694063Z","iopub.status.idle":"2025-05-05T20:53:54.659562Z","shell.execute_reply.started":"2025-05-05T20:53:51.694034Z","shell.execute_reply":"2025-05-05T20:53:54.658372Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Convert the report to a DataFrame for easier access to metrics\nimport pandas as pd\n\nreport_dict = classification_report(ytest_true_labels, ytest_pred_labels, target_names=label_encoder.classes_, output_dict=True)\n\nreport_df = pd.DataFrame(report_dict).transpose()\n\n# Extract precision, recall, and f1-score for each class\nprecision_values = report_df['precision'].dropna()\nrecall_values = report_df['recall'].dropna()\nf1_values = report_df['f1-score'].dropna()\n\n# Set up the figure for histograms\nplt.figure(figsize=(15, 5))\n\n# Plot histograms for precision, recall, and f1-score\nplt.subplot(1, 3, 1)\nplt.hist(precision_values, bins=20, color='blue', alpha=0.7)\nplt.title('Histogram of Precision')\nplt.xlabel('Precision')\nplt.ylabel('Number of Classes')\n\nplt.subplot(1, 3, 2)\nplt.hist(recall_values, bins=20, color='green', alpha=0.7)\nplt.title('Histogram of Recall')\nplt.xlabel('Recall')\nplt.ylabel('Number of Classes')\n\nplt.subplot(1, 3, 3)\nplt.hist(f1_values, bins=20, color='orange', alpha=0.7)\nplt.title('Histogram of F1-Score')\nplt.xlabel('F1-Score')\nplt.ylabel('Number of Classes')\n\n# Adjust layout\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:53:54.661233Z","iopub.execute_input":"2025-05-05T20:53:54.661704Z","iopub.status.idle":"2025-05-05T20:53:55.498121Z","shell.execute_reply.started":"2025-05-05T20:53:54.661660Z","shell.execute_reply":"2025-05-05T20:53:55.497064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Drop the last row (averages) and focus only on the classes\nclass_metrics_df = report_df.drop(columns=['support'])\n\n# Get the top 5 classes for precision, recall, and f1-score, along with their support\ntop_5_precision = report_df[['precision', 'support']].sort_values(by='precision', ascending=False).head(5)\ntop_5_recall = report_df[['recall', 'support']].sort_values(by='recall', ascending=False).head(5)\ntop_5_f1 = report_df[['f1-score', 'support']].sort_values(by='f1-score', ascending=False).head(5)\n\n# Get the worst 5 classes for precision, recall, and f1-score, along with their support\nbottom_5_precision = report_df[['precision', 'support']].sort_values(by='precision').head(5)\nbottom_5_recall = report_df[['recall', 'support']].sort_values(by='recall').head(5)\nbottom_5_f1 = report_df[['f1-score', 'support']].sort_values(by='f1-score').head(5)\n\n# Display the results\nprint(\"Top 5 classes by Precision:\")\nprint(top_5_precision)\nprint(\"\\nTop 5 classes by Recall:\")\nprint(top_5_recall)\nprint(\"\\nTop 5 classes by F1-Score:\")\nprint(top_5_f1)\n\nprint(\"\\nBottom 5 classes by Precision:\")\nprint(bottom_5_precision)\nprint(\"\\nBottom 5 classes by Recall:\")\nprint(bottom_5_recall)\nprint(\"\\nBottom 5 classes by F1-Score:\")\nprint(bottom_5_f1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:53:55.499199Z","iopub.execute_input":"2025-05-05T20:53:55.499575Z","iopub.status.idle":"2025-05-05T20:53:55.535588Z","shell.execute_reply.started":"2025-05-05T20:53:55.499546Z","shell.execute_reply":"2025-05-05T20:53:55.534441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras import layers, models, Input\n\n# Define the model\ninputs = Input(shape=(1024,))\nx = layers.Dense(512, activation='relu')(inputs)\nx = layers.LayerNormalization()(x)\n\n# Residual Block\nx_shortcut = x\nx = layers.Dense(512, activation='relu')(x)\nx = layers.LayerNormalization()(x)\nx = layers.Add()([x, x_shortcut])  # Residual connection\n\n# Deeper layers\nx = layers.Dense(256, activation='relu')(x)\nx = layers.Dropout(0.3)(x)\nx = layers.Dense(128, activation='relu')(x)\nx = layers.Dropout(0.2)(x)\n\n# Output layer\noutputs = layers.Dense(num_classes, activation='softmax')(x)\n\n# Build the model\nmodel = models.Model(inputs=inputs, outputs=outputs)\n\n# Compile the model\nmodel.compile(\n    optimizer='adam',\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\n# Show summary (optional)\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T19:39:12.096845Z","iopub.execute_input":"2025-05-05T19:39:12.097127Z","iopub.status.idle":"2025-05-05T19:39:12.210641Z","shell.execute_reply.started":"2025-05-05T19:39:12.097103Z","shell.execute_reply":"2025-05-05T19:39:12.209416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"early_stopping = EarlyStopping(\n    monitor='val_loss',  # Metric to monitor (usually validation loss or accuracy)\n    patience=5,         # Number of epochs with no improvement after which training will be stopped\n    verbose=1,           # Set to 1 to print messages when stopping happens\n    mode='min',          # 'min' for loss/error metrics, 'max' for accuracy metrics\n    restore_best_weights=True  # Restore model weights from the epoch with the best value of the monitored quantity.\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T19:39:12.211566Z","iopub.execute_input":"2025-05-05T19:39:12.211847Z","iopub.status.idle":"2025-05-05T19:39:12.216861Z","shell.execute_reply.started":"2025-05-05T19:39:12.211825Z","shell.execute_reply":"2025-05-05T19:39:12.215650Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Train Model ---\nprint(\"\\nStarting model training...\")\nepochs = 100\nhistory = model.fit(\n    xtrain,\n    ytrain_one_hot,\n    epochs=epochs,\n    validation_split=0.1, # Optional: use part of training data for validation during training\n    callbacks=[early_stopping]\n    # Or use validation_data=(xtest, ytest_one_hot) - be careful not to \"tune\" on test set\n)\n\nprint(\"Model training finished.\")\n\n# --- Evaluate Model (Optional) ---\nprint(\"\\nEvaluating model on the test set...\")\nloss, accuracy = model.evaluate(xtest, ytest_one_hot, verbose=0)\nprint(f\"Test Loss: {loss:.4f}\")\nprint(f\"Test Accuracy: {accuracy:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T19:39:12.218125Z","iopub.execute_input":"2025-05-05T19:39:12.218561Z","iopub.status.idle":"2025-05-05T20:05:55.207498Z","shell.execute_reply.started":"2025-05-05T19:39:12.218522Z","shell.execute_reply":"2025-05-05T20:05:55.206467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training & validation accuracy values\nplt.figure(figsize=(14, 5))\n\n# ---- Accuracy ----\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Training Accuracy')\nif 'val_accuracy' in history.history:\n    plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.title('Model Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True)\n\n# ---- Loss ----\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Training Loss')\nif 'val_loss' in history.history:\n    plt.plot(history.history['val_loss'], label='Validation Loss')\nplt.title('Model Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:05:55.208702Z","iopub.execute_input":"2025-05-05T20:05:55.208979Z","iopub.status.idle":"2025-05-05T20:05:55.781601Z","shell.execute_reply.started":"2025-05-05T20:05:55.208956Z","shell.execute_reply":"2025-05-05T20:05:55.780479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ytest_pred_probs = model.predict(xtest)  # Probabilities (N, 206)\nytest_pred_labels = np.argmax(ytest_pred_probs, axis=1)  # Predicted class indices\nytest_true_labels = np.argmax(ytest_one_hot, axis=1)     # True class indices\n\nfrom sklearn.metrics import classification_report\n\nprint(\"\\nClassification Report:\")\nprint(classification_report(ytest_true_labels, ytest_pred_labels, target_names=label_encoder.classes_))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:25:52.713893Z","iopub.execute_input":"2025-05-05T20:25:52.714396Z","iopub.status.idle":"2025-05-05T20:25:58.419498Z","shell.execute_reply.started":"2025-05-05T20:25:52.714362Z","shell.execute_reply":"2025-05-05T20:25:58.417827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Convert the report to a DataFrame for easier access to metrics\nimport pandas as pd\n\nreport_dict = classification_report(ytest_true_labels, ytest_pred_labels, target_names=label_encoder.classes_, output_dict=True)\n\nreport_df = pd.DataFrame(report_dict).transpose()\n\n# Extract precision, recall, and f1-score for each class\nprecision_values = report_df['precision'].dropna()\nrecall_values = report_df['recall'].dropna()\nf1_values = report_df['f1-score'].dropna()\n\n# Set up the figure for histograms\nplt.figure(figsize=(15, 5))\n\n# Plot histograms for precision, recall, and f1-score\nplt.subplot(1, 3, 1)\nplt.hist(precision_values, bins=20, color='blue', alpha=0.7)\nplt.title('Histogram of Precision')\nplt.xlabel('Precision')\nplt.ylabel('Number of Classes')\n\nplt.subplot(1, 3, 2)\nplt.hist(recall_values, bins=20, color='green', alpha=0.7)\nplt.title('Histogram of Recall')\nplt.xlabel('Recall')\nplt.ylabel('Number of Classes')\n\nplt.subplot(1, 3, 3)\nplt.hist(f1_values, bins=20, color='orange', alpha=0.7)\nplt.title('Histogram of F1-Score')\nplt.xlabel('F1-Score')\nplt.ylabel('Number of Classes')\n\n# Adjust layout\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:39:47.277980Z","iopub.execute_input":"2025-05-05T20:39:47.278528Z","iopub.status.idle":"2025-05-05T20:39:48.142023Z","shell.execute_reply.started":"2025-05-05T20:39:47.278490Z","shell.execute_reply":"2025-05-05T20:39:48.140388Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Drop the last row (averages) and focus only on the classes\nclass_metrics_df = report_df.drop(columns=['support'])\n\n# Get the top 5 classes for precision, recall, and f1-score, along with their support\ntop_5_precision = report_df[['precision', 'support']].sort_values(by='precision', ascending=False).head(5)\ntop_5_recall = report_df[['recall', 'support']].sort_values(by='recall', ascending=False).head(5)\ntop_5_f1 = report_df[['f1-score', 'support']].sort_values(by='f1-score', ascending=False).head(5)\n\n# Get the worst 5 classes for precision, recall, and f1-score, along with their support\nbottom_5_precision = report_df[['precision', 'support']].sort_values(by='precision').head(5)\nbottom_5_recall = report_df[['recall', 'support']].sort_values(by='recall').head(5)\nbottom_5_f1 = report_df[['f1-score', 'support']].sort_values(by='f1-score').head(5)\n\n# Display the results\nprint(\"Top 5 classes by Precision:\")\nprint(top_5_precision)\nprint(\"\\nTop 5 classes by Recall:\")\nprint(top_5_recall)\nprint(\"\\nTop 5 classes by F1-Score:\")\nprint(top_5_f1)\n\nprint(\"\\nBottom 5 classes by Precision:\")\nprint(bottom_5_precision)\nprint(\"\\nBottom 5 classes by Recall:\")\nprint(bottom_5_recall)\nprint(\"\\nBottom 5 classes by F1-Score:\")\nprint(bottom_5_f1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T20:42:16.483278Z","iopub.execute_input":"2025-05-05T20:42:16.483788Z","iopub.status.idle":"2025-05-05T20:42:16.517474Z","shell.execute_reply.started":"2025-05-05T20:42:16.483754Z","shell.execute_reply":"2025-05-05T20:42:16.514671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}