رؤية YOLO لعام 2026:

تخصيص المـدرب (Trainer)#

تم تصميم مسار التدريب في Ultralytics حول BaseTrainer ومقاتلي التدريب الخاصين بالمهام مثل DetectionTrainer. تتولى هذه الفئات حلقة التدريب، والتحقق من الصحة، وحفظ النقاط التفتيشية، وتسجيل السجلات بشكل جاهز للاستخدام. عندما تحتاج إلى مزيد من التحكم — مثل تتبع مقاييس مخصصة، أو ضبط أوزان الخسارة، أو تنفيذ جداول معدل التعلم — يمكنك إنشاء فئة فرعية من المدرب وتجاوز طرق معينة.

يستعرض هذا الدليل سبعة تخصيصات شائعة:

  1. تسجيل المقاييس المخصصة (درجة F1) في نهاية كل حقبة (epoch)
  2. إضافة أوزان الفئات للتعامل مع عدم توازن الفئات
  3. حفظ أفضل نموذج بناءً على مقياس مختلف
  4. تجميد الطبقة الأساسية (backbone) لأول N حقبة، ثم إلغاء التجميد
  5. تحديد معدلات تعلم لكل طبقة على حدة
  6. مزامنة BatchNorm عبر وحدات معالجة الرسوميات (GPUs) للتدريب متعدد وحدات معالجة الرسوميات
  7. تكوين قص التدرج (gradient clipping) لضبط الاستقرار
المتطلبات الأساسية

قبل قراءة هذا الدليل، تأكد من أنك على دراية أساسيات تدريب نماذج YOLO وصفحة التخصيص المتقدم، والتي تغطي هندسة BaseTrainer.

كيف تعمل المـدربات المخصصة#

تقبل فئة نموذج YOLO معامل trainer في طريقة train(). يتيح لك هذا تمرير فئة المدرب الخاصة بك والتي توسع السلوك الافتراضي:

from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer

class CustomTrainer(DetectionTrainer):
    """A custom trainer that extends DetectionTrainer with additional functionality."""

    # Add your customizations here

model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=10, trainer=CustomTrainer)

يرث المدرب المخصص الخاص بك كافة الوظائف من DetectionTrainer، لذا فأنت بحاجة فقط إلى تجاوز الطرق المحددة التي ترغب في تخصيصها.

تسجيل مقاييس مخصصة#

خطوة التحقق من الصحة تحسب الدقة (precision)، والاسترجاع (recall)، وmAP. إذا كنت بحاجة إلى مقاييس إضافية مثل درجة F1 لكل فئة، فتجاوز validate():

import numpy as np

from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import LOGGER

class MetricsTrainer(DetectionTrainer):
    """Custom trainer that computes and logs F1 score at the end of each epoch."""

    def validate(self):
        """Run validation and compute per-class F1 scores."""
        metrics, fitness = super().validate()
        if metrics is None:
            return metrics, fitness

        if hasattr(self.validator, "metrics") and hasattr(self.validator.metrics, "box"):
            box = self.validator.metrics.box
            f1_per_class = box.f1
            class_indices = box.ap_class_index
            names = self.validator.names

            valid_f1 = f1_per_class[f1_per_class > 0]
            mean_f1 = np.mean(valid_f1) if len(valid_f1) > 0 else 0.0

            LOGGER.info(f"Mean F1 Score: {mean_f1:.4f}")
            per_class_str = [
                f"{names[i]}: {f1_per_class[j]:.3f}" for j, i in enumerate(class_indices) if f1_per_class[j] > 0
            ]
            LOGGER.info(f"Per-class F1: {per_class_str}")

        return metrics, fitness

model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=5, trainer=MetricsTrainer)

يقوم هذا بتسجيل متوسط درجة F1 عبر جميع الفئات وتفصيل لكل فئة بعد كل تشغيل للتحقق.

المقاييس المتاحة

يوفر المدقق (validator) إمكانية الوصول إلى العديد من المقاييس من خلال self.validator.metrics.box:

