1. Matrice de Décision : FT vs RAG vs Prompting
Quand Fine-Tuner ?
Tâches spécialisées : Domaine médical, légal, technique
Format fixe : Structure de réponse toujours identique
Performance critique : Latence basse requise
Données propriétaires : Pas de fuite via contexte
Qualité supérieure : Priorité à la précision
Comparaison Coût-Performance
Approche
Latence
Coût Setup
Coût Inférence
Exactitude
Zero-Shot Prompting
Normal
Faible (~0€)
Faible
Moyenne
Few-Shot + RAG
Moyen
Moyen (~100€)
Moyen
Élevée
Fine-Tuning (LoRA)
Rapide
Élevé (~500€)
Faible
Très élevée
Full Fine-Tuning
Très rapide
Très élevé (~2000€)
Élevé
Maximale
Matrice Visuelle
Budget/Données │ Peu de données │ Données moyennes │ Beaucoup données
────────────────────┼─────────────────┼──────────────────┼─────────────────
Budget zéro │ Zero-Shot │ Few-Shot │ (RAG++)
Budget faible │ Few-Shot │ RAG │ LoRA+RAG
Budget moyen │ RAG │ LoRA+Prompt │ LoRA
Budget élevé │ LoRA │ LoRA Full │ Full FT
2. Préparation des Données
Format Alpaca (Instruction-Tuning)
{
"instruction": "Classe ce produit",
"input": "Un téléphone noir durable",
"output": "Catégorie: Électronique, Électronique/Téléphones"
}
Format standard pour instruction tuning
Champ "input" optionnel
Idéal pour tâches structurées
Format ShareGPT (Conversations)
{
"conversations": [
{"from": "human", "value": "Quelle est ta spécialité?"},
{"from": "gpt", "value": "Je suis expert en IA..."},
{"from": "human", "value": "Donne un exemple."},
{"from": "gpt", "value": "Par exemple..."}
]
}
Conversations multi-tours
Format naturel pour chatbots
Préserve contexte conversationnel
Format JSONL (Minimal)
{"prompt": "Résume ceci:", "completion": "Résumé..."}
{"prompt": "Classe:", "completion": "Classe..."}
{"prompt": "Génère:", "completion": "Contenu..."}
Une ligne = un exemple
Fichier line-delimited JSON
Permet datasets massifs
Checklist Qualité Données
Critère
Target
Vérification
Taille dataset
1K-100K exemples
len(dataset)
Doublons
<5%
set(texts) size
Balance classes
±20%
Counter(labels)
Longueur tokens
64-2048
tokenizer.encode()
Erreurs typo
<2%
spellchecker
Format cohérent
100%
validation schema
Pipeline de Nettoyage Python
import json
from collections import Counter
def clean_dataset(jsonl_path):
seen = set()
cleaned = []
with open(jsonl_path) as f:
for line in f:
obj = json.loads(line)
text = obj.get('prompt', '') + obj.get('completion', '')
# Pas de doublon
if text in seen:
continue
seen.add(text)
# Pas vide
if len(text) < 20:
continue
cleaned.append(obj)
return cleaned
3. SFT : Supervised Fine-Tuning Basiques
Processus SFT Standard
Modèle Pré-entraîné
↓
Entraînement Supervisé (Input → Output attendu)
↓
Optimisation des poids pour prediction exacte
↓
Modèle Spécialisé
Hyperparamètres Typiques
Paramètre
Recommandé
Plage
learning_rate
5e-5
1e-5 à 1e-4
batch_size
16-32
4 à 128
num_epochs
3
1 à 10
warmup_ratio
0.1
0.05 à 0.2
weight_decay
0.01
0.0 à 0.1
max_grad_norm
1.0
0.5 à 2.0
Régularisation : Overfitting vs Underfitting
Overfitting (train_loss bas, eval_loss haut)
Réduire epochs
Augmenter weight_decay
Ajouter données
Utiliser Dropout
Underfitting (loss élevée partout)
Augmenter epochs
Réduire learning_rate
Vérifier données (doublon?)
Modèle trop petit
Early Stopping
from transformers import EarlyStoppingCallback
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
callbacks=[
EarlyStoppingCallback(
early_stopping_patience=3,
early_stopping_threshold=0.0
)
]
)
trainer.train()
4. Taxonomie PEFT (Parameter-Efficient FT)
Famille PEFT Complète
Méthode
Paramètres à entraîner
Mémoire
Performance
Cas d'usage
LoRA
~0.1-1%
-60%
Excellente
Premier choix
QLoRA
~0.1% + quant
-80%
Très bonne
Mémoire limitée
Adapters
~3-5%
-40%
Bonne
Multi-tâches
Prefix Tuning
~0.1%
-50%
Moyenne
Génération
IA3
~0.01%
-85%
Variable
Modèles 100B+
Critères de Sélection
LoRA Premier choix, meilleur compromis coût/performance
QLoRA Quand mémoire très limitée (<24GB)
Adapters Si besoins multi-tâches parallèles
Prefix Pour génération autoégressive pures
IA3 Modèles énormes (100B+, 8-bit)
5. LoRA en Détail
Mathématique LoRA
h = W₀x + ΔWx = W₀x + BAx
où:
- W₀ = poids original (gelé, non-entraîné)
- B ∈ ℝ^(d_out × r)
- A ∈ ℝ^(r × d_in)
- r = rang << min(d_out, d_in)
Hyperparamètres LoRA
Paramètre
Recommandé
Effet
r (rank)
8-16
Plus haut = plus flexible, plus mémoire
lora_alpha
16-32
Scaling, souvent 2×rank
lora_dropout
0.05
Régularisation, evite overfitting
target_modules
["q_proj", "v_proj"]
Où appliquer LoRA
Target Modules par Modèle
# LLaMA / Mistral
target_modules = ["q_proj", "v_proj"]
# Qwen
target_modules = ["c_attn"]
# Phi-2
target_modules = ["q_proj", "v_proj", "dense"]
# Complet (attention + FFN)
target_modules = ["q_proj", "v_proj", "k_proj", "up_proj", "down_proj"]
Configuration LoRA Complète
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM
lora_config = LoraConfig(
r=8,
lora_alpha=16,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules=["q_proj", "v_proj"],
inference_mode=False
)
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B")
model = get_peft_model(model, lora_config)
print(model.print_trainable_parameters())
Fusion et Sauvegarde
# Après entraînement, fusionner poids
model = model.merge_and_unload()
# Sauvegarder le modèle fusionné (optionnel)
model.save_pretrained("./final_model")
tokenizer.save_pretrained("./final_model")
# Charger normal comme n'importe quel modèle
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("./final_model")
6. QLoRA : 4-Bit + LoRA
Architecture QLoRA
Modèle 70B (140GB) sans quantization
↓
Quantization 4-bit (10GB)
↓
LoRA adapters (100MB)
↓
Total: ~10.1GB (vs 140GB!)
↓
Fine-tune sur 24GB GPU
Types de Quantization
Format
Taille
Précision
Qualité
FP32
100%
32-bit
Parfaite
BF16/FP16
50%
16-bit
Excellente
INT8
25%
8-bit
Très bonne
NF4
25%
4-bit normalisé
Bonne
INT4
25%
4-bit
Acceptable
Code QLoRA Complet
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
# Config quantization
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
# Charger modèle quantifié
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-70b",
quantization_config=bnb_config,
device_map="auto"
)
# Appliquer LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
print(model.print_trainable_parameters())
Double Quantization
Normal 4-bit : 4-bit weights
Double quant : Quantize aussi les scales et zero points
Économie : -0.4 bits supplémentaires par paramètre
Trade-off : -5-10% performance, -10% mémoire
7. TRL & SFTTrainer
Transformer Reinforcement Learning Stack
TRL (Transformer Reinforcement Learning)
├── SFTTrainer (Supervised Fine-Tuning)
├── DPOTrainer (Direct Preference Opt)
├── PPOTrainer (Reinforcement Learning)
└── Utilities (Data, Models, Training)
Configuration SFTTrainer
from trl import SFTTrainer, SFTConfig
sft_config = SFTConfig(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
learning_rate=5e-5,
lr_scheduler_type="cosine",
warmup_steps=100,
gradient_accumulation_steps=4,
max_seq_length=2048,
# Logging
logging_steps=10,
save_steps=100,
eval_steps=50,
evaluation_strategy="steps",
# Distributed
ddp_find_unused_parameters=False,
report_to=["wandb"]
)
trainer = SFTTrainer(
model="mistralai/Mistral-7B",
train_dataset=train_dataset,
eval_dataset=eval_dataset,
args=sft_config,
peft_config=lora_config
)
trainer.train()
Intégration Weights & Biases
# Logging automatique
export WANDB_PROJECT="fine-tuning"
export WANDB_ENTITY="my-entity"
# Dans SFTConfig
report_to=["wandb"],
wandb_project="fine-tuning"
# Métriques tracées automatiquement:
# - train_loss, eval_loss
# - learning_rate
# - epoch
# - temps execution
8. DPO : Direct Preference Optimization
Formule DPO
L_DPO = -E[log σ(β log(π(y_w|x)/π_ref(y_w|x))
- β log(π(y_l|x)/π_ref(y_l|x)))]
où:
- y_w = réponse préférée (chosen)
- y_l = réponse rejetée (rejected)
- β = coefficient température (0.1 typique)
- π = modèle à optimiser
- π_ref = modèle de référence
Format Données DPO
{
"prompt": "Comment faire un gâteau?",
"chosen": "Mélanger farine, œufs... C'est savoureux!",
"rejected": "Je ne sais pas comment faire un gâteau."
}
Avantages vs RLHF
Aspect
DPO
RLHF
Reward Model
Non requis
Requis
Stabilité
Très bonne
Difficile
Complexité
Faible
Élevée
Performance
Comparable
Légèrement meilleure
Temps setup
Rapide (~1 semaine)
Lent (~2 semaines)
Code DPOTrainer
from trl import DPOTrainer, DPOConfig
dpo_config = DPOConfig(
output_dir="./dpo_results",
per_device_train_batch_size=4,
num_train_epochs=3,
learning_rate=5e-5,
beta=0.1, # Température de contraste
max_seq_length=2048
)
trainer = DPOTrainer(
model="mistralai/Mistral-7B",
train_dataset=train_dataset,
eval_dataset=eval_dataset,
args=dpo_config,
peft_config=lora_config
)
trainer.train()
9. RLHF en Pratique
Pipeline RLHF Complet
Phase 1: SFT
Modèle Base + Démonstrations → Modèle SFT
Phase 2: Reward Model
Modèle SFT + Comparaisons humaines → Reward Model
Phase 3: PPO
Modèle SFT + Reward Model → Modèle Aligné
Hyperparamètres PPO
Paramètre
Typique
Description
learning_rate
1e-5
Très faible pour stabilité
batch_size
64-128
Large pour stabilité
mini_batch_size
8-16
Pour gradient updates
ppo_epochs
4
Iterations par batch
cliprange
0.2
PPO clipping parameter
gamma
0.99
Discount factor
Code PPOTrainer
from trl import PPOTrainer, PPOConfig
ppo_config = PPOConfig(
model_name="mistralai/Mistral-7B",
learning_rate=1e-5,
batch_size=128,
mini_batch_size=16,
ppo_epochs=4,
gradient_accumulation_steps=8
)
trainer = PPOTrainer(
config=ppo_config,
model=model,
ref_model=ref_model,
tokenizer=tokenizer,
train_dataset=train_dataset,
data_collator=data_collator
)
# Boucle d'entraînement
for epoch in range(3):
for step, batch in enumerate(train_dataloader):
# Générer réponses
response_tensors = trainer.generate(**batch)
# Scorer avec reward model
rewards = reward_model.score(response_tensors)
# PPO update
stats = trainer.step(batch, response_tensors, rewards)
print(f"Step {step}: Loss = {stats['loss']}")
Complexité RLHF
Avantage Meilleure performance finale
Avantage Peut apprendre des reward subtiles
Inconvénient Nécessite reward model
Inconvénient Très instable (besoin tuning)
Inconvénient Coûteux en compute
10. ORPO, SimPO, KTO (Méthodes 2025-2026)
Nouvelle Génération d'Alignment
Méthode
Année
Approche
Avantages
ORPO
2024
SFT + Odds Ratio loss
Une étape, stable, efficace
SimPO
2024
Contrastive + preference
Très stable, rapide
KTO
2025
Theoretic optimal alignment
Performance maximale
ORPO (Odds Ratio Preference Optimization)
L_ORPO = L_SFT + λ * L_OR
où L_OR = -E[log σ(log odds(y_w|x) - log odds(y_l|x))]
odds(y|x) = P(y|x) / (1 - P(y|x))
Code ORPO
from trl import ORPOConfig, ORPOTrainer
orpo_config = ORPOConfig(
output_dir="./orpo_results",
per_device_train_batch_size=4,
num_train_epochs=3,
learning_rate=5e-5,
lambda_=0.1, # Poids du loss OR
max_seq_length=2048
)
trainer = ORPOTrainer(
model="mistralai/Mistral-7B",
args=orpo_config,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
peft_config=lora_config
)
trainer.train()
Comparaison des Approches d'Alignment
Méthode │ Étapes │ Reward Model │ Stabilité │ Performance
─────────────┼────────┼──────────────┼───────────┼────────────
Prompting │ 0 │ Non │ Très haute│ Basse
SFT │ 1 │ Non │ Très haute│ Moyenne
DPO │ 1 │ Non │ Très haute│ Haute
ORPO │ 1 │ Non │ Très haute│ Haute
SimPO │ 1 │ Non │ Très haute│ Très haute
RLHF │ 3 │ Oui │ Basse │ Maximale
KTO │ 1 │ Non │ Très haute│ Maximale
11. Model Merging
Stratégies de Fusion
Méthode
Complexité
Qualité
Cas d'usage
Simple Average
Très faible
Faible
Quick experiments
TIES-Merge
Faible
Bonne
Multiple fine-tunes
DARE
Moyen
Très bonne
Combiner LoRAs
Passthrough
Moyen
Excellente
LoRA selective merge
Gradient-based
Élevée
Maximale
Research/optimisation
mergekit Configuration
# config.yaml pour mergekit
models:
- model: base/Mistral-7B
parameters:
weight: 1.0
- model: finetune/medical-lora
parameters:
weight: 0.5
- model: finetune/legal-lora
parameters:
weight: 0.3
merge_method: ties
base_model: base/Mistral-7B
parameters:
normalize_weights: true
threshold: 0.05
# Exécution
mergekit-cli merge config.yaml ./merged_model
DARE (Drop And REscale)
# DARE merge pour LoRAs
import torch
def dare_merge(base_weight, delta, scaling_factor=1.0):
"""
Fusionner avec probabilité dropout + rescaling
"""
# Dropout aléatoire des changements
mask = torch.bernoulli(torch.ones_like(delta) * 0.7)
# Rescaling pour compenser
delta_scaled = delta * mask / (1 - 0.3)
# Fusion
merged = base_weight + scaling_factor * delta_scaled
return merged
Fusion de Multiples LoRAs
# Fusionner plusieurs LoRAs dans un seul
from peft import LoraConfig
# Charger base model
model = AutoModelForCausalLM.from_pretrained("mistral-7b")
# Fusion séquentielle
for lora_path in ["lora_medical", "lora_legal", "lora_code"]:
peft_model = PeftModel.from_pretrained(model, lora_path)
model = peft_model.merge_and_unload()
# Sauvegarder modèle fusionné
model.save_pretrained("./merged_expert_model")
12. Continued Pre-Training
Quand faire CPT?
Nouveau domaine : Vocabulaire totalement différent
Langue sous-représentée : Modèle pré-entraîné faible
Code spécifique : Langages niche (Rust, Solidity)
Données massives : >100GB de contenu domaine
CPT vs Fine-Tuning
Aspect
CPT
Fine-Tuning
Données requises
Très élevées (100GB+)
Modérées (10GB)
Temps entraînement
Très long (mois)
Court (heures/jours)
Coût
Très élevé (10k€+)
Moyen (100€-1k€)
Vocabulaire
Peut créer tokens
Utilise existant
Performance
Maximale dans domaine
Très bonne
Pipeline CPT
# 1. Préparer corpus domaine
corpus_files = glob.glob("./medical_data/*.txt")
dataset = load_dataset("text", data_files=corpus_files)
# 2. Configuration pré-entraînement
training_args = TrainingArguments(
output_dir="./cpt_model",
num_train_epochs=1,
per_device_train_batch_size=16,
gradient_accumulation_steps=4,
learning_rate=5e-4,
warmup_steps=10000,
logging_steps=100,
save_steps=5000,
max_steps=100000
)
# 3. Entraînement
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
data_collator=DataCollatorForLanguageModeling(
tokenizer=tokenizer,
mlm=False
)
)
trainer.train()
13. Multi-Task & Instruction Tuning
Approche Multi-Task
Dataset 1 (Q&A médicale)
Dataset 2 (Summarization légale)
Dataset 3 (Code generation)
↓
Unified format (instruction)
↓
Single model trained on all
↓
Généralisation améliorée
Format Instruction Unifiée
# Tous les datasets au format Alpaca
{
"instruction": "Répondre à une question médicale",
"input": "Qu'est-ce que l'hypertension?",
"output": "L'hypertension est l'élevation de la pression artérielle..."
}
{
"instruction": "Résumer ce texte légal",
"input": "Article 1234 du code civil...",
"output": "Résumé : ..."
}
{
"instruction": "Générer du code Python",
"input": "Fonction qui calcule Fibonacci",
"output": "def fibonacci(n): ..."
}
Avantages Multi-Task
✓ Un seul modèle pour multiple tâches
✓ Transfert de connaissances entre domaines
✓ Meilleure généralisation
✓ Réduction catastrophic forgetting
✓ Dataset unification simplifie pipeline
Data Mixing Strategies
Stratégie
Description
Cas d'usage
Round-robin
Alterner entre datasets
Datasets petits/équilibrés
Proportional
Mix selon taille
Datasets non-équilibrés
Stratified
Sampling par classe
Classes déséquilibrées
Curriculum
Facile → difficile
Données très hétérogènes
14. Évaluation de Fine-Tuning
Benchmarks Standard
Benchmark
Type
Taille
Domaine
MMLU
Multiple choice QA
15,908 questions
57 sujets (généraliste)
HumanEval
Code generation
164 problèmes
Python
GSM8K
Math reasoning
8,500 problèmes
Mathématiques grade 8
TruthfulQA
Véracité réponses
817 questions
Fact accuracy
BBH
Raisonnement difficile
23 tâches
Cognition complexe
LLM-as-Judge Evaluation
def evaluate_with_judge(model, test_cases, judge_model="gpt-4"):
"""Évaluer réponses avec un modèle judge"""
results = []
for case in test_cases:
# Générer réponse
response = model.generate(case["prompt"])
# Évaluer avec judge
eval_prompt = f"""
Question: {case['prompt']}
Réponse: {response}
Réponse de référence: {case['reference']}
Note cette réponse sur 5 critères :
1. Exactitude (1-5)
2. Complétude (1-5)
3. Clarté (1-5)
4. Utilité (1-5)
5. Respect des consignes (1-5)
Fournis notation JSON."""
scores = judge_model.evaluate(eval_prompt)
results.append(scores)
return average_scores(results)
Métriques Automatiques
Métrique
Usage
Plage
Meilleur
BLEU
Traduction, n-gram overlap
0-100
Plus haut
ROUGE-L
Résumé, LCS
0-1
Plus haut
BERTScore
Similarité sémantique
0-1
Plus haut
Perplexity
Qualité modèle LM
0-∞
Plus bas
Framework Evaluation Complet
from datasets import load_dataset
from evaluate import load
def full_evaluation(model, tokenizer, benchmark_name="mmlu"):
"""Pipeline complet évaluation"""
dataset = load_dataset(f"json", data_files=f"{benchmark_name}.json")
# Métriques
accuracy = load("accuracy")
bleu = load("bleu")
predictions = []
references = []
for example in dataset["test"]:
pred = model.generate(example["question"])
predictions.append(pred)
references.append(example["answer"])
# Calcul
acc_score = accuracy.compute(
predictions=predictions,
references=references
)
return {
"accuracy": acc_score["accuracy"],
"benchmark": benchmark_name
}
15. Axolotl : Framework Unifié YAML
Configuration Axolotl Complète
base_model: mistralai/Mistral-7B
model_type: AutoModelForCausalLM
tokenizer_type: AutoTokenizer
# Data
datasets:
- path: data/train.jsonl
type: alpaca
split: train
- path: data/eval.jsonl
type: alpaca
split: eval
# Training
learning_rate: 5e-5
num_epochs: 3
micro_batch_size: 4
gradient_accumulation_steps: 4
max_seq_length: 2048
warmup_ratio: 0.1
lr_scheduler: cosine
weight_decay: 0.01
# LoRA
peft:
adapter: lora
lora_r: 8
lora_alpha: 16
lora_dropout: 0.05
target_modules: [q_proj, v_proj]
bias: none
# Distributed
distributed_backend: nccl
ddp_timeout: 3600
# Logging
output_dir: ./axolotl_outputs
logging_steps: 10
save_steps: 100
eval_steps: 50
report_to: wandb
# GPU
device_map: auto
bf16: true
Lancement Axolotl
# Installation
pip install axolotl
# Single GPU
axolotl train config.yaml
# Multi-GPU
accelerate launch -m axolotl.cli.train config.yaml
# Inference
axolotl inference config.yaml \
--prompt "Your question here"
# Merge LoRA
axolotl merge_lora config.yaml
Avantages Axolotl
✓ Config YAML simple et lisible
✓ Multi-GPU/distributed natif
✓ Support multi datasets
✓ LoRA merging automatique
✓ Intégration Hugging Face Hub
16. Unsloth : Fine-Tuning Accéléré
Optimisations Unsloth
Optimisation
Speedup
Mémoire
Compatibilité
Flash Attention 2
1.3-1.5×
0%
Ampere+
RoPE optimization
1.2×
0%
All
Memory efficient LoRA
1.2×
-30%
All
Fused ops
1.5×
0%
CUDA compatible
Combined
2-2.5×
-60%
Ampere+
Installation et Usage
# Installation (GPU specific)
pip install unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git
# Ou pour RTX 3090, 4090
pip install unsloth[colab] @ git+https://github.com/unslothai/unsloth.git
# Usage - remplacer chargement modèle
from unsloth import FastLanguageModel
max_seq_length = 2048
dtype = None # Auto-detect
load_in_4bit = True
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/mistral-7b",
max_seq_length = max_seq_length,
dtype = dtype,
load_in_4bit = load_in_4bit,
)
# Ajouter LoRA
model = FastLanguageModel.get_peft_model(
model,
r = 16,
lora_alpha = 16,
lora_dropout = 0.05,
target_modules = ["q_proj", "v_proj"],
bias = "none",
use_gradient_checkpointing = True,
)
Modèles Supportés (Optimisés)
Modèle
Paramètres
Speedup
Support
Llama-2
7B, 13B, 70B
2.5×
Complet
Mistral
7B
2.3×
Complet
Phi
2, 2.5
2.1×
Complet
Qwen
7B, 14B
2.2×
Complet
17. Cloud GPU Providers : Comparatif
Fournisseurs Principaux 2026
Provider
GPU Premium
Coût/h H100
Setup
Support
RunPod
H100, A100, RTX 4090
$3.50
Instants
Excellent
Lambda Labs
H100, A100
$4.00
Minutes
Très bon
Vast.ai
Marché
$2.50
Rapide
Bon
AWS SageMaker
P4d (H100)
$12.99
Lent
Excellent
GCP Vertex
A100, L4
$8.00
Moyen
Bon
Estimation de Coûts
# Fine-tuning 7B model sur 10k examples
# Temps estimé: 2-4 heures
# Scenario 1: RTX 4090 ($3.50/h, RunPod)
hours = 3
cost = 3.5 * hours # $10.50
# Scenario 2: H100 ($3.50/h, RunPod)
hours = 1.5
cost = 3.5 * hours # $5.25
# Scenario 3: Local GPU RTX 3090 (~$2.50/h electricity)
hours = 5
cost = 2.5 * hours # $12.50
RunPod Setup
# 1. Créer compte RunPod.io
# 2. Obtenir API key
# 3. Script Python pour lancer job
import runpod
endpoint_id = runpod.create_endpoint(
name="fine-tuning-mistral",
image="runpod/pytorch:latest",
gpu_count=1,
volume_in_gb=50,
container_disk_in_gb=100
)
# 4. Exécuter training
result = runpod.run_job(
endpoint_id,
input={
"command": "python /workspace/train.py"
}
)
Recommandations Provider
Débutants RunPod (interface simple, bons GPUs)
Budget Vast.ai (moins cher, mais variable)
Professionnel Lambda Labs (setup rapide, support)
Enterprise AWS/GCP (scaling, monitoring)
18. DeepSpeed & FSDP : Distributed Training
Stratégies Distributed Training
Stratégie
Data Parallel
Model Parallel
Overhead
Cas d'usage
DDP
Oui
Non
5-10%
1-8 GPUs
DeepSpeed ZeRO-1
Oui
Non
5-10%
Optimiseur sharding
DeepSpeed ZeRO-2
Oui
Partial
10-15%
Optimiseur + gradients
DeepSpeed ZeRO-3
Oui
Oui
15-25%
Full model sharding
FSDP
Oui
Oui
15-25%
PyTorch native
Configuration DeepSpeed
# ds_config.json
{
"train_batch_size": 128,
"train_micro_batch_size_per_gpu": 4,
"gradient_accumulation_steps": 8,
"optimizer": {
"type": "AdamW",
"params": {
"lr": 5e-5,
"betas": [0.9, 0.95],
"eps": 1e-8,
"weight_decay": 0.01
}
},
"scheduler": {
"type": "WarmupDecay",
"params": {
"warmup_min_lr": 0,
"warmup_max_lr": 5e-5,
"warmup_num_steps": 1000,
"total_num_steps": 100000
}
},
"zero_optimization": {
"stage": 2,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
}
},
"activation_checkpointing": {
"partition_activations": true,
"cpu_checkpointing": false
}
}
# Lancer avec DeepSpeed
deepspeed --num_gpus=8 train.py \
--deepspeed ds_config.json
FSDP Configuration
# Transformer config + FSDP
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy
# Auto wrap small layers
auto_wrap_policy = size_based_auto_wrap_policy(
min_num_params=100_000_000 # 100M
)
# Wrap modèle
model = FSDP(
model,
auto_wrap_policy=auto_wrap_policy,
sharding_strategy=ShardingStrategy.FULL_SHARD
)
# Entraîner normalement
optimizer.zero_grad()
loss.backward()
optimizer.step()
19. Export & Conversion de Modèles
Pipeline Complet Export
Modèle Fine-Tuné (FP32/BF16)
↓
1. Merge LoRA → Modèle complet
↓
2. Quantization (optionnel) → GGUF/GPTQ
↓
3. Conversion format → GGML, ONNX, TensorRT
↓
4. Upload Hugging Face Hub
↓
5. Serve (vLLM, Ollama, TGI)
Fusion et Export LoRA
from peft import AutoPeftModelForCausalLM
from transformers import AutoTokenizer
# Charger modèle + LoRA
model = AutoPeftModelForCausalLM.from_pretrained(
"path/to/lora/checkpoint",
device_map="auto",
torch_dtype=torch.bfloat16
)
# Fusionner et décharger
merged_model = model.merge_and_unload()
# Sauvegarder
merged_model.save_pretrained("./final_model")
AutoTokenizer.from_pretrained("path/to/base/model").save_pretrained("./final_model")
Conversion GGUF (pour Ollama)
# 1. Convertir en GGUF
python ./llama.cpp/convert.py \
./final_model \
--outfile ./model.gguf \
--outtype q4_k_m # 4-bit K-quant
# 2. Tester localement avec Ollama
ollama create custom-model -f Modelfile
# Modelfile contenu:
FROM ./model.gguf:latest
PARAMETER temperature 0.7
PARAMETER top_k 50
PARAMETER top_p 0.95
# 3. Servir
ollama run custom-model "Votre prompt"
Upload Hugging Face Hub
from huggingface_hub import HfApi, ModelCard
from datetime import datetime
# Authentification
hf_api = HfApi()
# Créer Model Card
card = ModelCard.from_template(
template_path="model_card_template.md",
model_id="username/custom-model",
metrics=[{"name": "accuracy", "value": 0.92}]
)
# Upload
hf_api.upload_folder(
folder_path="./final_model",
repo_id="username/custom-model",
repo_type="model",
commit_message=f"Fine-tuned model - {datetime.now().isoformat()}"
)
# Rendre public
hf_api.update_repo_visibility(
repo_id="username/custom-model",
private=False
)
Formats de Sortie
Format
Taille
Vitesse
Compatibilité
FP32
100%
Normal
Universelle
BF16
50%
Rapide
GPU moderne
GGUF
25%
Très rapide
Ollama, CPU
GPTQ
25%
Rapide
GPU spécifiques
TorchScript
50%
Rapide
Production PyTorch
20. Vision Fine-Tuning
Modèles de Vision Spécialisés
Modèle
Type
Fine-Tuning
Cas d'usage
Stable Diffusion
Génération d'images
DreamBooth, LoRA
Images personnalisées
LLaVA
Vision + Langage
Full SFT, LoRA
Vision understanding
CLIP
Image-Text matching
Contrastive learning
Retrieval, classification
InternVL
Multimodal
Instruction tuning
Comprendre images + texte
Stable Diffusion LoRA
from diffusers import StableDiffusionPipeline
from peft import LoraConfig, get_peft_model
# Charger modèle
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5"
)
# Appliquer LoRA à l'UNet
unet = pipe.unet
lora_config = LoraConfig(
r=32,
lora_alpha=64,
target_modules=["to_q", "to_v"],
lora_dropout=0.05
)
unet = get_peft_model(unet, lora_config)
# Fine-tuner sur dataset custom
# ... training loop ...
# Générer avec LoRA
with torch.no_grad():
image = pipe(
prompt="a photo of a dog in style XYZ",
num_inference_steps=50
).images[0]
LLaVA Fine-Tuning
# Format données vision instruction
{
"image": "path/to/image.jpg",
"conversations": [
{
"from": "human",
"value": "Qu'est-ce que tu vois dans cette image?"
},
{
"from": "gpt",
"value": "Je vois un chien en train de courir..."
}
]
}
# Fine-tune avec LLaVA
python -m llava.train.train \
--model_name_or_path lmsys/llava-v1-7b \
--data_path ./vision_data.json \
--image_folder ./images \
--output_dir ./llava_ft \
--num_train_epochs 3 \
--per_device_train_batch_size 4 \
--per_device_eval_batch_size 4
Production Best Practices
Version Control Tracker tous les changements (DVC pour données)
Monitoring Logs, métriques, dashboard (Weights & Biases)
Testing Unit tests, integration tests, smoke tests
Documentation Model Card, README, API docs
Security Validation inputs, rate limiting, access control
Scaling vLLM pour multi-GPU, TGI pour cloud
Caching Redis pour cache, embedding cache
A/B Testing Comparer versions, track metrics