Chuyển đến nội dung

Cách huấn luyện YOLO TRÊN COCO JSON không cần chuyển đổi

Tại sao nên huấn luyện trực tiếp tại... COCO JSON

Chú thích trong COCO JSON định dạng này có thể được sử dụng trực tiếp cho Ultralytics YOLO đào tạo mà không cần chuyển đổi sang .txt các tập tin trước. Việc này được thực hiện bằng cách tạo lớp con. YOLODataset để phân tích COCO Xử lý JSON tức thời và tích hợp nó vào quy trình huấn luyện thông qua một trình huấn luyện tùy chỉnh.

Cách tiếp cận này giúp duy trì COCO JSON là nguồn thông tin duy nhất — không convert_coco() Gọi hàm, không cần sắp xếp lại thư mục, không cần tạo tệp nhãn trung gian. YOLO26 và tất cả những thứ khác Ultralytics YOLO Các mô hình phát hiện được hỗ trợ. Mô hình phân đoạn và tư thế yêu cầu các trường nhãn bổ sung (xem Câu hỏi thường gặp).

Bạn muốn chuyển đổi một lần duy nhất?

Xem COCO ĐẾN YOLO Hướng dẫn chuyển đổi đối với tiêu chuẩn convert_coco() quy trình làm việc.

Tổng quan về kiến trúc

Cần hai lớp học:

  1. COCOJSONDataset — đọc COCO JSON và các công cụ chuyển đổi các hộp giới hạn ĐẾN YOLO định dạng trong bộ nhớ trong quá trình huấn luyện
  2. COCOJSONTrainer — ghi đè build_dataset() để sử dụng COCOJSONDataset thay vì mặc định YOLODataset

Việc triển khai tuân theo cùng một mô hình như chức năng tích hợp sẵn. GroundingDataset, đồng thời cũng đọc trực tiếp các chú thích JSON. Ba phương thức được ghi đè: get_img_files(), cache_labels(), và get_labels().

Xây dựng COCO Lớp tập dữ liệu JSON

