Ultralytics YOLO27 : Inscris-toi sur la liste d’attente.
Get Started
Ultralytics YOLO en production·Échelle et observation·Leçon 9/9
Leçonintermédiaire

Réglage des coûts et de la latence

Profile l’ensemble du pipeline, actionne les leviers qui réduisent réellement la latence et les coûts, dans le bon ordre, puis arrête-toi une fois le budget atteint.

Les équipes perdent des semaines à gagner quelques millisecondes sur un modèle alors qu’une écriture en base de données ou un décodeur RTSP constitue le véritable goulot d’étranglement. Les principaux leviers sont peu nombreux et bien mesurés : YOLO26s prend 2,2 fois plus de temps que YOLO26n sur CPU pour gagner 7,7 points de mAP, et effectuer une détection une image sur deux divise la charge de travail par deux. Cette leçon t’apprend à commencer par le profilage, à actionner le levier le plus efficace qui convient et à t’arrêter au budget que tu as fixé à l’avance.

Résultat

Profile un pipeline de bout en bout, applique un levier en mesurant la latence et mAP avant et après, puis justifie le point d’arrêt.

Parcours accéléré
Si tu connais déjà les bases, voici la version courte.
  1. Commence par le profilage : les étapes r.speed ainsi que tes propres mesures des temps de décodage et de sortie.

  2. Leviers à actionner dans l’ordre : modèle plus petit → imgsz plus petit → environnement d’exécution plus rapide et FP16 → INT8 → vid_stride.

  3. Mesure la latence et mAP avant et après chaque changement, sur le matériel cible et avec le même jeu de validation.

  4. Fixe d’abord le budget (jours, perte de mAP, FPS cible) ; une fois le budget épuisé, améliore plutôt les données.

Pratique

Commencer par mesurer l’ensemble du pipeline#

Chaque objet Results répartit son propre temps en plusieurs étapes :

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
totals = {"preprocess": 0.0, "inference": 0.0, "postprocess": 0.0}
for n, r in enumerate(model.predict(video, stream=True, verbose=False), 1):
    for stage in totals:
        totals[stage] += r.speed[stage]
print({stage: round(ms / n, 1) for stage, ms in totals.items()}, "ms per frame")
{'preprocess': 0.7, 'inference': 36.1, 'postprocess': 0.7} ms per frame

Ici, l’inférence représente 95 % du temps du modèle ; les leviers liés au modèle seront donc utiles. Mesure de la même façon le décodage et tes propres opérations de sortie (ajoute time.perf_counter() autour de chacune), puis applique la règle correspondant à ton observation :

MesurePriorité de correction
60 ms de décodage RTSP, 5 ms d’inférenceDécodage matériel, résolution de flux plus faible ou davantage de threads de décodage
30 ms d’inférence, 100 ms de post-traitement PythonVectorise avec NumPy ou déplace le traitement dans un processus de travail
5 ms d’inférence, 200 ms d’écritures en base de donnéesRegroupe les écritures ou rends-les asynchrones
L’inférence est prédominanteLe classement des leviers ci-dessous

Classement des leviers#

Essaie les leviers en commençant par le premier et arrête-toi lorsque tu atteins le budget :

LevierGain de vitesse typiqueCoût typique en précision
Modèle plus petit (m → s, s → n)1.5–2.5×4,5 à 7,7 points de mAP sur COCO ; moins pour les tâches spécialisées
imgsz plus petit (640 → 480)environ 1,8×Les petits objets éloignés sont les premiers touchés
Environnement d’exécution plus rapide, FP16environ 1,5× sur GPUGénéralement aucun ; vérifie la parité
INT8 avec calibration1,2× supplémentaire sur GPU ; jusqu’à 2,9× sur CPU1 point (YOLO26n) à 4–6 points (modèles plus grands) ; QAT permet d’en récupérer la majeure partie
Moins d’images (vid_stride=N)jusqu’à N×Les objets rapides et les événements brefs peuvent passer inaperçus
Taille de lot supérieure (serveurs)Débit supérieur, mais pas latence plus faibleAucun