السمة (Attribute)الوصف
f1درجة F1 لكل فئة
image_metricsقاموس مقاييس لكل صورة يتضمن الدقة، الاسترجاع، F1، TP، FP، و FN
pالدقة لكل فئة
rالاسترجاع لكل فئة
ap50AP عند IoU 0.5 لكل فئة
apAP عند IoU 0.5:0.95 لكل فئة
mp، mrمتوسط الدقة والاسترجاع
map50، mapمتوسط مقاييس AP

إضافة أوزان للفئات#

إذا كان مجموعة بياناتك تحتوي على فئات غير متوازنة (مثل عيب نادر في فحص التصنيع)، يمكنك زيادة وزن الفئات ضعيفة التمثيل في دالة الخسارة. يجعل هذا النموذج يعاقب التصنيفات الخاطئة على الفئات النادرة بشكل أكبر.

لتخصيص الخسارة، قم بإنشاء فئات فرعية من فئات الخسارة، والنموذج، والمـدرب:

import torch
from torch import nn

from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.nn.tasks import DetectionModel
from ultralytics.utils import RANK
from ultralytics.utils.loss import E2ELoss, v8DetectionLoss

class WeightedDetectionLoss(v8DetectionLoss):
    """Detection loss with class weights applied to BCE classification loss."""

    def __init__(self, model, class_weights=None, tal_topk=10, tal_topk2=None):
        """Initialize loss with optional per-class weights for BCE."""
        super().__init__(model, tal_topk=tal_topk, tal_topk2=tal_topk2)
        if class_weights is not None:
            self.bce = nn.BCEWithLogitsLoss(
                pos_weight=class_weights.to(self.device),
                reduction="none",
            )

class WeightedE2ELoss(E2ELoss):
    """E2E Loss with class weights for YOLO26."""

    def __init__(self, model, class_weights=None):
        """Initialize E2E loss with weighted detection loss."""

        def weighted_loss_fn(model, tal_topk=10, tal_topk2=None):
            return WeightedDetectionLoss(model, class_weights=class_weights, tal_topk=tal_topk, tal_topk2=tal_topk2)

        super().__init__(model, loss_fn=weighted_loss_fn)

class WeightedDetectionModel(DetectionModel):
    """Detection model that uses class-weighted loss."""

    def init_criterion(self):
        """Initialize weighted loss criterion with per-class weights."""
        class_weights = torch.ones(self.nc)
        class_weights[0] = 2.0  # upweight class 0
        class_weights[1] = 3.0  # upweight rare class 1
        return WeightedE2ELoss(self, class_weights=class_weights)

class WeightedTrainer(DetectionTrainer):
    """Trainer that returns a WeightedDetectionModel."""

    def get_model(self, cfg=None, weights=None, verbose=True):
        """Return a WeightedDetectionModel."""
        model = WeightedDetectionModel(cfg, nc=self.data["nc"], verbose=verbose and RANK == -1)
        if weights:
            model.load(weights)
        return model

model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=10, trainer=WeightedTrainer)
حساب الأوزان من مجموعة البيانات

يمكنك حساب أوزان الفئات تلقائياً من توزيع تسميات مجموعة البيانات الخاصة بك. النهج الشائع هو ترجيح التردد العكسي:

import numpy as np

# class_counts: number of instances per class
class_counts = np.array([5000, 200, 3000])
# Inverse frequency: rarer classes get higher weight
class_weights = max(class_counts) / class_counts
# Result: [1.0, 25.0, 1.67]
تحميل نموذج بفئات مخصصة

يتم تخزين الفئات المخصصة مثل WeightedDetectionModel في نقطة التفتيش عن طريق الإشارة. عند تحديدها في نص برمجي للتدريب، فإنها تنتمي إلى وحدة __main__، لذا فإن تحميل best.pt من نص برمجي مختلف يرفع AttributeError: Can't get attribute 'WeightedDetectionModel' on <module '__main__'>.

قم بتعريف الفئات المخصصة في وحدة مخصصة بحيث تظل قابلة للاستيراد، وتأكد من أن تلك الوحدة موجودة على PYTHONPATH الخاص بك في وقت التحميل.

