ML Learning Hub
Visionintermédiaire

Détection d'Objets : YOLO & Faster-RCNN

De la classification d'images à la localisation et l'étiquetage de chaque objet dans la scène

Des fenêtres glissantes aux détecteurs single-shot — IoU, boîtes d'ancrage, NMS, mAP et le compromis architectures deux-étapes vs une-étape.

45 min
11 diagrammes
8 Concepts Couverts

Prérequis

CNN Architectures

Concepts Couverts

IoUAnchor BoxesNMSmAPYOLOFaster-RCNNFPNTwo-Stage vs One-Stage

Formules Clés

IoU

Intersection sur Union — mesure de qualité de la boîte englobante ; IoU > 0,5 est conventionnellement une détection correcte

mAP

Précision Moyenne Moyenne — aire sous la courbe Précision-Rappel, moyennée sur toutes les classes

Perte YOLO

Somme pondérée : régression de boîte + confiance d'objectivité + probabilités de classe

NMS

Garder uniquement la boîte la plus confiante quand plusieurs boîtes se chevauchent fortement sur le même objet

Simulation Interactive

Loading visualization…
🎯

Au-Delà de la Classification : Où et Quoi ?

motivation

La classification d'images répond à « y a-t-il un chat ? ». La détection répond à « où sont les chats, et y a-t-il aussi des chiens ? ». Ce passage d'une seule étiquette à un nombre variable de sorties (classe, boîte englobante) est ce qui fait de la détection d'objets la tâche centrale en conduite autonome, imagerie médicale, caisse de commerce et surveillance. Chaque voiture autonome exécute un détecteur en temps réel traitant 30+ images par seconde. L'évolution des classificateurs à fenêtre glissante (DPM, 2010) → détecteurs à deux étapes (RCNN, 2014 ; Faster-RCNN, 2015) → détecteurs à une étape (YOLO v1, 2016 → v8, 2023) est l'un des domaines les plus dynamiques de la vision par ordinateur.

L'Autopilot de Tesla fait passer 8 caméras par un réseau de détection personnalisé à 36 FPS sur une puce à 72 TOPS. L'intégralité du modèle doit tenir dans un budget de latence serré tout en détectant des objets à 200m.

💡

Deux Étapes vs Une Étape : Le Compromis Fondamental

intuition

**Détecteurs à deux étapes (Faster-RCNN) :** Étape 1 — le Réseau de Propositions de Régions (RPN) suggère ~300 régions candidates pouvant contenir des objets. Étape 2 — une tête de classification + régression affine chaque proposition. Avantage : haute précision. Inconvénient : lent (étapes séquentielles). **Détecteurs à une étape (YOLO, SSD) :** Divisez l'image en une grille. Chaque cellule prédit directement les décalages de boîte englobante, le score d'objectivité et les probabilités de classe en un seul passage avant. Avantage : rapide (capable de temps réel). Inconvénient : plus difficile à entraîner, rate les petits objets qui se chevauchent. **Basé sur ancrage vs sans ancrage :** YOLO v1-v3 utilisait des boîtes d'ancrage. YOLO v8 / FCOS / CenterNet sont sans ancrage — prédisent directement centre + largeur/hauteur de la boîte, plus simple et souvent meilleur.

YOLO = 'You Only Look Once.' L'idée : au lieu d'exécuter un classificateur à des milliers de positions de fenêtre glissante, prédire toutes les boîtes simultanément en un seul passage du réseau.

⚙️

Pipeline d'Inférence YOLO

algorithm
1

Divisez l'image d'entrée en une grille S×S (ex. 13×13 pour une entrée de 416px dans YOLO v2).

2

