← Back to list

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…

Prathyusha Reddy · 2024-04-05 08:40 · 261 claps · 5.2 min read
#audio-analytics #sound-classification #bird-sounds #feature-extraction #eda
Open on Medium ↗
Wiki topics: ML · Machine Learning GRW · Growth & Analytics 💻 · Programming 🐾 · Pets & Animals 🎵 · Music & Audio

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