Spaces:
Sleeping
Sleeping
| """ | |
| App Gradio pour visualiser les embeddings de romans français | |
| Interface minimaliste avec style Seaborn | |
| """ | |
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from datasets import load_dataset | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| # ==================== CONFIGURATION ==================== | |
| DATASET_NAME = "hoololi/novels-embeddings-dataset" | |
| # Configuration du style visuel | |
| plt.style.use('default') | |
| sns.set_style("whitegrid") | |
| sns.set_palette("deep") | |
| # ==================== CHARGEMENT DES DONNÉES ==================== | |
| # Charger le dataset au démarrage | |
| print("🔄 Initialisation de l'application...") | |
| try: | |
| # Approche hybride : essayer plusieurs URLs parquet | |
| print("📊 Tentative de chargement parquet...") | |
| # URLs possibles pour le fichier parquet | |
| possible_urls = [ | |
| f"https://huggingface.co/datasets/{DATASET_NAME}/resolve/main/data/train-00000-of-00001-5211c5ec6e8e9559.parquet", # Nom exact avec hash | |
| f"https://huggingface.co/datasets/{DATASET_NAME}/resolve/main/data/train-00000-of-00001.parquet", | |
| f"https://huggingface.co/datasets/{DATASET_NAME}/resolve/main/train.parquet", | |
| f"https://huggingface.co/datasets/{DATASET_NAME}/resolve/main/data.parquet", | |
| f"https://huggingface.co/datasets/{DATASET_NAME}/resolve/main/dataset.parquet" | |
| ] | |
| df_full = None | |
| for url in possible_urls: | |
| try: | |
| print(f"🔗 Essai: {url}") | |
| df_full = pd.read_parquet(url) | |
| print(f"✅ Succès avec: {url}") | |
| break | |
| except Exception as url_error: | |
| print(f"❌ Échec: {url_error}") | |
| continue | |
| if df_full is not None: | |
| print(f"✅ Fichier parquet chargé: {len(df_full)} lignes") | |
| print(f"📊 Colonnes disponibles: {list(df_full.columns)}") | |
| # Sélectionner seulement les colonnes nécessaires | |
| columns_needed = ['roman', 'tsne_x', 'tsne_y'] | |
| # Vérifier que les colonnes existent | |
| missing_cols = [col for col in columns_needed if col not in df_full.columns] | |
| if missing_cols: | |
| print(f"⚠️ Colonnes manquantes: {missing_cols}") | |
| print(f"📋 Colonnes disponibles: {list(df_full.columns)}") | |
| raise Exception(f"Colonnes requises manquantes: {missing_cols}") | |
| dataset_df = df_full[columns_needed].copy() | |
| print(f"✅ Dataset filtré: {len(dataset_df)} phrases") | |
| available_novels = sorted(dataset_df['roman'].unique().tolist()) | |
| print(f"📚 Romans disponibles: {available_novels}") | |
| else: | |
| raise Exception("Aucune URL parquet n'a fonctionné") | |
| except Exception as e: | |
| print(f"❌ Erreur de chargement parquet: {e}") | |
| print("🔄 Fallback vers données de test...") | |
| # Fallback : données de test | |
| try: | |
| import numpy as np | |
| np.random.seed(42) | |
| test_data = [] | |
| romans = ["Proust - Test", "Dumas - Test", "Chrétien - Test", "Zola - Test"] | |
| for i, roman in enumerate(romans): | |
| for j in range(100): # 100 points par roman | |
| test_data.append({ | |
| 'roman': roman, | |
| 'tsne_x': np.random.normal(i*10, 5), # Clusters séparés | |
| 'tsne_y': np.random.normal(i*5, 3) | |
| }) | |
| dataset_df = pd.DataFrame(test_data) | |
| available_novels = romans | |
| print(f"✅ Données de test créées: {len(dataset_df)} phrases") | |
| except Exception as test_error: | |
| print(f"❌ Impossible de créer les données de test: {test_error}") | |
| dataset_df = None | |
| available_novels = [] | |
| # ==================== FONCTIONS DE VISUALISATION ==================== | |
| def create_embeddings_plot(selected_novels, figsize=(10, 8)): | |
| """Crée le graphique t-SNE avec les romans sélectionnés""" | |
| if dataset_df is None or not selected_novels: | |
| # Graphique vide en cas d'erreur | |
| fig, ax = plt.subplots(figsize=figsize) | |
| ax.text(0.5, 0.5, 'Aucune donnée à afficher', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=16) | |
| ax.set_xlim(0, 1) | |
| ax.set_ylim(0, 1) | |
| return fig | |
| # Filtrer les données selon les romans sélectionnés | |
| filtered_df = dataset_df[dataset_df['roman'].isin(selected_novels)] | |
| if filtered_df.empty: | |
| fig, ax = plt.subplots(figsize=figsize) | |
| ax.text(0.5, 0.5, 'Aucun roman sélectionné', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=16) | |
| ax.set_xlim(0, 1) | |
| ax.set_ylim(0, 1) | |
| return fig | |
| # Calculer les bornes globales pour garder l'échelle fixe | |
| all_x = dataset_df['tsne_x'] | |
| all_y = dataset_df['tsne_y'] | |
| x_margin = (all_x.max() - all_x.min()) * 0.05 | |
| y_margin = (all_y.max() - all_y.min()) * 0.05 | |
| x_min, x_max = all_x.min() - x_margin, all_x.max() + x_margin | |
| y_min, y_max = all_y.min() - y_margin, all_y.max() + y_margin | |
| # Créer le graphique avec style Seaborn | |
| fig, ax = plt.subplots(figsize=figsize) | |
| # Scatter plot avec Seaborn | |
| scatter = sns.scatterplot( | |
| data=filtered_df, | |
| x='tsne_x', | |
| y='tsne_y', | |
| hue='roman', | |
| alpha=0.7, | |
| s=60, # Taille des points | |
| ax=ax | |
| ) | |
| # Configuration du graphique | |
| plt.xlabel('t-SNE dimension 1', fontsize=12) | |
| plt.ylabel('t-SNE dimension 2', fontsize=12) | |
| # Fixer les bornes pour garder l'échelle constante | |
| plt.xlim(x_min, x_max) | |
| plt.ylim(y_min, y_max) | |
| # Configuration de la légende - en bas | |
| legend = plt.legend( | |
| title='Romans', | |
| bbox_to_anchor=(0.5, -0.15), | |
| loc='upper center', | |
| fontsize=10, | |
| title_fontsize=12, | |
| ncol=2 # Répartir sur 2 colonnes pour gagner de la place | |
| ) | |
| legend.set_frame_on(True) | |
| legend.get_frame().set_facecolor('white') | |
| legend.get_frame().set_alpha(0.9) | |
| # Grille et style | |
| plt.grid(True, alpha=0.3) | |
| plt.tight_layout() | |
| return fig | |
| # ==================== INTERFACE GRADIO ==================== | |
| def create_gradio_interface(): | |
| """Crée l'interface Gradio minimaliste""" | |
| if not available_novels: | |
| # Interface d'erreur | |
| with gr.Blocks(title="Erreur - Dataset non disponible") as demo: | |
| gr.Markdown("# ❌ Erreur") | |
| gr.Markdown(f"Impossible de charger le dataset `{DATASET_NAME}`") | |
| return demo | |
| # Interface principale | |
| with gr.Blocks( | |
| title="📚 Embeddings de Romans Français", | |
| theme=gr.themes.Soft(), | |
| css=""" | |
| .gradio-container { | |
| max-width: 1200px; | |
| margin: auto; | |
| } | |
| .plot-container { | |
| display: flex; | |
| justify-content: center; | |
| } | |
| """ | |
| ) as demo: | |
| # Header | |
| gr.Markdown( | |
| """ | |
| # 📚 Visualisation d'Embeddings de Romans Français | |
| Explorez comment les styles littéraires se regroupent dans l'espace des embeddings. | |
| """ | |
| ) | |
| # Contrôles - Checkboxes horizontales pour chaque roman | |
| gr.Markdown("### 🎛️ Sélection des romans") | |
| with gr.Row(): | |
| # Créer les checkboxes dynamiquement en horizontal | |
| novel_checkboxes = {} | |
| for novel in available_novels: | |
| novel_checkboxes[novel] = gr.Checkbox( | |
| label=novel, | |
| value=True, # Tous cochés par défaut | |
| interactive=True | |
| ) | |
| # Graphique principal | |
| plot_display = gr.Plot( | |
| label="📊 Projection t-SNE des embeddings", | |
| value=create_embeddings_plot(available_novels) | |
| ) | |
| # Événements - Mise à jour quand les checkboxes changent | |
| checkbox_inputs = list(novel_checkboxes.values()) | |
| def update_from_checkboxes(*checkbox_values): | |
| # Reconstruire le dictionnaire novel -> bool | |
| novel_selection = {novel: value for novel, value in zip(available_novels, checkbox_values)} | |
| selected_novels = [novel for novel, selected in novel_selection.items() if selected] | |
| # Créer le graphique | |
| fig = create_embeddings_plot(selected_novels) | |
| return fig | |
| # Connecter tous les checkboxes à la fonction de mise à jour | |
| for checkbox in checkbox_inputs: | |
| checkbox.change( | |
| fn=update_from_checkboxes, | |
| inputs=checkbox_inputs, | |
| outputs=[plot_display] | |
| ) | |
| return demo | |
| # ==================== LANCEMENT ==================== | |
| def create_demo(): | |
| """Fonction pour Hugging Face Spaces""" | |
| return create_gradio_interface() | |
| if __name__ == "__main__": | |
| # Créer et lancer l'interface | |
| demo = create_gradio_interface() | |
| demo.launch( | |
| share=True, | |
| debug=True, | |
| server_name="0.0.0.0", | |
| server_port=7860 | |
| ) | |
| # Export pour Spaces | |
| demo = create_gradio_interface() |