كيفية تدريب YOLO COCO دون الحاجة إلى تحويلها
لماذا التدريب مباشرة على COCO
التعليقات التوضيحية في COCO يمكن استخدام هذا التنسيق مباشرةً لـ Ultralytics YOLO التدريب دون التحويل إلى .txt الملفات أولاً. ويتم ذلك عن طريق إنشاء فئة فرعية YOLODataset لتحليل COCO في الوقت الفعلي ودمجها في مسار التدريب من خلال أداة تدريب مخصصة.
يحافظ هذا النهج على ملف COCO باعتباره المصدر الوحيد للمعلومات — لا convert_coco() استدعاء، دون إعادة تنظيم الدليل، ودون ملفات تسمية وسيطة. YOLO26 كما يتم دعم جميع نماذجYOLO الأخرى Ultralytics . تتطلب نماذج التجزئة وتحديد الوضع حقول تصنيف إضافية (انظر الأسئلة الشائعة).
هل تبحث عن تحويل لمرة واحدة بدلاً من ذلك؟
انظر إلى دليل YOLO COCO YOLO بالنسبة للمعيار convert_coco() سير العمل.
نظرة عامة على البنية
هناك حاجة إلى فصلين دراسيين:
COCOJSONDataset— يقرأ ملفات COCO ويحولها المربعات المحيطة إلى YOLO في الذاكرة أثناء التدريبCOCOJSONTrainer— التجاوزاتbuild_dataset()لاستخدامCOCOJSONDatasetبدلاً من الإعداد الافتراضيYOLODataset
يتبع التنفيذ نفس نمط الوظيفة المدمجة GroundingDataset، والتي تقرأ أيضًا تعليقات JSON مباشرةً. تم تجاوز ثلاث طرق: get_img_files(), cache_labels()، و get_labels().
إنشاء فئة مجموعة بيانات COCO
في COCOJSONDataset تُورث الفئة من YOLODataset ويستبدل منطق تحميل التسميات. بدلاً من قراءة .txt الملفات من دليل "labels"، فتفتح ملف COCO وتقوم بتكرار معالجة التعليقات التوضيحية المجمعة حسب الصورة، وتحوّل كل مربع تحديد من تنسيق COCO [x_min, y_min, width, height] إلى تنسيق المركز YOLO [x_center, y_center, width, height]. التعليقات الجماعية (iscrowd: 1) ويتم تخطي المربعات ذات المساحة الصفرية تلقائيًا.
في get_img_files() تُرجع هذه الطريقة قائمة فارغة لأن مسارات الصور يتم تحديدها من ملف JSON file_name المساحة الداخلية cache_labels(). يتم فرز معرّفات الفئات وإعادة تعيينها إلى مؤشرات فئات ذات مؤشر يبدأ من الصفر، بحيث تعمل كل من أنظمة المعرّفات التي تبدأ من 1 ( COCO القياسية) وأنظمة المعرّفات غير المتجاورة بشكل صحيح.
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"]
يتم حفظ العلامات التي تمت معالجتها في ملف .cache ملف بجوار ملف JSON (على سبيل المثال instances_train.cache). وفي عمليات التدريب اللاحقة، يتم تحميل ذاكرة التخزين المؤقت مباشرةً، متخطيةً عملية تحليل JSON. وإذا تغير ملف JSON، يفشل فحص التجزئة ويتم إعادة إنشاء ذاكرة التخزين المؤقت تلقائيًّا.
ربط مجموعة البيانات بمسار التدريب
التغيير الوحيد المطلوب في المدرب هو التجاوز build_dataset(). الإعداد الافتراضي DetectionTrainer يبني YOLODataset الذي يبحث عن .txt ملفات التسمية. عن طريق استبدالها بـ COCOJSONDataset، يقرأ المدرب البيانات من ملف COCO بدلاً من ذلك.
يتم استرداد مسار ملف JSON من ملف مخصص train_json / val_json field in the data config (see Step 3). During training, mode="train" يقرر train_json؛ أثناء عملية التحقق من الصحة، mode="val" يقرر val_json. إذا val_json إذا لم يتم تعيينه، يتم الرجوع إلى 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,
)
تكوين ملف dataset.yaml COCO yaml COCO
في dataset.yaml يستخدم المعيار path, train، و val حقول لتحديد مواقع مجلدات الصور. حقلان إضافيان، train_json و val_json، حدد ملفات COCO التوضيحية COCO التي COCOJSONTrainer يقول. nc و names تحدد الحقول عدد الفئات وأسمائها، بما يتوافق مع الترتيب المصنف لـ categories في ملف 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
هيكل المجلدات المتوقع:
my_dataset/
images/
train/
img_001.jpg
...
val/
img_100.jpg
...
annotations/
instances_train.json
instances_val.json
dataset.yaml
تدريب على تشغيل COCO
بعد تجهيز فئة مجموعة البيانات وفئة المدرب وملف التكوين YAML، يتم التدريب وفقًا للمعيار القياسي model.train() المكالمة. والفرق الوحيد عن الجري التدريبي العادي هو trainer=COCOJSONTrainer المعلمة، التي توجه Ultralytics أداة تحميل مجموعات البيانات المخصصة بدلاً من الأداة الافتراضية.
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.train(data="dataset.yaml", epochs=100, imgsz=640, trainer=COCOJSONTrainer)
تعمل سلسلة التدريب بالكامل كما هو متوقع، بما في ذلك التحقق من الصحة وحفظ نقاط التحقق وتسجيل المقاييس.
التنفيذ الكامل
للتسهيل، يرد أدناه التنفيذ الكامل في شكل نص برمجي واحد يمكن نسخه ولصقه. ويشمل هذا النص مجموعة البيانات المخصصة، والمدرب المخصص، ووظيفة التدريب. احفظ هذا النص بجانب ملفك dataset.yaml وتشغيله مباشرة.
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)
للاطلاع على توصيات بشأن المعلمات الفائقة ، راجع دليل «نصائح تدريب النموذج ».
الأسئلة الشائعة
ما الفرق بين هذه الدالة ودالة convert_coco()؟
convert_coco() يكتب .txt نسخ ملفات التسميات إلى القرص كعملية تحويل تتم مرة واحدة. تعمل هذه الطريقة على تحليل ملف JSON في بداية كل دورة تدريب وتحويل التسميات في الذاكرة. استخدم convert_coco() عندما يُفضل استخدام التسميات الدائمة YOLO استخدم هذه الطريقة للحفاظ على ملف COCO باعتباره المصدر الوحيد الموثوق دون إنشاء ملفات إضافية.
هل يمكن لـ YOLO على بيانات COCO دون الحاجة إلى كتابة كود مخصص؟
ليس مع Ultralytics الحالي Ultralytics ، الذي يتوقع استخدام YOLO .txt العلامات بشكل افتراضي. يقدم هذا الدليل الحد الأدنى من التعليمات البرمجية المخصصة اللازمة — فئة واحدة لمجموعة البيانات وفئة واحدة للمدرب. وبمجرد تعريفها، لا يتطلب التدريب سوى model.train() اتصل.
هل يدعم هذا التطبيق تقسيم الشاشة وتقدير وضع الجسم؟
يغطي هذا الدليل اكتشاف الكائنات. لإضافة تجزئة المثيل الدعم، بما في ذلك segmentation بيانات المضلعات المستمدة من COCO في segments حقل كل قاموس تسميات. بالنسبة لـ تقدير الوضعية، تشمل keypoints. إن GroundingDataset كود المصدر يوفر نموذجًا مرجعيًا لتنفيذ معالجة المقاطع.
هل تعمل عمليات التوسيع مع مجموعة البيانات المخصصة هذه؟
نعم. COCOJSONDataset يمتد YOLODataset، لذا فإن جميع الميزات المدمجة عمليات توسيع البيانات — فسيفساء, mixup, نسخ-لصقوغيرها — تعمل دون تعديل.
كيف يتم ربط معرّفات الفئات بفهارس الفئات؟
يتم فرز الفئات حسب id ويتم تعيينها إلى مؤشرات متسلسلة تبدأ من 0. وهذا يعالج المعرفات التي تبدأ من 1 (معيار COCO)، والمعرفات التي تبدأ من 0، والمعرفات غير المتجاورة. names قاموس في dataset.yaml يجب أن يتبع نفس الترتيب الذي يتبعه COCO categories مصفوفة.
هل هناك انخفاض في الأداء مقارنةً بالملصقات التي تم تحويلها مسبقًا؟
يتم تحليل ملف COCO مرة واحدة خلال الجولة الأولى من التدريب. ويتم حفظ التسميات التي تم تحليلها في ملف .cache ملف، وبالتالي يتم تحميل العمليات اللاحقة على الفور دون الحاجة إلى إعادة التحليل. وتكون سرعة التدريب مماثلة YOLO القياسية YOLO نظرًا لأن التعليقات التوضيحية يتم الاحتفاظ بها في الذاكرة. ويتم إعادة إنشاء ذاكرة التخزين المؤقت تلقائيًا في حالة تغيير ملف JSON.