← Blog

Mode opératoire : entraîner un SLM pour l'industrie, de A à Z

Guide pas-à-pas pour fine-tuner un Small Language Model industriel. Du choix du modèle de base au déploiement en production. RunPod, Weights & Biases, LoRA, quantization GGUF. Avec les commandes exactes.

Pourquoi un SLM et pas un LLM

Il y a une confusion persistante dans l’industrie francaise en 2026. Quand un DSI entend “intelligence artificielle generative”, il pense GPT-4, Claude, Gemini — des modeles massifs, heberges aux Etats-Unis, factures au token, et qui necessitent d’envoyer les donnees de production dans le cloud d’un tiers. Il pense aussi que c’est la seule option.

Ce n’est pas la seule option.

Un Small Language Model (SLM) — entre 0,5 et 8 milliards de parametres — peut resoudre 80 % des cas d’usage textuels d’une ETI industrielle, a un cout 50 a 100 fois inferieur, avec une latence 10 fois plus faible, et sans qu’une seule donnee ne quitte le reseau de l’entreprise.

La nuance est dans le mot “resoudre”. Un LLM generaliste sait tout, mais ne sait rien de votre processus. Un SLM fine-tune ne sait qu’une chose — votre processus — et il la fait mieux que le generaliste. C’est la difference entre un couteau suisse et un outil de coupe specifique. En atelier, on utilise l’outil specifique.

Trois cas d’usage ou le SLM ecrase le LLM

Classification de tickets SAV. Un operateur saisit un texte libre (“la piece se decolle apres 2 jours, le client est furieux”). Le modele doit classer le ticket dans une categorie (defaut adhesion, defaut dimensionnel, defaut esthetique, reclamation delai) et lui attribuer une priorite. Un Qwen2.5-1.5B fine-tune sur 2 000 tickets historiques atteint 94 % de precision, repond en 80 ms sur CPU, et coute 0 euro par requete une fois deploye.

Extraction de nomenclature. Un rapport technique de fournisseur contient des references pieces, des quantites, des specifications. Le modele doit extraire ces champs dans un JSON structure. Un Gemma-2-2B fine-tune sur 500 exemples annotes fait le travail avec 96 % d’extraction correcte. Le meme exercice avec GPT-4o coute 0,03 euro par page et envoie les specs fournisseur chez OpenAI.

Resume de rapports qualite. Un audit interne de 30 pages doit etre synthetise en 5 points cles pour le comite de direction. Un Mistral-7B fine-tune produit des resumes factuels, calibres sur le format interne, en 3 secondes. Le cout marginal est le prix de l’electricite.

Le calcul economique

LLM cloud (GPT-4o)SLM on-premise (Qwen 1.5B)
Cout par requete0,01 — 0,05 EUR~0 EUR (electricite)
10 000 requetes/mois100 — 500 EUR/mois< 5 EUR/mois
Latence1 — 5 s50 — 200 ms
DonneesCloud USOn-premise
PersonnalisationPrompt engineeringFine-tuning complet

Sur un an, la difference de cout entre un LLM cloud et un SLM on-premise finance largement le fine-tuning initial. Le point mort est atteint des le deuxieme mois pour un cas d’usage a volume.


Etape 1 — Choisir le modele de base

Le choix du modele de base conditionne tout le reste : le GPU necessaire, le volume de donnees requis, la latence en inference, et la qualite finale. Le critere principal est la taille du modele en parametres. Le critere secondaire est la licence.

