rocketship_data / opencode-projects /predict_genre.py
vicky4s4s's picture
Upload 10 files
bf356c4 verified
import warnings
warnings.filterwarnings("ignore")
import pandas as pd
import numpy as np
import pickle
MODEL_PATH = "genre_model.pkl"
SCALER_PATH = "genre_scaler.pkl"
ENCODER_PATH = "genre_encoder.pkl"
NUMERICAL_FEATURES = [
"melody_complexity (vocals)",
"melody_range (vocals)",
"melody_variability (vocals)",
"tempo_bpm_original (mix)",
"danceability custom (mix)",
"loudness_integrated_lufs custom (mix)",
"loudness_range_lu custom (mix)",
"energy_librosa (mix)",
"energy_librosa_std (mix)",
"energy_essentia (mix)",
"energy_essentia_std (mix)",
"energy_combined (mix)",
"spectral_centroid_mean custom (mix)",
"mfcc_mean_1 (mix)",
"mfcc_mean_2 (mix)",
"chroma_mean (mix)",
"spectral_contrast_mean (mix)",
"repetition_score custom (mix)",
"pitch_mean (mix)",
"pitch_std (mix)",
"rms_energy_mean (mix)",
"rms_energy_std (mix)",
"zero_crossing_rate (mix)",
]
def engineer_features(df, feature_cols):
df = df.copy()
df['energy_per_tempo'] = df['energy_combined (mix)'] / (df['tempo_bpm_original (mix)'] + 1)
df['dance_energy_ratio'] = df['danceability custom (mix)'] * df['energy_combined (mix)']
df['loudness_range_ratio'] = df['loudness_range_lu custom (mix)'] / (abs(df['loudness_integrated_lufs custom (mix)']) + 1)
df['melody_energy'] = df['melody_variability (vocals)'] * df['energy_combined (mix)']
df['spectral_complexity'] = df['spectral_centroid_mean custom (mix)'] * df['spectral_contrast_mean (mix)']
df['mfcc_ratio'] = df['mfcc_mean_1 (mix)'] / (abs(df['mfcc_mean_2 (mix)']) + 1)
df['rhythm_strength'] = df['tempo_bpm_original (mix)'] * df['danceability custom (mix)']
df['pitch_variation'] = df['pitch_std (mix)'] / (df['pitch_mean (mix)'] + 1)
df['rms_energy_ratio'] = df['rms_energy_mean (mix)'] / (df['rms_energy_std (mix)'] + 1)
df['chroma_energy'] = df['chroma_mean (mix)'] * df['energy_combined (mix)']
df['zero_tempo'] = df['zero_crossing_rate (mix)'] * df['tempo_bpm_original (mix)']
df['tempo_category'] = np.where(df['tempo_bpm_original (mix)'] < 100, 0, np.where(df['tempo_bpm_original (mix)'] < 130, 1, 2))
df['energy_category'] = np.where(df['energy_combined (mix)'] < 0.3, 0, np.where(df['energy_combined (mix)'] < 0.6, 1, 2))
df['dance_category'] = np.where(df['danceability custom (mix)'] < 0.5, 0, np.where(df['danceability custom (mix)'] < 0.75, 1, 2))
engineered = ['energy_per_tempo', 'dance_energy_ratio', 'loudness_range_ratio', 'melody_energy', 'spectral_complexity',
'mfcc_ratio', 'rhythm_strength', 'pitch_variation', 'rms_energy_ratio', 'chroma_energy', 'zero_tempo',
'tempo_category', 'energy_category', 'dance_category']
return df, feature_cols + engineered
class GenrePredictor:
def __init__(self):
with open(MODEL_PATH, "rb") as f:
self.model = pickle.load(f)
with open(SCALER_PATH, "rb") as f:
self.scaler = pickle.load(f)
with open(ENCODER_PATH, "rb") as f:
self.genre_encoder, self.all_subgenres, self.all_features = pickle.load(f)
def predict(self, feature_dict):
df = pd.DataFrame([feature_dict])
df, _ = engineer_features(df, NUMERICAL_FEATURES)
for col in self.all_features:
if col not in df.columns:
df[col] = 0
values = df[self.all_features].values[0]
values = np.nan_to_num(values, nan=0, posinf=0, neginf=0)
input_scaled = self.scaler.transform(values.reshape(1, -1))
genre_idx = self.model.predict(input_scaled)[0]
genre = self.genre_encoder.inverse_transform([genre_idx])[0]
genre_probs = self.model.predict_proba(input_scaled)[0]
top_indices = np.argsort(genre_probs)[::-1][:5]
similar = [(self.genre_encoder.classes_[i], genre_probs[i]) for i in top_indices]
related_subs = [s for s in self.all_subgenres if genre.lower() in s.lower()]
if not related_subs:
related_subs = self.all_subgenres[:10]
return {
"genre": genre,
"similar_genres": similar,
"subgenres": related_subs
}
if __name__ == "__main__":
predictor = GenrePredictor()
# Example: Pass a row dict to predict
song_features = {
"melody_complexity (vocals)": 2.5,
"melody_range (vocals)": 30.0,
"melody_variability (vocals)": 0.55,
"tempo_bpm_original (mix)": 140.0,
"danceability custom (mix)": 0.70,
"loudness_integrated_lufs custom (mix)": -12.0,
"loudness_range_lu custom (mix)": 5.0,
"energy_librosa (mix)": 0.5,
"energy_librosa_std (mix)": 0.15,
"energy_essentia (mix)": 0.3,
"energy_essentia_std (mix)": 0.15,
"energy_combined (mix)": 0.45,
"spectral_centroid_mean custom (mix)": 0.13,
"mfcc_mean_1 (mix)": 150.0,
"mfcc_mean_2 (mix)": -20.0,
"chroma_mean (mix)": 0.45,
"spectral_contrast_mean (mix)": 20.0,
"repetition_score custom (mix)": 0.007,
"pitch_mean (mix)": 200.0,
"pitch_std (mix)": 80.0,
"rms_energy_mean (mix)": 0.5,
"rms_energy_std (mix)": 0.15,
"zero_crossing_rate (mix)": 0.05,
}
print("\n" + "=" * 60)
print("GENRE PREDICTION WITH DICT INPUT")
print("=" * 60)
print("\nInput Dictionary:")
for k, v in song_features.items():
print(f" {k}: {v}")
result = predictor.predict(song_features)
print("\n" + "=" * 60)
print("PREDICTION RESULT")
print("=" * 60)
print(f"\n GENRE: {result['genre']}")
print("\n Similar Genres:")
for g, prob in result['similar_genres']:
print(f" - {g}: {prob:.1%}")
print("\n Sub-genres:")
for sub in result['subgenres'][:8]:
print(f" - {sub}")