Ultralytics YOLO27:
Get Started

YOLO'yu Dönüştürmeden COCO JSON ile Eğitme#

Açıklamalar, önce .txt dosyalarına dönüştürülmeden doğrudan COCO JSON biçiminde Ultralytics YOLO eğitimi için kullanılabilir. Bunun için YOLODataset sınıfı alt sınıflandırılır; böylece COCO JSON anında ayrıştırılır ve özel bir eğitici aracılığıyla eğitim iş akışına bağlanır.

Neden Doğrudan COCO JSON ile Eğitim Yapmalı?#

Bu yaklaşım, COCO JSON'ı tek doğruluk kaynağı olarak tutar — convert_coco() çağrısı yok, dizin yeniden düzenlemesi yok, ara etiket dosyaları yok. YOLO26 ve diğer tüm Ultralytics YOLO algılama modelleri desteklenir. Segmentasyon ve poz modelleri ek etiket alanları gerektirir (SSS bölümüne bak).

Bunun yerine tek seferlik bir dönüştürme mi arıyorsun?

Standart convert_coco() iş akışı için COCO'dan YOLO'ya Dönüştürme kılavuzuna bak.

Mimariye Genel Bakış#

İki sınıf gerekiyor:

  1. COCODataset — COCO JSON'ı okur ve sınırlayıcı kutuları eğitim sırasında bellekte YOLO biçimine dönüştürür
  2. COCOTrainer — varsayılan YOLODataset yerine COCODataset kullanmak için build_dataset() öğesini geçersiz kılar

Uygulama, JSON açıklamalarını doğrudan okuyan yerleşik GroundingDataset sınıfının basitleştirilmiş bir sürümüdür. Burada üç yöntem geçersiz kılınmıştır — get_img_files(), cache_labels() ve get_labels() — GroundingDataset ise kendi önbellek karması ve örnek sayısı kontrolleri dâhil olmak üzere daha fazla yöntemi geçersiz kılar.

COCO JSON Veri Kümesi Sınıfını Oluşturma#

COCODataset sınıfı, YOLODataset sınıfından kalıtım alır ve etiket yükleme mantığını geçersiz kılar. Etiket dizinindeki .txt dosyalarını okumak yerine COCO JSON dosyasını açar, görüntüye göre gruplanmış açıklamalar arasında gezinir ve her sınırlayıcı kutuyu COCO piksel biçimi [x_min, y_min, width, height]'ten YOLO'nun normalize edilmiş merkez biçimi [x_center, y_center, width, height]'e dönüştürür. Kalabalık açıklamaları (iscrowd: 1) ve sıfır alanlı kutular otomatik olarak atlanır.