Arbre de decision

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#1a1a2e', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#e94560', 'lineColor': '#e94560', 'secondaryColor': '#16213e', 'tertiaryColor': '#0f3460', 'fontSize': '14px'}}}%%
flowchart TD
    A["Quel est votre cas d'usage ?"] --> B{"Tache simple ?\n(classification, extraction,\nNER, yes/no)"}
    B -->|Oui| C{"Volume de donnees\nd'entrainement"}
    B -->|Non| D{"Tache complexe ?\n(resume, generation,\nraisonnement)"}
    
    C -->|"< 1 000 ex."| E["**Qwen2.5-0.5B**\n0,5B params\nRTX 4060 suffit\n2 Go VRAM"]
    C -->|"> 1 000 ex."| F["**Phi-3-mini**\n3,8B params\nRTX 4090\n8 Go VRAM"]
    
    D -->|"Resume, reformulation"| G{"Contrainte\nde latence"}
    D -->|"Raisonnement,\nanalyse multi-doc"| H["**LLaMA-3.1-8B**\n8B params\nA100 40GB\n18 Go VRAM"]
    
    G -->|"< 200 ms"| I["**Gemma-2-2B**\n2B params\nRTX 4090\n6 Go VRAM"]
    G -->|"Latence flexible"| J["**Mistral-7B-v0.3**\n7B params\nA100 40GB\n16 Go VRAM"]

    style A fill:#1a1a2e,stroke:#e94560,color:#ffffff
    style B fill:#16213e,stroke:#e94560,color:#ffffff
    style C fill:#16213e,stroke:#e94560,color:#ffffff
    style D fill:#16213e,stroke:#e94560,color:#ffffff
    style E fill:#0f3460,stroke:#e94560,color:#ffffff
    style F fill:#0f3460,stroke:#e94560,color:#ffffff
    style G fill:#16213e,stroke:#e94560,color:#ffffff
    style H fill:#0f3460,stroke:#e94560,color:#ffffff
    style I fill:#0f3460,stroke:#e94560,color:#ffffff
    style J fill:#0f3460,stroke:#e94560,color:#ffffff

Tableau comparatif des modeles de base

ModeleParamsVRAM minLicenceForcesFaiblesses
Qwen2.5-0.5B0,5B2 GoApache 2.0Ultra-leger, multilingue, rapideLimites en generation longue
Phi-3-mini3,8B8 GoMITExcellent raisonnement pour sa tailleAnglais-centrique
Gemma-2-2B2B6 GoGemma LicenseBon equilibre taille/qualiteLicence restrictive pour usage commercial > 1M users
Qwen2.5-1.5B1,5B4 GoApache 2.0Multilingue, bon en francaisMoins de communaute que LLaMA
Mistral-7B-v0.37B16 GoApache 2.0Reference francophone, communaute activeNecessite A100 pour fine-tuning
LLaMA-3.1-8B8B18 GoLlama 3.1 LicenseMeilleur modele open-weight 8BLicence Meta, pas de GPU grand public

Regle de pouce. Si votre tache peut etre formulee comme une classification ou une extraction de champs, commencez par Qwen2.5-0.5B. Si elle implique de la generation de texte (resume, reformulation, redaction), montez a 2-3B. N’allez a 7-8B que si les tests sur un modele plus petit montrent une qualite insuffisante. Plus petit = plus rapide, moins cher, plus facile a deployer.


Etape 2 — Preparer les donnees

Le fine-tuning est un exercice de donnees, pas de code. La qualite du modele final est directement proportionnelle a la qualite du jeu de donnees. Le code d’entrainement est generique — il est le meme pour tout le monde. Les donnees sont specifiques — elles sont votre avantage competitif.

Format JSONL

Le standard pour le fine-tuning supervisee est le format JSONL (JSON Lines). Chaque ligne est un objet JSON autonome avec trois champs.

{"instruction": "Classifie ce ticket SAV dans une categorie.", "input": "La peinture se decolle apres 3 semaines d'utilisation en exterieur. Piece ref. PNT-4200.", "output": "Categorie: defaut adhesion\nPriorite: haute\nCode defaut: ADH-EXT\nAction: retour fournisseur peinture"}
{"instruction": "Classifie ce ticket SAV dans une categorie.", "input": "Cote longueur 145.2 mm au lieu de 145.0 +/- 0.05. Lot 2026-W12.", "output": "Categorie: defaut dimensionnel\nPriorite: critique\nCode defaut: DIM-HT\nAction: blocage lot, reglage machine"}
{"instruction": "Classifie ce ticket SAV dans une categorie.", "input": "Le client demande ou en est sa commande du 15 mars.", "output": "Categorie: reclamation delai\nPriorite: normale\nCode defaut: N/A\nAction: transfert service commercial"}

Structure des donnees

Chaque exemple doit contenir :

  • instruction : la consigne donnee au modele. Identique ou tres similaire pour tous les exemples d’une meme tache.
  • input : le texte d’entree. Variable, representatif de la realite terrain. Inclut le bruit, les fautes, les abbreviations que les operateurs utilisent reellement.
  • output : la reponse attendue. C’est le label. Il doit etre exact, complet, et coherent avec la politique interne.