# weighted_model.py
from ultralytics.nn.tasks import DetectionModel

class WeightedDetectionModel(DetectionModel):
    """Detection model that uses class-weighted loss."""
# inference script
from weighted_model import WeightedDetectionModel  # noqa: F401 - must be importable at checkpoint load time

from ultralytics import YOLO

model = YOLO("runs/detect/train/weights/best.pt")
metrics = model.val()

حفظ أفضل نموذج حسب مقياس مخصص#

يحفظ المدرب best.pt بناءً على اللياقة (fitness)، والتي تكون افتراضياً للكشف mAP@0.5:0.95 (الأوزان [0.0, 0.0, 0.0, 1.0] لـ [P, R, mAP@0.5, mAP@0.5:0.95]). لاستخدام مقياس مختلف (مثل mAP@0.5 أو الاسترجاع)، تجاوز validate() وأرجع المقياس الذي اخترته كقيمة اللياقة. سيستخدم save_model() المضمن ذلك تلقائياً بعد ذلك:

from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer

class CustomSaveTrainer(DetectionTrainer):
    """Trainer that saves the best model based on mAP@0.5 instead of default fitness."""

    def validate(self):
        """Override fitness to use mAP@0.5 for best model selection."""
        metrics, fitness = super().validate()
        if metrics:
            fitness = metrics.get("metrics/mAP50(B)", fitness)
            if self.best_fitness is None or fitness > self.best_fitness:
                self.best_fitness = fitness
        return metrics, fitness

model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=20, trainer=CustomSaveTrainer)
المقاييس المتاحة

تشمل المقاييس الشائعة المتوفرة في self.metrics بعد التحقق من الصحة ما يلي:

المفتاحالوصف
metrics/precision(B)الدقة (Precision)
metrics/recall(B)الاسترجاع (Recall)
metrics/mAP50(B)mAP عند IoU 0.5
metrics/mAP50-95(B)mAP عند IoU 0.5:0.95

تجميد وإلغاء تجميد العمود الفقري (Backbone)#

غالباً ما تستفيد سير عمل التعلم النقلي (transfer learning) من تجميد الطبقة الأساسية المُدربة مسبقاً لأول N حقبة، مما يسمح لرأس الكشف بالتكيف قبل الضبط الدقيق (fine-tuning) للشبكة بأكملها. توفر Ultralytics معامل freeze لتجميد الطبقات في بداية التدريب، ويمكنك استخدام رد اتصال (callback) لإلغاء تجميدها بعد N حقبة:

from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import LOGGER

FREEZE_EPOCHS = 5

def unfreeze_backbone(trainer):
    """Callback to unfreeze all layers after FREEZE_EPOCHS."""
    if trainer.epoch == FREEZE_EPOCHS:
        LOGGER.info(f"Epoch {trainer.epoch}: Unfreezing all layers for fine-tuning")
        for name, param in trainer.model.named_parameters():
            if not param.requires_grad:
                param.requires_grad = True
                LOGGER.info(f"  Unfroze: {name}")
        trainer.freeze_layer_names = [".dfl"]

class FreezingTrainer(DetectionTrainer):
    """Trainer with backbone freezing for first N epochs."""

    def __init__(self, *args, **kwargs):
        """Initialize and register the unfreeze callback."""
        super().__init__(*args, **kwargs)
        self.add_callback("on_train_epoch_start", unfreeze_backbone)

model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=20, freeze=10, trainer=FreezingTrainer)

يقوم معامل freeze=10 بتجميد أول 10 طبقات (المؤشرات 0-9) في بداية التدريب، والتي تغطي معظم الطبقة الأساسية لـ YOLO26. تمتد الطبقة الأساسية من الطبقات 0-10، لذا يترك freeze=10 كتلة C2PSA النهائية (الطبقة 10) قابلة للتدريب؛ استخدم freeze=11 لتجميد الطبقة الأساسية بأكملها. يتم تشغيل رد الاتصال on_train_epoch_start في بداية كل حقبة ويقوم بإلغاء تجميد كافة المعلمات بمجرد اكتمال فترة التجميد.

