MonEspaceSanté FAQ — Mistral Small 3.2 24B (QLoRA, GGUF)

Ce modèle est comparé à d'autres modèles ici : https://huggingface.co/fenyo/MonEspaceSante-FAQ-Mistral-Small-24B-GGUF/blob/main/REPORT.md

Assistant conversationnel francophone, spécialisé sur la FAQ du service public Mon espace santé. Le modèle est un fine-tuning QLoRA de Mistral Small 3.2 24B Instruct (2506), fusionné en pleine précision puis exporté au format GGUF pour une inférence locale (llama.cpp / Ollama).

  • Quantification fournie ici : Q4_K_M (≈ 14,3 Go) — le meilleur compromis taille / qualité / vitesse, recommandé pour un usage mono-utilisateur sur GPU 24–32 Go.
  • Langue : français uniquement.
  • Domaine : questions des usagers sur Mon espace santé (création/opposition de profil, dossier médical, ajout de documents, messagerie sécurisée, confidentialité, accès des professionnels, etc.).

Ce dépôt ne contient que le décodeur de texte quantifié. Le modèle de base Mistral Small 3.2 est multimodal ; le mode vision a été retiré à l'export car inutile pour un assistant FAQ textuel.


Utilisation

Ollama (recommandé)

Le system prompt et les hyperparamètres d'inférence sont embarqués dans le Modelfile : inutile d'ajouter un quelconque pré-prompt, il suffit de poser la question.

# Modelfile
FROM ./faqmes-clean-q4.gguf
TEMPLATE """{{ if .System }}[SYSTEM_PROMPT]{{ .System }}[/SYSTEM_PROMPT]{{ end }}[INST]{{ .Prompt }}[/INST]"""
PARAMETER stop "</s>"
PARAMETER temperature 0.3
PARAMETER top_p 0.9
PARAMETER repeat_penalty 1.1
PARAMETER num_ctx 4096
SYSTEM """Tu es un assistant qui répond en français aux questions sur MonEspaceSanté. Réponds de façon directe, factuelle et concise, en te basant uniquement sur les informations officielles du service."""
ollama create faqmes -f Modelfile
ollama run faqmes "Comment puis-je m'opposer à la création de Mon espace santé ?"

llama.cpp

./llama-cli -m faqmes-clean-q4.gguf -c 4096 \
  --temp 0.3 --top-p 0.9 --repeat-penalty 1.1 \
  -p "[SYSTEM_PROMPT]Tu es un assistant qui répond en français aux questions sur MonEspaceSanté. Réponds de façon directe, factuelle et concise, en te basant uniquement sur les informations officielles du service.[/SYSTEM_PROMPT][INST]Mon espace santé est-il obligatoire ?[/INST]"

Gabarit de prompt : Mistral v3 ([SYSTEM_PROMPT]…[/SYSTEM_PROMPT][INST]…[/INST]), token d'arrêt </s>.


Comment ce modèle a été produit (pipeline complet)

Tout le pipeline — collecte, curation, génération synthétique, fine-tuning, fusion, quantification, évaluation — est reproductible et a tourné en local sous WSL2 sur un GPU NVIDIA RTX 5090 (32 Go).

1. Collecte des données — accès au CMS plutôt que scraping du SPA

Le site monespacesante.fr est une SPA Vite : le HTML rendu ne contient pas le texte de la FAQ. Plutôt que de piloter un navigateur headless, il suffit d'inspecter /js/config.js et de découvrir l'API CMS Drupal (JSON:API) qui alimente le site (…/gateway/cms/jsonapi/faq). Un seul appel structuré renvoie l'intégralité du corpus FAQ — catégories, questions, réponses HTML — bien plus propre que du scraping.

  • Le HTML des réponses est converti en texte lisible en préservant la structure (paragraphes, listes), puis déséchappé.
  • Sorties :
    • faq_pairs.jsonl89 paires question/réponse officielles (la « vérité terrain »).
    • documents.jsonl — un document par réponse officielle, servant de contexte à la génération synthétique.

2. Curation — NeMo Curator

Curation avec NVIDIA NeMo Curator (pipeline Ray / exécuteur Xenna, sur CPU) :

  • Normalisation Unicode NFC + écrasement des espaces parasites en conservant les sauts de paragraphe (DocumentModifier).
  • Filtrage des documents quasi vides (ScoreFilter sur le nombre de mots).
  • Les étapes GPU lourdes (déduplication floue / sémantique) sont volontairement omises : le corpus est une seule petite FAQ déjà structurée et exempte de doublons, donc l'unicité exacte est garantie par construction. Inutile d'ajouter de la complexité qui n'apporte rien.