Combien de donnees ?

L’ordre de grandeur depend de la complexite de la tache.

TacheMinimum viableIdealMaximum utile
Classification binaire (oui/non)2005002 000
Classification multi-classes (5-10 classes)5002 00010 000
Extraction de champs (NER)3001 0005 000
Resume / reformulation5002 00010 000
Generation guidee (rapports)1 0005 00020 000

Au-dela du maximum utile, ajouter des donnees n’ameliore plus la performance. En deca du minimum viable, le modele n’apprend pas le pattern de maniere fiable.

Nettoyage

Avant de lancer un entrainement, verifier systematiquement :

  1. Doublons exacts. Deux exemples identiques biaisent l’evaluation si l’un est dans le train et l’autre dans le val.
  2. Incoherences. Deux exemples avec le meme input mais des outputs differents. Le modele ne peut pas apprendre de contradictions.
  3. Longueur. Un output de 2 000 tokens au milieu d’outputs de 50 tokens va perturber l’entrainement. Homogeneiser.
  4. Encodage. UTF-8 partout. Les caracteres speciaux (accents, unites, symboles) doivent etre presents dans les donnees si on veut que le modele les gere.

Split train / validation

import json
import random

with open("dataset.jsonl") as f:
    data = [json.loads(line) for line in f]

random.seed(42)
random.shuffle(data)

split = int(0.9 * len(data))
train = data[:split]
val = data[split:]

with open("train.jsonl", "w") as f:
    for item in train:
        f.write(json.dumps(item, ensure_ascii=False) + "\n")

with open("val.jsonl", "w") as f:
    for item in val:
        f.write(json.dumps(item, ensure_ascii=False) + "\n")

print(f"Train: {len(train)} exemples | Val: {len(val)} exemples")

Le ratio 90/10 est standard. Pour de petits jeux de donnees (< 500 exemples), un 80/20 donne une meilleure estimation de la performance reelle.


Etape 3 — Provisionner le GPU

Le fine-tuning d’un SLM necessite un GPU avec suffisamment de VRAM. Pas besoin d’acheter du materiel : les plateformes cloud GPU a la demande font le travail.

RunPod

RunPod est la plateforme que nous utilisons pour les entrainements en production. Le modele economique est simple : on loue un GPU a l’heure, on lance l’entrainement, on recupere les poids, on eteint.

Alternative : Lambda Labs propose des H100 et A100 a des tarifs competitifs, avec un accent sur les workloads d’entrainement lourds (> 8 heures). Leur offre “Lambda Cloud” inclut un stockage persistant gratuit de 200 Go, ce qui evite les transferts de donnees repetitifs entre sessions.

Choix du GPU

Modele a fine-tunerGPU recommandeVRAMCout RunPod (spot)Cout RunPod (on-demand)
Qwen2.5-0.5BRTX 4060 Ti16 Go~0,20 EUR/h~0,35 EUR/h
Gemma-2-2B / Qwen2.5-1.5BRTX 409024 Go~0,40 EUR/h~0,75 EUR/h
Mistral-7B / LLaMA-3.1-8BA100 40GB40 Go~1,00 EUR/h~1,65 EUR/h
LLaMA-3.1-8B (batch large)A100 80GB80 Go~1,60 EUR/h~2,40 EUR/h

Spot vs on-demand. Les instances spot coutent 40 a 60 % moins cher mais peuvent etre interrompues. Pour un entrainement de 2-3 heures, le risque est acceptable. Pour un entrainement de 12 heures, prenez du on-demand et activez les checkpoints.

Connexion SSH

Une fois le pod provisionne sur RunPod, la connexion se fait en SSH.

# Recuperer l'adresse dans le dashboard RunPod
ssh root@{POD_IP} -p {PORT} -i ~/.ssh/id_runpod

# Verifier le GPU
nvidia-smi

Vous devez voir votre GPU avec la VRAM attendue. Si nvidia-smi retourne une erreur, le pod est mal provisionne — detruisez-le et recreez-en un.

Transfert des donnees

