Aller au contenu principal

Module 9 — Fusion des adaptateurs et export

Le modèle est affiné, mesuré, contrôlé. Reste à le rendre utilisable — pour un serveur d'inférence, pour Ollama sur un poste, pour un partage interne. Ce module couvre trois étapes indissociables : fusionner l'adaptateur dans les poids, convertir au format GGUF, publier proprement avec sa licence.

Fusion : quand faut-il fusionner, et quand ne pas le faire

Un modèle affiné par LoRA est en réalité deux fichiers : le modèle de base gelé et l'adaptateur. À l'inférence, chaque produit matriciel calcule d'abord le résultat avec les poids de base, puis y ajoute la contribution de l'adaptateur. Ce surcoût est modeste — quelques pour cent — mais il est constant.

La fusion consiste à écrire une bonne fois la matrice W=W+(α/r)BAW' = W + (\alpha/r) \cdot B \cdot A dans les poids de base. Une fois fusionné, le modèle se comporte exactement comme un modèle classique, sans adaptateur à charger, et l'on peut appliquer n'importe quel outil qui attend un modèle standard — dont les convertisseurs GGUF.

Deux cas où l'on ne fusionne pas. D'abord, quand on veut servir plusieurs adaptateurs avec un seul modèle de base (fin du module 5) : la fusion casse cette architecture. Ensuite, quand le modèle est en 4 bits (QLoRA) : la fusion dans un modèle quantifié abîme la qualité. Il faut alors recharger le modèle de base en bfloat16, y attacher l'adaptateur, fusionner, puis re-quantifier si besoin.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

nom_base = "mistralai/Mistral-7B-Instruct-v0.3"
chemin_adapt = "./mistral-comptes-rendus/checkpoint-450"

# On recharge le modele de base en bfloat16 (PAS en 4 bits).
base = AutoModelForCausalLM.from_pretrained(
nom_base, torch_dtype=torch.bfloat16, device_map="cpu",
)
tokeniseur = AutoTokenizer.from_pretrained(nom_base)

# On attache l'adaptateur et on fusionne.
modele = PeftModel.from_pretrained(base, chemin_adapt)
modele = modele.merge_and_unload() # fusion definitive

# Sauvegarde standard.
modele.save_pretrained("./mistral-comptes-rendus-fusionne", safe_serialization=True)
tokeniseur.save_pretrained("./mistral-comptes-rendus-fusionne")

Le résultat est un dossier de 14 gigaoctets (pour un modèle de 7 milliards en bfloat16) que n'importe quel outil compatible transformers peut charger sans savoir qu'un adaptateur est passé par là.

Exporter au format GGUF

GGUF — Georgi Gerganov Unified Format — est le format binaire utilisé par llama.cpp et par Ollama pour servir un modèle de langage sur CPU ou sur GPU grand public. Il regroupe dans un seul fichier les poids quantifiés à un niveau choisi, le tokeniseur et les métadonnées.

La conversion se fait par le script convert_hf_to_gguf.py distribué avec llama.cpp :

# Clonage et compilation minimaux de llama.cpp.
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make -j

# Conversion du modele fusionne vers GGUF en fp16 (etape intermediaire).
python convert_hf_to_gguf.py ../mistral-comptes-rendus-fusionne \
--outfile ../mistral-comptes-rendus-f16.gguf \
--outtype f16

# Quantification finale, plusieurs niveaux disponibles.
./quantize ../mistral-comptes-rendus-f16.gguf \
../mistral-comptes-rendus-Q4_K_M.gguf Q4_K_M

Les niveaux de quantification courants et leur usage :

NiveauTaille (7B)QualitéContexte typique
Q8_07,2 Goquasi identique à fp16serveur avec GPU modeste
Q6_K5,5 Goperte imperceptiblecompromis qualité/place
Q5_K_M4,8 Gotrès bonnepar défaut Ollama
Q4_K_M4,1 Gobonne, léger écartposte bureau, laptop
Q3_K_M3,3 Goperceptibledépannage, gros modèles