Hàm COCOJSONDataset lớp kế thừa từ YOLODataset và ghi đè lên logic tải nhãn. Thay vì đọc .txt các tệp từ thư mục nhãn, nó sẽ mở COCO Tệp JSON này lặp lại các chú thích được nhóm theo hình ảnh và chuyển đổi từng hộp giới hạn từ COCO định dạng pixel [x_min, y_min, width, height] ĐẾN YOLO định dạng trung tâm chuẩn hóa [x_center, y_center, width, height]Chú thích đám đông (iscrowd: 1Các ô có diện tích bằng 0 sẽ tự động bị bỏ qua.

Hàm get_img_files() Phương thức này trả về một danh sách rỗng vì đường dẫn hình ảnh được xác định từ JSON. file_name cánh đồng bên trong cache_labels(). ID danh mục được sắp xếp và ánh xạ lại thành chỉ số lớp bắt đầu từ 0, do đó cả chỉ số bắt đầu từ 1 (tiêu chuẩn) đều được sử dụng. COCO Các lược đồ ID không liền kề hoạt động chính xác.

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 COCOJSONDataset(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"]

Các nhãn đã được phân tích cú pháp được lưu vào một .cache tệp tin nằm cạnh tệp JSON (ví dụ: instances_train.cacheTrong các lần chạy huấn luyện tiếp theo, bộ nhớ đệm được tải trực tiếp, bỏ qua bước phân tích cú pháp JSON. Nếu tệp JSON thay đổi, quá trình kiểm tra mã băm sẽ thất bại và bộ nhớ đệm sẽ được xây dựng lại tự động.

Kết nối tập dữ liệu với quy trình huấn luyện

Thay đổi duy nhất cần thực hiện trong trình huấn luyện là ghi đè lên. build_dataset(). Mặc định DetectionTrainer xây dựng một YOLODataset quét .txt các tập tin nhãn. Bằng cách thay thế nó bằng COCOJSONDataset, người huấn luyện đọc từ COCO Thay vào đó là JSON.

Đường dẫn đến tệp JSON được lấy từ một tệp tùy chỉnh. train_json / val_json field in the data config (see Step 3). During training, mode="train" quyết tâm train_json; trong quá trình xác thực, mode="val" quyết tâm val_json. Nếu như val_json nếu chưa được thiết lập, nó sẽ sử dụng phương án dự phòng. train_json.

from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import colorstr


class COCOJSONTrainer(DetectionTrainer):
    """Trainer that uses COCOJSONDataset 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 COCOJSONDataset(
            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,
        )

Đang cấu hình tập dữ liệu. yaml vì COCO JSON

Hàm dataset.yaml sử dụng tiêu chuẩn path, train, và val Các trường để xác định vị trí thư mục hình ảnh. Hai trường bổ sung, train_jsonval_json, chỉ định COCO các tệp chú thích COCOJSONTrainer đọc. Cái ncnames Các trường xác định số lượng lớp và tên của chúng, phù hợp với thứ tự đã được sắp xếp. categories trong 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

Cấu trúc thư mục dự kiến:

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

Tập luyện chạy bộ COCO JSON

Sau khi đã thiết lập lớp tập dữ liệu, lớp huấn luyện và cấu hình YAML, quá trình huấn luyện sẽ diễn ra theo quy trình chuẩn. model.train() cuộc gọi. Điểm khác biệt duy nhất so với một buổi chạy luyện tập thông thường là... trainer=COCOJSONTrainer lập luận, điều này cho biết Ultralytics Sử dụng trình tải tập dữ liệu tùy chỉnh thay vì trình tải mặc định.

from ultralytics import YOLO

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

Toàn bộ quy trình huấn luyện diễn ra như mong đợi, bao gồm xác thực , lưu điểm kiểm tra và ghi nhật ký số liệu.

Triển khai đầy đủ

Để thuận tiện, toàn bộ mã nguồn được cung cấp bên dưới dưới dạng một đoạn mã sao chép-dán duy nhất. Nó bao gồm tập dữ liệu tùy chỉnh, trình huấn luyện tùy chỉnh và lệnh gọi huấn luyện. Hãy lưu tập tin này cùng với tệp của bạn. dataset.yaml và chạy trực tiếp.

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 COCOJSONDataset(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 COCOJSONTrainer(DetectionTrainer):
    """Trainer that uses COCOJSONDataset 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 COCOJSONDataset(
            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=COCOJSONTrainer)

Để biết các khuyến nghị về siêu tham số , hãy xem hướng dẫn Mẹo huấn luyện mô hình .

Câu hỏi thường gặp

Sự khác biệt giữa hàm này và convert_coco() là gì?

convert_coco() viết .txt Ghi nhãn các tập tin vào ổ đĩa dưới dạng chuyển đổi một lần. Phương pháp này phân tích cú pháp JSON ở đầu mỗi lần chạy huấn luyện và chuyển đổi các chú thích trong bộ nhớ. Sử dụng convert_coco() khi vĩnh viễn YOLO - Định dạng nhãn được ưu tiên; hãy sử dụng phương pháp này để giữ nguyên COCO JSON là nguồn thông tin duy nhất mà không cần tạo thêm tệp tin nào khác.

Có thể YOLO tàu hỏa trên COCO JSON không cần mã tùy chỉnh?

Không phải với tình hình hiện tại Ultralytics đường ống, dự kiến YOLO .txt Các nhãn được thiết lập mặc định. Hướng dẫn này cung cấp mã tùy chỉnh tối thiểu cần thiết — một lớp dữ liệu và một lớp huấn luyện. Sau khi được định nghĩa, quá trình huấn luyện chỉ cần một tệp tiêu chuẩn. model.train() gọi.

Liệu điều này có hỗ trợ phân đoạn và ước lượng tư thế không?

Hướng dẫn này bao gồm phát hiện đối tượngĐể thêm vào phân vùng thể hiện hỗ trợ, bao gồm segmentation dữ liệu đa giác từ COCO chú thích trong segments trường của mỗi từ điển nhãn. Ví dụ: ước tính tư thế, bao gồm keypoints. Các GroundingDataset mã nguồn Cung cấp một triển khai tham chiếu để xử lý các phân đoạn.

Liệu các kỹ thuật tăng cường dữ liệu có hoạt động với bộ dữ liệu tùy chỉnh này không?

Đúng. COCOJSONDataset mở rộng YOLODataset, vậy nên tất cả đều được tích hợp sẵn tăng cường dữ liệumosaic, mixup, copy-pastevà những ứng dụng khác — chạy mà không cần chỉnh sửa.

Mã định danh danh mục được ánh xạ tới chỉ số lớp như thế nào?

Các danh mục được sắp xếp theo id và được ánh xạ tới các chỉ số tuần tự bắt đầu từ 0. Điều này xử lý các ID dựa trên chỉ số 1 (tiêu chuẩn). COCO ), ID dựa trên chỉ số 0 và ID không liền kề. names từ điển trong dataset.yaml nên tuân theo cùng thứ tự sắp xếp như COCO categories mảng.

Liệu có sự hao phí hiệu năng nào so với việc sử dụng nhãn đã được chuyển đổi trước đó không?

Cái COCO Dữ liệu JSON được phân tích cú pháp một lần trong lần chạy huấn luyện đầu tiên. Các nhãn đã được phân tích cú pháp được lưu vào một tệp. .cache Tệp được lưu trữ, do đó các lần chạy tiếp theo sẽ tải ngay lập tức mà không cần phân tích cú pháp lại. Tốc độ huấn luyện tương đương với tiêu chuẩn. YOLO Quá trình huấn luyện diễn ra vì các chú thích được lưu trữ trong bộ nhớ. Bộ nhớ đệm được xây dựng lại tự động nếu tệp JSON thay đổi.



📅 Được tạo 0 ngày trước ✏️ Cập nhật 0 ngày trước
glenn-jocherraimbekovm

Bình luận