# Depuis votre machine locale vers le pod RunPod
scp -P {PORT} train.jsonl val.jsonl root@{POD_IP}:/workspace/data/

Etape 4 — Setup de l’environnement

Sur le pod RunPod, l’environnement Python doit etre installe avec les bonnes dependances. Chaque librairie a un role precis.

# Creer un environnement propre (optionnel si le pod a deja Python 3.10+)
pip install --upgrade pip

# Installer le stack de fine-tuning
pip install torch transformers peft bitsandbytes wandb trl datasets accelerate

# Verifier que CUDA est disponible
python3 -c "import torch; print(f'CUDA: {torch.cuda.is_available()}, GPU: {torch.cuda.get_device_name(0)}')"
LibrairieRole
torchBackend de calcul GPU (PyTorch)
transformersChargement du modele et du tokenizer (Hugging Face)
peftLoRA, QLoRA, adapters — fine-tuning efficient
bitsandbytesQuantization 4-bit/8-bit pour reduire la VRAM
wandbMonitoring d’entrainement (Weights & Biases)
trlSFTTrainer — simplifie le fine-tuning supervisee
datasetsChargement et preprocessing des donnees
accelerateGestion multi-GPU et mixed precision

Login Weights & Biases

wandb login
# Coller la cle API depuis https://wandb.ai/authorize

W&B est gratuit pour les projets individuels et les equipes jusqu’a 100 Go de logs. L’alternative open-source est MLflow, mais W&B offre une meilleure experience pour le suivi d’entrainements LLM.

Verification du modele

from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "Qwen/Qwen2.5-1.5B"  # Adapter selon votre choix

tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto",
    trust_remote_code=True
)

print(f"Modele charge: {model_name}")
print(f"Parametres: {model.num_parameters():,}")
print(f"VRAM utilisee: {torch.cuda.memory_allocated() / 1e9:.1f} Go")

Si le modele se charge sans erreur et que la VRAM utilisee est inferieure a 60 % de la VRAM totale, vous avez de la marge pour l’entrainement. Si elle depasse 70 %, activez la quantization 4-bit (voir la section LoRA ci-dessous).


Etape 5 — Fine-tuning avec LoRA

LoRA (Low-Rank Adaptation) est la technique qui rend le fine-tuning de SLM accessible. Au lieu de re-entrainer tous les parametres du modele (ce qui necessiterait 4 a 10 fois plus de VRAM), LoRA n’entraine qu’un petit nombre d’adapteurs — typiquement 0,5 a 2 % des parametres totaux. Le modele original reste fige. Seuls les adapteurs sont appris.

LoRA reduit la VRAM necessaire de 60 a 80 %, le temps d’entrainement de 50 %, et le stockage du modele fine-tune a quelques centaines de Mo au lieu de plusieurs Go.

Le script complet

Voici le script d’entrainement complet. Il est fonctionnel tel quel — copiez-le, adaptez les chemins et le nom du modele, et lancez-le.

import torch
from datasets import load_dataset
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    TrainingArguments,
)
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer

# ── Configuration ──────────────────────────────────────────────
MODEL_NAME = "Qwen/Qwen2.5-1.5B"
OUTPUT_DIR = "./output-slm-industrie"
TRAIN_FILE = "./data/train.jsonl"
VAL_FILE = "./data/val.jsonl"
WANDB_PROJECT = "slm-industrie"

# ── Chargement modele + tokenizer ──────────────────────────────
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    torch_dtype=torch.bfloat16,
    device_map="auto",
    trust_remote_code=True,
)

# ── Configuration LoRA ─────────────────────────────────────────
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=16,                          # Rang de la decomposition (8-64)
    lora_alpha=32,                 # Facteur de scaling (2x le rang)
    lora_dropout=0.05,             # Regularisation legere
    target_modules=[               # Couches a adapter
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj"
    ],
    bias="none",
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# >>> trainable params: 13,631,488 || all params: 1,543,714,816 || 0.88%

# ── Chargement des donnees ─────────────────────────────────────
dataset_train = load_dataset("json", data_files=TRAIN_FILE, split="train")
dataset_val = load_dataset("json", data_files=VAL_FILE, split="train")

