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 } df = pd.read_csv("all_genres_clean.csv",low_memory=False) df_input = df[NUMERICAL_FEATURES] model = GenrePredictor() answer = model.predict(df_input.iloc[105148].to_dict()) print(answer) print("Original :", df[["genre","sub_genres"]].iloc[105148])