Clemylia's picture
Update app.py
90e835a verified
# 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 : <votre_nom>/<nom_du_modèle>."
)
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()