def format_prompt(example):
    """Formatte un exemple en prompt d'entrainement."""
    text = f"### Instruction:\n{example['instruction']}\n\n"
    if example.get("input"):
        text += f"### Input:\n{example['input']}\n\n"
    text += f"### Output:\n{example['output']}"
    return {"text": text}

dataset_train = dataset_train.map(format_prompt)
dataset_val = dataset_val.map(format_prompt)

# ── Arguments d'entrainement ───────────────────────────────────
training_args = TrainingArguments(
    output_dir=OUTPUT_DIR,
    num_train_epochs=3,
    per_device_train_batch_size=4,
    per_device_eval_batch_size=4,
    gradient_accumulation_steps=4,  # Batch effectif = 4 x 4 = 16
    learning_rate=2e-4,
    lr_scheduler_type="cosine",
    warmup_ratio=0.05,
    bf16=True,
    logging_steps=10,
    eval_strategy="steps",
    eval_steps=50,
    save_strategy="steps",
    save_steps=100,
    save_total_limit=3,
    load_best_model_at_end=True,
    metric_for_best_model="eval_loss",
    greater_is_better=False,
    report_to="wandb",
    run_name="slm-industrie-lora-r16",
    dataloader_pin_memory=True,
    gradient_checkpointing=True,   # Economise 30-40% de VRAM
)

# ── Lancement de l'entrainement ────────────────────────────────
trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset_train,
    eval_dataset=dataset_val,
    processing_class=tokenizer,
    dataset_text_field="text",
    max_seq_length=1024,
    packing=True,                  # Pack les exemples courts ensemble
)

trainer.train()

# ── Sauvegarde ─────────────────────────────────────────────────
trainer.save_model(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
print(f"Modele sauvegarde dans {OUTPUT_DIR}")

Explication des hyperparametres cles

r=16 — Le rang de la decomposition LoRA. Plus r est grand, plus le modele a de capacite d’apprentissage, mais plus il consomme de VRAM et risque l’overfitting. Pour la plupart des taches industrielles, r=16 est le bon compromis. Si vous avez peu de donnees (< 500 exemples), descendez a r=8. Si votre tache est complexe et que vous avez beaucoup de donnees, montez a r=32.

lora_alpha=32 — Le facteur de scaling. La regle de pouce est de le fixer a 2 fois le rang. alpha/r donne le facteur de mise a l’echelle effectif des adapteurs.

learning_rate=2e-4 — Le taux d’apprentissage. Pour LoRA, 1e-4 a 3e-4 fonctionne dans la grande majorite des cas. Si le modele diverge (loss qui explose), divisez par 2. Si le modele converge trop lentement, multipliez par 2.

gradient_checkpointing=True — Echange du temps de calcul contre de la VRAM. Ralentit l’entrainement de 20 % mais reduit la consommation VRAM de 30 a 40 %. Indispensable pour fine-tuner un 7B sur un A100 40GB.

packing=True — Concatene les exemples courts pour remplir la fenetre de contexte (max_seq_length). Accelere l’entrainement de 30 a 50 % quand les exemples sont courts (< 200 tokens).

Lancement

# Depuis le pod RunPod
python3 train.py

# Ou en arriere-plan avec logs
nohup python3 train.py > training.log 2>&1 &
tail -f training.log

L’entrainement typique dure :

ModeleDatasetGPUDuree
Qwen2.5-0.5B2 000 exemples, 3 epochsRTX 4090~15 min
Gemma-2-2B2 000 exemples, 3 epochsRTX 4090~45 min
Qwen2.5-1.5B5 000 exemples, 3 epochsRTX 4090~1h30
Mistral-7B5 000 exemples, 3 epochsA100 40GB~2h30
LLaMA-3.1-8B5 000 exemples, 3 epochsA100 40GB~3h00

Etape 6 — Monitoring avec Weights & Biases

Lancer un entrainement sans monitoring, c’est comme lancer une production sans SPC. On ne sait pas si ca marche, on ne sait pas quand ca derive, et on ne sait pas quand s’arreter.

Ce que vous voyez sur le dashboard W&B

Une fois l’entrainement lance avec report_to="wandb", le dashboard Weights & Biases se met a jour en temps reel. Voici ce que vous devez surveiller.

Courbe de loss (train/loss). C’est l’indicateur principal. La loss doit decroitre de maniere reguliere pendant les premieres centaines de steps, puis se stabiliser. Une loss qui remonte indique un overfitting ou un learning rate trop eleve.

Courbe de validation (eval/loss). C’est la metrique qui compte vraiment. La loss de validation doit suivre la loss d’entrainement avec un leger ecart. Si la loss de validation remonte alors que la loss d’entrainement continue de descendre, le modele overfitte — il memorise les donnees d’entrainement au lieu d’apprendre le pattern. C’est le signal d’arret.

Learning rate. Avec un scheduler cosine, le learning rate doit former une courbe en cloche inversee : montee rapide pendant le warmup (5 % des steps), puis descente progressive. Si la courbe ne ressemble pas a ca, verifier la configuration du scheduler.

Signaux d’alerte

SignalDiagnosticAction
Loss qui ne descend pasLearning rate trop faible ou donnees incoherentesMultiplier lr par 2, ou verifier les donnees
Loss qui explose (NaN)Learning rate trop eleve ou probleme numeriqueDiviser lr par 5, verifier bf16 vs fp16
eval_loss remonte apres step NOverfitting a partir de step NReprendre le checkpoint du step N
Loss tres basse (< 0.1) trop viteLe modele a memorise le train setReduire le nombre d’epochs, augmenter le dropout
train_loss et eval_loss ne descendent plusLe modele a convergeArreter l’entrainement

Early stopping

Le parametre load_best_model_at_end=True dans les TrainingArguments fait deja une forme d’early stopping : a la fin de l’entrainement, le meilleur checkpoint (selon eval_loss) est charge. Pour un early stopping strict (arret des que eval_loss remonte pendant N evaluations), ajouter un callback :

from transformers import EarlyStoppingCallback

trainer = SFTTrainer(
    # ... meme config que precedemment ...
    callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
)

Avec early_stopping_patience=3, l’entrainement s’arrete si la eval_loss n’ameliore pas pendant 3 evaluations consecutives.


Etape 7 — Quantization GGUF pour deploiement

L’entrainement est termine. Vous avez un modele fine-tune en bfloat16 qui pese plusieurs Go et qui necessite un GPU pour l’inference. Pour deployer en production — surtout sur CPU, sur un serveur classique, ou sur un poste de travail — il faut quantizer le modele.

La quantization reduit la taille du modele de 60 a 75 % et permet l’inference sur CPU, avec une perte de qualite typiquement inferieure a 2 %.

Fusionner les adapteurs LoRA

Avant de quantizer, il faut fusionner les adapteurs LoRA avec le modele de base pour obtenir un modele standalone.

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

# Charger le modele de base
base_model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-1.5B",
    torch_dtype="auto",
    device_map="auto",
    trust_remote_code=True,
)