Pour chaque cellule : prédisez B boîtes englobantes (chacune : x, y, l, h relatifs à la cellule, + score d'objectivité) et C probabilités de classe.

3

Coordonnées de boîte : x, y sont des décalages depuis le centre de la cellule (0–1), l/h sont des décalages d'échelle logarithmique depuis les tailles d'ancrage.

4

Objectivité × probabilité de classe = score de confiance spécifique à la classe pour chaque boîte.

5

Appliquez la Suppression Non Maximale (NMS) : pour chaque classe, triez les boîtes par confiance, conservez la plus haute, supprimez les boîtes avec IoU > 0,5 avec la boîte conservée, répétez.

6

Sortie finale : liste de longueur variable de tuples (classe, confiance, x1, y1, x2, y2).

</>

Détection d'Objets avec YOLOv8 (Ultralytics)

code
python72 lines
class="tok-comment"># pip install ultralytics
from ultralytics import YOLO
import numpy as np
import cv2

class="tok-comment"># ── class="tok-num">1. Charger YOLO v8 préentraîné ────────────────────────────────────────────
modele = YOLO(class="tok-str">"yolov8n.pt")   class="tok-comment"># modèle nano (class="tok-num">3,2M params, le plus rapide)
class="tok-comment"># Autres tailles : yolov8s.pt, yolov8m.pt, yolov8l.pt, yolov8x.pt

class="tok-comment"># ── class="tok-num">2. Inférence sur une seule image ─────────────────────────────────────────
resultats = modele(class="tok-str">"chemin/vers/image.jpg", conf=class="tok-num">0.25, iou=class="tok-num">0.5)

for r in resultats:
    boites = r.boxes
    for boite in boites:
        x1, y1, x2, y2 = boite.xyxy[class="tok-num">0].tolist()  class="tok-comment"># coordonnées absolues en pixels
        conf  = boite.conf[class="tok-num">0].item()              class="tok-comment"># score de confiance
        cls   = int(boite.cls[class="tok-num">0].item())          class="tok-comment"># indice de classe
        label = modele.names[cls]
        print(fclass="tok-str">"{label} : {conf:.2f} à ({x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f})")

class="tok-comment"># ── class="tok-num">3. Affinage sur un jeu de données personnalisé ────────────────────────────
class="tok-comment"># Format du jeu de données : format texte YOLO
class="tok-comment"># data.yaml :
class="tok-comment">#   train: /chemin/vers/images_entrainement
class="tok-comment">#   val:   /chemin/vers/images_validation
class="tok-comment">#   nc: class="tok-num">3
class="tok-comment">#   names: [class="tok-str">'chat', class="tok-str">'chien', class="tok-str">'voiture']

modele = YOLO(class="tok-str">"yolov8s.pt")     class="tok-comment"># démarrer depuis préentraîné ImageNet
resultats = modele.train(
    data=class="tok-str">"data.yaml",
    epochs=class="tok-num">50,
    imgsz=class="tok-num">640,
    batch=class="tok-num">16,
    lr0=class="tok-num">0.01,                   class="tok-comment"># taux dclass="tok-str">'apprentissage initial
    lrf=class="tok-num">0.01,                   class="tok-comment"># fraction du lr final
    augment=True,               class="tok-comment"># mosaïque, retournement, échelle
    device=class="tok-num">0,                   class="tok-comment"># GPU class="tok-num">0
)
print(f"mAP50 : {resultats.metrics.mAP50:.4f}")

class="tok-comment"># ── class="tok-num">4. Calcul de l'IoU depuis zéro ───────────────────────────────────────────
def iou(boite1, boite2):
    class="tok-str">"""boite = [x1, y1, x2, y2]"""
    x1 = max(boite1[class="tok-num">0], boite2[class="tok-num">0]); y1 = max(boite1[class="tok-num">1], boite2[class="tok-num">1])
    x2 = min(boite1[class="tok-num">2], boite2[class="tok-num">2]); y2 = min(boite1[class="tok-num">3], boite2[class="tok-num">3])
    inter = max(class="tok-num">0, x2-x1) * max(class="tok-num">0, y2-y1)
    aire1 = (boite1[class="tok-num">2]-boite1[class="tok-num">0]) * (boite1[class="tok-num">3]-boite1[class="tok-num">1])
    aire2 = (boite2[class="tok-num">2]-boite2[class="tok-num">0]) * (boite2[class="tok-num">3]-boite2[class="tok-num">1])
    return inter / (aire1 + aire2 - inter + class="tok-num">1e-6)

verite_terrain = [class="tok-num">100, class="tok-num">50, class="tok-num">250, class="tok-num">200]
prediction     = [class="tok-num">110, class="tok-num">60, class="tok-num">260, class="tok-num">210]
print(fclass="tok-str">"\nIoU = {iou(verite_terrain, prediction):.4f}")

class="tok-comment"># ── class="tok-num">5. NMS manuel ─────────────────────────────────────────────────────────────
def nms(boites, scores, seuil_iou=class="tok-num">0.5):
    class="tok-str">"""boites : (N,class="tok-num">4) xyxy, scores : (N,)"""
    ordre  = np.argsort(scores)[::-class="tok-num">1]
    garder = []
    while len(ordre) > class="tok-num">0:
        i = ordre[class="tok-num">0]
        garder.append(i)
        ious = np.array([iou(boites[i], boites[j]) for j in ordre[class="tok-num">1:]])
        ordre = ordre[class="tok-num">1:][ious < seuil_iou]
    return garder

boites_test  = np.array([[class="tok-num">100,class="tok-num">50,class="tok-num">250,class="tok-num">200],[class="tok-num">105,class="tok-num">55,class="tok-num">255,class="tok-num">205],[class="tok-num">200,class="tok-num">100,class="tok-num">350,class="tok-num">250]])
scores_test  = np.array([class="tok-num">0.95, class="tok-num">0.87, class="tok-num">0.72])
gardees      = nms(boites_test, scores_test)
print(fclass="tok-str">"Boîtes conservées : {gardees}")  class="tok-comment"># [class="tok-num">0, class="tok-num">2] — boîte class="tok-num">1 supprimée
⚠️

mAP et le Piège du Seuil IoU

pitfall

mAP@0,5 (seuil IoU 0,5) et mAP@0,5:0,95 (moyenne sur les seuils IoU de 0,5 à 0,95 par pas de 0,05) racontent des histoires très différentes. Un modèle avec un excellent mAP@0,5 mais un mauvais mAP@0,5:0,95 localise les objets de manière lâche — acceptable pour les tâches grossières, mauvais pour la saisie robotique. De plus, mAP traite toutes les classes de manière égale, ce qui cache les mauvaises performances sur les classes rares. Pour les jeux de données déséquilibrés, reportez l'AP par classe séparément. Pièges courants : (1) Oublier de normaliser les coordonnées de boîte par la taille de l'image. (2) Utiliser un seuil de confiance trop bas pendant NMS — gardez conf_threshold ≈ 0,25 lors de l'inférence. (3) Surapprentissage sur les petits jeux de données — utilisez toujours une augmentation forte.

Une amélioration de 1% de mAP sur le benchmark COCO représente des mois de recherche — le contexte importe pour comparer les modèles dans votre domaine.

?Vérification des Connaissances

La progression est sauvegardée dans votre navigateur — aucun compte requis.

Besoin d'un ingénieur IA ou data scientist ?

Je conçois des modèles ML sur mesure, des agents IA, de la vision par ordinateur et de l'automatisation — de l'idée à la production.