So trainierst du YOLO COCO , ohne diese zu konvertieren
Warum direkt mit COCO trainieren?
Annotationen in COCO JSON Das Format kann direkt für Ultralytics YOLO Training, ohne zu konvertieren .txt zuerst die Dateien. Dies geschieht durch Erstellen einer Unterklasse YOLODataset um COCO in Echtzeit zu analysieren und sie über einen benutzerdefinierten Trainer in die Trainingspipeline einzubinden.
Bei diesem Ansatz bleibt die COCO die einzige verlässliche Quelle – keine convert_coco() Aufruf, keine Neuorganisation des Verzeichnisses, keine Zwischen-Label-Dateien. YOLO26 und alle anderen Ultralytics YOLO Detektionsmodelle werden unterstützt. Segmentierungs- und Pose-Modelle erfordern zusätzliche Label-Felder (siehe FAQ), oder Auto-Modus mit angegebener Auslastungsfraktion (
Möchten Sie stattdessen eine einmalige Konvertierung durchführen?
Siehe die Leitfaden zur COCO zu YOLO Konvertierung für den Standard convert_coco() Arbeitsablauf.
Architektur-Überblick
Es werden zwei Klassen benötigt:
COCODataset— liest COCO und konvertiert Bounding Boxes während des Trainings in YOLO im SpeicherCOCOTrainer— Überschreibtbuild_dataset()zur VerwendungCOCODatasetanstelle der StandardeinstellungYOLODataset
Die Implementierung folgt dem gleichen Muster wie die integrierte GroundingDataset, der auch JSON-Annotationen direkt liest. Drei Methoden werden überschrieben: get_img_files(), cache_labels()und get_labels().
Erstellen der COCO JSON Dataset-Klasse
Die COCODataset Klasse erbt von YOLODataset und überschreibt die Logik zum Laden von Labels. Anstatt zu lesen .txt Dateien aus einem Verzeichnis „labels“; es öffnet die COCO , durchläuft die nach Bildern gruppierten Annotationen und konvertiert jede Begrenzungsbox aus COCO [x_min, y_min, width, height] in das YOLO Center-Format [x_center, y_center, width, height]. Crowd-Annotationen (iscrowd: 1) und Boxen mit Nullfläche werden automatisch übersprungen.
Die get_img_files() Die Methode gibt eine leere Liste zurück, da die Bildpfade aus dem JSON-Datensatz ermittelt werden file_name Feld innen cache_labels(). Kategorie-IDs werden sortiert und auf nullbasierte Klassenindizes umgewandelt, sodass sowohl 1-basierte (Standard-COCO) als auch nicht-zusammenhängende ID-Schemata korrekt funktionieren.
import json
from collections import defaultdict
from pathlib import Path
import numpy as np
from ultralytics.data.dataset import DATASET_CACHE_VERSION, YOLODataset
from ultralytics.data.utils import get_hash, load_dataset_cache_file, save_dataset_cache_file
from ultralytics.utils import TQDM
class COCODataset(YOLODataset):
"""Dataset that reads COCO JSON annotations directly without conversion to .txt files."""
def __init__(self, *args, json_file="", **kwargs):
self.json_file = json_file
super().__init__(*args, data={"channels": 3}, **kwargs)
def get_img_files(self, img_path):
"""Image paths are resolved from the JSON file, not from scanning a directory."""
return []
def cache_labels(self, path=Path("./labels.cache")):
"""Parse COCO JSON and convert annotations to YOLO format. Results are saved to a .cache file."""
x = {"labels": []}
with open(self.json_file) as f:
coco = json.load(f)
images = {img["id"]: img for img in coco["images"]}
# Sort categories by ID and map to 0-indexed classes
categories = {cat["id"]: i for i, cat in enumerate(sorted(coco["categories"], key=lambda c: c["id"]))}
img_to_anns = defaultdict(list)
for ann in coco["annotations"]:
img_to_anns[ann["image_id"]].append(ann)
for img_info in TQDM(coco["images"], desc="reading annotations"):
h, w = img_info["height"], img_info["width"]
im_file = Path(self.img_path) / img_info["file_name"]
if not im_file.exists():
continue
self.im_files.append(str(im_file))
bboxes = []
for ann in img_to_anns.get(img_info["id"], []):
if ann.get("iscrowd", False):
continue
# COCO: [x, y, w, h] top-left in pixels -> YOLO: [cx, cy, w, h] center normalized
box = np.array(ann["bbox"], dtype=np.float32)
box[:2] += box[2:] / 2 # top-left to center
box[[0, 2]] /= w # normalize x
box[[1, 3]] /= h # normalize y
if box[2] <= 0 or box[3] <= 0:
continue
cls = categories[ann["category_id"]]
bboxes.append([cls, *box.tolist()])
lb = np.array(bboxes, dtype=np.float32) if bboxes else np.zeros((0, 5), dtype=np.float32)
x["labels"].append(
{
"im_file": str(im_file),
"shape": (h, w),
"cls": lb[:, 0:1],
"bboxes": lb[:, 1:],
"segments": [],
"normalized": True,
"bbox_format": "xywh",
}
)
x["hash"] = get_hash([self.json_file, str(self.img_path)])
save_dataset_cache_file(self.prefix, path, x, DATASET_CACHE_VERSION)
return x
def get_labels(self):
"""Load labels from .cache file if available, otherwise parse JSON and create the cache."""
cache_path = Path(self.json_file).with_suffix(".cache")
try:
cache = load_dataset_cache_file(cache_path)
assert cache["version"] == DATASET_CACHE_VERSION
assert cache["hash"] == get_hash([self.json_file, str(self.img_path)])
self.im_files = [lb["im_file"] for lb in cache["labels"]]
except (FileNotFoundError, AssertionError, AttributeError, KeyError, ModuleNotFoundError):
cache = self.cache_labels(cache_path)
cache.pop("hash", None)
cache.pop("version", None)
return cache["labels"]
Die analysierten Bezeichnungen werden in einer .cache Datei neben der JSON-Datei (z. B. instances_train.cache). Bei nachfolgenden Trainingsläufen wird der Cache direkt geladen, wodurch das JSON-Parsing übersprungen wird. Wenn sich die JSON-Datei ändert, schlägt die Hash-Prüfung fehl und der Cache wird automatisch neu aufgebaut.
Verbinden des Datensatzes mit der Trainings-Pipeline
Die einzige Änderung, die im Trainer erforderlich ist, ist das Überschreiben build_dataset(). Der Standard DetectionTrainer baut ein YOLODataset das nach .txt Label-Dateien. Indem man es ersetzt durch COCODataset, liest der Trainer stattdessen aus dem COCO-JSON.
Der Pfad zur JSON-Datei wird aus einer benutzerdefinierten train_json / val_json Feld in der Datenkonfiguration (siehe Schritt 3). Während des Trainings, mode="train" beschließt, train_json; während der Validierung, mode="val" beschließt, val_json. Wenn val_json ist nicht festgelegt, greift auf train_json.
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import colorstr
class COCOTrainer(DetectionTrainer):
"""Trainer that uses COCODataset for direct COCO JSON training."""
def build_dataset(self, img_path, mode="train", batch=None):
json_file = self.data["train_json"] if mode == "train" else self.data.get("val_json", self.data["train_json"])
return COCODataset(
img_path=img_path,
json_file=json_file,
imgsz=self.args.imgsz,
batch_size=batch,
augment=mode == "train",
hyp=self.args,
rect=self.args.rect or mode == "val",
cache=self.args.cache or None,
single_cls=self.args.single_cls or False,
stride=int(self.model.stride.max()) if hasattr(self, "model") and self.model else 32,
pad=0.0 if mode == "train" else 0.5,
prefix=colorstr(f"{mode}: "),
task=self.args.task,
classes=self.args.classes,
fraction=self.args.fraction if mode == "train" else 1.0,
)
Konfiguration von dataset.yaml für COCO JSON
Die dataset.yaml verwendet den Standard path, trainund val Felder zum Auswählen der Bildverzeichnisse. Zwei weitere Felder, train_json und val_json, geben Sie die COCO-Annotationsdateien an, die COCOTrainer heißt. Das nc und names Die Felder legen die Anzahl der Klassen und deren Namen fest, entsprechend der sortierten Reihenfolge von categories im JSON.
path: /path/to/images # root directory with train/ and val/ subfolders
train: train
val: val
# COCO JSON annotation files
train_json: /path/to/annotations/instances_train.json
val_json: /path/to/annotations/instances_val.json
nc: 80
names:
0: person
1: bicycle
# ... remaining class names
Erwartete Verzeichnisstruktur:
my_dataset/
images/
train/
img_001.jpg
...
val/
img_100.jpg
...
annotations/
instances_train.json
instances_val.json
dataset.yaml
Lauftraining auf COCO
Sobald die Datensatzklasse, die Trainerklasse und die YAML-Konfiguration bereitstehen, läuft das Training nach dem Standardablauf ab model.train() Aufruf. Der einzige Unterschied zu einem normalen Trainingslauf ist der trainer=COCOTrainer Argument, das Ultralytics anweist, den benutzerdefinierten Dataset-Loader anstelle des Standard-Loaders zu verwenden.
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.train(data="dataset.yaml", epochs=100, imgsz=640, trainer=COCOTrainer)
Die gesamte Trainingspipeline läuft wie erwartet, einschließlich Validierung, Speichern von Checkpoints und Protokollierung von Metriken.
Vollständige Umsetzung
Der Einfachheit halber finden Sie unten die vollständige Implementierung als ein einziges Skript zum Kopieren und Einfügen. Es enthält den benutzerdefinierten Datensatz, den benutzerdefinierten Trainer und den Trainingsaufruf. Speichern Sie dieses Skript zusammen mit Ihrem dataset.yaml und direkt ausführen.
import json
from collections import defaultdict
from pathlib import Path
import numpy as np
from ultralytics import YOLO
from ultralytics.data.dataset import DATASET_CACHE_VERSION, YOLODataset
from ultralytics.data.utils import get_hash, load_dataset_cache_file, save_dataset_cache_file
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import TQDM, colorstr
class COCODataset(YOLODataset):
"""Dataset that reads COCO JSON annotations directly without conversion to .txt files."""
def __init__(self, *args, json_file="", **kwargs):
self.json_file = json_file
super().__init__(*args, data={"channels": 3}, **kwargs)
def get_img_files(self, img_path):
return []
def cache_labels(self, path=Path("./labels.cache")):
x = {"labels": []}
with open(self.json_file) as f:
coco = json.load(f)
images = {img["id"]: img for img in coco["images"]}
categories = {cat["id"]: i for i, cat in enumerate(sorted(coco["categories"], key=lambda c: c["id"]))}
img_to_anns = defaultdict(list)
for ann in coco["annotations"]:
img_to_anns[ann["image_id"]].append(ann)
for img_info in TQDM(coco["images"], desc="reading annotations"):
h, w = img_info["height"], img_info["width"]
im_file = Path(self.img_path) / img_info["file_name"]
if not im_file.exists():
continue
self.im_files.append(str(im_file))
bboxes = []
for ann in img_to_anns.get(img_info["id"], []):
if ann.get("iscrowd", False):
continue
box = np.array(ann["bbox"], dtype=np.float32)
box[:2] += box[2:] / 2
box[[0, 2]] /= w
box[[1, 3]] /= h
if box[2] <= 0 or box[3] <= 0:
continue
cls = categories[ann["category_id"]]
bboxes.append([cls, *box.tolist()])
lb = np.array(bboxes, dtype=np.float32) if bboxes else np.zeros((0, 5), dtype=np.float32)
x["labels"].append(
{
"im_file": str(im_file),
"shape": (h, w),
"cls": lb[:, 0:1],
"bboxes": lb[:, 1:],
"segments": [],
"normalized": True,
"bbox_format": "xywh",
}
)
x["hash"] = get_hash([self.json_file, str(self.img_path)])
save_dataset_cache_file(self.prefix, path, x, DATASET_CACHE_VERSION)
return x
def get_labels(self):
cache_path = Path(self.json_file).with_suffix(".cache")
try:
cache = load_dataset_cache_file(cache_path)
assert cache["version"] == DATASET_CACHE_VERSION
assert cache["hash"] == get_hash([self.json_file, str(self.img_path)])
self.im_files = [lb["im_file"] for lb in cache["labels"]]
except (FileNotFoundError, AssertionError, AttributeError, KeyError, ModuleNotFoundError):
cache = self.cache_labels(cache_path)
cache.pop("hash", None)
cache.pop("version", None)
return cache["labels"]
class COCOTrainer(DetectionTrainer):
"""Trainer that uses COCODataset for direct COCO JSON training."""
def build_dataset(self, img_path, mode="train", batch=None):
json_file = self.data["train_json"] if mode == "train" else self.data.get("val_json", self.data["train_json"])
return COCODataset(
img_path=img_path,
json_file=json_file,
imgsz=self.args.imgsz,
batch_size=batch,
augment=mode == "train",
hyp=self.args,
rect=self.args.rect or mode == "val",
cache=self.args.cache or None,
single_cls=self.args.single_cls or False,
stride=int(self.model.stride.max()) if hasattr(self, "model") and self.model else 32,
pad=0.0 if mode == "train" else 0.5,
prefix=colorstr(f"{mode}: "),
task=self.args.task,
classes=self.args.classes,
fraction=self.args.fraction if mode == "train" else 1.0,
)
model = YOLO("yolo26n.pt")
model.train(data="dataset.yaml", epochs=100, imgsz=640, trainer=COCOTrainer)
Empfehlungen zu Hyperparametern finden Sie im Leitfaden „Tipps zum Modelltraining “.
FAQ
Was ist der Unterschied zwischen dieser Funktion und convert_coco()?
convert_coco() schreibt .txt Dateien als einmalige Konvertierung auf die Festplatte speichern. Bei diesem Ansatz wird das JSON zu Beginn jedes Trainingsdurchlaufs analysiert und die Annotationen werden im Arbeitsspeicher konvertiert. Verwenden Sie convert_coco() wenn permanente YOLOLabels bevorzugt werden; nutze diesen Ansatz, um die COCO als einzige verlässliche Quelle zu erhalten, ohne zusätzliche Dateien zu generieren.
Kann YOLO mit COCO JSON ohne benutzerdefinierten Code trainieren?
Nicht mit der aktuellen Ultralytics , die YOLO erwartet .txt standardmäßig Labels. Diese Anleitung enthält den minimal erforderlichen benutzerdefinierten Code – eine Datensatzklasse und eine Trainerklasse. Nach der Definition erfordert das Training lediglich einen Standard- model.train() Aufruf.
Unterstützt dies Segmentierung und Posenschätzung?
Dieser Leitfaden behandelt Objekterkennung durchzuführen. Um hinzuzufügen Instanzsegmentierung Unterstützung, einschließlich der segmentation Polygondaten aus COCO in der segments Feld jedes Etikettenwörterbuchs. Für Pose-Schätzung, umfassen keypoints. Der GroundingDataset Quellcode bietet eine Referenzimplementierung für die Verarbeitung von Segmenten.
Funktionieren Augmentierungen mit diesem benutzerdefinierten Datensatz?
Ja. COCODataset erweitert YOLODataset, sodass alle integrierten Datenerweiterungen — Mosaik, Mixup, Copy-Paste, und andere — laufen ohne Modifikation.
Wie werden Kategorie-IDs den Klassenindizes zugeordnet?
Kategorien sind sortiert nach id und sequenziellen Indizes ab 0 zugeordnet. Dies verarbeitet 1-basierte IDs (Standard COCO), 0-basierte IDs und nicht-zusammenhängende IDs. Der names Wörterbuch in dataset.yaml sollte derselben sortierten Reihenfolge wie das COCO folgen categories Array.
Gibt es im Vergleich zu vorab konvertierten Beschriftungen einen Leistungsaufwand?
Das COCO wird beim ersten Trainingsdurchlauf einmalig geparst. Die geparsten Labels werden in einer .cache Datei, sodass nachfolgende Durchläufe sofort geladen werden, ohne dass eine erneute Analyse erforderlich ist. Die Trainingsgeschwindigkeit entspricht der YOLO , da die Annotationen im Speicher gehalten werden. Der Cache wird automatisch neu aufgebaut, wenn sich die JSON-Datei ändert.