hoololi's picture
Upload app.py
95258cd verified
"""
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()