اختيار ما تريد تجميده
  • freeze=10 يقوم بتجميد أول 10 طبقات، المؤشرات 0-9 (معظم الطبقة الأساسية لـ YOLO26؛ استخدم freeze=11 لتضمين كتلة C2PSA النهائية في الطبقة 10)
  • freeze=[0, 1, 2, 3] يقوم بتجميد طبقات معينة حسب الفهرس
  • القيم الأعلى لـ FREEZE_EPOCHS تمنح الرأس وقتاً أطول للتكيف قبل تغيير الطبقة الأساسية

معدلات التعلم لكل طبقة#

يمكن أن تستفيد الأجزاء المختلفة من الشبكة من معدلات تعلم مختلفة. الاستراتيجية الشائعة هي استخدام معدل تعلم أقل للطبقة الأساسية المُدربة مسبقاً للحفاظ على الميزات المُتعلمة، مع السماح لرأس الكشف بالتكيف بسرعة أكبر بمعدل أعلى:

import torch

from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer
from ultralytics.utils import LOGGER
from ultralytics.utils.torch_utils import unwrap_model

class PerLayerLRTrainer(DetectionTrainer):
    """Trainer with different learning rates for backbone and head."""

    def build_optimizer(self, model, name="auto", lr=0.001, momentum=0.9, decay=1e-5, iterations=1e5):
        """Build optimizer with separate learning rates for backbone and head."""
        backbone_params = []
        head_params = []

        unwrapped = unwrap_model(model)
        backbone_len = len(unwrapped.yaml["backbone"])  # YOLO26 backbone spans layers 0-10 (C2PSA at layer 10)

        for k, v in unwrapped.named_parameters():
            if not v.requires_grad:
                continue
            is_backbone = any(k.startswith(f"model.{i}.") for i in range(backbone_len))
            if is_backbone:
                backbone_params.append(v)
            else:
                head_params.append(v)

        backbone_lr = lr * 0.1

        optimizer = torch.optim.AdamW(
            [
                {"params": backbone_params, "lr": backbone_lr, "weight_decay": decay},
                {"params": head_params, "lr": lr, "weight_decay": decay},
            ],
        )

        LOGGER.info(
            f"PerLayerLR optimizer: backbone ({len(backbone_params)} params, lr={backbone_lr}) "
            f"| head ({len(head_params)} params, lr={lr})"
        )
        return optimizer

model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=20, trainer=PerLayerLRTrainer)

متغير RT-DETR#

بالنسبة لـ RT-DETR، النمط هو نفسه مع تحسينين. يتم قراءة طول الطبقة الأساسية من model.yaml["backbone"] بحيث يعمل نفس المدرب عبر متغيرات RT-DETR (متغيرات RT-DETR-L، RT-DETR-X، وطبقات ResNet-50/101 الأساسية) دون ترميز عدد الطبقات بشكل ثابت. يتم أيضاً تقسيم المعلمات إلى مجموعات وزن، وBatchNorm، وانحياز (bias) داخل كل قسم بحيث يتم استبعاد اضمحلال الوزن من معلمات BatchNorm والانحيازات، مما يطابق سياسة المدرب الافتراضي. هذا مفيد بشكل خاص للضبط الدقيق لـ RT-DETR، حيث يتم تهيئة رأس فك التشفير عادةً عشوائياً بينما تحمل الطبقة الأساسية ميزات مُدربة مسبقاً تستفيد من معدل تعلم أقل:

import torch
from torch import nn

from ultralytics import RTDETR
from ultralytics.models.rtdetr.train import RTDETRTrainer
from ultralytics.utils import LOGGER, colorstr
from ultralytics.utils.torch_utils import unwrap_model