# Charger et fusionner les adapteurs LoRA
model = PeftModel.from_pretrained(base_model, "./output-slm-industrie")
merged_model = model.merge_and_unload()

# Sauvegarder le modele fusionne
merged_model.save_pretrained("./merged-model")
AutoTokenizer.from_pretrained("./output-slm-industrie").save_pretrained("./merged-model")
print("Modele fusionne sauvegarde dans ./merged-model")

Conversion GGUF

Le format GGUF est le standard pour l’inference CPU via llama.cpp et Ollama. La conversion utilise le script officiel du projet llama.cpp.

# Cloner llama.cpp (si pas deja fait)
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
pip install -r requirements.txt

# Convertir en GGUF quantize Q4_K_M (meilleur rapport taille/qualite)
python3 convert_hf_to_gguf.py ../merged-model --outtype q4_K_M --outfile ../slm-industrie-q4km.gguf

Niveaux de quantization

FormatTaille (1.5B)Taille (7B)QualiteUsage
BF16 (original)3,0 Go14 Go100 %GPU uniquement
Q8_01,6 Go7,7 Go~99 %Serveur avec RAM > 16 Go
Q5_K_M1,1 Go5,3 Go~97 %Serveur standard
Q4_K_M0,9 Go4,4 Go~95 %Recommande pour production
Q3_K_M0,7 Go3,5 Go~90 %Edge / IoT
Q2_K0,5 Go2,7 Go~80 %Experimental

Q4_K_M est le sweet spot. Il offre 95 % de la qualite originale pour 30 % de la taille. C’est le format que nous deploiements en production chez nos clients industriels.


