Chirps and Chips: ML and DL Algorithms for Bird Sound Analysis
Bird sound categorization is essential for environmental monitoring and animal protection. We explore the field of bird sound…
Chirps and Chips: ML and DL Algorithms for Bird Sound Analysis
Bird sound categorization is essential for environmental monitoring and animal protection. We explore the field of bird sound classification with the Cornell dataset in this blog. On this difficult challenge, we compare the performance of several machine learning (ML) and deep learning (DL) methods.

Utilizing machine learning, we use XGBClassifier, Gradient Boost Classifier, Random Forest, Decision Tree, and cross-validation scores for assessment. We use the ResNet and LSTM architectures on the DL side. The goal is to show how successful these algorithms are in classifying bird sounds. Let us investigate these models’ performance using the Cornell bird sound dataset.
Let’s dive into the code
Importing Libraries
import os
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.express as px
import folium
from folium import Marker , GeoJson , Choropleth , Circle
from folium.plugins import HeatMap , MarkerCluster
import librosa.display
from IPython.display import Audio
Loading dataset and EDA
df =pd.read_csv("/kaggle/input/birdsong-recognition/train.csv")
df.head()
df["year"] = df["date"].apply(lambda x : x.split("-")[0])
df["month"] = df["date"].apply(lambda x : x.split("-")[1])
group_year = df.groupby(["year"]).size().reset_index(name = "counts")
group_year = group_year.iloc[3:]
group_month = df.groupby(["month"]).size().reset_index(name = "counts")
fig = make_subplots(rows=1, cols=2, specs=[[{"type": "pie"}, {"type": "pie"}]], subplot_titles=('Distribution of Channels', 'Distribution of Sampling rate'))
group_ch = df.groupby(["channels"]).size().reset_index(name = "counts")
fig.append_trace(go.Pie(labels = group_ch["channels"] ,
values = group_ch["counts"],),
row = 1 , col =1)
group_sr = df.groupby(["sampling_rate"]).size().reset_index(name = "counts")
fig.append_trace(go.Pie(labels = group_sr["sampling_rate"] ,
values = group_sr["counts"],),
row = 1 , col =2)
fig.show()
Let us see what the audio looks like
!pip install --upgrade librosa
fig, ax = plt.subplots(4, figsize = (20, 9))
fig.suptitle('Waveplots', fontsize=16)
audio_path1 = '/kaggle/input/birdsong-recognition/train_audio/amekes/XC214257.mp3'
audio_path2 = '/kaggle/input/birdsong-recognition/train_audio/annhum/XC120842.mp3'
audio_path3 = '/kaggle/input/birdsong-recognition/train_audio/balori/XC16971.mp3'
audio_path4 = '/kaggle/input/birdsong-recognition/train_audio/bkcchi/XC135477.mp3'
y1, sr1 = librosa.load(audio_path1)
y2, sr2 = librosa.load(audio_path2)
y3, sr3 = librosa.load(audio_path3)
y4, sr4 = librosa.load(audio_path4)
librosa.display.waveshow(y=y1, sr=sr1, color = "#3371FF", ax=ax[0])
librosa.display.waveshow(y=y2 , sr=sr2, color = "#F7A81E", ax=ax[1])
librosa.display.waveshow(y=y3 , sr=sr3, color = "#2BF71E", ax=ax[2])
librosa.display.waveshow(y=y4 , sr=sr4, color = "#F71E6D", ax=ax[3])