class RTDETRBackboneLRTrainer(RTDETRTrainer):
    """RT-DETR trainer with a lower learning rate for backbone parameters."""

    backbone_lr_ratio = 0.1  # backbone learning rate as a fraction of head learning rate

    def build_optimizer(self, model, name="auto", lr=0.001, momentum=0.9, decay=1e-5, iterations=1e5):
        """Build an AdamW optimizer with six param groups: head and backbone x {weight, bn, bias}."""
        # Resolve optimizer name; "auto" maps to AdamW with RT-DETR-style defaults
        canonical = {"Adam", "Adamax", "AdamW", "NAdam", "RAdam", "auto"}
        name = {x.lower(): x for x in canonical}.get(name.lower(), name)
        if name == "auto":
            name, lr, momentum = "AdamW", 1e-4, 0.9
        self.args.warmup_bias_lr = 0.0  # RT-DETR warms biases from 0, unlike YOLO's 0.1
        if name not in {"Adam", "Adamax", "AdamW", "NAdam", "RAdam"}:
            raise NotImplementedError(f"This trainer only supports AdamW-family optimizers; got {name}")

        # Identify backbone parameters from model.yaml and route each param into a (section, kind) group
        unwrapped = unwrap_model(model)
        backbone_len = len(unwrapped.yaml["backbone"])
        norm_types = tuple(v for k, v in nn.__dict__.items() if "Norm" in k)
        groups = {f"{s}_{k}": [] for s in ("head", "backbone") for k in ("weight", "bn", "bias")}

        for module_name, module in unwrapped.named_modules():
            for param_name, param in module.named_parameters(recurse=False):
                if not param.requires_grad:
                    continue
                fullname = f"{module_name}.{param_name}" if module_name else param_name
                parts = fullname.split(".")
                section = (
                    "backbone"
                    if len(parts) > 1 and parts[0] == "model" and parts[1].isdigit() and int(parts[1]) < backbone_len
                    else "head"
                )
                if "bias" in param_name:
                    kind = "bias"
                elif isinstance(module, norm_types) or "logit_scale" in fullname:
                    kind = "bn"
                else:
                    kind = "weight"
                groups[f"{section}_{kind}"].append(param)

        # Build the optimizer with per-group lr and weight decay; backbone groups use lr * backbone_lr_ratio
        backbone_lr = lr * self.backbone_lr_ratio
        param_groups = [
            {"params": groups["head_weight"], "lr": lr, "weight_decay": decay, "param_group": "weight"},
            {"params": groups["head_bn"], "lr": lr, "weight_decay": 0.0, "param_group": "bn"},
            {"params": groups["head_bias"], "lr": lr, "weight_decay": 0.0, "param_group": "bias"},
            {"params": groups["backbone_weight"], "lr": backbone_lr, "weight_decay": decay, "param_group": "weight"},
            {"params": groups["backbone_bn"], "lr": backbone_lr, "weight_decay": 0.0, "param_group": "bn"},
            {"params": groups["backbone_bias"], "lr": backbone_lr, "weight_decay": 0.0, "param_group": "bias"},
        ]
        param_groups = [pg for pg in param_groups if pg["params"]]  # drop empty groups
        optimizer = getattr(torch.optim, name)(param_groups, betas=(momentum, 0.999))

        LOGGER.info(
            f"{colorstr('optimizer:')} {name}(lr={lr}, backbone_lr={backbone_lr}) with parameter groups\n"
            f"  Head:     {len(groups['head_bn'])} bn, {len(groups['head_weight'])} weight(decay={decay}), "
            f"{len(groups['head_bias'])} bias (lr={lr})\n"
            f"  Backbone: {len(groups['backbone_bn'])} bn, {len(groups['backbone_weight'])} weight(decay={decay}), "
            f"{len(groups['backbone_bias'])} bias (lr={backbone_lr})"
        )
        return optimizer

model = RTDETR("rtdetr-l.pt")
model.train(data="coco8.yaml", epochs=20, trainer=RTDETRBackboneLRTrainer)
اختيار `backbone_lr_ratio`