Etape 8 — Deploiement local avec Ollama

Ollama est le runtime le plus simple pour deployer un SLM en production. Il transforme un fichier GGUF en serveur d’inference avec une API REST compatible OpenAI. Installation en une commande, demarrage en une commande.

Installation d’Ollama

# Linux
curl -fsSL https://ollama.com/install.sh | sh

# Verifier l'installation
ollama --version

Creer le Modelfile

Le Modelfile est la recette qui indique a Ollama comment charger et configurer votre modele.

# Modelfile
FROM ./slm-industrie-q4km.gguf

PARAMETER temperature 0.1
PARAMETER top_p 0.9
PARAMETER num_ctx 2048

SYSTEM """Tu es un assistant qualite industriel specialise dans la classification de tickets SAV et l'extraction de donnees techniques. Tu reponds en francais, de maniere structuree et factuelle. Tu ne fais pas de supposition quand les donnees sont insuffisantes."""

Import et test

# Creer le modele dans Ollama
ollama create slm-industrie -f Modelfile

# Tester en interactif
ollama run slm-industrie "Classifie ce ticket : La vis M6x20 inox casse au serrage a 8 Nm. Lot W14."

# Verifier que le modele est disponible
ollama list

Integration API

Une fois le modele charge dans Ollama, il est accessible via une API REST sur le port 11434. Toute application interne peut l’appeler.

import requests

def classify_ticket(ticket_text: str) -> dict:
    """Appelle le SLM pour classifier un ticket SAV."""
    response = requests.post(
        "http://localhost:11434/api/generate",
        json={
            "model": "slm-industrie",
            "prompt": f"Classifie ce ticket SAV dans une categorie.\n\nTicket: {ticket_text}",
            "stream": False,
            "options": {"temperature": 0.1}
        }
    )
    return response.json()

# Exemple d'utilisation
result = classify_ticket("Bavure sur piece injectee ref. PLT-300, moule cavite 3")
print(result["response"])

L’API est compatible avec le format OpenAI (/v1/chat/completions), ce qui permet de remplacer un appel GPT-4 par un appel local en changeant uniquement l’URL de base.

from openai import OpenAI

# Remplacer l'appel cloud par l'appel local
client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused")

response = client.chat.completions.create(
    model="slm-industrie",
    messages=[
        {"role": "system", "content": "Tu es un assistant qualite industriel."},
        {"role": "user", "content": "Classifie ce ticket : surface rayee sur piece polie ref. MIR-200"}
    ],
    temperature=0.1
)
print(response.choices[0].message.content)

Etape 9 — Couts reels, de bout en bout

Voici le cout reel d’un fine-tuning SLM, de la preparation des donnees au deploiement. Ces chiffres sont bases sur nos propres entrainements chez BCUB3, pas sur des estimations theoriques.

Cout d’entrainement

ModeleGPUDuree entrainementCout GPU (spot)Cout GPU (on-demand)
Qwen2.5-0.5BRTX 409015 min0,10 EUR0,19 EUR
Qwen2.5-1.5BRTX 40901h300,60 EUR1,13 EUR
Gemma-2-2BRTX 409045 min0,30 EUR0,56 EUR
Mistral-7BA100 40GB2h302,50 EUR4,13 EUR
LLaMA-3.1-8BA100 40GB3h003,00 EUR4,95 EUR

Cout total du projet (premiere iteration)

PosteCoutTemps
Preparation des donnees (annotation)0 EUR (equipe interne)2-5 jours
GPU pour entrainement (3 runs d’iteration)1 — 15 EUR1-10 heures
Weights & Biases (plan gratuit)0 EUR
Serveur de deploiement (existant ou VPS)20 — 50 EUR/mois
Temps ingenieur (setup + iteration)2-3 jours
Total premiere iteration< 100 EUR1 semaine

Le cout total d’un fine-tuning SLM est inferieur au cout d’un mois d’abonnement a un LLM cloud pour un usage a volume equivalent.

Cout d’exploitation

PosteSLM on-premiseLLM cloud
Serveur (VPS 8 vCPU, 32 Go RAM)50 EUR/mois
API LLM200-2 000 EUR/mois
Maintenance2h/mois0
Total annuel~700 EUR2 400-24 000 EUR

