# Fichier : app.py import os import torch import gradio as gr from datasets import load_dataset from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer from huggingface_hub import HfApi # ============================================================================== # 🛠️ Configuration et Modèles de Base Lamina # ============================================================================== # Dictionnaire des modèles de base disponibles (Modèle_ID: Nom_Affiché + Description) MODELS_CONFIG = { "Clemylia/lamina-basic": { "name": "lamina-basic (Micro - 3.8M params)", "desc": "🚀 Ressources limitées. Nécessite au moins 50 rows Q/R courtes. Attention, la qualité sera limitée.", "max_seq": 100 # Longueur de séquence ajustée pour ce petit modèle }, "Clemylia/Small-lamina-pretrain": { "name": "Small-lamina-pretrain (Petit - 51M params)", "desc": "✨ Bon compromis. Nécessite quelques centaines de rows Q/R pour de bons résultats. Très recommandé.", "max_seq": 128 }, "Clemylia/lamina-suite-pretrain": { "name": "lamina-suite-pretrain (Grand - 714M params)", "desc": "⚠️ Modèle très grand. Entraînement long (heures) ! Nécessite de grandes datasets et potentiellement un GPU T4/supérieur.", "max_seq": 256 } } # Clés pour le Dropdown Gradio MODEL_CHOICES = list(MODELS_CONFIG.keys()) # ============================================================================== # 🧠 Fonction Principale de Fine-Tuning # ============================================================================== def train_and_push_model( hf_token: str, hf_username: str, model_name: str, num_epochs: int, dataset_repo: str, base_model_repo: str ): """ Exécute le fine-tuning d'un modèle Lamina et le publie sur Hugging Face. """ # --- 0. Initialisation et Connexion --- # Récupération de la configuration spécifique du modèle choisi config = MODELS_CONFIG[base_model_repo] max_seq_length = config["max_seq"] yield f"Début du processus pour le modèle de base : **{config['name']}**" # ⚠️ Vérification du Token et connexion if not hf_token or not hf_username: yield "❌ ERREUR : Le Token HF ou le Nom d'utilisateur est manquant. Veuillez les fournir." return os.environ["HUGGING_FACE_HUB_TOKEN"] = hf_token api = HfApi() # Définition du nom final du repository sur le compte de l'utilisateur final_repo_name = f"{hf_username}/{model_name}" # --- 1. Chargement du Modèle et du Tokenizer --- yield f"Chargement du modèle : **{base_model_repo}**..." try: model = AutoModelForCausalLM.from_pretrained(base_model_repo) tokenizer = AutoTokenizer.from_pretrained(base_model_repo) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token yield "✅ Modèle et Tokenizer chargés." except Exception as e: yield f"❌ ERREUR lors du chargement du modèle/tokenizer : {e}" return # --- 2. Préparation du Dataset --- yield f"Chargement du dataset de Q&R : **{dataset_repo}**..." try: # Assurez-vous que le split 'train' est présent dataset = load_dataset(dataset_repo, split="train") yield f"Dataset chargé. Nombre de lignes initiales : **{len(dataset)}**" except Exception as e: yield f"❌ ERREUR lors du chargement de la dataset '{dataset_repo}'. Vérifiez le nom/ID et sa disponibilité. Détail : {e}" return # Fonction de formatage et de tokenisation (celle que tu as fournie) def format_and_tokenize(example): # Utilisation de 'reponse' comme dans ton exemple formatted_text = f"### Instruction:\n{example['question']}\n\n### Response:\n{example['reponse']}{tokenizer.eos_token}" tokenized_output = tokenizer( formatted_text, truncation=True, padding="max_length", max_length=max_seq_length ) tokenized_output["labels"] = tokenized_output["input_ids"].copy() return tokenized_output yield "Tokenisation et formatage du dataset en cours..." try: processed_dataset = dataset.map(format_and_tokenize, remove_columns=dataset.column_names) yield "✅ Dataset formaté et tokenisé." except Exception as e: yield f"❌ ERREUR lors du formatage de la dataset. Assurez-vous que les colonnes 'question' et 'reponse' existent. Détail : {e}" return # --- 3. Fine-Tuning --- output_dir = "./Lamina-FineTune-Temp" training_args = TrainingArguments( output_dir=output_dir, num_train_epochs=num_epochs, per_device_train_batch_size=2, # Taille de batch par défaut raisonnable logging_steps=5, learning_rate=5e-5, save_total_limit=2, # Ajout du push vers le hub pour la fin push_to_hub=False # On va le faire manuellement après l'entraînement ) trainer = Trainer( model=model, args=training_args, train_dataset=processed_dataset, tokenizer=tokenizer, ) yield f"🚀 Lancement du fine-tuning pour **{num_epochs}** époques..." try: trainer.train() yield "✅ Fine-tuning terminé !" except Exception as e: yield f"❌ ERREUR lors de l'entraînement. Cela peut être dû à un manque de ressources ou une erreur dans le dataset. Détail : {e}" return # --- 4. Sauvegarde et Publication sur Hugging Face --- # Sauvegarde locale du modèle entraîné final_model_path = "./Lamina-Trained-Final" trainer.save_model(final_model_path) tokenizer.save_pretrained(final_model_path) yield f"📦 Modèle sauvegardé localement. Publication sur **{final_repo_name}** en cours..." try: # Création ou utilisation d'un repository existant api.create_repo(repo_id=final_repo_name, exist_ok=True) # Téléchargement des fichiers api.upload_folder( folder_path=final_model_path, repo_id=final_repo_name, repo_type="model", commit_message=f"Fine-tune de {base_model_repo} par le Lamina Generator" ) final_link = f"https://huggingface.co/{final_repo_name}" yield f"🎉 **SUCCÈS !** Votre modèle Lamina a été publié !\n" yield f"Consultez votre modèle ici : {final_link}\n" yield f"**Prochaine étape :** Créez un Space de démonstration pour votre nouveau modèle !" except Exception as e: yield f"❌ ERREUR lors de la publication sur Hugging Face. Vérifiez votre token (autorisation d'écriture) et votre nom d'utilisateur. Détail : {e}" return # ============================================================================== # 🖥️ Interface Gradio # ============================================================================== # Description détaillée pour l'interface description = """ # 🧠 Lamina Generator : Créez votre propre SLM Lamina sans code ! Bienvenue Clemylia Community ! Cet outil vous permet de *fine-tuner* un de mes modèles Lamina de *pretrain* (lamina-basic, Small-lamina-pretrain, lamina-suite-pretrain) avec votre propre dataset de questions/réponses. Votre nouveau modèle sera automatiquement publié sur votre compte Hugging Face ! --- **⚠️ Avertissements Importants :** 1. **Token Nécessaire :** Vous devez utiliser un **Hugging Face Token avec les permissions d'écriture** (`write`) pour que le modèle puisse être publié sur votre compte. 2. **Dataset :** Votre dataset doit être **publique** et contenir au moins deux colonnes nommées : `question` et `reponse`. 3. **Taille :** Le temps d'entraînement dépend du modèle de base choisi et de la taille de votre dataset. Le grand modèle (`lamina-suite-pretrain`) peut prendre **plusieurs heures**. Choisissez votre modèle de base ci-dessous pour voir la description complète et les recommandations. """ with gr.Blocks(title="Lamina Generator") as demo: gr.Markdown(description) # Ajout d'un affichage dynamique pour les descriptions de modèle model_info_output = gr.Markdown("Sélectionnez un modèle de base ci-dessous.") def update_model_info(model_id): if model_id in MODELS_CONFIG: config = MODELS_CONFIG[model_id] return f"**Modèle sélectionné :** {config['name']} \n\n**Recommandation :** {config['desc']}" return "Sélectionnez un modèle de base ci-dessous." with gr.Row(): base_model_dropdown = gr.Dropdown( label="1. Choisir le Modèle de Base Lamina", choices=MODEL_CHOICES, value=MODEL_CHOICES[1], # Default to Small-lamina-pretrain interactive=True ) base_model_dropdown.change(update_model_info, inputs=base_model_dropdown, outputs=model_info_output) # Affichage des infos/avertissements du modèle gr.Markdown("---") model_info_output = gr.Markdown(update_model_info(MODEL_CHOICES[1])) gr.Markdown("---") with gr.Accordion("🔑 Informations d'Authentification (Nécessaires pour publier)", open=True): hf_token_input = gr.Textbox( label="2. Votre Hugging Face Write Token", type="password", placeholder="hf_xxxxxxxxxxxxxxxxxxxxxx", info="Token avec permission d'écriture pour la publication du modèle." ) hf_username_input = gr.Textbox( label="3. Votre Nom d'utilisateur Hugging Face", placeholder="Clemylia", info="L'utilisateur sous lequel le modèle sera publié." ) with gr.Accordion("📚 Configuration du Fine-Tuning", open=True): dataset_repo_input = gr.Textbox( label="4. Nom/ID Complet de votre Dataset (Hugging Face)", placeholder="votre_nom_utilisateur/nom_de_votre_dataset", info="Le dataset doit contenir les colonnes 'question' et 'reponse'." ) model_name_input = gr.Textbox( label="5. Nom du Modèle Final", placeholder="mon-super-lamina", info="Le modèle sera publié sous le nom : /." ) epochs_input = gr.Slider( minimum=1, maximum=100, value=3, step=1, label="6. Nombre d'Époques d'Entraînement", info="Plus d'époques = meilleure mémorisation, mais risque de 'sur-apprentissage'." ) # Zone de Log et Bouton output_log = gr.Markdown("Prêt à entraîner ! Remplissez les champs et cliquez sur le bouton.") train_button = gr.Button("🤖 Entraîner mon propre Lamina ! 🚀", variant="primary") # Lien du bouton à la fonction Python train_button.click( fn=train_and_push_model, inputs=[ hf_token_input, hf_username_input, model_name_input, epochs_input, dataset_repo_input, base_model_dropdown ], outputs=output_log ) if __name__ == "__main__": demo.launch()