Les chiffres proviennent des tableaux de référence YOLO26, TensorRT et OpenVINO, ainsi que des mesures ci-dessous ; les tiens seront différents, alors mesure.

Modèle plus petit#

Le tableau de référence YOLO26 sur COCO :

ModèlemAP50-95Latence CPU ONNX (ms)Latence T4 TensorRT10 (ms)
YOLO26n40.938.91.7
YOLO26s48.687.22.5
YOLO26m53.1220.04.7

Sur CPU, chaque réduction d’un niveau accélère le traitement de 2,2 à 2,5×. L’écart de précision sur COCO représente le pire des cas : un détecteur d’entrepôt à 3 classes avec de grands objets perd généralement beaucoup moins en passant de m à n qu’un détecteur généraliste à 80 classes. Le modèle plus petit mérite donc toujours d’être testé sur ton propre jeu de validation.

Entrée plus petite#

La charge de calcul évolue approximativement avec le nombre de pixels ; imgsz=480 nécessite donc environ 56 % du travail requis en 640. Mesures effectuées sur le CPU de l’ordinateur portable de test, avec ONNX Runtime et une taille de lot de 1 (médiane de 25 exécutions sur bus.jpg) :

Modèle640480320
YOLO26n21.3 ms12.1 ms7.1 ms
YOLO26s55.0 ms24.2 ms12.9 ms

Règle de décision : si ton plus petit objet important couvre encore environ 10 pixels ou plus à la nouvelle taille, essaie-la ; si les objets sont petits ou éloignés (drones, étagères éloignées de la caméra), conserve la résolution et réduis plutôt la taille du modèle.

Moins d’images#

Si l’inférence limite ta fréquence d’images et que la scène évolue lentement, effectue une détection toutes les N images avec vid_stride=N et laisse le suivi combler les intervalles :

import time

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
model.predict(video, vid_stride=30, verbose=False)  # warm up so the first timing is fair

for stride in (1, 2, 3):
    start, ids, frames = time.perf_counter(), set(), 0
    for r in model.track(video, stream=True, vid_stride=stride, tracker="bytetrack.yaml", classes=[0], verbose=False):
        frames += 1
        if r.boxes.is_track:
            ids.update(r.boxes.id.int().tolist())
    print(f"vid_stride={stride}: {frames} frames in {time.perf_counter() - start:.2f} s, {len(ids)} person IDs")
vid_stride=1: 62 frames in 2.66 s, 32 person IDs
vid_stride=2: 31 frames in 1.19 s, 30 person IDs
vid_stride=3: 20 frames in 0.95 s, 28 person IDs

Traiter la moitié des images prend environ deux fois moins de temps, et le suivi a conservé 30 des 32 identités. À vid_stride=3, il en a perdu 4 : c’est le signal qu’il faut s’arrêter pour cette scène à allure de marche.

Réduire les coûts du cloud#

Vue d’ensemble du déploiement sur Ultralytics Platform : 59 requêtes HTTP en 24 heures, taux d’erreur de 0 %, latence P95 de 226 ms et point de terminaison de 1 vCPU et 4 Gio dans us-west2 facturé 0,17 $ par heure

Le coût correspond aux heures d’instance divisées par le travail utile. Une fiche comme celle-ci contient les deux chiffres dont tu as besoin : le coût horaire (0,17 $ pour 1 vCPU et 4 Gio ici) et la quantité de travail effectuée (59 requêtes en 24 heures à une latence P95 de 226 ms ; le point de terminaison est donc presque inactif). Ensuite :

  • Augmente le taux d’utilisation. Un GPU utilisé à 30 % gaspille 70 % de ta facture. Regroupe les flux en lots (voir les leçons précédentes) ou sers-les avec Triton Inference Server.
  • Choisis une taille adaptée. Évalue les performances sur un GPU plus petit ou sur un CPU avec OpenVINO avant de supposer qu’il t’en faut un grand.
  • Utilise une capacité préemptible ou à la demande pour les traitements par lots. Le retraitement de vidéos archivées tolère les interruptions ; les flux en direct, non.

