{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":44224,"databundleVersionId":5188730,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport librosa\nimport os\nimport numpy as np\nmetadata = pd.read_csv('/kaggle/input/birdclef-2023/train_metadata.csv')\n\nmetadata.head()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:22:26.671900Z","iopub.execute_input":"2025-10-26T13:22:26.672497Z","iopub.status.idle":"2025-10-26T13:22:26.747015Z","shell.execute_reply.started":"2025-10-26T13:22:26.672471Z","shell.execute_reply":"2025-10-26T13:22:26.746449Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# Count samples per class\n# =========================\nprint(\"✅ Sample counts per class:\")\nprint(metadata[\"primary_label\"].value_counts())\n\n# =========================\n# Optional: define class names manually\n# =========================\n# You can get the list of all unique classes\nclass_names = metadata[\"primary_label\"].unique().tolist()\nprint(\"Class Names:\", class_names)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:23:54.582611Z","iopub.execute_input":"2025-10-26T13:23:54.583189Z","iopub.status.idle":"2025-10-26T13:23:54.596938Z","shell.execute_reply.started":"2025-10-26T13:23:54.583165Z","shell.execute_reply":"2025-10-26T13:23:54.596300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import librosa\nimport matplotlib.pyplot as plt\nfrom IPython.display import Audio\nimport os\n\n# Path to a sample audio file from BirdCLEF-2023\naudio_dir = \"/kaggle/input/birdclef-2023/train_audio\"\nsample_file = \"abethr1/XC128013.ogg\"  # example file\naudio_file_path = os.path.join(audio_dir, sample_file)\n\n# 1️⃣ Load audio\naudio_data, sample_rate = librosa.load(audio_file_path, sr=None)\n\n# 2️⃣ Listen to audio\nAudio(audio_file_path)\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:26:09.311512Z","iopub.execute_input":"2025-10-26T13:26:09.311794Z","iopub.status.idle":"2025-10-26T13:26:09.380351Z","shell.execute_reply.started":"2025-10-26T13:26:09.311772Z","shell.execute_reply":"2025-10-26T13:26:09.379652Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# 3️⃣ Optional: plot waveform\nplt.figure(figsize=(12, 4))\nplt.plot(audio_data)\nplt.title(\"Waveform of \" + sample_file)\nplt.xlabel(\"Sample Index\")\nplt.ylabel(\"Amplitude\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:26:19.590898Z","iopub.execute_input":"2025-10-26T13:26:19.591589Z","iopub.status.idle":"2025-10-26T13:26:19.947839Z","shell.execute_reply.started":"2025-10-26T13:26:19.591561Z","shell.execute_reply":"2025-10-26T13:26:19.947145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport librosa\nfrom concurrent.futures import ThreadPoolExecutor\n\n# Load BirdCLEF-2023 metadata\nmeta = pd.read_csv(\"/kaggle/input/birdclef-2023/train_metadata.csv\")\n\ndef process_audio_file(file_path, sample_rate=22050, duration=5):\n    try:\n        audio, sr = librosa.load(file_path, sr=sample_rate, duration=duration)\n        mfccs = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=13)\n        mfccs = np.mean(mfccs.T, axis=0)\n        return mfccs\n    except Exception as e:\n        print(f\"Error processing {file_path}: {e}\")\n        return None\n\ndef load_audio_files(dataframe, audio_dir=\"/kaggle/input/birdclef-2023/train_audio\", sample_rate=22050, duration=5):\n    audio_data = []\n    labels = []\n\n    with ThreadPoolExecutor() as executor:\n        futures = []\n        for _, row in dataframe.iterrows():\n            file_path = os.path.join(audio_dir, row['filename'])\n            futures.append(executor.submit(process_audio_file, file_path, sample_rate, duration))\n            labels.append(row['primary_label'])\n\n        for future in futures:\n            result = future.result()\n            if result is not None:\n                audio_data.append(result)\n\n    return np.array(audio_data), np.array(labels)\n\n# Load first 200 files for a quick test\nX, y = load_audio_files(meta.head(200))\nprint(\"X shape:\", X.shape, \"y shape:\", y.shape)\n\nprint(X[1])\nprint(y[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:28:56.392433Z","iopub.execute_input":"2025-10-26T13:28:56.393004Z","iopub.status.idle":"2025-10-26T13:28:59.758275Z","shell.execute_reply.started":"2025-10-26T13:28:56.392981Z","shell.execute_reply":"2025-10-26T13:28:59.757619Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\nle = LabelEncoder()\ny_encoded = le.fit_transform(y)  # convert bird names to integer labels\nnum_classes = len(np.unique(y_encoded))  # this will replace 10\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:31:32.703029Z","iopub.execute_input":"2025-10-26T13:31:32.703642Z","iopub.status.idle":"2025-10-26T13:31:32.707984Z","shell.execute_reply.started":"2025-10-26T13:31:32.703616Z","shell.execute_reply":"2025-10-26T13:31:32.707156Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nX_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded)\nX_train = X_train.reshape((X_train.shape[0], 1, X_train.shape[1]))\nX_test = X_test.reshape((X_test.shape[0], 1, X_test.shape[1]))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:31:52.017141Z","iopub.execute_input":"2025-10-26T13:31:52.017464Z","iopub.status.idle":"2025-10-26T13:31:52.023933Z","shell.execute_reply.started":"2025-10-26T13:31:52.017442Z","shell.execute_reply":"2025-10-26T13:31:52.023190Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras import layers, models\n\ninput_shape = (X_train.shape[1], X_train.shape[2])  \nmodel = models.Sequential([\n    layers.GRU(64, input_shape=input_shape, return_sequences=True),\n    layers.GRU(32),\n    layers.Dense(64, activation='relu'),\n    layers.Dense(128, activation='relu'),\n    layers.Dense(num_classes, activation='softmax')  # <- dynamically set\n])\n\nmodel.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:32:08.302609Z","iopub.execute_input":"2025-10-26T13:32:08.303365Z","iopub.status.idle":"2025-10-26T13:32:08.369395Z","shell.execute_reply.started":"2025-10-26T13:32:08.303341Z","shell.execute_reply":"2025-10-26T13:32:08.368786Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(X_train, y_train, epochs=175, batch_size=32, validation_split=0.2)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:32:37.493048Z","iopub.execute_input":"2025-10-26T13:32:37.493951Z","iopub.status.idle":"2025-10-26T13:32:51.881092Z","shell.execute_reply.started":"2025-10-26T13:32:37.493922Z","shell.execute_reply":"2025-10-26T13:32:51.880550Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report\nimport numpy as np\n\ntarget_names = le.classes_  # This gives the bird species codes\npredictions = model.predict(X_test)\npredicted_classes = np.argmax(predictions, axis=1)\nactual_classes = y_test\n\ntaxonomy = pd.read_csv(\"/kaggle/input/birdclef-2023/eBird_Taxonomy_v2021.csv\")\ntax_map = dict(zip(taxonomy[\"SPECIES_CODE\"], taxonomy[\"PRIMARY_COM_NAME\"]))\n\n# Convert target_names codes to common names\ntarget_names_common = [tax_map.get(code, code) for code in target_names]\nreport = classification_report(actual_classes, predicted_classes, target_names=target_names_common)\nprint(report)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:35:05.180893Z","iopub.execute_input":"2025-10-26T13:35:05.181391Z","iopub.status.idle":"2025-10-26T13:35:05.316277Z","shell.execute_reply.started":"2025-10-26T13:35:05.181368Z","shell.execute_reply":"2025-10-26T13:35:05.315562Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import librosa\nimport numpy as np\n\ndef predict_bird(file_path, model, le, tax_map, duration=5, n_mfcc=13):\n    \"\"\"\n    Predicts the bird species from an audio file using the trained GRU model.\n\n    Args:\n        file_path (str): Path to the audio file.\n        model (keras.Model): Trained GRU model.\n        le (LabelEncoder): Label encoder fitted on training labels.\n        tax_map (dict): Mapping from species code to common name.\n        duration (float): Seconds of audio to load (default 5s).\n        n_mfcc (int): Number of MFCCs to extract (default 13).\n    \n    Returns:\n        tuple: (predicted_code, predicted_name)\n    \"\"\"\n    try:\n        # Load audio\n        y_audio, sr = librosa.load(file_path, sr=None, duration=duration)\n        \n        # Extract MFCCs\n        mfcc = librosa.feature.mfcc(y=y_audio, sr=sr, n_mfcc=n_mfcc)\n        mfcc_mean = np.mean(mfcc.T, axis=0)\n        \n        # Reshape for GRU input (1 sample, 1 time step, features)\n        mfcc_input = mfcc_mean.reshape(1, 1, n_mfcc)\n        \n        # Predict probabilities\n        probs = model.predict(mfcc_input, verbose=0)\n        pred_index = np.argmax(probs, axis=1)[0]\n        \n        # Get species code and name\n        pred_code = le.inverse_transform([pred_index])[0]\n        pred_name = tax_map.get(pred_code, \"Unknown\")\n        \n        print(f\"✅ Predicted Code: {pred_code}\")\n        print(f\"✅ Predicted Name: {pred_name}\")\n        return pred_code, pred_name\n    except Exception as e:\n        print(f\"Error predicting {file_path}: {e}\")\n        return None, None\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:40:04.989834Z","iopub.execute_input":"2025-10-26T13:40:04.990420Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"audio_file = \"/kaggle/input/birdclef-2023/train_audio/abethr1/XC128013.ogg\"\npredict_bird(audio_file, model, le, tax_map)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:40:57.158491Z","iopub.execute_input":"2025-10-26T13:40:57.159005Z","iopub.status.idle":"2025-10-26T13:40:57.317851Z","shell.execute_reply.started":"2025-10-26T13:40:57.158983Z","shell.execute_reply":"2025-10-26T13:40:57.317217Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = model.predict(X_test)\n\npredicted_classes = np.argmax(predictions, axis=1)\n\nactual_classes = y_test\n\nfor i in range(10):\n    pred_name = tax_map.get(le.inverse_transform([predicted_classes[i]])[0], \"Unknown\")\n    actual_name = tax_map.get(le.inverse_transform([actual_classes[i]])[0], \"Unknown\")\n    print(f'Predicted: {pred_name}, Actual: {actual_name}')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:38:27.940029Z","iopub.execute_input":"2025-10-26T13:38:27.940681Z","iopub.status.idle":"2025-10-26T13:38:28.038514Z","shell.execute_reply.started":"2025-10-26T13:38:27.940657Z","shell.execute_reply":"2025-10-26T13:38:28.037490Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Plot training & validation accuracy and loss\nplt.figure(figsize=(14, 5))\n\n# Accuracy plot\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], marker='o', label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], marker='o', label='Validation Accuracy')\nplt.title('Model Accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.grid(True)\nplt.legend()\n\n# Loss plot\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], marker='o', label='Train Loss')\nplt.plot(history.history['val_loss'], marker='o', label='Validation Loss')\nplt.title('Model Loss')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.grid(True)\nplt.legend()\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:41:12.452502Z","iopub.execute_input":"2025-10-26T13:41:12.452774Z","iopub.status.idle":"2025-10-26T13:41:12.821454Z","shell.execute_reply.started":"2025-10-26T13:41:12.452755Z","shell.execute_reply":"2025-10-26T13:41:12.820670Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import precision_recall_curve\nimport matplotlib.pyplot as plt\n\n# Use your encoded classes and mapped bird names\nactual_classes = y_test  # already encoded\npredictions = model.predict(X_test)  # softmax outputs\n\n# Get mapped bird names from taxonomy (optional)\ntaxonomy = pd.read_csv(\"/kaggle/input/birdclef-2023/eBird_Taxonomy_v2021.csv\")\ntax_map = dict(zip(taxonomy[\"SPECIES_CODE\"], taxonomy[\"PRIMARY_COM_NAME\"]))\nclass_names = [tax_map.get(code, code) for code in le.classes_]  # le from label encoding\n\nprecision = {}\nrecall = {}\nthresholds = {}\n\n# Compute precision-recall curve for each bird class\nfor i, class_name in enumerate(class_names):\n    precision[i], recall[i], thresholds[i] = precision_recall_curve(\n        (actual_classes == i).astype(int), predictions[:, i]\n    )\n\n# Plot all curves\nplt.figure(figsize=(12, 8))\nfor i, class_name in enumerate(class_names):\n    plt.plot(recall[i], precision[i], label=class_name)\n\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve per Bird Species')\nplt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:42:06.641577Z","iopub.execute_input":"2025-10-26T13:42:06.641859Z","iopub.status.idle":"2025-10-26T13:42:06.991415Z","shell.execute_reply.started":"2025-10-26T13:42:06.641839Z","shell.execute_reply":"2025-10-26T13:42:06.990654Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# True labels and predicted classes\ntrue_labels = y_test\npredicted_classes = np.argmax(model.predict(X_test), axis=1)\n\n# Map label indices to bird names\ntaxonomy = pd.read_csv(\"/kaggle/input/birdclef-2023/eBird_Taxonomy_v2021.csv\")\ntax_map = dict(zip(taxonomy[\"SPECIES_CODE\"], taxonomy[\"PRIMARY_COM_NAME\"]))\nclass_names = [tax_map.get(code, code) for code in le.classes_]  # le = LabelEncoder\n\n# Compute confusion matrix\ncm = confusion_matrix(true_labels, predicted_classes)\n\n# Plot\nplt.figure(figsize=(12, 10))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n            xticklabels=class_names, \n            yticklabels=class_names)\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.title('Confusion Matrix of Bird Species Predictions')\nplt.xticks(rotation=90)\nplt.yticks(rotation=0)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T13:43:29.210728Z","iopub.execute_input":"2025-10-26T13:43:29.211010Z","iopub.status.idle":"2025-10-26T13:43:29.941794Z","shell.execute_reply.started":"2025-10-26T13:43:29.210989Z","shell.execute_reply":"2025-10-26T13:43:29.941118Z"}},"outputs":[],"execution_count":null}]}