diff --git a/app_glob.py b/app_glob.py index d5783a3a883acd5bf8d48a7eaf7f8ca494981cec..0d0c2662c4c066b3ef5178d771e587a187e68342 100644 --- a/app_glob.py +++ b/app_glob.py @@ -1,40 +1,37 @@ -from flask import Flask, render_template, request, jsonify -import pandas as pd +from flask import Flask, request, jsonify, render_template import pickle -import os +import pandas as pd +import numpy as np +import traceback app = Flask(__name__) # Charger les modèles et scalers -model_paths = { - "binaire": "random_forest_model_binaire.pkl", - "sup0": "random_forest_model_sup0.pkl" -} -scaler_paths = { - "binaire": "scaler_binaire.pkl", - "sup0": "scaler_sup0.pkl" -} - -models = {} -scalers = {} - -for key in model_paths: - with open(model_paths[key], "rb") as model_file: - models[key] = pickle.load(model_file) - with open(scaler_paths[key], "rb") as scaler_file: - scalers[key] = pickle.load(scaler_file) - -# Charger les features -features_path = "features.txt" -if os.path.exists(features_path): - with open(features_path, "r") as f: - features = f.read().splitlines() -else: - features = [] +try: + models = { + "binaire": pickle.load(open("random_forest_model_binaire.pkl", "rb")), + "sup0": pickle.load(open("random_forest_model_sup0.pkl", "rb")), + } + + scalers = { + "binaire": pickle.load(open("scaler_binaire.pkl", "rb")), + "sup0": pickle.load(open("scaler_sup0.pkl", "rb")), + } +except Exception as e: + print("❌ Erreur lors du chargement des modèles ou scalers :", str(e)) + exit(1) + +# Liste des features attendues +expected_features = [ + "year", "acousticness", "danceability", "explicit", + "instrumentalness", "key", "liveness", "loudness", "mode", + "speechiness", "tempo", "valence", "nb_caracteres_sans_espaces", + "nb_artistes", "featuring", "duree_minute", "categorie_annee", "categorie_tempo" +] @app.route('/') -def index(): - return render_template('index-glob.html', features=features) +def home(): + return render_template('index-glob.html') @app.route('/predict', methods=['POST']) def predict(): @@ -46,14 +43,79 @@ def predict_sup0(): def predict_with_model(model_key): try: - data = request.get_json() - input_data = [float(data[feature]) for feature in features] - df_input = pd.DataFrame([input_data], columns=features) - df_scaled = scalers[model_key].transform(df_input) - prediction = models[model_key].predict(df_scaled) - return jsonify({"prediction": int(prediction[0])}) + data = request.form.to_dict() + + print("📩 Données reçues :", data) # Debugging + + # 🔹 Vérifier les valeurs et convertir en float sauf "name" + for key in data: + if key != "name": + try: + data[key] = float(data[key]) + except ValueError: + return jsonify({"error": f"Valeur invalide pour {key}"}), 400 + + + # 🔹 Calcul des features manquantes + data["nb_caracteres_sans_espaces"] = len(data.get("name", "").replace(" ", "")) + data["nb_artistes"] = data.get("artists", "").count(',') + 1 if "artists" in data else 1 + data["featuring"] = 1 if data["nb_artistes"] > 1 else 0 + data["duree_minute"] = float(data["duration_ms"]) / 60000 if "duration_ms" in data else 0 + + # Catégorie année + year = int(data["year"]) + data["categorie_annee"] = 3 if year < 1954 else 2 if year < 2002 else 1 + + # Catégorie tempo + tempo = float(data["tempo"]) + if 40 <= tempo < 60: + data["categorie_tempo"] = 1 + elif 60 <= tempo < 66: + data["categorie_tempo"] = 2 + elif 66 <= tempo < 76: + data["categorie_tempo"] = 3 + elif 76 <= tempo < 108: + data["categorie_tempo"] = 4 + elif 108 <= tempo < 120: + data["categorie_tempo"] = 5 + elif 120 <= tempo < 163: + data["categorie_tempo"] = 6 + elif 163 <= tempo < 200: + data["categorie_tempo"] = 7 + elif 200 <= tempo <= 208: + data["categorie_tempo"] = 8 + else: + data["categorie_tempo"] = 9 + + # 🔹 Supprimer les colonnes inutiles + data.pop("name", None) + data.pop("artists", None) + data.pop("duration_ms", None) + + # 🔹 Vérifier la présence des features attendues + missing_features = [f for f in expected_features if f not in data] + if missing_features: + return jsonify({"error": f"⚠️ Features manquantes: {missing_features}"}), 400 + + # 🔹 Construire DataFrame + input_data = pd.DataFrame([[data[f] for f in expected_features]], columns=expected_features) + + print("🔍 Features préparées :", input_data) # Debugging + + # 🔹 Normalisation + input_data_scaled = scalers[model_key].transform(input_data) + + # 🔹 Prédiction + prediction = models[model_key].predict(input_data_scaled) + + print("✅ Prédiction :", prediction) # Debugging + + return jsonify({"predictions": float(prediction[0])}) + except Exception as e: - return jsonify({"error": str(e)}) + print("❌ ERREUR SERVEUR :", str(e)) # Log de l'erreur serveur + print(traceback.format_exc()) # Stack trace + return jsonify({"error": str(e)}), 500 if __name__ == '__main__': app.run(debug=True) diff --git a/ml_binairefinal.py b/ml_binairefinal.py index 83ed42b319b2c4d1fb6b40609e3731a4c4cc99e1..6b891b7183e1fc2f883c0551cc0e8705c00175fe 100644 --- a/ml_binairefinal.py +++ b/ml_binairefinal.py @@ -22,7 +22,7 @@ def train_random_forest(df): 'release_date', 'date_sortie', 'duration_ms', 'nom_artiste']) # Liste des features dans le bon ordre (doit être identique dans l'API Flask) - expected_features = ['year', 'acousticness', 'danceability', 'energy', 'explicit', + expected_features = ['year', 'acousticness', 'danceability','explicit', 'instrumentalness', 'key', 'liveness', 'loudness', 'mode', 'speechiness', 'tempo', 'valence', 'nb_caracteres_sans_espaces', 'nb_artistes', 'featuring', 'duree_minute', 'categorie_annee', 'categorie_tempo'] diff --git a/mlsup0final.py b/mlsup0final.py index 5f196339fe7f14a5fe78485cd68a710c090aa025..d104dc9fa77a5a3f697bb415a84fae64dbd1b651 100644 --- a/mlsup0final.py +++ b/mlsup0final.py @@ -13,7 +13,7 @@ print('HELLO') df = pd.read_csv("data_sup_0popularity.csv") # 1️⃣ Vérifier que les features sont bien présentes -expected_features = ['year', 'acousticness', 'danceability', 'energy', 'explicit', +expected_features = ['year', 'acousticness', 'danceability', 'explicit', 'instrumentalness', 'key', 'liveness', 'loudness', 'mode', 'speechiness', 'tempo', 'valence', 'nb_caracteres_sans_espaces', 'nb_artistes', 'featuring', 'duree_minute', 'categorie_annee', 'categorie_tempo'] diff --git a/random_forest_model_binaire.pkl b/random_forest_model_binaire.pkl index 15c9b8599437cbd56cb6ad961a44277b31343d7a..d6dd7a1feb7630794fb61ec40444c7ead151e8e2 100644 Binary files a/random_forest_model_binaire.pkl and b/random_forest_model_binaire.pkl differ diff --git a/random_forest_model_sup0.pkl b/random_forest_model_sup0.pkl index 0cb2bf8f07d682e9d39e5e0661b74ce5e054242b..dc89669f8952ecb09ffbad10437dcc158e125e70 100644 Binary files a/random_forest_model_sup0.pkl and b/random_forest_model_sup0.pkl differ diff --git a/scaler_binaire.pkl b/scaler_binaire.pkl index 7081d6a4697e8d126c81ef4eb851b5a1967923a6..0541b1bae3ea4276274c726c322ee04cf3b3c677 100644 Binary files a/scaler_binaire.pkl and b/scaler_binaire.pkl differ diff --git a/scaler_sup0.pkl b/scaler_sup0.pkl index 46b67ebe499fd85ade2187ed9bf0d040cf43ff78..46a34f7e2fc70c9ed42800a96c9a3ac0f72ee1cd 100644 Binary files a/scaler_sup0.pkl and b/scaler_sup0.pkl differ diff --git a/templates/index-glob.html b/templates/index-glob.html index d1de2ba52b0578a93e4c330f5511b2eb225442fd..f0ea52635456de5d8a3820a4acef62ba2b8cc9b3 100644 --- a/templates/index-glob.html +++ b/templates/index-glob.html @@ -12,7 +12,6 @@ background-color: #121212; color: white; } - /* Sidebar */ .sidebar { width: 220px; background-color: #181818; @@ -44,6 +43,7 @@ .content { flex-grow: 1; padding: 20px; + text-align: center; } .tab-content { display: none; @@ -52,19 +52,20 @@ display: block; } form { - max-width: 600px; + max-width: 900px; margin: auto; background: #282828; padding: 20px; border-radius: 10px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 15px; } - label { display: block; - margin-top: 10px; font-weight: bold; } - input[type="text"], input[type="number"] { + input { width: 100%; padding: 8px; margin-top: 5px; @@ -74,6 +75,7 @@ color: white; } button { + grid-column: span 2; margin-top: 20px; padding: 10px 20px; background-color: #1DB954; @@ -94,112 +96,106 @@ </head> <body> - <!-- Sidebar pour naviguer entre les onglets --> + <!-- Barre de navigation --> <div class="sidebar"> <img src="https://upload.wikimedia.org/wikipedia/commons/2/26/Spotify_logo_with_text.svg" alt="Spotify Logo"> - <button onclick="showTab('tab1')">Prédiction Standard</button> - <button onclick="showTab('tab2')">Prédiction (>0)</button> + <button onclick="showTab('tab1')">Prédiction Binaire</button> + <button onclick="showTab('tab2')">Prédiction Supérieure à 0</button> </div> <!-- Contenu principal --> <div class="content"> <h1>Prédire la Popularité</h1> - <!-- Formulaire 1 : Prédiction Standard --> + <!-- Formulaire Binaire --> <div id="tab1" class="tab-content active"> - <form id="predictionForm"> - <label for="name">Titre de la chanson :</label> - <input type="text" id="name" name="name" required> - - <label for="year">Année (Sous format YYYY):</label> - <input type="number" step="0.0001" id="year" name="year" required> - - <label for="acousticness">Acousticness (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="acousticness" name="acousticness" required> - - <label for="danceability">Danceability (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="danceability" name="danceability" required> - - <label for="energy">Energy (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="energy" name="energy" required> - - <label for="explicit">Explicit (0 ou 1 en entier):</label> - <input type="number" id="explicit" name="explicit" required> - - <label for="instrumentalness">Instrumentalness (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="instrumentalness" name="instrumentalness" required> - - <label for="key">Key (0 à 11 en chiffre entier):</label> - <input type="number" id="key" name="key" required> - - <label for="liveness">Liveness (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="liveness" name="liveness" required> - - <label for="loudness">Loudness (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="loudness" name="loudness" required> - - <label for="mode">Mode (0 ou 1 en entier):</label> - <input type="number" id="mode" name="mode" required> - - <label for="speechiness">Speechiness (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="speechiness" name="speechiness" required> - - <label for="tempo">Tempo (Sous format d'un nombre décimal jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="tempo" name="tempo" required> - - <label for="valence">Valence (0 à 1 jusqu'à 4 chiffres après la virgule):</label> - <input type="number" step="0.0001" id="valence" name="valence" required> - - <button type="button" onclick="sendPrediction('/predict')">Prédire</button> + <form id="formBinaire" onsubmit="sendPrediction(event, '/predict')"> + <label for="name_bin">Titre de la chanson :</label> + <input type="text" id="name_bin" name="name" required> + + <label for="year_bin">Année :</label> + <input type="number" id="year_bin" name="year" required> + + <label for="acousticness_bin">Acousticness :</label> + <input type="number" step="0.0001" id="acousticness_bin" name="acousticness" required> + + <label for="danceability_bin">Danceability :</label> + <input type="number" step="0.0001" id="danceability_bin" name="danceability" required> + + <label for="explicit_bin">Explicit :</label> + <input type="number" id="explicit_bin" name="explicit" required> + + <label for="instrumentalness_bin">Instrumentalness :</label> + <input type="number" step="0.0001" id="instrumentalness_bin" name="instrumentalness" required> + + <label for="key_bin">Key :</label> + <input type="number" id="key_bin" name="key" required> + + <label for="liveness_bin">Liveness :</label> + <input type="number" step="0.0001" id="liveness_bin" name="liveness" required> + + <label for="loudness_bin">Loudness :</label> + <input type="number" step="0.0001" id="loudness_bin" name="loudness" required> + + <label for="mode_bin">Mode :</label> + <input type="number" id="mode_bin" name="mode" required> + + <label for="speechiness_bin">Speechiness :</label> + <input type="number" step="0.0001" id="speechiness_bin" name="speechiness" required> + + <label for="tempo_bin">Tempo :</label> + <input type="number" step="0.0001" id="tempo_bin" name="tempo" required> + + <label for="valence_bin">Valence :</label> + <input type="number" step="0.0001" id="valence_bin" name="valence" required> + + <button type="submit">Prédire</button> </form> </div> - <!-- Formulaire 2 : Prédiction (>0) --> + <!-- Formulaire Supérieur à 0 --> <div id="tab2" class="tab-content"> - <form id="predictionFormSup0"> - <label for="name">Titre de la chanson :</label> - <input type="text" id="name" name="name" required> - - <label for="year">Année :</label> - <input type="number" id="year" name="year" required> - - <label for="acousticness">Acousticness :</label> - <input type="number" step="0.0001" id="acousticness" name="acousticness" required> - - <label for="danceability">Danceability :</label> - <input type="number" step="0.0001" id="danceability" name="danceability" required> - - <label for="energy">Energy :</label> - <input type="number" step="0.0001" id="energy" name="energy" required> - - <label for="explicit">Explicit (0 ou 1) :</label> - <input type="number" id="explicit" name="explicit" required> - - <label for="instrumentalness">Instrumentalness :</label> - <input type="number" step="0.0001" id="instrumentalness" name="instrumentalness" required> - - <label for="key">Key :</label> - <input type="number" id="key" name="key" required> - - <label for="liveness">Liveness :</label> - <input type="number" step="0.0001" id="liveness" name="liveness" required> - - <label for="loudness">Loudness :</label> - <input type="number" step="0.0001" id="loudness" name="loudness" required> - - <label for="mode">Mode :</label> - <input type="number" id="mode" name="mode" required> - - <label for="speechiness">Speechiness :</label> - <input type="number" step="0.0001" id="speechiness" name="speechiness" required> - - <label for="tempo">Tempo :</label> - <input type="number" step="0.0001" id="tempo" name="tempo" required> - - <label for="valence">Valence :</label> - <input type="number" step="0.0001" id="valence" name="valence" required> - - <button type="button" onclick="sendPrediction('/predict_sup0', 'predictionFormSup0')">Prédire (>0)</button> + <form id="formSup0" onsubmit="sendPrediction(event, '/predict_sup0')"> + <label for="name_bin">Titre de la chanson :</label> + <input type="text" id="name_sup0" name="name" required> + + <label for="year_bin">Année :</label> + <input type="number" id="year_sup0" name="year" required> + + <label for="acousticness_bin">Acousticness :</label> + <input type="number" step="0.0001" id="acousticness_sup0" name="acousticness" required> + + <label for="danceability_bin">Danceability :</label> + <input type="number" step="0.0001" id="danceability_sup0" name="danceability" required> + + <label for="explicit_bin">Explicit :</label> + <input type="number" id="explicit_sup0" name="explicit" required> + + <label for="instrumentalness_bin">Instrumentalness :</label> + <input type="number" step="0.0001" id="instrumentalness_sup0" name="instrumentalness" required> + + <label for="key_bin">Key :</label> + <input type="number" id="key_sup0" name="key" required> + + <label for="liveness_bin">Liveness :</label> + <input type="number" step="0.0001" id="liveness_sup0" name="liveness" required> + + <label for="loudness_bin">Loudness :</label> + <input type="number" step="0.0001" id="loudness_sup0" name="loudness" required> + + <label for="mode_bin">Mode :</label> + <input type="number" id="mode_sup0" name="mode" required> + + <label for="speechiness_bin">Speechiness :</label> + <input type="number" step="0.0001" id="speechiness_sup0" name="speechiness" required> + + <label for="tempo_bin">Tempo :</label> + <input type="number" step="0.0001" id="tempo_sup0" name="tempo" required> + + <label for="valence_bin">Valence :</label> + <input type="number" step="0.0001" id="valence_sup0" name="valence" required> + + <button type="submit">Prédire (>0)</button> </form> </div> @@ -207,29 +203,19 @@ </div> <script> - // Fonction pour afficher un onglet et cacher l'autre function showTab(tabId) { - document.querySelectorAll('.tab-content').forEach(tab => { - tab.style.display = "none"; - }); + document.querySelectorAll('.tab-content').forEach(tab => tab.style.display = "none"); document.getElementById(tabId).style.display = "block"; } - // Fonction pour envoyer la requête au bon endpoint - async function sendPrediction(endpoint, formId = "predictionForm") { - const form = document.getElementById(formId); - const formData = new FormData(form); - - const response = await fetch(endpoint, { - method: "POST", - body: formData - }); - + async function sendPrediction(event, endpoint) { + event.preventDefault(); + const formData = new FormData(event.target); + const response = await fetch(endpoint, { method: "POST", body: formData }); const result = await response.json(); document.getElementById("result").innerText = `Prédiction: ${result.predictions}`; } - // Afficher l'onglet par défaut showTab('tab1'); </script>