3. Génération synthétique (SDG) — closed-QA ancré, sans CoT

Les 89 paires officielles ne couvrent qu'une formulation par question. Or les usagers reformulent de mille façons. On augmente donc le corpus par génération synthétique ancrée (closed-QA) :

  • Modèle enseignant : qwen3.5:27b servi localement par Ollama.
  • Chaîne de pensée désactivée : on appelle l'API native /api/chat avec think=false. C'est le seul moyen fiable de couper le reasoning de qwen3.5 (la route OpenAI /v1 ignore le drapeau) — on veut des réponses directes, pas des traces de CoT.
  • Stratégie : pour chaque réponse officielle, on demande k = 6 paires question/réponse distinctes, avec consigne stricte de ne rien inventer :
    • questions variées telles qu'un usager les poserait réellement (reformulations, synonymes, formulations directes/indirectes) ;
    • réponses factuelles, concises (1 à 4 phrases), entièrement justifiées par le contexte fourni.
  • Échantillonnage : temperature=0.4, top_p=0.9, num_predict=1536.
  • Robustesse : parsing tolérant du tableau JSON (suppression d'éventuels <think>…</think>, extraction [ … ]), concurrence asynchrone (8 requêtes simultanées), et déduplication sur la question normalisée (casse/espaces).

L'ancrage strict sur le texte source est essentiel : c'est ce qui évite que l'enseignant n'« hallucine » des procédures ou des chiffres absents de la FAQ officielle.

4. Mise en forme du dataset de fine-tuning

  • Fusion : paires officielles d'abord (qualité maximale ; elles gagnent les égalités de déduplication), puis l'augmentation synthétique.
  • Déduplication finale sur la question normalisée.
  • Format chat à trois tours {system, user, assistant}, le system étant le prompt de domaine (cf. ci-dessus).
  • Découpage 95 % / 5 % (graine 42) → 584 exemples d'entraînement / 30 de validation (614 paires uniques : 89 officielles + 525 synthétiques après déduplication).

📦 Le jeu de données complet (questions réelles + synthétiques, avec provenance et catégorie) est publié séparément : fenyo/MonEspaceSante-FAQ-QA.

5. Fine-tuning — QLoRA avec Unsloth

Entraînement avec Unsloth (patches d'optimisation appliqués avant trl/transformers/ peft), apprentissage sur les réponses uniquement.

Paramètre Valeur Pourquoi
Base Mistral-Small-3.2-24B-Instruct-2506 (BnB NF4 4-bit) non-raisonneur, francophone natif, tient en 32 Go en QLoRA
max_seq_len 4096 réponses FAQ courtes, mais marge confortable
LoRA r 16 capacité suffisante sans surapprendre ~600 exemples
LoRA alpha 32 (facteur d'échelle 2.0) convention alpha = 2·r
LoRA dropout 0.0 dataset propre et homogène
Modules cibles q,k,v,o,gate,up,down_proj (les 7) adaptation complète des blocs attention + MLP
Optimiseur paged_adamw_8bit empreinte mémoire minimale
LR 2e-4, cosine, warmup_ratio=0.05 standard QLoRA, décroissance douce
Batch 2 × grad_accum 8 = batch effectif 16 stabilité du gradient sur petit corpus
Époques 2 (74 steps) la perte plafonne ; au-delà, surapprentissage d'un 24B sur ~600 ex.
Précision bf16 supportée par la RTX 5090
Masquage train_on_responses_only ([INST]/[/INST]) n'apprend que les tours assistant
Graine 42 reproductibilité
gradient_checkpointing "unsloth" mémoire d'activations réduite

Courbe de perte (train) — convergence nette et saine :

step 10 20 30 40 50 60 70
loss 1.41 1.08 0.91 0.79 0.49 0.41 0.38

Le choix de 2 époques est délibéré : la perte est déjà à ~0.38 et continue de descendre, mais pousser davantage sur un dataset de ~600 exemples ferait surapprendre un modèle de 24 B (perte de fluidité générale). Le r=16 / alpha=32 et le dropout=0 ont été retenus pour le même équilibre capacité ↔ généralisation.

6. Fusion (merge) — dans la base fp16, pas dans la base 4-bit

C'est le point technique le plus important de l'export. Un adaptateur QLoRA est entraîné au-dessus d'une base quantifiée 4-bit. Deux stratégies de fusion sont alors possibles :

  1. Fusionner l'adaptateur dans la base 4-bit déquantifiée — pratique, mais cela fige le bruit de quantification de la base dans tous les poids du modèle fusionné. À l'inférence, ce bruit s'accumule et dégrade nettement les générations longues (répétitions, dérives), et ce quel que soit le niveau de quantification GGUF choisi ensuite (de q4 à q8).
  2. Fusionner l'adaptateur dans la base fp16 d'origine (pleine précision) — seules les couches réellement adaptées par la LoRA changent, le reste du réseau reste intact.

On retient la stratégie 2. Concrètement :

  • chargement de la base fp16 unsloth/Mistral-Small-3.2-24B-Instruct-2506 (≈ 48 Go) sur CPU (device_map="cpu", bf16, low_cpu_mem_usage), application de l'adaptateur via PEFT puis merge_and_unload() ;
  • tie_word_embeddings=False (la base possède un lm_head distinct des embeddings) ;
  • ré-agencement du décodeur de texte en MistralForCausalLM standard (suppression de la tour de vision et du multi-modal projector, inutiles pour un GGUF textuel ; correction du préfixe de tenseurs introduit par l'empaquetage multimodal de transformers).

Cette fusion en pleine précision est ce qui rend le modèle stable même en q4.

7. Conversion & quantification GGUF — llama.cpp

  • convert_hf_to_gguf.py → GGUF F16 (≈ 47 Go).
  • llama-quantize → plusieurs niveaux testés et comparés :
Quant Taille Verdict
Q4_K_M 14,3 Go livrable recommandé : propre, rapide, sans dégénérescence
Q5_K_M 16,8 Go gain de précision marginal ; option « qualité max »
Q6_K / Q8_0 19 / 25 Go non nécessaires : aucun gain perceptible après la fusion fp16

Après la fusion fp16, Q4_K_M est déjà parfaitement stable ; c'est donc lui qui est publié ici comme livrable principal.

8. Évaluation

Deux approches complémentaires ont été employées :

a) Évaluation autonome de robustesse (eval/run_eval.ps1) — 12 questions représentatives de la FAQ, posées sans pré-prompt (le system embarqué suffit). Pour chaque réponse on mesure automatiquement :

  • l'arrêt naturel (done_reason == "stop", pas de coupure par limite de tokens) ;
  • un ratio de répétition = mots / mots distincts ;
  • la plus longue répétition de mots consécutifs.

Une réponse est marquée FAIL en cas de boucle (ratio ≥ 3.0, ou run ≥ 4, ou arrêt par longueur avec ratio ≥ 2.0).

Résultat : 12 / 12 PASS pour Q4_K_M. Toutes les réponses s'arrêtent naturellement, ratio de répétition compris entre 1.07 et 2.08, plus longue répétition = 1 (aucune), longueurs de 30 à 217 tokens. Le Q5_K_M obtient lui aussi 12 / 12.

b) Juge LLM sur jeu de validation tenu à l'écart (eval/eval.py) — chaque question de validation est posée au modèle candidat, puis la réponse est notée de 1 à 5 par un juge (qwen3.5:27b) contre la réponse de référence (5 = factuellement correcte et complète). L'orchestration enchaîne d'abord toutes les réponses du candidat puis tous les jugements, pour ne charger qu'un seul modèle à la fois en VRAM (le candidat ~14–25 Go et le juge ~17 Go ne tiennent pas ensemble dans 32 Go).

Inspection manuelle des réponses : elles sont factuelles, concises, en bon français, et restent strictement dans le périmètre Mon espace santé même sur des questions ambiguës ne mentionnant jamais le service (« C'est obligatoire ? », « Comment ajouter un document ? »).


Limites & usage prévu

  • Domaine fermé : conçu pour la FAQ Mon espace santé. Hors de ce périmètre, le modèle de base répond de façon générique — il ne s'agit pas d'un assistant médical et il ne donne aucun conseil de santé.
  • Connaissances figées à la date de collecte de la FAQ ; les procédures du service réel peuvent évoluer. Vérifiez toujours sur le site officiel.
  • Français uniquement.
  • Petit corpus (~600 paires) : excellent sur les intentions couvertes par la FAQ, moins pertinent sur des questions très éloignées.

Attribution & licence

  • Modèle de base : mistralai/Mistral-Small-3.2-24B-Instruct-2506 — licence Apache 2.0.
  • Données : contenu public de la FAQ Mon espace santé (service public français).
  • Outils : Unsloth, NVIDIA NeMo Curator, llama.cpp, Ollama, qwen3.5 (enseignant SDG).
  • Modèle dérivé distribué sous Apache 2.0.
Downloads last month
914
GGUF
Model size
24B params
Architecture
llama
Hardware compatibility
Log In to add your hardware

We're not able to determine the quantization variants.

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for fenyo/MonEspaceSante-FAQ-Mistral-Small-24B-GGUF