Görüntü yolları, cache_labels() içindeki JSON file_name alanından çözümlendiği için get_img_files() yöntemi boş bir liste döndürür. Kategori kimlikleri sıralanır ve sıfır tabanlı sınıf indekslerine yeniden eşlenir; böylece hem 1 tabanlı (standart COCO) hem de kesintili kimlik şemaları doğru şekilde çalışır.

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):
        """Initialize the dataset with a COCO JSON annotation file."""
        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."""
        self.fraction = 1.0  # fraction is applied while scanning a directory, which this dataset skips
        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)

        # 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",
                }
            )
        if not x["labels"]:
            raise RuntimeError(f"No images listed in {self.json_file} were found in {self.img_path}")
        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"]

Ayrıştırılan etiketler, JSON'ın yanındaki bir .cache dosyasına kaydedilir (ör. instances_train.cache). Sonraki eğitim çalıştırmalarında önbellek doğrudan yüklenir ve JSON ayrıştırması atlanır.

Önbellek anahtarı, JSON'ın içeriği değil dosya boyutudur

get_hash(), dosya içerikleri yerine dosya boyutlarını ve yollarını karmalar; bu nedenle yeniden çalıştırma JSON'ı yalnızca JSON'ın bayt sayısı değiştiğinde yeniden ayrıştırır. Görüntü eklemek veya kaldırmak görüntü dizininin boyutunu da değiştirip yeniden oluşturmayı tetikleyebilir, ancak buna güvenme — karma tek tek görüntü dosyalarını hiçbir zaman incelemez; bu yüzden bir görüntüyü başka bir görüntüyle değiştirmek boyutu değiştirmeyebilir. Bayt sayısını koruyan bir düzenleme — koordinatı biraz kaydırmak, iscrowd değerini tersine çevirmek veya aynı uzunluktaki iki sınıf adını değiştirmek — eski önbelleği yerinde bırakır ve herhangi bir uyarı vermeden eski açıklamalarla eğitim yapılmasına neden olur; görüntüyü bulunduğu yerde değiştirmek de aynı nedenle algılanmaz. Açıklamaları veya görüntüleri yerinde düzenledikten sonra .cache dosyasını sil.

Veri Kümesini Eğitim İş Akışına Bağlama#

Eğiticide gereken tek değişiklik build_dataset() öğesini geçersiz kılmaktır. Varsayılan DetectionTrainer, .txt etiket dosyalarını tarayan bir YOLODataset oluşturur. Bunun yerine COCODataset kullanıldığında eğitici COCO JSON'dan okur.

JSON dosyasının yolu, veri yapılandırmasındaki özel train_json / val_json alanından alınır (dataset.yaml yapılandırma bölümüne bak). Eğitim sırasında mode="train", train_json değerine çözümlenir; doğrulama sırasında mode="val", val_json değerine çözümlenir. İki anahtar da gereklidir — iki bölüm farklı görüntü dizinlerini okur; bu nedenle eğitim JSON'ı eksik bir val_json yerine kullanılamaz.

Veri kümesi ayrıca fraction değerini 1.0 olarak sıfırlar. BaseDataset, bir görüntü dizinini tararken bu bağımsız değişkeni uygular; COCODataset ise bu adımı atlar ve bu nedenle kısmi veri kümesi isteğini yerine getiremez. Değeri sıfırlamak, veri kümesinin yok saydığı bir değeri kabul ediyormuş gibi görünmesini önler. Yerleşik GroundingDataset de aynı nedenle aynı ödünü verir.

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):
        """Build a COCODataset for the given split using the JSON file from the data config."""
        json_file = self.data["train_json"] if mode == "train" else self.data["val_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,
        )

COCO JSON için dataset.yaml dosyasını yapılandırma#

dataset.yaml, görüntü dizinlerinin konumunu belirlemek için standart path, train ve val alanlarını kullanır. Burada path değerinin görüntü kök dizinine işaret ettiğine dikkat et; bu nedenle train ve val yalnızca bölüm adlarından oluşur. Bu, path değerinin veri kümesi kök dizini olduğu ve bölümlerin images/ öneki taşıdığı dönüştürme kılavuzundan farklıdır. İki ek alan olan train_json ve val_json, COCOTrainer tarafından okunan COCO açıklama dosyalarını belirtir. names alanı, JSON'daki categories sıralamasına göre sınıf adlarını listeler ve sınıf sayısı bu alandan türetilir; dolayısıyla nc ayarlaman gerekmez.

path: /path/to/my_dataset/images # root with train/ and val/ image subfolders
train: train
val: val

# COCO JSON annotation files (use absolute paths; these custom keys are not resolved against `path`)
train_json: /path/to/my_dataset/annotations/instances_train.json
val_json: /path/to/my_dataset/annotations/instances_val.json

names:
    0: person
    1: bicycle
    # ... remaining class names

Beklenen dizin yapısı:

my_dataset/
  images/
    train/
      img_001.jpg
      ...
    val/
      img_100.jpg
      ...
  annotations/
    instances_train.json
    instances_val.json
  dataset.yaml

COCO JSON ile Eğitimi Çalıştırma#

Veri kümesi sınıfı, eğitici sınıfı ve YAML yapılandırması hazır olduğunda eğitim, standart model.train() çağrısıyla yürütülür. Normal bir eğitim çalıştırmasından tek fark, Ultralytics'e varsayılan yerine özel veri kümesi yükleyicisini kullanmasını söyleyen trainer=COCOTrainer bağımsız değişkenidir.

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
model.train(data="dataset.yaml", epochs=100, imgsz=640, trainer=COCOTrainer)

Eksiksiz eğitim iş akışı; eğitim sırasında doğrulama, kontrol noktalarının kaydedilmesi ve metriklerin günlüğe kaydedilmesi dâhil, beklendiği gibi çalışır.

Bağımsız `model.val()` için kendi geçersiz kılması gerekir

Yalnızca eğitim sırasındaki doğrulama COCOTrainer.build_dataset üzerinden yürütülür. Ayrı bir model.val() çağrısı, görüntülerin yanındaki .txt etiketlerini tarayan ve hiçbirini bulamayan standart YOLODataset sınıfını oluşturur. Hata vermez: görüntüler arka plan olarak sayılır; dolayısıyla doğrulama tamamlanır ve tüm metrikler 0 olarak raporlanır; No labels found in ... ve no labels found in detect set, cannot compute metrics without labels uyarıları gösterilir. Bir eğitim çalıştırmasının dışında doğrulama yapmak için aynı build_dataset geçersiz kılma işlemini içeren bir doğrulayıcı alt sınıfı oluştur ve bunu model.val(validator=...) öğesine ilet.

Eksiksiz Uygulama#

Kolaylık olması için eksiksiz uygulama, aşağıda tek bir kopyala-yapıştır betiği olarak sunulmuştur. Özel veri kümesini, özel eğiticiyi ve eğitim çağrısını içerir. Bunu dataset.yaml dosyanın yanına kaydet ve doğrudan çalıştır.

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):
        """Initialize the dataset with a COCO JSON annotation file."""
        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."""
        self.fraction = 1.0  # fraction is applied while scanning a directory, which this dataset skips
        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)

        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",
                }
            )
        if not x["labels"]:
            raise RuntimeError(f"No images listed in {self.json_file} were found in {self.img_path}")
        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"]