النقطة البديّة الشائعة هي backbone_lr_ratio = 0.1، مطابقة لإعداد RT-DETR الأصلي مع طبقتها الأساسية HGNetV2. تقترح الأدبيات تحجيم النسبة عكسياً مع حجم الطبقة الأساسية ومقياس بيانات التدريب المسبق: الطبقات الأساسية الكبيرة المُدربة مسبقاً على مجموعات بيانات كبيرة جداً (على سبيل المثال ViT-L/H المُدرب بـ DINO، أو CLIP، أو MAE على مئات الملايين من الصور) تستخدم عادةً نسباً أصغر مثل 0.01 أو أقل للحفاظ على الميزات المُتعلمة جيداً، بينما تتسامح الطبقات الأساسية الأصغر ذات التدريب المسبق الأخف مع نسب أكبر مثل 0.5 أو أعلى.

جدول معدل التعلم

لا يزال جدولة معدل التعلم المضمنة (cosine أو linear) تطبق فوق معدلات التعلم الأساسية لكل مجموعة. سيتبع كل من معدلي تعلم الطبقة الأساسية والرأس نفس جدول الاضمحلال، مع الحفاظ على النسبة بينهما طوال فترة التدريب.

الجمع بين التقنيات

يمكن الجمع بين هذه التخصيصات في فئة مـدرب واحدة عن طريق تجاوز طرق متعددة وإضافة استدعاءات حسب الحاجة.

مزامنة BatchNorm للتدريب متعدد وحدات معالجة الرسومات#

عند التدريب على وحدات معالجة رسوميات متعددة باستخدام DistributedDataParallel، تحسب طبقات BatchNorm2d الافتراضية الإحصاءات بشكل مستقل على كل وحدة معالجة رسوميات. للضبط الدقيق لـ RT-DETR والوصفات الأخرى التي تستخدم أحجام دفعات صغيرة لكل وحدة معالجة رسوميات، يمكن أن تكون إحصاءات الدفعات لكل وحدة معالجة رسوميات صاخبة. تقوم طريقة SyncBatchNorm في PyTorch بمزامنة المتوسط والتباين عبر جميع الرتب لإحصاء دفعة عالمية واحدة، مما يحسن التقارب غالباً على حساب تكلفة اتصال إضافية صغيرة بين وحدات معالجة الرسوميات.

يجب أن يحدث التحويل بعد أن يكون النموذج على وحدة معالجة الرسوميات ولكن قبل أن يغلفه DDP. الخطاف الأنشط لهذا هو set_model_attributes()، والذي يستدعيه BaseTrainer في تلك النافذة بالضبط:

from torch import nn

from ultralytics import RTDETR
from ultralytics.models.rtdetr.train import RTDETRTrainer

class SyncBNTrainer(RTDETRTrainer):
    """RT-DETR trainer that converts BatchNorm to SyncBatchNorm for multi-GPU training."""

    def set_model_attributes(self):
        """Run the parent setup, then convert BN to SyncBatchNorm when training on multiple GPUs."""
        super().set_model_attributes()
        if self.world_size > 1:
            self.model = nn.SyncBatchNorm.convert_sync_batchnorm(self.model)

model = RTDETR("rtdetr-l.pt")
model.train(data="coco8.yaml", epochs=20, device=[0, 1], trainer=SyncBNTrainer)

يضمن حارس world_size > 1 أن المدرب آمن للاستخدام في عمليات وحدة معالجة الرسوميات الفردية أيضاً؛ على وحدة معالجة رسوميات فردية، يتم تخطي التحويل ويستمر التدريب باستخدام BatchNorm2d العادي. يعمل نفس النمط مع YOLO عن طريق تبديل الفئة الأصلية إلى DetectionTrainer.

متى تستخدم SyncBatchNorm
السيناريوالتوصية
تدريب متعدد وحدات معالجة الرسومات، دفعة صغيرة لكل وحدة (≤ 16)تمكين
تدريب متعدد وحدات معالجة الرسومات، دفعة كبيرة لكل وحدة (≥ 32)اختياري؛ فائدة طفيفة
تدريب وحدة معالجة رسومات واحدةغير قابل للتطبيق (تم التخطي)

قص التدرج القابل للتهيئة#