Pièges#

Optimiser le modèle alors qu'il n'est pas le goulot d'étranglement

Symptôme : deux semaines de réglages ont fait gagner 10 ms, et le système reste lent. Solution : commence par profiler l'ensemble du pipeline et corrige l'étape la plus lente, quelle qu'elle soit.

Ignorer des images après l'inférence

Symptôme : if i % 2: continue dans la boucle des résultats ne permet aucune économie. Solution : le modèle a déjà traité cette image ; ignore-la à la source avec vid_stride.

Utiliser le traitement par lots pour réduire la latence

Symptôme : le débit a augmenté, mais chaque image arrive plus tard. Solution : des lots plus grands augmentent le débit par dollar, pas la latence par image ; utilise-les pour de nombreux flux ou des tâches hors ligne, pas pour une seule caméra en temps réel.

Le levier des données

Lorsque tu as épuisé les leviers liés au modèle, l'amélioration des données est la prochaine piste. Un jeu de données qui correspond à la production permet à un modèle plus petit d'atteindre la précision qu'exigeait un modèle plus grand : une réduction des coûts sans perte de précision.

Essayer

Rédige un budget d'optimisation (en jours, perte maximale de mAP et FPS cible sur le matériel cible). Profile ton pipeline avec r.speed, ainsi qu'avec tes propres mesures de décodage et de sortie, choisis le levier le plus efficace adapté au goulot d'étranglement, applique-le et consigne la latence et le mAP50-95 avant et après, sur le même matériel et le même jeu de validation. Remets le budget, le profil, le tableau avant-après et indique où tu t'es arrêté.

Valider
git add -A && git commit -m "perf(production): tuned model + runtime for target latency budget"
Terminé lorsque
Tu as terminé la leçon lorsque tous les éléments suivants sont réunis.
  • Tu sais, chiffres à l'appui, quelle étape domine le temps de traitement de bout en bout d'une image.

  • Tu as appliqué un levier et consigné la latence et le mAP avant et après, sur le même matériel et le même jeu de validation.

  • Tu t'es arrêté au budget que tu avais défini et tu peux dire ce que tu essaierais ensuite.

Afficher la solution
from ultralytics import YOLO

if __name__ == "__main__":  # validation starts worker processes on macOS and Windows
    data, img = "coco8.yaml", "https://ultralytics.com/images/bus.jpg"  # use your val YAML and a production frame
    for weights, imgsz in (("yolo26s.pt", 640), ("yolo26n.pt", 640), ("yolo26n.pt", 480)):
        model = YOLO(weights)
        runs = [model(img, imgsz=imgsz, verbose=False)[0].speed["inference"] for _ in range(20)][5:]
        m = model.val(data=data, imgsz=imgsz, batch=1, plots=False, verbose=False).box.map
        print(f"{weights} imgsz={imgsz}: {sorted(runs)[len(runs) // 2]:.1f} ms, mAP50-95 {m:.3f}")

Chaque ligne écarte les cinq premières exécutions comme phase de chauffe et indique la médiane des autres. Choisis la première ligne qui respecte le budget de latence tout en maintenant la perte de mAP dans les limites de ton budget. Exemple de budget détaillé : « deux jours, au maximum 1 point de mAP, 20 FPS sur le serveur du magasin ». Si yolo26s en 640 dépasse 50 ms par image et que yolo26n en 640 respecte cette limite avec une perte inférieure ou égale à 1 point sur ton jeu de validation, arrête-toi là ; ne consacre pas la deuxième journée à la résolution 480.

Et ensuite ?

Cours terminé. Passe l'examen pour obtenir ton certificat, puis poursuis avec Développer avec Ultralytics Platform, qui exécute ce flux de travail dans le cadre d'un système géré de bout en bout.