# Visualize an STFT power spectrum
audio_path = '/kaggle/input/birdsong-recognition/train_audio/bawwar/XC134100.mp3'
y, sr = librosa.load(audio_path)
plt.figure(figsize=(12, 8))
D = librosa.amplitude_to_db(librosa.stft(y))
plt.subplot(4, 2, 1)
librosa.display.specshow(D, y_axis='linear')
plt.colorbar(format='%+2.0f dB')
plt.title('Linear-frequency power spectrogram')
# logarithmic scale
plt.subplot(4, 2, 2)
librosa.display.specshow(D, y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Log-frequency power spectrogram')
#CQT scale
CQT = librosa.amplitude_to_db(librosa.cqt(y, sr=sr), ref=np.max)
plt.subplot(4, 2, 3)
librosa.display.specshow(CQT, y_axis='cqt_hz')
plt.colorbar(format='%+2.0f dB')
plt.title('Constant-Q power spectrogram (Hz)')
CQT = librosa.amplitude_to_db(librosa.cqt(y, sr=sr), ref=np.max)
plt.subplot(4, 2, 4)
librosa.display.specshow(CQT, y_axis='cqt_note')
plt.colorbar(format='%+2.0f dB')
plt.title('Constant-Q power spectrogram (note)')
#Chromagram
C = librosa.feature.chroma_cqt(y=y, sr=sr)
plt.subplot(4, 2, 5)
librosa.display.specshow(C, y_axis='chroma')
plt.colorbar()
plt.title('Chromagram')
# Log power spectrogram
plt.subplot(4, 2, 6)
librosa.display.specshow(D, x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Log power spectrogram')

s = (df.dtypes == "object")
list1= list(s[s].index)
print(list1)
df = df.drop(["filename" ,"url" ,"ebird_code", "author","sci_name" ,"secondary_labels" ,"xc_id","file_type" ,"description","date"] , axis =1)
from sklearn.preprocessing import LabelEncoder
# Assuming df is your DataFrame
label_encoder = LabelEncoder()
mapping_dict = {}
for column in df.columns:
df[column] = label_encoder.fit_transform(df[column])
mapping_dict[column] = dict(zip(label_encoder.classes_, label_encoder.transform(label_encoder.classes_)))
# Now df is transformed, and mapping_dict contains the mapping information
df
MODEL BUILDING
selected_features = X.columns[k_best.get_support()]
# Display the selected features
print("Selected Features:", selected_features)
# Create a RandomForestClassifier
model = RandomForestClassifier(n_estimators=100 , random_state=42)
# Train the model
model.fit(X_train, y_train)
# Make predictions on the test set
y_pred = model.predict(X_test)
# Calculate accuracy
accuracy = accuracy_score(y_test, y_pred)
# score = classification_report(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")
# print(score)
Accuracy: 70.86%
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier()
dt.fit(X_train, y_train)
# Make predictions on the test set
y_pred = dt.predict(X_test)
# Calculate accuracy
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")
# score = classification_report(y_test, y_pred)
# print(score)
Accuracy: 99.87%
from xgboost import XGBClassifier
xgb = XGBClassifier(n_estimators=100)
xgb.fit(X_train, y_train)
# Make predictions on the test set
y_pred = xgb.predict(X_test)
# Calculate accuracy
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")
# score = classification_report(y_test, y_pred)
# print(score)
Accuracy: 95.66%
from sklearn.ensemble import GradientBoostingClassifier
gbc = GradientBoostingClassifier(n_estimators=10)
gbc.fit(X_train, y_train)
y_pred = gbc.predict(X_test)
# print("Gradient Boosting Classifier Classification Report:")
# print(classification_report(y_test, y_pred))
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy * 100:.2f}%")
Accuracy: 89.54%
from sklearn.model_selection import cross_val_score
xgb_model = XGBClassifier(n_estimators=100)
# Perform 5-fold cross-validation
cv_scores = cross_val_score(xgb_model, X_train , y_train, cv=5, scoring='accuracy')
# Display the cross-validation scores
print("Cross-Validation Scores:", cv_scores)
print("Mean Accuracy:", cv_scores.mean())
Cross-Validation Scores: [0.953125 0.95065789 0.94490132 0.95226337 0.95802469]
Mean Accuracy: 0.9517944552739875
Deep Learning models — ResNet and LSTM
import pandas as pd
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense
from tensorflow.keras.utils import to_categorical
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2, f_regression
from sklearn.metrics import classification_report
# Split the data into features (X) and target variable (y)
X = df.drop('species', axis=1)
y = df['species']
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# # Feature selection (optional, can be commented out if not needed)
# k_best = SelectKBest(chi2, k=9) # You can choose chi2 or f_regression
# X_train = k_best.fit_transform(X_train, y_train)
# X_test = k_best.transform(X_test)
# Assuming your features are numerical, convert them to a NumPy array
X_train = X_train.to_numpy()
X_test = X_test.to_numpy()
# Reshape features for CNN input (assuming 1D features)
X_train_reshaped = X_train.reshape(X_train.shape[0], X_train.shape[1], 1)
X_test_reshaped = X_test.reshape(X_test.shape[0], X_test.shape[1], 1)
# Convert labels to categorical format for multi-class classification
y_train_cat = to_categorical(y_train)
y_test_cat = to_categorical(y_test)
# Define the ResNet model
model = Sequential([
Conv1D(32, kernel_size=3, activation="relu", input_shape=(X_train_reshaped.shape[1], 1)),
MaxPooling1D(pool_size=2),
# Add residual blocks here (refer to previous examples for structure)
Flatten(),
Dense(128, activation="relu"),
Dense(len(np.unique(y)), activation="softmax") # Output layer with number of bird species
])
# Compile the model
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
# Train the model
model.fit(X_train_reshaped, y_train_cat, epochs=50, validation_data=(X_test_reshaped, y_test_cat))
# Evaluate the model
loss, accuracy = model.evaluate(X_test_reshaped, y_test_cat)
print(f"Test Accuracy: {accuracy:.4f}")
# Make predictions (optional)
# y_pred = model.predict(X_test_reshaped)
# print(classification_report(y_test, y_pred))
from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Flatten, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split
# Split the data into features (X) and target variable (y)
X = df.drop('species', axis=1)
y = df['species']
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Assuming your features are numerical, convert them to a NumPy array
X_train = X_train.to_numpy()
X_test = X_test.to_numpy()
# Reshape features for CNN input (assuming 1D features)
X_train_reshaped = X_train.reshape(X_train.shape[0], X_train.shape[1], 1)
X_test_reshaped = X_test.reshape(X_test.shape[0], X_test.shape[1], 1)
# Convert labels to categorical format for multi-class classification
y_train_cat = to_categorical(y_train)
y_test_cat = to_categorical(y_test)
# Define the ConvLSTM model
model = Sequential([
Conv1D(32, kernel_size=3, activation="relu", input_shape=(X_train_reshaped.shape[1], 1)),
MaxPooling1D(pool_size=2),
LSTM(64, return_sequences=True), # LSTM layer with return_sequences=True
LSTM(32), # Another LSTM layer
Flatten(),
Dense(128, activation="relu"),
Dense(len(np.unique(y)), activation="softmax") # Output layer with number of bird species
])
# Compile the model
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
# Train the model
model.fit(X_train_reshaped, y_train_cat, epochs=50, validation_data=(X_test_reshaped, y_test_cat))
# Evaluate the model
loss, accuracy = model.evaluate(X_test_reshaped, y_test_cat)
print(f"Test Accuracy: {accuracy:.4f}")
# Make predictions (optional)
# y_pred = model.predict(X_test_reshaped)
# print(classification_report(y_test, y_pred))
In conclusion, The study on Cornell bird sound classification revealed varying performance among ML and DL algorithms. While DL models like ResNet and LSTM show promise, their effectiveness depends on the dataset and task complexity. The research underscores the importance of exploring diverse algorithms for such challenges. Further investigation is necessary to fully grasp their real-world applicability in bird sound classification.
For complete code click here.
메타데이터
- post_id
- cf7dff81d665
- slug
- chirps-and-chips-ml-and-dl-algorithms-for-bird-sound-analysis-cf7dff81d665
- url
- https://medium.com/@pratyushareddy1629/chirps-and-chips-ml-and-dl-algorithms-for-bird-sound-analysis-cf7dff81d665
- canonical_url
- https://medium.com/@pratyushareddy1629/chirps-and-chips-ml-and-dl-algorithms-for-bird-sound-analysis-cf7dff81d665
- author_url
- https://medium.com/@pratyushareddy1629
- status
- ok
- fetched_at
- 2026-07-13 11:49:26