Collecter des données représentatives de haute qualité
Recueille des images dans les conditions réelles de déploiement, intègre les métadonnées de capture à chaque nom de fichier, suis la couverture par rapport à la spécification et échantillonne les vidéos sans saturer le jeu de données.
La plupart des échecs des jeux de données sont dus à la collecte. Une équipe capture des exemples nets avec une seule caméra en une semaine, obtient d’excellents résultats en validation, puis le modèle s’effondre sur le deuxième site. La collecte fixe le plafond : aucune qualité d’annotation, augmentation ou valeur d’hyperparamètre ne peut apprendre au modèle une scène absente du jeu de données.
Pratique
La diversité prime sur le volume#
Compare deux jeux de données destinés au même déploiement :
| Jeu de données | Résultat de validation | En production |
|---|---|---|
| 10 000 images presque identiques, une seule caméra, uniquement à midi | Élevé : les images de validation ressemblent aux images d’entraînement | Les performances chutent avec une autre caméra et au crépuscule |
| 1 500 images réparties entre 12 scénarios de la spécification | Plus modeste | Tient la route, car la production ressemble aux données |
Le score de validation du premier jeu de données est élevé uniquement parce que la validation mesure la seule condition qu’il couvre. En production, un modèle n’est performant qu’à la hauteur du scénario qu’il couvre le moins bien. Optimise d’abord la collecte pour couvrir la spécification, puis pour augmenter le volume.
Choisis les sources dans le bon ordre#
La majeure partie du jeu de données devrait provenir de la configuration de production réelle. Le guide de collecte et d’annotation des données décrit chaque source plus en détail :
| Source | Quand c’est utile | Quand c’est nuisible |
|---|---|---|
| Caméras de production, en direct | Au plus près du déploiement | Lent ; des accords sur les données peuvent être nécessaires |
| Caméras de production, archives | Beaucoup de diversité en peu de temps | Les anciennes vidéos peuvent ne pas correspondre à la configuration actuelle |
| Jeux de données publics (COCO, Open Images, VOC) | Démarrage avec des classes courantes | Décalage de domaine par rapport à tes scènes |
| Captures mises en scène | Cas limites précis à la demande | Trop propres ; risque de trop s’y fier |
| Images synthétiques ou générées | Dangers et défaillances rares que tu ne peux pas attendre | Le modèle peut apprendre le style du générateur |
| Collecte de données sur le Web | Diversité à faible coût | Qualité inégale et risques liés aux licences |
Utilise des images mises en scène, publiques et synthétiques pour combler des lacunes précises et nommées dans la spécification, et suis leur proportion dans le jeu de données pour mesurer leur utilité. Sur Ultralytics Platform, Générer des images similaires ajoute à un jeu de données des variantes générées à partir d’une image existante, non annotées et prêtes à être annotées.
Intègre les métadonnées au nom de fichier#
Chaque image doit contenir au minimum un ID de caméra, un jour de capture et un numéro d’image ou de séquence vidéo. Sans ces informations, tu ne peux pas créer ensuite des partitions par groupe, retracer l’origine d’une régression ni collecter davantage de données pour un scénario sous-représenté. Le nom de fichier est le moyen le plus durable de les conserver : cam2_2026-05-01_000124.jpg résiste à tous les outils, exportations et téléversements. La recherche de jeux de données d’Ultralytics Platform fonctionne avec les noms de fichiers et les métadonnées personnalisées ; ces clés restent donc consultables après le téléversement.
Ce script crée le jeu de données d’entraînement utilisé dans le reste du cours. Il copie les images annotées de COCO128 et les renomme comme si quatre caméras les avaient capturées sur quatre jours, en surreprésentant cam1, comme c’est souvent le cas avec une caméra facile d’accès :
import shutil
from pathlib import Path
from ultralytics.data.utils import check_det_dataset, img2label_paths
data = check_det_dataset("coco128.yaml") # 128 COCO images stand in for your camera captures
out = Path("datasets/practice")
(out / "images").mkdir(parents=True, exist_ok=True)
(out / "labels").mkdir(parents=True, exist_ok=True)
for i, image in enumerate(sorted(Path(data["train"]).glob("*.jpg"))):
camera = ("cam1", "cam1", "cam2", "cam3", "cam4")[i % 5] # cam1 over-collected, like a real site
name = f"{camera}_2026-05-0{i // 32 + 1}_{i:06d}" # camera, capture day, frame number
shutil.copy(image, out / "images" / f"{name}.jpg")
label = Path(img2label_paths([str(image)])[0])
if label.exists():
shutil.copy(label, out / "labels" / f"{name}.txt")
print(len(list((out / "images").glob("*.jpg"))), "images written to", out)128 images written to datasets/practice
Le téléversement vers Ultralytics Platform conserve ces noms de fichiers. La boîte de dialogue de téléversement accepte également les vidéos et les jeux de données complets ; ses paramètres comprennent le floutage des visages pour protéger la vie privée.
Suis la couverture par rapport à la spécification#
Avec les métadonnées intégrées aux noms de fichiers, la couverture se mesure par un décompte. Définis les quotas à partir de ta spécification et laisse le script indiquer ce qu’il faut collecter ensuite :
from collections import Counter
from pathlib import Path
QUOTA = {"cam1": 30, "cam2": 30, "cam3": 30, "cam4": 30} # images per camera, from your spec
images = sorted(Path("datasets/practice/images").glob("*.jpg"))
cameras = Counter(p.stem.split("_")[0] for p in images)
days = Counter(p.stem.split("_")[1] for p in images)
for camera, quota in QUOTA.items():
gap = quota - cameras[camera]
print(f"{camera}: {cameras[camera]:3d} / {quota} {'ok' if gap <= 0 else f'collect {gap} more'}")
print("days:", dict(sorted(days.items())))cam1: 52 / 30 ok
cam2: 26 / 30 collect 4 more
cam3: 25 / 30 collect 5 more
cam4: 25 / 30 collect 5 more
days: {'2026-05-01': 32, '2026-05-02': 32, '2026-05-03': 32, '2026-05-04': 32}La règle de décision : arrête de collecter auprès de toute source qui dépasse son quota et concentre tes efforts sur les lacunes. Ici, cam1 représente déjà 40 % des images. Une semaine supplémentaire de vidéos provenant de cette source augmenterait le volume, mais n’améliorerait pas la couverture.
Échantillonne les vidéos sans saturer le jeu de données#
La vidéo est la source la moins coûteuse et la plus facile à mal exploiter. À 30 images par seconde, une minute produit 1 800 images presque identiques ; annoter deux fois le même moment coûte cher et gonfle les métriques. Ne conserve une image que si la scène a changé depuis la dernière image conservée :
from pathlib import Path
import cv2
from ultralytics.utils.downloads import safe_download
video = "solutions_ci_demo.mp4" # a short public demo clip; swap in your own footage
safe_download(f"https://github.com/ultralytics/assets/releases/download/v0.0.0/{video}")
Path("frames").mkdir(exist_ok=True)
cap = cv2.VideoCapture(video)
fps = cap.get(cv2.CAP_PROP_FPS)
read = kept = 0
last = None
while True:
ok, frame = cap.read()
if not ok:
break
thumb = cv2.cvtColor(cv2.resize(frame, (64, 64)), cv2.COLOR_BGR2GRAY).astype(float)
if last is None or abs(thumb - last).mean() > 10: # keep a frame only when the scene has changed
cv2.imwrite(f"frames/clip01_{read:06d}.jpg", frame) # the clip ID stays in the filename
kept, last = kept + 1, thumb
read += 1
print(f"{read} frames at {fps:.0f} FPS -> {kept} kept")62 frames at 30 FPS -> 6 keptLa séquence de deux secondes contient 62 images, mais seulement 6 moments visuellement distincts. Le script compare de minuscules vignettes en niveaux de gris ; il est donc rapide même sur plusieurs heures de vidéos. Augmente le seuil de 10 pour conserver moins d’images provenant de caméras fixes ; réduis-le pour les scènes où les éléments se déplacent rapidement. Répartis aussi le budget entre les séquences : 20 images de chacune de 50 séquences valent mieux que 1 000 images d’une seule.
Deux images séparées d’une seconde partagent le même éclairage, le même arrière-plan et généralement les mêmes objets. Si elles se retrouvent à la fois dans l’entraînement et dans la validation, la validation évalue le modèle sur des images qu’il a déjà pratiquement vues. Conserve l’identifiant du clip dans chaque nom de fichier pour que Répartir correctement le jeu de données puisse garder chaque clip ensemble.
Évite les pièges lors de la collecte#
| Piège | Symptôme | Correctif |
|---|---|---|
| Collecte sur une seule semaine | mAP de validation élevée, mais échecs lors d’autres saisons ou changements de conditions | Étale la collecte sur plusieurs semaines ou mois |
| Une seule caméra | Échec avec la deuxième caméra | Collecte des images avec chaque type de caméra que tu déploies |
| Données uniquement mises en scène | Échec face à un désordre réel | Garde les images mises en scène minoritaires et suis leur proportion |
| Images dupliquées | Métriques artificiellement gonflées, travail d’annotation gaspillé | Échantillonne selon les changements de scène ; Ultralytics Platform ignore aussi les copies exactes lors du téléversement |
| Un seul opérateur ou créneau de travail | Apparence imprévisible et déséquilibre des classes | Échantillonne sur plusieurs créneaux, sites et personnes |
| Cas rares manquants | Les événements de sécurité les plus importants passent inaperçus | Collecte ciblée, mise en scène ou complément de données synthétiques |
Biais et consentement#
Le biais est un problème de collecte, pas de modélisation : aucun hyperparamètre ne peut corriger l’absence d’un groupe dans les données. Le guide de collecte des données présente quatre leviers : la diversité des sources, une représentation équilibrée, des réévaluations régulières de la couverture et des mesures d’atténuation comme le suréchantillonnage des groupes sous-représentés.
Si les images contiennent des visages, des plaques d’immatriculation ou d’autres données personnelles, tu dois obtenir le consentement et définir une politique de conservation avant le début de la collecte. L’entrée du glossaire confidentialité des données est un bon point de départ, et Confidentialité, régions et conformité explique comment conserver les données dans la juridiction appropriée avec dataRegion.
Afficher la solution
Pour le jeu d’exercice, le rapport de couverture indique directement les actions à prendre :
| Source | Nombre / quota | Action |
|---|---|---|
cam1 | 52 / 30 | Arrête la collecte : elles représentent déjà 40 % de toutes les images |
cam2 | 26 / 30 | Collecte-en 4 de plus |
cam3 | 25 / 30 | Collecte-en 5 de plus |
cam4 | 25 / 30 | Collecte-en 5 de plus |
Chaque caméra couvre les quatre jours ; les lacunes concernent donc chaque caméra, et non chaque jour. Répartis les 14 nouvelles images sur au moins deux nouveaux jours pour qu’elles apportent une nouvelle condition plutôt que davantage d’images du même après-midi. Si une caméra ne peut pas combler sa lacune (par exemple, si elle ne voit que la nuit), ajoute une ligne aux spécifications pour la condition manquante et indique si des images mises en scène ou générées la comblent, afin de pouvoir en mesurer l’effet par la suite.