#!/usr/bin/env python3 """ Script pour tester les résultats de Yourbench et vérifier les datasets sur le Hub Hugging Face. """ import os import sys import json import argparse import requests import tempfile from datetime import datetime from typing import Dict, List, Any, Optional, Tuple # Vérifier si les bibliothèques nécessaires sont installées try: from dotenv import load_dotenv from huggingface_hub import HfApi, DatasetInfo, ModelInfo from loguru import logger import pandas as pd except ImportError: print("Installation des dépendances...") import subprocess subprocess.run(["pip", "install", "python-dotenv", "huggingface_hub", "loguru", "pandas", "pyarrow"], check=True) from dotenv import load_dotenv from huggingface_hub import HfApi, DatasetInfo, ModelInfo from loguru import logger import pandas as pd # Charger les variables d'environnement depuis .env load_dotenv() # Configuration de la journalisation logger.remove() logger.add(sys.stderr, format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}") logger.add("yourbench_tests.log", rotation="10 MB", retention="1 week") def configure_argument_parser() -> argparse.ArgumentParser: """Configure le parser d'arguments.""" parser = argparse.ArgumentParser(description="Tester les résultats de Yourbench et vérifier les datasets") parser.add_argument("--dataset", type=str, help="Nom du dataset à vérifier (sans le nom de l'organisation)") parser.add_argument("--org", type=str, default=os.environ.get("HF_ORGANIZATION", "yourbench"), help="Organisation Hugging Face (défaut: valeur de HF_ORGANIZATION dans .env ou 'yourbench')") parser.add_argument("--verbose", "-v", action="store_true", help="Afficher des informations détaillées") return parser class YourbenchTester: """Classe pour tester les résultats et datasets de Yourbench.""" def __init__(self, organization: str, verbose: bool = False): """Initialise le testeur Yourbench. Args: organization: Nom de l'organisation sur Hugging Face verbose: Afficher des informations détaillées """ self.organization = organization self.verbose = verbose self.hf_token = os.environ.get("HF_TOKEN") if not self.hf_token: logger.error("Variable d'environnement HF_TOKEN non trouvée dans le fichier .env") sys.exit(1) self.api = HfApi(token=self.hf_token) logger.info(f"Initialisation du testeur pour l'organisation: {organization}") def test_dataset_exists(self, dataset_name: str) -> Optional[DatasetInfo]: """Vérifie si un dataset existe sur le Hub. Args: dataset_name: Nom du dataset à vérifier Returns: Informations sur le dataset s'il existe, None sinon """ full_dataset_name = f"{self.organization}/{dataset_name}" logger.info(f"Vérification de l'existence du dataset: {full_dataset_name}") try: dataset_info = self.api.dataset_info(full_dataset_name) logger.success(f"Dataset {full_dataset_name} trouvé!") if self.verbose: logger.info(f"ID: {dataset_info.id}") logger.info(f"Dernière modification: {dataset_info.lastModified}") logger.info(f"SHA: {dataset_info.sha}") return dataset_info except Exception as e: logger.error(f"Impossible de trouver le dataset {full_dataset_name}: {str(e)}") return None def analyze_dataset_content(self, dataset_name: str) -> Tuple[bool, Dict[str, Any]]: """Analyse le contenu d'un dataset. Args: dataset_name: Nom du dataset à analyser Returns: Tuple contenant un booléen indiquant si l'analyse a réussi et un dictionnaire de statistiques """ full_dataset_name = f"{self.organization}/{dataset_name}" logger.info(f"Analyse du contenu du dataset: {full_dataset_name}") stats = { "fichiers": 0, "taille_totale": 0, "fichiers_json": 0, "fichiers_parquet": 0, "a_questions": False, "nb_questions": 0, "structure_parquet": {}, "types_documents": set() } try: # Lister les fichiers dans le dataset files = self.api.list_repo_files(full_dataset_name, repo_type="dataset") stats["fichiers"] = len(files) if self.verbose: logger.info(f"Fichiers trouvés dans le dataset: {len(files)}") for file in files[:10]: # Limiter à 10 fichiers pour éviter un affichage trop verbeux logger.info(f" - {file}") if len(files) > 10: logger.info(f" ... et {len(files) - 10} fichiers supplémentaires") # Vérifier la présence de fichiers questions question_files = [f for f in files if "question" in f.lower() and f.endswith(".json")] stats["fichiers_json"] = len([f for f in files if f.endswith(".json")]) # Vérifier les fichiers Parquet qui sont utilisés par Yourbench parquet_files = [f for f in files if f.endswith(".parquet")] stats["fichiers_parquet"] = len(parquet_files) if parquet_files: logger.info(f"Fichiers Parquet trouvés: {len(parquet_files)}") # Analyser un échantillon de fichiers Parquet for parquet_file in parquet_files[:3]: # Limiter à 3 fichiers pour l'analyse category = parquet_file.split('/')[0] if '/' in parquet_file else "unknown" logger.info(f"Analyse du fichier Parquet: {parquet_file} (catégorie: {category})") try: # Télécharger le fichier Parquet temp_file = self.api.hf_hub_download( repo_id=full_dataset_name, filename=parquet_file, repo_type="dataset" ) # Lire le fichier Parquet avec pandas df = pd.read_parquet(temp_file) # Ajouter des statistiques stats["structure_parquet"][category] = { "colonnes": list(df.columns), "nb_lignes": len(df), "exemple": df.iloc[0].to_dict() if len(df) > 0 else {} } # Vérifier si ce fichier contient des questions if any(col for col in df.columns if "question" in col.lower()): stats["a_questions"] = True question_col = next(col for col in df.columns if "question" in col.lower()) stats["nb_questions"] = len(df) # Récupérer un exemple de question if len(df) > 0 and question_col in df.columns: logger.info(f"Exemple de question: {df[question_col].iloc[0][:100]}...") # Identifier les types de documents si disponible if "doc_type" in df.columns and len(df) > 0: doc_types = df["doc_type"].unique() stats["types_documents"].update(doc_types) except Exception as e: logger.warning(f"Erreur lors de l'analyse du fichier {parquet_file}: {str(e)}") # Convertir le set en liste pour la sérialisation JSON stats["types_documents"] = list(stats["types_documents"]) if question_files: stats["a_questions"] = True # Analyser un fichier de questions pour comprendre sa structure sample_file = question_files[0] content = self.api.hf_hub_download( repo_id=full_dataset_name, filename=sample_file, repo_type="dataset" ) with open(content, 'r') as f: data = json.load(f) if isinstance(data, list): stats["nb_questions"] = len(data) elif isinstance(data, dict) and "questions" in data: stats["nb_questions"] = len(data["questions"]) logger.success(f"Fichiers de questions trouvés: {len(question_files)}") logger.info(f"Exemple de fichier analysé: {sample_file}") logger.info(f"Nombre de questions trouvées: {stats['nb_questions']}") return True, stats except Exception as e: logger.error(f"Erreur lors de l'analyse du dataset {full_dataset_name}: {str(e)}") return False, stats def check_evaluation_results(self, dataset_name: str) -> bool: """Vérifie s'il existe des résultats d'évaluation pour ce dataset. Args: dataset_name: Nom du dataset à vérifier Returns: True si des résultats d'évaluation existent, False sinon """ logger.info(f"Recherche de résultats d'évaluation pour le dataset: {dataset_name}") try: # Lister tous les datasets de l'organisation datasets = self.api.list_datasets(author=self.organization) # Chercher les datasets d'évaluation eval_datasets = [ds for ds in datasets if ds.id.startswith(f"{self.organization}/evaluation-")] if self.verbose: logger.info(f"Datasets d'évaluation trouvés: {len(eval_datasets)}") for ds in eval_datasets[:5]: logger.info(f" - {ds.id}") # Vérifier si le dataset spécifié est mentionné dans les évaluations for eval_ds in eval_datasets: try: # Télécharger le README pour voir si le dataset est mentionné readme_path = self.api.hf_hub_download( repo_id=eval_ds.id, filename="README.md", repo_type="dataset" ) with open(readme_path, 'r') as f: readme_content = f.read() if dataset_name in readme_content: logger.success(f"Résultats d'évaluation trouvés dans: {eval_ds.id}") return True except: continue logger.warning(f"Aucun résultat d'évaluation trouvé pour le dataset: {dataset_name}") return False except Exception as e: logger.error(f"Erreur lors de la recherche de résultats d'évaluation: {str(e)}") return False def check_model_performances(self, dataset_name: str) -> Dict[str, float]: """Vérifie les performances des modèles sur le dataset spécifié. Args: dataset_name: Nom du dataset à vérifier Returns: Dictionnaire des performances des modèles (model_name -> score) """ logger.info(f"Vérification des performances des modèles sur le dataset: {dataset_name}") performances = {} try: # Cette partie est spéculative car nous ne connaissons pas la structure exacte # des résultats. Une approche possible serait de chercher des fichiers JSON # contenant des métriques dans les datasets d'évaluation. # Chercher les datasets d'évaluation datasets = self.api.list_datasets(author=self.organization) eval_datasets = [ds for ds in datasets if ds.id.startswith(f"{self.organization}/evaluation-")] for eval_ds in eval_datasets: try: files = self.api.list_repo_files(eval_ds.id, repo_type="dataset") result_files = [f for f in files if "result" in f.lower() and f.endswith(".json")] for result_file in result_files: file_path = self.api.hf_hub_download( repo_id=eval_ds.id, filename=result_file, repo_type="dataset" ) with open(file_path, 'r') as f: results = json.load(f) # Analyse basique des résultats (à adapter selon la structure réelle) if "model_name" in results and "metrics" in results: model_name = results["model_name"] metrics = results["metrics"] # Prendre la première métrique trouvée comme score if metrics and isinstance(metrics, dict): first_metric = list(metrics.keys())[0] performances[model_name] = metrics[first_metric] except: continue if performances: logger.success(f"Performances trouvées pour {len(performances)} modèles") for model, score in performances.items(): logger.info(f" - {model}: {score}") else: logger.warning("Aucune performance de modèle trouvée") return performances except Exception as e: logger.error(f"Erreur lors de la vérification des performances: {str(e)}") return {} def main(): """Fonction principale.""" parser = configure_argument_parser() args = parser.parse_args() if not args.dataset: logger.error("Veuillez spécifier un dataset avec --dataset") parser.print_help() return # Créer le testeur tester = YourbenchTester(args.org, args.verbose) # 1. Vérifier l'existence du dataset dataset_info = tester.test_dataset_exists(args.dataset) if not dataset_info: logger.error(f"Le dataset {args.org}/{args.dataset} n'existe pas ou n'est pas accessible") return # 2. Analyser le contenu du dataset success, stats = tester.analyze_dataset_content(args.dataset) if success: logger.info("\n=== Statistiques du dataset ===") logger.info(f"Nombre de fichiers: {stats['fichiers']}") logger.info(f"Fichiers JSON: {stats['fichiers_json']}") logger.info(f"Fichiers Parquet: {stats['fichiers_parquet']}") logger.info(f"Contient des questions: {'Oui' if stats['a_questions'] else 'Non'}") if stats['a_questions']: logger.info(f"Nombre de questions: {stats['nb_questions']}") if 'types_documents' in stats and stats['types_documents']: logger.info(f"Types de documents: {', '.join(stats['types_documents'])}") # Afficher la structure des fichiers Parquet if 'structure_parquet' in stats and stats['structure_parquet']: logger.info("\n=== Structure des fichiers Parquet ===") for category, info in stats['structure_parquet'].items(): logger.info(f"\nCatégorie: {category}") logger.info(f"Nombre de lignes: {info['nb_lignes']}") logger.info(f"Colonnes: {', '.join(info['colonnes'])}") if args.verbose and 'exemple' in info and info['exemple']: logger.info("\nExemple de ligne:") for key, value in info['exemple'].items(): # Tronquer les valeurs trop longues if isinstance(value, str) and len(value) > 100: value = value[:100] + "..." logger.info(f" {key}: {value}") # 3. Vérifier s'il existe des résultats d'évaluation has_evaluations = tester.check_evaluation_results(args.dataset) if has_evaluations: # 4. Vérifier les performances des modèles performances = tester.check_model_performances(args.dataset) if performances: logger.info("\n=== Classement des modèles ===") # Trier les modèles par score (du plus élevé au plus bas) sorted_models = sorted(performances.items(), key=lambda x: x[1], reverse=True) for i, (model, score) in enumerate(sorted_models, 1): logger.info(f"{i}. {model}: {score:.4f}") logger.success("Test terminé !") if __name__ == "__main__": main()