{"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":"gpu","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Import pandas for data handling\nimport pandas as pd\n\n# Define the path to the metadata file\nmetadata_path = '/kaggle/input/birdclef-2025/train.csv'\n\n# Load the metadata into a DataFrame\nmetadata = pd.read_csv(metadata_path)\n\n# Display the first few rows to inspect the structure\nprint(\"First few rows of the metadata:\")\nprint(metadata.head())\n\n# Check for missing values in each column\nprint(\"\\nMissing values in each column:\")\nprint(metadata.isnull().sum())\n\n# Display the unique bird species (assuming 'primary_label' is the species column)\nprint(\"\\nUnique bird species:\")\nprint(metadata['primary_label'].unique())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T01:59:24.465076Z","iopub.execute_input":"2025-04-01T01:59:24.465371Z","iopub.status.idle":"2025-04-01T01:59:24.967413Z","shell.execute_reply.started":"2025-04-01T01:59:24.465341Z","shell.execute_reply":"2025-04-01T01:59:24.966598Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport librosa\n\n# Load metadata\nmetadata_path = '/kaggle/input/birdclef-2025/train.csv'\nmetadata = pd.read_csv(metadata_path)\n\n# Example audio file path\nexample_file_path = '/kaggle/input/birdclef-2025/train_audio/' + metadata['filename'].iloc[0]\n\n# Preprocess audio function (flexible for any file path)\ndef preprocess_audio(file_path, sample_rate=32000, duration=5):\n    audio, sr = librosa.load(file_path, sr=sample_rate, duration=duration)\n    return audio, sr\n\n# Test the function\naudio, sr = preprocess_audio(example_file_path)\nprint(f\"Loaded audio with sample rate {sr} and length {len(audio)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T01:59:24.968778Z","iopub.execute_input":"2025-04-01T01:59:24.969099Z","iopub.status.idle":"2025-04-01T01:59:37.834856Z","shell.execute_reply.started":"2025-04-01T01:59:24.969068Z","shell.execute_reply":"2025-04-01T01:59:37.833912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import libraries for audio processing\nimport librosa\nimport numpy as np\n\ndef preprocess_audio(file_path, target_sr=22050, duration=5):\n    \"\"\"\n    Load and preprocess an audio file.\n    \n    Parameters:\n    - file_path: Path to the audio file (str).\n    - target_sr: Target sample rate in Hz (int, default: 22050).\n    - duration: Target duration in seconds (int, default: 5).\n    \n    Returns:\n    - audio: Preprocessed audio array (numpy array).\n    \"\"\"\n    try:\n        # Load audio file with the target sample rate\n        audio, sr = librosa.load(file_path, sr=target_sr)\n        \n        # Calculate target length in samples\n        target_length = int(target_sr * duration)\n        \n        # Trim or pad audio to the target duration\n        if len(audio) < target_length:\n            audio = np.pad(audio, (0, target_length - len(audio)), mode='constant')\n        else:\n            audio = audio[:target_length]\n        \n        # Normalize audio to range [-1, 1]\n        audio = librosa.util.normalize(audio)\n        \n        # Optional: Basic noise reduction with pre-emphasis\n        audio = librosa.effects.preemphasis(audio)\n        \n        return audio\n    \n    except Exception as e:\n        print(f\"Error processing {file_path}: {e}\")\n        return None\n\n# Test the function with an example file\n# Replace 'species1/audio1.wav' with an actual filename from your metadata\nexample_file_path = '/kaggle/input/birdclef-2025/train_audio/' + metadata['filename'].iloc[0]\npreprocessed_audio = preprocess_audio(example_file_path)\nif preprocessed_audio is not None:\n    print(f\"Shape of preprocessed audio: {preprocessed_audio.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T01:59:37.836346Z","iopub.execute_input":"2025-04-01T01:59:37.836759Z","iopub.status.idle":"2025-04-01T01:59:38.449854Z","shell.execute_reply.started":"2025-04-01T01:59:37.836734Z","shell.execute_reply":"2025-04-01T01:59:38.449064Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Import necessary libraries\nimport numpy as np\nimport librosa\nimport librosa.display\nimport matplotlib.pyplot as plt\nfrom sklearn.preprocessing import StandardScaler\n\n# Reuse the preprocess_audio function from Step 1\ndef preprocess_audio(file_path, target_sr=22050, duration=5):\n    \"\"\"\n    Load and preprocess an audio file.\n    \"\"\"\n    try:\n        audio, sr = librosa.load(file_path, sr=target_sr)\n        target_length = int(target_sr * duration)\n        if len(audio) < target_length:\n            audio = np.pad(audio, (0, target_length - len(audio)), mode='constant')\n        else:\n            audio = audio[:target_length]\n        audio = librosa.util.normalize(audio)\n        audio = librosa.effects.preemphasis(audio)\n        return audio\n    except Exception as e:\n        print(f\"Error processing {file_path}: {e}\")\n        return None\n\n# Function to extract Mel spectrogram features\ndef extract_mel_spectrogram(audio, sr=22050, n_mels=128, hop_length=512, n_fft=2048):\n    \"\"\"\n    Convert audio to Mel spectrogram.\n    \n    Parameters:\n    - audio: Preprocessed audio array (numpy array).\n    - sr: Sample rate (int, default: 22050).\n    - n_mels: Number of Mel bands (int, default: 128).\n    - hop_length: Number of samples between successive frames (int, default: 512).\n    - n_fft: Length of FFT window (int, default: 2048).\n    \n    Returns:\n    - mel_spec_db: Mel spectrogram in decibels (numpy array).\n    \"\"\"\n    # Compute Mel spectrogram\n    mel_spec = librosa.feature.melspectrogram(y=audio, sr=sr, n_mels=n_mels, hop_length=hop_length, n_fft=n_fft)\n    \n    # Convert to decibels (log scale)\n    mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n    \n    return mel_spec_db\n\n# Function to resize spectrogram for EfficientNet B0 input (224x224)\ndef prepare_for_efficientnet(mel_spec_db, target_size=(224, 224)):\n    \"\"\"\n    Resize Mel spectrogram to match EfficientNet B0 input size.\n    \n    Parameters:\n    - mel_spec_db: Mel spectrogram in decibels (numpy array).\n    - target_size: Desired output size (tuple, default: (224, 224)).\n    \n    Returns:\n    - resized_spec: Resized spectrogram (numpy array).\n    \"\"\"\n    # Normalize spectrogram values to [0, 1]\n    scaler = StandardScaler()\n    mel_spec_normalized = scaler.fit_transform(mel_spec_db)\n    \n    # Resize to target size (EfficientNet B0 expects 224x224x3, we'll replicate channels later)\n    from scipy.ndimage import zoom\n    height, width = mel_spec_normalized.shape\n    zoom_factors = (target_size[0] / height, target_size[1] / width)\n    resized_spec = zoom(mel_spec_normalized, zoom_factors, order=1)\n    \n    # Ensure the shape matches target_size\n    resized_spec = resized_spec[:target_size[0], :target_size[1]]\n    \n    return resized_spec\n\n# Test the feature extraction pipeline\n# Load metadata to get a sample file\nmetadata_path = '/kaggle/input/birdclef-2025/train.csv'\nmetadata = pd.read_csv(metadata_path)\nexample_file_path = '/kaggle/input/birdclef-2025/train_audio/' + metadata['filename'].iloc[0]\n\n# Step 1: Preprocess audio\naudio = preprocess_audio(example_file_path)\nif audio is not None:\n    print(f\"Preprocessed audio shape: {audio.shape}\")\n    \n    # Step 2: Extract Mel spectrogram\n    mel_spec_db = extract_mel_spectrogram(audio)\n    print(f\"Mel spectrogram shape: {mel_spec_db.shape}\")\n    \n    # Step 2: Prepare for EfficientNet\n    resized_spec = prepare_for_efficientnet(mel_spec_db)\n    print(f\"Resized spectrogram shape: {resized_spec.shape}\")\n    \n    # Optional: Visualize the spectrogram\n    plt.figure(figsize=(10, 4))\n    librosa.display.specshow(mel_spec_db, sr=22050, hop_length=512, x_axis='time', y_axis='mel')\n    plt.colorbar(format='%+2.0f dB')\n    plt.title('Mel Spectrogram')\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T01:59:38.450867Z","iopub.execute_input":"2025-04-01T01:59:38.451082Z","iopub.status.idle":"2025-04-01T01:59:40.262575Z","shell.execute_reply.started":"2025-04-01T01:59:38.451065Z","shell.execute_reply":"2025-04-01T01:59:40.261762Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Install transformers if not already available\n!pip install transformers\n\n# Import libraries\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nimport pandas as pd\nimport numpy as np\nimport librosa\nfrom sklearn.model_selection import train_test_split\nfrom transformers import EfficientNetModel\nfrom sklearn.preprocessing import StandardScaler\nfrom scipy.ndimage import zoom\nimport os\n\n# Reuse Step 1 & 2 functions\ndef preprocess_audio(file_path, target_sr=22050, duration=5):\n    try:\n        audio, sr = librosa.load(file_path, sr=target_sr)\n        target_length = int(target_sr * duration)\n        if len(audio) < target_length:\n            audio = np.pad(audio, (0, target_length - len(audio)), mode='constant')\n        else:\n            audio = audio[:target_length]\n        audio = librosa.util.normalize(audio)\n        audio = librosa.effects.preemphasis(audio)\n        return audio\n    except Exception as e:\n        print(f\"Error processing {file_path}: {e}\")\n        return None\n\ndef extract_mel_spectrogram(audio, sr=22050, n_mels=128, hop_length=512, n_fft=2048):\n    mel_spec = librosa.feature.melspectrogram(y=audio, sr=sr, n_mels=n_mels, hop_length=hop_length, n_fft=n_fft)\n    mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n    return mel_spec_db\n\ndef prepare_for_efficientnet(mel_spec_db, target_size=(224, 224)):\n    scaler = StandardScaler()\n    mel_spec_normalized = scaler.fit_transform(mel_spec_db)\n    height, width = mel_spec_normalized.shape\n    zoom_factors = (target_size[0] / height, target_size[1] / width)\n    resized_spec = zoom(mel_spec_normalized, zoom_factors, order=1)\n    resized_spec = resized_spec[:target_size[0], :target_size[1]]\n    return resized_spec\n\n# Custom Dataset with lightweight initialization\nclass BirdCLEFDataset(Dataset):\n    def __init__(self, metadata, audio_dir, label_map):\n        self.audio_dir = audio_dir\n        self.label_map = label_map\n        valid_metadata = []\n        for idx, row in metadata.iterrows():\n            file_path = f\"{audio_dir}/{row['filename']}\"\n            if os.path.exists(file_path):\n                valid_metadata.append(row)\n            else:\n                print(f\"Skipping {file_path}: File not found\")\n        self.metadata = pd.DataFrame(valid_metadata)\n        print(f\"Dataset size after filtering: {len(self.metadata)} samples\")\n    \n    def __len__(self):\n        return len(self.metadata)\n    \n    def __getitem__(self, idx):\n        file_path = f\"{self.audio_dir}/{self.metadata['filename'].iloc[idx]}\"\n        audio = preprocess_audio(file_path)\n        if audio is None:\n            dummy_img = torch.zeros(3, 224, 224, dtype=torch.float32)\n            dummy_label = self.label_map[self.metadata['primary_label'].iloc[idx]]\n            return dummy_img, dummy_label\n        \n        mel_spec_db = extract_mel_spectrogram(audio)\n        resized_spec = prepare_for_efficientnet(mel_spec_db)\n        img = np.stack([resized_spec] * 3, axis=-1)  # Shape: (224, 224, 3)\n        img = torch.tensor(img, dtype=torch.float32).permute(2, 0, 1)  # Shape: (3, 224, 224)\n        \n        label = self.label_map[self.metadata['primary_label'].iloc[idx]]\n        return img, label\n\n# Load and prepare data\nmetadata_path = '/kaggle/input/birdclef-2025/train.csv'\nmetadata = pd.read_csv(metadata_path)\ntrain_metadata, val_metadata = train_test_split(\n    metadata,\n    test_size=0.2,\n    stratify=metadata['primary_label'],\n    random_state=42\n)\n\n# Create label mapping\nunique_labels = metadata['primary_label'].unique()\nlabel_map = {label: idx for idx, label in enumerate(unique_labels)}\nnum_classes = len(unique_labels)\nprint(f\"Number of classes: {num_classes}\")\n\naudio_dir = '/kaggle/input/birdclef-2025/train_audio'\ntrain_dataset = BirdCLEFDataset(train_metadata, audio_dir, label_map)\nval_dataset = BirdCLEFDataset(val_metadata, audio_dir, label_map)\n\n# DataLoaders\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=2)\n\n# Load EfficientNet B0 from Hugging Face\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nbase_model = EfficientNetModel.from_pretrained('google/efficientnet-b0')\n\n# Custom EfficientNet\nclass CustomEfficientNet(nn.Module):\n    def __init__(self, base_model, num_classes):\n        super(CustomEfficientNet, self).__init__()\n        self.base_model = base_model\n        self.fc = nn.Linear(1280, num_classes)  # pooler_output is (batch_size, 1280)\n    \n    def forward(self, x):\n        # Input: (batch_size, 3, 224, 224)\n        outputs = self.base_model(x)\n        x = outputs.pooler_output  # Shape: (batch_size, 1280)\n        x = self.fc(x)  # Shape: (batch_size, num_classes)\n        return x\n\nmodel = CustomEfficientNet(base_model, num_classes)\nmodel.to(device)\n\n# Define loss and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n\n# Training function\ndef train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=5):\n    for epoch in range(num_epochs):\n        model.train()\n        running_loss = 0.0\n        for i, (inputs, labels) in enumerate(train_loader):\n            inputs, labels = inputs.to(device), labels.to(device)\n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            running_loss += loss.item()\n        epoch_loss = running_loss / len(train_loader)\n        print(f\"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}\")\n        \n        # Validation\n        model.eval()\n        correct = 0\n        total = 0\n        with torch.no_grad():\n            for inputs, labels in val_loader:\n                inputs, labels = inputs.to(device), labels.to(device)\n                outputs = model(inputs)\n                _, predicted = torch.max(outputs.data, 1)\n                total += labels.size(0)\n                correct += (predicted == labels).sum().item()\n        \n        val_accuracy = 100 * correct / total\n        print(f\"Validation Accuracy: {val_accuracy:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T01:59:40.263242Z","iopub.execute_input":"2025-04-01T01:59:40.263609Z","iopub.status.idle":"2025-04-01T02:01:16.316348Z","shell.execute_reply.started":"2025-04-01T01:59:40.263587Z","shell.execute_reply":"2025-04-01T02:01:16.314894Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train the model\ntrain_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=5)\n\n# Save the trained model\noutput_path = '/kaggle/working/output/trained_efficientnet_b0.pth'\ntorch.save(model.state_dict(), output_path)\nprint(f\"Model saved to {output_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-01T02:01:16.318129Z","iopub.execute_input":"2025-04-01T02:01:16.318533Z","iopub.status.idle":"2025-04-01T03:33:53.904368Z","shell.execute_reply.started":"2025-04-01T02:01:16.318476Z","shell.execute_reply":"2025-04-01T03:33:53.903078Z"}},"outputs":[],"execution_count":null}]}