{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","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"},{"sourceId":11819288,"sourceType":"datasetVersion","datasetId":7423992},{"sourceId":12051777,"sourceType":"datasetVersion","datasetId":7505901}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🐦 BirdCLEF 2025 - Audio Preprocessing Cleaning the Data\n\nThis notebook is focused on preparing the BirdCLEF 2025 dataset for training an audio classification model. The workflow includes loading and cleaning data, chunking audio into 10-second segments, filtering out human voice using a VAD model.","metadata":{}},{"cell_type":"markdown","source":"## 🔗 BirdCLEF 2025 - Project Notebook Links\n\nHere are the different stages of my BirdCLEF 2025 pipeline, organized by functionality:\n\n### 📊 Data Preparation\n- [BirdCLEF 2025 - Data Preparation](https://www.kaggle.com/code/sheemamasood/birdclef-2025-data-prepartion)\n\n### 🎛️ Mel Spectrogram Generation\n- [BirdCLEF 2025 - Mel Generation](https://www.kaggle.com/code/sheemamasood/birdclef2025-mel-generation)\n\n### 🏷️ Pseudo Labelling for SSL\n- [BirdCLEF 2025 - Pseudo Labelling for SSL](https://www.kaggle.com/code/sheemamasood/birdclef2025-psedolabelling-for-ssl)\n\n### 🧠 Model Training\n- [BirdCLEF 2025 - Model Training (Phase 1)](https://www.kaggle.com/code/sheemamasood/birdclef2025-model-training-phase1)\n\n### 📦 Inference & Submissions\n- [BirdCLEF 2025 - Submissions](https://www.kaggle.com/code/sheemamasood/birdclef2025-submissions)\n","metadata":{}},{"cell_type":"markdown","source":"###  Import Required Libraries","metadata":{}},{"cell_type":"code","source":"# Basic utilities\nimport os\nimport math\nimport time\nimport random\nimport gc \nimport logging\nimport warnings\nfrom pathlib import Path\nimport librosa.display\nimport matplotlib.pyplot as plt\nimport torch\nimport torchaudio\n\n\n# Data handling\nimport numpy as np\nimport pandas as pd\n\n# Visualization\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm.notebook import tqdm  \n\nprint(\"all libraries imported in the environment\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:01.102834Z","iopub.execute_input":"2025-06-16T23:44:01.103039Z","iopub.status.idle":"2025-06-16T23:44:09.799655Z","shell.execute_reply.started":"2025-06-16T23:44:01.103013Z","shell.execute_reply":"2025-06-16T23:44:09.798421Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Check Librosa Library","metadata":{}},{"cell_type":"code","source":"# Audio processing\nimport librosa\nprint(f\"librosa version : {librosa.__version__}\")\nprint(f\"librosa files :{librosa.__file__}\")\n\ny = np.random.randn(32000)\nD = librosa.stft(y, hop_length=512)\nD_stretched = librosa.phase_vocoder(D, rate=1.1, hop_length=512)\nprint(D_stretched.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:09.800305Z","iopub.execute_input":"2025-06-16T23:44:09.801029Z","iopub.status.idle":"2025-06-16T23:44:24.082036Z","shell.execute_reply.started":"2025-06-16T23:44:09.800999Z","shell.execute_reply":"2025-06-16T23:44:24.080913Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Define Configuration Class","metadata":{}},{"cell_type":"code","source":"class Config:\n    # Audio settings\n    FS = 32000  # Sampling rate (audio)\n\n    # Mel spectrogram parameters (for converting audio to image)\n    N_FFT = 1024       # FFT window size\n    HOP_LENGTH = 512   # Step size for each frame\n    N_MELS = 128       # Number of mel bands\n    FMIN = 50          # Minimum Mel frequency\n    FMAX = 14000       # Maximum Mel frequency\n\n    # Parameters for audio duration and spectrogram size\n    TARGET_DURATION = 10.0  # Length of each audio (in seconds)\n    TARGET_SHAPE = (3, 256, 256)  # Size of the spectrogram image\n\n    # No limit on the number of samples during training (full dataset)\n    N_MAX = None  \n\n    # flag for training mode\n    TRAINING_MODE = True  \n    \n    # Additional training-specific configurations\n    EPOCHS = 10  \n    BATCH_SIZE = 32  \n    LEARNING_RATE = 0.001  \n\n# Create the config object\nconfig = Config()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:24.083886Z","iopub.execute_input":"2025-06-16T23:44:24.084378Z","iopub.status.idle":"2025-06-16T23:44:24.089954Z","shell.execute_reply.started":"2025-06-16T23:44:24.084328Z","shell.execute_reply":"2025-06-16T23:44:24.088938Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Load BirdCLEF 2025 Dataset","metadata":{}},{"cell_type":"code","source":"# Root path where all files and folders are stored\nDATA_ROOT = '/kaggle/input/birdclef-2025'\n\n# Load CSVs\ntrain_df = pd.read_csv(os.path.join(DATA_ROOT, 'train.csv'))\ntaxonomy_df = pd.read_csv(os.path.join(DATA_ROOT, 'taxonomy.csv'))\nlocation_df = pd.read_csv(os.path.join(DATA_ROOT, 'recording_location.txt'), delimiter='\\t')\nsample_submission = pd.read_csv(os.path.join(DATA_ROOT, 'sample_submission.csv'))\nworking_df = pd.read_csv(\"/kaggle/input/melspectrogramofbirdclef-2025/working_df.csv\")\n\n\nprint(f\"✅ Loaded train_df: {train_df.shape}\")\nprint(f\"✅ Loaded taxonomy_df: {taxonomy_df.shape}\")\nprint(f\"✅ Loaded location_df: {location_df.shape}\")\nprint(f\"✅ Loaded sample_submission: {sample_submission.shape}\")\nprint(f\"✅ Loaded working_df: {working_df.shape}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:33.989872Z","iopub.execute_input":"2025-06-16T23:44:33.990176Z","iopub.status.idle":"2025-06-16T23:44:34.504594Z","shell.execute_reply.started":"2025-06-16T23:44:33.990151Z","shell.execute_reply":"2025-06-16T23:44:34.503770Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Data Inspection","metadata":{}},{"cell_type":"code","source":"print(\"=\"*50)\nprint(f\"🗺️  Working_df Shape: {working_df.shape}\")\nprint(\"=\"*50)\n\nprint(\"=\"*50)\nprint(\"\\n📊 working Data Types:\")\nprint(working_df.info())\nprint(\"=\"*50)\n\n\n#  Missing Values Check\nprint(\"=\"*50)\nprint(\"\\n❌ Missing Values in Working Data:\")\nprint(working_df.isnull().sum())\nprint(\"=\"*50)\n\nprint(\"=\"*50)\nprint(\"\\n🔹 Sample Rows from working Data:\")\ndisplay(working_df.sample(5))\nprint(\"=\"*50)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:34.542949Z","iopub.execute_input":"2025-06-16T23:44:34.543254Z","iopub.status.idle":"2025-06-16T23:44:34.619647Z","shell.execute_reply.started":"2025-06-16T23:44:34.543231Z","shell.execute_reply":"2025-06-16T23:44:34.618637Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Save Master Label List","metadata":{}},{"cell_type":"code","source":"# Ek bar run karo:\nimport pickle\nall_labels = sorted(working_df['primary_label'].unique())  # full_df = poora data\nwith open(\"master_label_list.pkl\", \"wb\") as f:\n    pickle.dump(all_labels, f)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:35.068077Z","iopub.execute_input":"2025-06-16T23:44:35.068412Z","iopub.status.idle":"2025-06-16T23:44:35.075056Z","shell.execute_reply.started":"2025-06-16T23:44:35.068385Z","shell.execute_reply":"2025-06-16T23:44:35.074169Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ✂️ Audio Chunking Function\n\nWe define the `split_audio_into_10sec_chunks()` function, which performs the following tasks:\n\n- Loads each audio file from the dataset\n- Splits the audio into non-overlapping 10-second chunks\n- Stores metadata for each chunk (such as start time, duration, label, and file path) in a new DataFrame\n\nThis chunk-level data allows us to:\n\n- Standardize the audio input length for training deep learning models\n- Apply voice detection and other filtering techniques at a finer granularity\n\nThe chunked metadata plays a crucial role in improving data quality and model robustness.\n\n---\n","metadata":{}},{"cell_type":"code","source":"def split_audio_into_10sec_chunks(df, sample_rate=config.FS, chunk_len_sec=10):\n    chunk_records = []\n\n    for idx, row in tqdm(df.iterrows(), total=len(df)):\n        y, sr = librosa.load(row['filepath'], sr=sample_rate)\n        duration_sec = librosa.get_duration(y=y, sr=sr)\n\n        samples_per_chunk = int(chunk_len_sec * sr)\n        num_chunks = int(duration_sec // chunk_len_sec)\n\n        for i in range(num_chunks):\n            start_sample = i * samples_per_chunk\n            end_sample = (i + 1) * samples_per_chunk\n\n            chunk_records.append({\n                'chunk_id': f\"{row['samplename']}_chunk{i}\",\n                'primary_label': row['primary_label'],\n                'rating': row['rating'],\n                'filename': row['filename'],\n                'target': row['target'],\n                'filepath': row['filepath'],\n                'samplename': row['samplename'],\n                'class': row['class'],\n                'secondary_labels': row['secondary_labels'],\n                'secondary_target': row['secondary_target'],\n                'duration': row['duration'],\n                'start_sec': i * chunk_len_sec,\n                'end_sec': (i + 1) * chunk_len_sec,\n                'start_sample': start_sample,\n                'end_sample': end_sample\n            })\n\n    chunked_df = pd.DataFrame(chunk_records)\n    return chunked_df\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:35.491924Z","iopub.execute_input":"2025-06-16T23:44:35.492239Z","iopub.status.idle":"2025-06-16T23:44:35.500276Z","shell.execute_reply.started":"2025-06-16T23:44:35.492216Z","shell.execute_reply":"2025-06-16T23:44:35.499069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Split all dataset into 10 sec chunks\nchunked_df = split_audio_into_10sec_chunks(working_df)\n\nchunked_df.to_csv(\"10_sec_chunked_dataset.csv\", index=False)\nprint(\"CSV saved successfully!\")\n\n#Total Chunks Created\nprint(f\"Total 10-sec chunks: {len(chunked_df)}\")\n\n#Chunks per Class (Label)\nprint(chunked_df['primary_label'].value_counts())\n\n#Average Chunks per File\navg_chunks = chunked_df.groupby(\"filename\").size().mean()\nprint(f\"Average chunks per file: {avg_chunks:.2f}\")\n\n#Histogram Plot\n\nchunked_df['primary_label'].value_counts().plot(kind='bar', figsize=(12, 5))\nplt.title(\"Number of 10-sec Chunks per Class\")\nplt.xlabel(\"Class\")\nplt.ylabel(\"Chunk Count\")\nplt.tight_layout()\nplt.savefig(\"Number of 10-sec Chunks per Class.png\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-16T23:44:42.112639Z","iopub.execute_input":"2025-06-16T23:44:42.112980Z","execution_failed":"2025-06-17T00:05:52.813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Loading train_soundscapes for splitting into 10 sec chunkes for training","metadata":{}},{"cell_type":"code","source":"import os\nimport librosa\nimport pandas as pd\nfrom tqdm import tqdm\n\ndef split_soundscape_files_into_chunks(folder_path, sample_rate=config.FS, chunk_len_sec=10):\n    soundscape_files = [f for f in os.listdir(folder_path) if f.endswith('.ogg')]\n    chunk_records = []\n\n    # tqdm for progress bar (same as your function)\n    for file in tqdm(soundscape_files, desc=\"Processing soundscapes\"):\n        file_path = os.path.join(folder_path, file)\n        try:\n            y, sr = librosa.load(file_path, sr=sample_rate)\n            duration_sec = librosa.get_duration(y=y, sr=sr)\n            samples_per_chunk = int(chunk_len_sec * sr)\n            num_chunks = int(duration_sec // chunk_len_sec)\n\n            samplename = file.replace(\".ogg\", \"\")\n\n            for i in range(num_chunks):\n                start_sample = i * samples_per_chunk\n                end_sample = (i + 1) * samples_per_chunk\n\n                chunk_records.append({\n                    'chunk_id': f\"{samplename}_chunk{i}\",\n                    'filename': file,\n                    'filepath': file_path,\n                    'samplename': samplename,\n                    'start_sec': i * chunk_len_sec,\n                    'end_sec': (i + 1) * chunk_len_sec,\n                    'start_sample': start_sample,\n                    'end_sample': end_sample,\n                    'duration': duration_sec,\n                })\n        except Exception as e:\n            print(f\"❌ Failed to process {file}: {e}\")\n\n    chunked_df = pd.DataFrame(chunk_records)\n    return chunked_df\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-17T00:05:52.813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"folder_path = \"/kaggle/input/birdclef-2025/train_soundscapes\"\n\nsoundscape_chunked_df = split_soundscape_files_into_chunks(folder_path)\nsoundscape_chunked_df.to_csv(\"soundscape_10sec_chunks.csv\", index=False)\n\nprint(\"✅ Soundscape chunk CSV saved successfully!\")\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-17T00:05:52.813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"soundscape_chunked_df.head(2)","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-17T00:05:52.813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Silero Voice Activity Detection (VAD) Model\nLoads the pre-trained Silero VAD model using torch.hub. This model detects segments in audio where human speech is present, which helps us filter out chunks that contain human voice.","metadata":{}},{"cell_type":"code","source":"# Device configuration\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Using device: {device}\")\n\nvad_model, (get_speech_timestamps, _, read_audio, _, _) = torch.hub.load(\n    repo_or_dir='snakers4/silero-vad',\n    model='silero_vad',\n    trust_repo=True\n)\nvad_model = vad_model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T18:31:27.353922Z","iopub.execute_input":"2025-05-25T18:31:27.354236Z","iopub.status.idle":"2025-05-25T18:31:27.756893Z","shell.execute_reply.started":"2025-05-25T18:31:27.354214Z","shell.execute_reply":"2025-05-25T18:31:27.755627Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 🧹 Voice Activity Detection (VAD) and Silence Filtering\n\nThis block filters 10-second audio chunks to retain only clean samples with no human voice and no silence. \n\nSteps performed:\n1. **Loads audio chunks** using metadata from `chunked_df` (which includes start and end sample indices).\n2. **Checks for silence**: If the average amplitude of the chunk is below a defined threshold (`SILENCE_THRESHOLD`), the chunk is skipped.\n3. **Runs Voice Activity Detection (VAD)** using Silero's pre-trained VAD model to detect human speech.\n4. **Keeps only clean chunks**: Chunks with no detected human voice and no silence are added to `clean_chunks`.\n\nFinally, the clean metadata is saved as a new CSV file: `clean_chunks_10sec_vad_filtered.csv`, which will be used for generating mel spectrograms fo\n","metadata":{}},{"cell_type":"code","source":"vad_model.eval()\n\nclean_chunks = []\n\n# Define silence threshold (tune this if needed)\nSILENCE_THRESHOLD = 0.01  # very low amplitude, tweak if needed\n\nfor idx, row in tqdm(chunked_df.iterrows(), total=len(chunked_df)):\n    # Load full audio file\n    waveform, sr = torchaudio.load(row['filepath'])\n    \n    # Extract chunk samples from waveform\n    start_sample = row['start_sample']\n    end_sample = row['end_sample']\n    chunk_audio = waveform[0, start_sample:end_sample].to(device)\n    \n    # --- Silence check ---\n    if chunk_audio.abs().mean() < SILENCE_THRESHOLD:\n        continue  # skip silent chunk\n    \n    # --- VAD check (human voice) ---\n    speech_timestamps = get_speech_timestamps(chunk_audio, vad_model, sampling_rate=sr)\n    \n    # If NO human speech detected and not silent, keep chunk\n    if len(speech_timestamps) == 0:\n        clean_chunks.append(row)\n\n# Create new DataFrame with only clean & non-silent chunks\nclean_train_chunked_df = pd.DataFrame(clean_chunks)\n\n# Save to CSV\nclean_train_chunked_df.to_csv('train_audio_10sec_chunks_VAD_filtered.csv', index=False)\n\nprint(f\"Clean and non-silent chunks count: {len(clean_train_chunked_df)}\")\nprint(\"Saved to clean_chunks_10sec_vad_filtered.csv\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ✅ VAD Filtering  (soundscapes ke liye):","metadata":{}},{"cell_type":"code","source":"vad_model.eval()\n\nclean_chunks = []\n\n# Define silence threshold (tune this if needed)\nSILENCE_THRESHOLD = 0.01  # very low amplitude, tweak if needed\n\nfor idx, row in tqdm(soundscape_chunked_df.iterrows(), total=len(soundscape_chunked_df)):\n    # Load full audio file\n    waveform, sr = torchaudio.load(row['filepath'])\n\n    # Extract chunk samples from waveform\n    start_sample = row['start_sample']\n    end_sample = row['end_sample']\n    chunk_audio = waveform[0, start_sample:end_sample].to(device)\n\n    # --- Silence check ---\n    if chunk_audio.abs().mean() < SILENCE_THRESHOLD:\n        continue  # skip silent chunk\n\n    # --- VAD check (human voice) ---\n    speech_timestamps = get_speech_timestamps(chunk_audio, vad_model, sampling_rate=sr)\n\n    # If NO human speech detected and not silent, keep chunk\n    if len(speech_timestamps) == 0:\n        clean_chunks.append(row)\n\n# Create new DataFrame with only clean & non-silent chunks\nclean_soundscape_chunked_df = pd.DataFrame(clean_chunks)\n\n# Save to CSV\nclean_soundscape_chunked_df.to_csv('clean_soundscapes_chunks_10sec_vad_filtered.csv', index=False)\n\nprint(f\"✅ Clean and non-silent chunks count: {len(clean_soundscape_chunked_df)}\")\nprint(\"📁 Saved to clean_chunks_10sec_vad_filtered_from_soundscapes.csv\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"chunked_df.to_csv(\"train_audio_10sec_chunks.csv\", index=False)\nclean_train_chunked_df.to_csv(\"train_audio_10sec_chunks_VAD_filtered.csv\", index=False)\n\nsoundscape_chunked_df.to_csv(\"soundscape_10sec_chunks.csv\", index=False)\nclean_soundscape_chunked_df.to_csv(\"clean_soundscapes_chunks_10sec_vad_filtered.csv\", index=False)\nprint(\"All files are saved\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T18:36:15.807215Z","iopub.execute_input":"2025-05-25T18:36:15.807743Z","iopub.status.idle":"2025-05-25T18:36:17.802247Z","shell.execute_reply.started":"2025-05-25T18:36:15.807710Z","shell.execute_reply":"2025-05-25T18:36:17.799667Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🏁 Conclusion\n\nIn this notebook, we completed the crucial preprocessing (data preparation ) steps for the BirdCLEF 2025 audio dataset:\n\n- Loaded and explored the metadata and train_soundscapes to understand data quality and distribution.\n- Implemented a method to split long audio recordings into manageable 10-second chunks.\n- Used the Silero Voice Activity Detection (VAD) model to automatically filter out chunks containing human voices, ensuring cleaner bird audio samples.\n\n\nThese preprocessing steps are essential for improving model accuracy by focusing on high-quality, relevant audio segments and reducing noise and irrelevant data. The prepared dataset now provides a solid foundation for training robust and reliable bird species classification models.\n","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}