import streamlit as st import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from userData import load_and_predict import tempfile from sklearn.metrics import classification_report, confusion_matrix, accuracy_score # 1. Définir la couleur de fond de Streamlit (gris foncé par défaut) STREAMLIT_BG_COLOR = "#0E1117" # Couleur par défaut de Streamlit (dark theme) # 2. Configurer le style de Matplotlib pour correspondre plt.style.use('dark_background') # Style sombre de base plt.rcParams['figure.facecolor'] = STREAMLIT_BG_COLOR # Fond de la figure plt.rcParams['axes.facecolor'] = STREAMLIT_BG_COLOR # Fond du graphique # Configuration de la page st.set_page_config(page_title="CSI Activity Recognition", layout="wide") st.markdown(""" """, unsafe_allow_html=True) # ----------- Fonctions utiles ----------- @st.cache_data def load_csv_file(file_path): return pd.read_csv(file_path) # Fonction pour créer une matrice de confusion de taille réduite def plot_confusion_matrix(cm, title="Matrice de Confusion"): # Réduire la taille de la figure fig, ax = plt.subplots(figsize=(5, 4)) # Taille réduite (était 8, 6) # Créer le heatmap avec des paramètres ajustés sns.heatmap( cm, annot=True, # Afficher les valeurs fmt="d", # Format entier cmap="Purples", # Palette de couleurs ax=ax, cbar=False, # Supprimer la barre de couleur pour gagner de l'espace annot_kws={"size": 8} # Réduire la taille des annotations ) # Configurer les titres et labels ax.set_title(title, fontsize=10) ax.set_xlabel("Prédit", fontsize=8) ax.set_ylabel("Réel", fontsize=8) # Réduire la taille des ticks ax.tick_params(axis='both', which='major', labelsize=7) # Ajuster l'espacement plt.tight_layout() return fig # ----------- Sidebar ----------- with st.sidebar: # Image petite et centrée col1, col2, col3 = st.sidebar.columns([1, 2, 1]) with col2: st.image("cerveau.png", width=100) # Custom colored title instead of st.title st.markdown('', unsafe_allow_html=True) # Custom colored subheader instead of st.subheader st.markdown('', unsafe_allow_html=True) room_path_dataSet = st.text_input("Dataset (.csv)", "data/Dataset/dataset.csv") y_true_path = "data/Dataset/y_true.npy" y_pred_path = "data/Dataset/y_pred.npy" gwo_results_path = "data/Dataset/gwo_results.csv" st.markdown('', unsafe_allow_html=True) # Custom colored subheader uploaded_fileX = st.file_uploader("y_pred.npy", type=["npy"]) uploaded_fileY = st.file_uploader("y_true.npy", type=["npy"]) st.markdown('', unsafe_allow_html=True) uploaded_sample = st.file_uploader(" Charger un fichier CSV pour tester", type=["csv"], key="single_pred") st.markdown("---") # ----------- Main Content ----------- st.title(" Évaluation complète du Modèle") st.markdown("Analyse des performances du modèle de reconnaissance d'activités CSI") # Création des onglets principaux tab_eval, tab_pred, tab_custom = st.tabs([ " Évaluation Complète", " Prédiction Unique", " Évaluation Perso" ]) # Onglet 1: Évaluation Complète with tab_eval: st.header("Analyse Complète du Modèle") # Bouton principal de lancement if st.button("🚀 Lancer l'Évaluation Complète", type="primary"): with st.spinner("Analyse en cours..."): try: # Chargement des données y_true = np.load(y_true_path, allow_pickle=True) y_pred = np.load(y_pred_path, allow_pickle=True) results_df = pd.read_csv(gwo_results_path) dataset = load_csv_file(room_path_dataSet) tableau = pd.read_csv("data/Dataset/labels.csv") # Création des onglets tab1, tab2, tab3, tab4, tab5 = st.tabs([ "Données CSI", "Résultats GWO", "Évolution Accuracy", "Rapport de Classification", "Matrice de Confusion" ]) # Onglet 1: Décomposition des données with tab1: st.header("Aperçu du Dataset CSI") st.dataframe(dataset) st.header("Visualisation des données CSI") st.dataframe(tableau) activity_counts = tableau['Activité'].value_counts().reset_index() activity_counts.columns = ['Activité', 'Nombre'] file_path = "data/Dataset/labels.csv" df = pd.read_csv(file_path) # === 2. Créer le graphique avec seaborn === st.subheader("📊 Nombre d'occurrences par activité") # Graphique avec seaborn fig, ax = plt.subplots(figsize=(8, 4)) sns.barplot(x='Activité', y='Nombre', data=df, palette='viridis', ax=ax,edgecolor='white',linewidth=0.5) ax.set_title("Nombre d'occurrences par activité",color='white') ax.set_xlabel("Activité",color='white') ax.set_ylabel("Nombre",color='white') ax.tick_params(colors='white') # Set grid and spines ax.grid(color='gray', linestyle=':', linewidth=0.5) for spine in ax.spines.values(): spine.set_edgecolor('gray') ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha="right",color='white') plt.tight_layout() # === 3. Afficher le graphique === st.pyplot(fig,use_container_width=False) # Onglet 2: Résultats GWO with tab2: st.header("Résultats d'optimisation GWO") st.dataframe(results_df) st.markdown("---") st.header("Meilleurs hyperparamètres") best_config = results_df.sort_values(by="val_loss").iloc[0] # Création d'un dataframe pour une meilleure présentation best_params_df = pd.DataFrame({ 'Paramètre': best_config.index, 'Valeur': best_config.values }).reset_index(drop=True) # Style du tableau st.dataframe( best_params_df.style .set_properties(**{'border': '1px solid #dee2e6'}) .highlight_max(subset=['Valeur'], color='#de2e03') .highlight_min(subset=['Valeur'], color='#de2e03'), height=(len(best_params_df) + 1) * 35 + 3, use_container_width=True ) # Métriques en dessous col1, col2 = st.columns(2) with col1: st.metric("Meilleure accuracy", f"{best_config['val_accuracy']:.4f}") with col2: st.metric("Plus faible loss", f"{best_config['val_loss']:.4f}") # Onglet 3: Évolution Accuracy with tab3: st.header("Progression de l'accuracy") fig, ax = plt.subplots(figsize=(10, 5)) ax.plot(results_df["val_accuracy"], marker="o", linestyle="-", color="#6366F1") ax.set_xlabel("Itérations") ax.set_ylabel("Accuracy") ax.grid(True, alpha=0.3) st.pyplot(fig) # Onglet 4: Rapport de classification with tab4: st.header("Rapport de classification détaillé") report = classification_report(y_true, y_pred, output_dict=True) st.dataframe(pd.DataFrame(report).transpose()) st.markdown("---") st.header("Metrics globales") acc = accuracy_score(y_true, y_pred) col1, col2, col3 = st.columns(3) with col1: st.metric("Accuracy", f"{acc:.2%}") with col2: st.metric("Précision moyenne", f"{report['weighted avg']['precision']:.2%}") with col3: st.metric("Rappel moyen", f"{report['weighted avg']['recall']:.2%}") # Onglet 5: Matrice de confusion with tab5: st.header("Matrice de confusion") cm = confusion_matrix(y_true, y_pred) # Utiliser des colonnes pour contraindre la largeur col1, col2, col3 = st.columns([1, 2, 1]) with col2: # Utiliser notre fonction personnalisée pour créer une matrice plus petite fig = plot_confusion_matrix(cm) st.pyplot(fig) except FileNotFoundError as e: st.error(f"❌ Fichier non trouvé : {e}") except Exception as e: st.error(f"❌ Erreur : {e}") # Onglet 2: Prédiction Unique with tab_pred: st.header(" Prédiction sur échantillon unique") # Afficher le bouton en permanence if st.button("Lancer la Prédiction", type="primary", key="single_pred_btn"): # Vérifier l'upload seulement quand on clique if uploaded_sample is None: st.warning(" Veuillez d'abord télécharger un fichier CSV dans la sidebar") else: with st.spinner("Prédiction en cours..."): with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as tmp: tmp.write(uploaded_sample.read()) result = load_and_predict(tmp.name, model_path="data/Dataset/best_model.h5") # Affichage des résultats (identique à avant) col1, col2 = st.columns([1, 2]) with col1: st.markdown(""" ### Résultat **Classe réelle:** `{}` **Classe prédite:** `{}` """.format(result[0], result[1])) st.markdown(""" ### Probabilités """) probs_df = pd.DataFrame({ 'Classe': result[3], 'Probabilité': result[2] }) st.dataframe( probs_df.style.format({"Probabilité": "{:.4f}"}), height=200 ) with col2: st.markdown(""" ### Visualisation """) fig4, ax4 = plt.subplots(figsize=(8, 4)) bars = ax4.bar(result[3], result[2], color='#6366F1') ax4.set_ylim(0, 1) ax4.set_ylabel("Probabilité", fontsize=10) ax4.set_title(f"Probabilités par Classe", fontsize=12) plt.xticks(rotation=45, fontsize=9) for bar in bars: height = bar.get_height() ax4.text(bar.get_x() + bar.get_width()/2., height, f'{height:.2f}', ha='center', va='bottom', fontsize=8) st.pyplot(fig4) # Onglet 3: Évaluation Personnalisée with tab_custom: st.header("Évaluation avec Vos Fichiers") # Afficher le bouton en permanence if st.button("Analyser les Fichiers", type="primary", key="custom_eval_btn"): # Vérifier les uploads seulement quand on clique if uploaded_fileX is None or uploaded_fileY is None: st.warning(" Veuillez d'abord télécharger les fichiers y_pred.npy et y_true.npy dans la sidebar") else: with st.spinner("Traitement en cours..."): try: y_pred_user = np.load(uploaded_fileX, allow_pickle=True) y_true_user = np.load(uploaded_fileY, allow_pickle=True) # Sous-onglets pour l'évaluation personnalisée custom_tab1, custom_tab2 = st.tabs([" Classification", " Confusion"]) with custom_tab1: st.header("Rapport de Classification") report = classification_report(y_true_user, y_pred_user, output_dict=True) st.dataframe( pd.DataFrame(report).transpose(), height=400, use_container_width=True ) col1, col2, col3 = st.columns(3) with col1: st.metric("Accuracy", f"{accuracy_score(y_true_user, y_pred_user):.2%}") with col2: st.metric("Précision", f"{report['weighted avg']['precision']:.2%}") with col3: st.metric("Rappel", f"{report['weighted avg']['recall']:.2%}") with custom_tab2: st.header("Matrice de Confusion") cm = confusion_matrix(y_true_user, y_pred_user) fig = plot_confusion_matrix(cm, "Matrice de Confusion Personnalisée") st.pyplot(fig) except Exception as e: st.error(f" Erreur lors du chargement : {str(e)}") # Message d'information (conditionnel) if uploaded_fileX is None or uploaded_fileY is None: st.info(""" Pour utiliser cette fonctionnalité : 1. Téléchargez un fichier **y_pred.npy** (prédictions) 2. Téléchargez un fichier **y_true.npy** (vraies labels) dans la section sidebar """) # Style CSS pour la sidebar st.markdown(""" """, unsafe_allow_html=True)