يقوم المدرب الافتراضي بقص التدرجات إلى max_norm=10.0 في optimizer_step()، وهي قيمة فضفاضة تم ضبطها لنماذج YOLO حيث نادراً ما تتجاوز التدرجات ذلك. تستخدم كواشف عائلة DETR (RT-DETR، وDEIM، وDINO) عادةً قيمًا أكثر إحكامًا بكثير مثل 0.1 لتحقيق الاستقرار في طبقات الانتباه المتبادل لفك التشفير، حيث يمكن أن ترتفع مقادير التدرج. لتجاوز قيمة القص، أنشئ فئة فرعية من المدرب وتجاوز optimizer_step():

import torch

from ultralytics import RTDETR
from ultralytics.models.rtdetr.train import RTDETRTrainer

class CustomClipTrainer(RTDETRTrainer):
    """RT-DETR trainer with configurable gradient clipping."""

    clip_grad_norm = 0.1  # max gradient norm; set to 0 to disable clipping

    def optimizer_step(self):
        """Run an optimizer step with a configurable gradient-norm clip."""
        self.scaler.unscale_(self.optimizer)
        if self.clip_grad_norm > 0:
            torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=self.clip_grad_norm)
        self.scaler.step(self.optimizer)
        self.scaler.update()
        self.optimizer.zero_grad()
        if self.ema:
            self.ema.update(self.model)

model = RTDETR("rtdetr-l.pt")
model.train(data="coco8.yaml", epochs=20, trainer=CustomClipTrainer)

عمل نفس المدرب لـ YOLO عن طريق تبديل الفئة الأصلية إلى DetectionTrainer (from ultralytics.models.yolo.detect import DetectionTrainer) وتحميل نقطة تفتيش YOLO بـ YOLO("yolo26n.pt"). جسم optimizer_step غير متغيّر.

قيم `clip_grad_norm` النموذجية
عائلة الهندسةالنموذجي max_norm
عائلة RT-DETR / DEIM / DETR0.1
YOLO (افتراضي Ultralytics)10.0
تعطيل القص0

الأسئلة الشائعة#

  • قم بتمرير فئة المدرب المخصصة الخاصة بك (وليس مثيلًا) إلى معامل trainer في model.train():

    from ultralytics import YOLO
    
    model = YOLO("yolo26n.pt")
    model.train(data="coco8.yaml", trainer=MyCustomTrainer)

    تتولى فئة YOLO إنشاء مثيل المدرب داخليًا. راجع صفحة التخصيص المتقدم لمزيد من التفاصيل حول هندسة المدرب.

  • طرق أساسية متاحة للتخصيص:

    الطريقة (Method)الغرض
    validate()تشغيل التحقق وإرجاع المقاييس
    build_optimizer()بناء المُحسِّن
    save_model()حفظ نقاط فحص التدريب
    get_model()إرجاع مثيل النموذج
    get_validator()إرجاع مثيل أداة التحقق
    get_dataloader()بناء مُحمل البيانات
    preprocess_batch()المعالجة الأولية لمجموعة الإدخال
    label_loss_items()تنسيق عناصر الخسارة للتسجيل

    للحصول على مرجع API الكامل، راجع توثيق BaseTrainer.

  • نعم، بالنسبة للتخصيصات الأبسط، غالبًا ما تكون ردود الاتصال (callbacks) كافية. تشمل أحداث رد الاتصال المتوفرة on_train_start، وon_train_epoch_start، وon_train_epoch_end، وon_fit_epoch_end، وon_model_save. تتيح لك هذه الارتباط بحلقة التدريب دون إنشاء فئات فرعية. يوضح مثال تجميد الطبقة الأساسية أعلاه هذا النهج.

  • إذا كان تغييرك أبسط (مثل ضبط مكاسب الخسارة)، يمكنك تعديل المعلمات الفائقة (hyperparameters) مباشرة:

    model.train(data="coco8.yaml", box=10.0, cls=1.5, dfl=2.0)

    التغييرات الهيكلية في الخسارة (مثل إضافة أوزان الفئات)، تحتاج إلى إنشاء فئة فرعية للخسارة والنموذج كما هو موضح في قسم أوزان الفئات.

التعليقات