class COCOTrainer(DetectionTrainer):
    """Trainer that uses COCODataset for direct COCO JSON training."""

    def build_dataset(self, img_path, mode="train", batch=None):
        """Build a COCODataset for the given split using the JSON file from the data config."""
        json_file = self.data["train_json"] if mode == "train" else self.data["val_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)

Artık anotasyonların tek doğruluk kaynağı olarak kaldığı ve ara .txt dosyalarının oluşturulmadığı, Ultralytics YOLO'yu doğrudan COCO JSON üzerinde eğiten minimal bir veri kümesine ve eğiticiye sahipsin. Segmentasyon ve poz tahminini kapsamak için cache_labels() yöntemini segments veya keypoints ile genişlet ve Model Eğitimi İpuçları kılavuzundaki hiperparametre ayarlama önerilerine göz at.

Sık Sorulan Sorular#

  • convert_coco(), tek seferlik dönüştürme olarak .txt etiket dosyalarını diske yazar. Bu yaklaşım, her eğitim çalıştırmasının başında JSON'ı ayrıştırır ve açıklamaları bellekte dönüştürür. Kalıcı YOLO biçimindeki etiketleri tercih ediyorsan convert_coco() kullan; ek dosyalar oluşturmadan COCO JSON'ı tek doğruluk kaynağı olarak tutmak istiyorsan bu yaklaşımı kullan.

  • Varsayılan olarak YOLO .txt etiketlerini bekleyen mevcut Ultralytics iş akışıyla bu mümkün değil. Bu kılavuz gereken minimal özel kodu sunuyor — bir veri kümesi sınıfı ve bir eğitici sınıfı. Bu sınıflar tanımlandıktan sonra eğitim için yalnızca standart bir model.train() çağrısı gerekir.

  • Bu kılavuz nesne algılamayı ele alıyor. Örnek segmentasyonu desteği eklemek için, COCO açıklamalarındaki segmentation çokgen verisini her etiket sözlüğünün segments alanına ekle. Poz tahmini için keypoints öğesini ekle. GroundingDataset kaynak kodu, segmentlerin işlenmesine yönelik bir referans uygulaması sunar.

  • Evet. COCODataset, YOLODataset sınıfını genişletir; bu nedenle yerleşik tüm veri artırmaları — mozaik, mixup, CutMix ve diğerleri — değişiklik yapılmadan çalışır.

  • Kategoriler id değerine göre sıralanır ve 0'dan başlayan ardışık indekslere eşlenir. Bu, 1 tabanlı kimlikleri (standart COCO), 0 tabanlı kimlikleri ve kesintili kimlikleri işler. dataset.yaml içindeki names sözlüğü, COCO categories dizisiyle aynı sıralamayı izlemelidir.

  • COCO JSON, ilk eğitim çalıştırmasında bir kez ayrıştırılır. Ayrıştırılan etiketler bir .cache dosyasına kaydedilir; böylece sonraki çalıştırmalarda yeniden ayrıştırılmadan anında yüklenir. Açıklamalar bellekte tutulduğundan eğitim hızı standart YOLO eğitimiyle aynıdır. Önbellek, JSON'ın dosya boyutuna göre anahtarlanır; bu nedenle dosya uzunluğunu değiştirmeyen bir düzenleme yaptıktan sonra .cache dosyasını sil.

Yorumlar