schema-bac / app.py
hamoud-7's picture
Update app.py
ac34952 verified
Raw
History Blame Contribute Delete
7.47 kB
import os
import gradio as gr
import torch
from PIL import Image
# Importation directe de la classe spécifique pour Qwen2.5-VL
from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration
import spaces # Import crucial pour utiliser les GPU gratuits de Hugging Face
# --- CONFIGURATION ---
# REMPLACEZ 'votre_vrai_username' par votre véritable identifiant Hugging Face
MODEL_ID = "hamoud-7/schema-bac-qwen-model"
HF_TOKEN = os.environ.get("HF_TOKEN")
print("⏳ Chargement du processeur et du modèle de vision Qwen2.5-VL...")
processor = AutoProcessor.from_pretrained(MODEL_ID, token=HF_TOKEN)
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
MODEL_ID,
torch_dtype=torch.float16,
device_map="auto",
token=HF_TOKEN
)
print("✅ Application prête !")
# --- FONCTION D'INFÉRENCE PROPULSÉE PAR ZEROGPU ---
@spaces.GPU # Active le GPU gratuitement à la demande pour l'utilisateur
def run_inference(image_pil, question_text):
if image_pil is None or not question_text.strip():
return "Erreur : Veuillez fournir une image ET une question."
# Préparation du prompt au format Qwen-VL
messages = [
{"role": "user", "content": [
{"type": "image", "image": image_pil},
{"type": "text", "text": question_text}
]}
]
# Application du template de chat standard
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
# Traitement des entrées pour le modèle (gère automatiquement les images et le texte)
inputs = processor(text=[text], images=[image_pil], padding=True, return_tensors="pt").to("cuda")
# Génération de la réponse
with torch.no_grad():
generated_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)
# Découpage des tokens générés pour ne garder que la réponse de l'assistant
generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
response_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
return response_text.strip()
# --- INTERFACE DESIGN GRADIO ---
logo_url = "https://imgur.com/GDKnbVp.png"
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
.gradio-container {
font-family: 'Poppins', sans-serif !important;
background: linear-gradient(135deg, #f5f7fb 0%, #eef1f8 100%) !important;
width: 100% !important;
max-width: 100% !important; /* Modifié pour occuper tout l'espace disponible */
margin: 0 auto !important;
padding: 0 10px !important;
}
/* --- En-tête --- */
#header-block {
background: linear-gradient(120deg, #4338CA 0%, #6366F1 55%, #4F46E5 100%);
border-radius: 22px;
padding: 34px 20px !important;
margin-bottom: 26px;
box-shadow: 0 12px 30px rgba(79, 70, 229, 0.28);
}
#header-block h1 {
color: #ffffff !important;
font-weight: 700 !important;
letter-spacing: 0.3px;
}
#header-block p {
color: #E0E7FF !important;
}
/* --- Cartes --- */
.card {
background: #ffffff !important;
border-radius: 18px !important;
padding: 22px !important;
box-shadow: 0 4px 22px rgba(15, 23, 42, 0.07) !important;
border: 1px solid #EEF2FF !important;
}
.card h3, .card .prose h3 {
margin-top: 0 !important;
color: #312E81 !important;
font-weight: 600 !important;
}
/* --- Bouton principal --- */
#submit-btn {
background: linear-gradient(120deg, #4F46E5, #6366F1) !important;
border: none !important;
color: #ffffff !important;
font-weight: 600 !important;
border-radius: 12px !important;
box-shadow: 0 6px 16px rgba(79, 70, 229, 0.35) !important;
transition: transform 0.15s ease, box-shadow 0.15s ease !important;
}
#submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(79, 70, 229, 0.45) !important;
}
/* --- Zone de réponse --- */
#output-box textarea {
background: #F8FAFC !important;
border-radius: 12px !important;
font-size: 1rem !important;
border: 1px solid #E2E8F0 !important;
}
/* --- Exemples --- */
#examples-card .gr-samples-table, #examples-card table {
border-radius: 12px !important;
overflow: hidden !important;
}
footer {visibility: hidden}
"""
with gr.Blocks(
theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="blue"),
title="SCHÉMA BAC",
css=custom_css,
fill_width=True # Ajouté pour forcer l'interface à s'étaler horizontalement
) as demo:
with gr.Column(elem_id="header-block"):
gr.HTML(f'''
<div style="text-align: center;">
<img src="{logo_url}" alt="SCHÉMA BAC Logo" style="height: 70px; display: block; margin: 0 auto 12px auto;">
<h1 style="margin:0; font-size: 2.1rem;">SCHÉMA BAC</h1>
<p style="font-size: 1.05rem; margin-top: 8px; max-width: 640px; margin-left:auto; margin-right:auto;">
L'intelligence artificielle au service de la compréhension des schémas scientifiques.
</p>
</div>
''')
with gr.Row(equal_height=True):
with gr.Column(scale=1, min_width=320, elem_classes="card"): # min_width adapté pour maintenir la structure côte à côte
gr.Markdown("### 1️⃣ Téléchargez votre schéma")
image_input = gr.Image(type="pil", label="", height=280)
gr.Markdown("### 2️⃣ Posez votre question")
question_input = gr.Textbox(
label="",
placeholder="Ex : Quelle est la fonction de la structure numéro 3 ?",
lines=3
)
submit_btn = gr.Button("🔍 Analyser le schéma", variant="primary", elem_id="submit-btn")
with gr.Column(scale=1, min_width=320, elem_classes="card"): # min_width adapté pour maintenir la structure côte à côte
gr.Markdown("### 🤖 Analyse de l'assistant")
output_text = gr.Textbox(
label="",
interactive=False,
lines=14,
elem_id="output-box",
placeholder="La réponse de l'IA apparaîtra ici..."
)
with gr.Group(elem_classes="card", elem_id="examples-card"):
gr.Markdown("### 📚 Exemples à essayer — cliquez pour tester")
gr.Examples(
examples=[
[
# 1. On remplace "examples" par "images"
# 2. Changez "votre_image_1.jpg" par le nom exact de votre premier fichier
os.path.join("images", "image.jpg"),
"Quel est le rôle de la structure numéro 3 ?"
],
[
# Faites de même pour la deuxième image
os.path.join("images", "image (3).jpg"),
"Que représente la structure numéro 3 sur cette coupe de rein ?"
],
[
# Et pour la troisième image
os.path.join("images", "image (1).jpg"),
"Quelle est la fonction de la structure numéro 2 ?"
],
],
inputs=[image_input, question_input],
label=""
)
submit_btn.click(fn=run_inference, inputs=[image_input, question_input], outputs=output_text)
if __name__ == "__main__":
demo.launch()