Etape 10 — Checklist de sortie avant mise en production

Avant de deployer un SLM fine-tune en production, ces huit points doivent etre valides. Pas de raccourci.

1. Eval loss stable et coherente

La loss de validation a la fin de l’entrainement doit etre stable (pas de tendance a la hausse sur les derniers checkpoints). Verifier sur le dashboard W&B que la courbe eval_loss est plate ou en legere descente sur les 20 % finaux des steps.

2. Test sur des exemples hors-dataset

Tester le modele sur 20 a 50 exemples qui n’etaient ni dans le train set ni dans le val set. Ces exemples doivent etre representatifs de la production reelle — y compris les cas limites, les fautes de frappe, les inputs mal formates. Mesurer la precision sur ces exemples. Si elle est inferieure de plus de 5 points a la precision sur le val set, le modele overfitte.

3. Test de regression

Verifier que le modele fine-tune n’a pas perdu ses capacites de base. Un SLM fine-tune pour la classification de tickets doit encore etre capable de produire du texte coherent en francais. Tester avec 5 prompts generiques. Si le modele produit du charabia, le fine-tuning a ete trop agressif (learning rate trop eleve ou trop d’epochs).

4. Latence mesuree

Mesurer la latence d’inference sur le hardware de production (pas sur le GPU d’entrainement). Un SLM quantize Q4_K_M sur un CPU moderne doit repondre en moins de 500 ms pour un output de 100 tokens. Si la latence depasse les specifications, envisager un modele plus petit ou un format de quantization plus agressif.

# Mesurer la latence avec Ollama
time ollama run slm-industrie "Classifie : piece rayee ref. XYZ-100" --verbose

5. Consommation memoire validee

Verifier que le modele charge ne depasse pas 70 % de la RAM disponible sur le serveur de production. Laisser de la marge pour le systeme d’exploitation et les autres services.

# Verifier la RAM utilisee par Ollama
ollama ps

6. API fonctionnelle

Tester l’appel API depuis l’application cliente (ERP, GMAO, ou script Python). Verifier le format de la reponse, les codes HTTP, et le comportement en cas d’erreur (modele non charge, input vide, input trop long).

7. Monitoring en place

Mettre en place un log des requetes et des reponses du modele. Pas pour la surveillance des utilisateurs, mais pour detecter le drift : si la distribution des inputs change (nouveaux types de defauts, nouveau vocabulaire), le modele doit etre re-fine-tune.

8. Procedure de rollback documentee

Si le modele se comporte mal en production, il faut pouvoir revenir a la version precedente en moins de 5 minutes. Avec Ollama, c’est un ollama rm + ollama create avec l’ancien Modelfile. Documenter la procedure et la tester.

# Rollback en 3 commandes
ollama rm slm-industrie
ollama create slm-industrie -f Modelfile.v1
ollama run slm-industrie "test rapide"

Conclusion — Le SLM est l’outil de coupe specifique

Le fine-tuning d’un SLM n’est pas un projet de recherche. C’est un mode operatoire industriel. Les etapes sont claires, les outils sont matures, les couts sont derisoires compares a la valeur creee.

Ce qui fait la difference entre un POC et un deploiement, ce n’est pas la sophistication du modele. C’est la qualite des donnees, la rigueur du monitoring, et la simplicite du deploiement.

Un Qwen2.5-1.5B bien fine-tune, bien quantize, bien deploye, rend plus de service qu’un GPT-4o interroge sans structure. Parce qu’il connait votre metier, qu’il tourne dans votre mur, et qu’il coute le prix d’un cafe par mois.

Les commandes sont dans cet article. Les donnees sont dans votre systeme qualite. Il ne reste qu’a executer.


BCUB3 accompagne les ETI industrielles dans le deploiement de modeles IA souverains — du diagnostic au fine-tuning en production. Contactez-nous pour un audit de vos cas d’usage.

Cet article vous a été utile ?

BCUB3 est une petite structure. Si vous pensez à un collègue ou un partenaire qui pourrait en tirer quelque chose, la meilleure manière de nous aider est de partager le lien. Et si vous avez un cas concret à discuter, parlons-en directement.

Prendre un RDV de cadrage