Pour notre modèle de comptes rendus déployé sur un poste avec 16 gigaoctets de mémoire vive, Q4_K_M est le point d'équilibre : environ 4 gigaoctets, une qualité indiscernable de fp16 dans 95 % des sorties.

Le déploiement Ollama

Ollama charge un GGUF via un Modelfile, l'équivalent d'un Dockerfile pour les modèles.

# Modelfile
FROM ./mistral-comptes-rendus-Q4_K_M.gguf

# Le gabarit de conversation doit correspondre au modele de base.
TEMPLATE """[INST] {{ .Prompt }} [/INST]"""

PARAMETER temperature 0.2
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
PARAMETER stop "[INST]"

SYSTEM """Tu resumes une reunion en compte rendu structure."""
ollama create mistral-comptes-rendus -f Modelfile
ollama run mistral-comptes-rendus "..."

Le TEMPLATE doit correspondre au gabarit de conversation du modèle de base — c'est là qu'un chat template mal appliqué au module 2 se paie. Une consigne SYSTEM par défaut, une température basse (0,2) et une fenêtre de contexte adaptée à la longueur de séquence d'entraînement forment le socle raisonnable.

Publier sur le Hub

La publication sur huggingface.co prend une commande.

from huggingface_hub import HfApi

api = HfApi()
api.create_repo("notre-org/mistral-comptes-rendus", exist_ok=True)
api.upload_folder(
folder_path="./mistral-comptes-rendus-fusionne",
repo_id="notre-org/mistral-comptes-rendus",
repo_type="model",
)

Un fichier README.md accompagne obligatoirement le modèle. Il doit contenir la carte de modèle — quatre sections indispensables :

  • modèle de base utilisé et lien vers sa page ;
  • jeu de données d'affinage — origine, licence, taille ;
  • usage prévu et exemples de code minimaux ;
  • limitations connues et évaluations menées (module 10).

La licence du modèle de base : la question qu'on oublie

Un modèle affiné hérite des contraintes du modèle de base. Ignorer cette règle expose à un rappel juridique très concret.

Modèle de baseLicenceUsage commercial
Llama 3Meta Llama 3 Community Licenseoui, sauf plus de 700 millions d'utilisateurs actifs
Mistral 7BApache 2.0oui, sans restriction
Qwen2 7BTongyi Qianwenoui, sous conditions
Gemma 2Gemma Terms of Useoui, avec clauses de conformité
FalconApache 2.0oui, sans restriction

Deux règles pratiques. Si vous ne connaissez pas la licence de votre modèle de base, ne publiez rien tant que ce n'est pas clarifié. Si vous prévoyez un usage commercial, Mistral et Falcon sous Apache 2.0 sont les choix les plus simples juridiquement — beaucoup d'affinages commerciaux les préfèrent pour cette raison, et non pour leur qualité.

Le jeu de données aussi

La licence du modèle est une chose, celle du jeu d'affinage en est une autre. Un modèle sous Apache 2.0 affiné sur des sorties d'un modèle commercial fermé peut violer les conditions d'usage de ce dernier (module 2). La carte de modèle doit expliciter les deux origines.

En résumé

  • La fusion intègre l'adaptateur dans les poids de base ; on ne fusionne pas si l'on veut plusieurs adaptateurs, et jamais depuis un modèle quantifié.
  • Le format GGUF est la lingua franca pour llama.cpp et Ollama ; Q4_K_M équilibre qualité et place pour un déploiement grand public.
  • Un Modelfile Ollama transporte le gabarit de conversation, la consigne système, la température et la fenêtre de contexte.
  • La carte de modèle publiée avec le poids est obligatoire ; la licence du modèle de base doit être vérifiée avant tout usage commercial.

Module suivant : évaluer proprement avant et après affinage, sans se raconter d'histoire.