Перейти к содержанию

Настройка тренера

Конвейер Ultralytics построен на основе BaseTrainer и тренеры по конкретным задачам, такие как DetectionTrainer. Эти классы обрабатывают цикл обучения, валидацию, контрольную точку и выход из системы. Если вам требуется более точный контроль — отслеживание пользовательских метрик, настройка веса потерь или реализация графиков скорости обучения — вы можете создать подкласс тренера и переопределить определенные методы.

В этом руководстве описаны пять распространенных настроек:

  1. Регистрация пользовательских метрик (оценка F1) в конце каждого эпоха
  2. Добавление весов классов для устранения дисбаланса классов
  3. Сохранение лучшей модели на основе другого показателя
  4. Замораживание магистрали на первые N эпох, затем размораживание
  5. Указание скорости обучения для каждого слоя

Предварительные требования

Перед тем как читать это руководство, убедитесь, что вы знакомы с основами обучение 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."""

    pass  # Add your customizations here


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

Ваш пользовательский тренер наследует все функции от DetectionTrainer, поэтому вам нужно переопределить только те методы, которые вы хотите настроить.

Регистрация пользовательских метрик

Параметр валидация шаг вычисляет точность, полнотой (recall)и mAP. Если вам нужны дополнительные метрики, такие как по классам F1 score, переопределить 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 по всем классам и разбивку по классам после каждого цикла валидации.

Доступные метрики

Валидатор предоставляет доступ ко многим метрикам через self.validator.metrics.box:

АтрибутОписание
f1Оценка F1 для каждого класса
pТочность по классу
rВозврат по классу
ap50AP IoU ,5 на класс
apAP IoU ,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]

Сохранение лучшей модели по пользовательскому показателю

Тренер сохраняет best.pt на основе физической подготовки, по умолчанию 0.9 × mAP@0.5:0.95 + 0.1 × mAP@0.5. Чтобы использовать другую метрику (например, 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)Точность
metrics/recall(B)Полнота (Recall)
metrics/mAP50(B)mAP IoU ,5
metrics/mAP50-95(B)mAP IoU ,5:0,95

Замораживание и размораживание магистрали

Перенос обучения рабочие процессы часто выигрывают от замораживания предварительно обученной основы на первые N эпох, что позволяет детектирующей головке адаптироваться перед точная настройка всю сеть. 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 слоев (основу) в начале обучения. Параметр on_train_epoch_start обратный вызов срабатывает в начале каждой эпохи и размораживает все параметры по окончании периода замораживания.

Выбор того, что заморозить

  • freeze=10 замораживает первые 10 слоев (обычно это основная часть в YOLO )
  • 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 = []

        for k, v in unwrap_model(model).named_parameters():
            if not v.requires_grad:
                continue
            is_backbone = any(k.startswith(f"model.{i}.") for i in range(10))
            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)

Планировщик скорости обучения

Встроенный планировщик скорости обучения (cosine или linear) по-прежнему применяется в дополнение к базовым скоростям обучения для каждой группы. Скорости обучения основной части и головы будут следовать одному и тому же графику снижения, сохраняя соотношение между ними на протяжении всего обучения.

Комбинирование техник

Эти настройки можно объединить в один класс тренера, переопределив несколько методов и добавив обратные вызовы по мере необходимости.

Часто задаваемые вопросы

Как передать пользовательский тренер в YOLO?

Передайте свой пользовательский класс тренера (не экземпляр) в trainer параметре в model.train():

from ultralytics import YOLO

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

Параметр YOLO класс обрабатывает создание экземпляров тренера внутренне. См. Расширенная настройка страницу для получения более подробной информации об архитектуре тренера.

Какие методы BaseTrainer я могу переопределить?

Основные методы, доступные для настройки:

МетодЦель
validate()Запустить валидацию и вернуть метрики
build_optimizer()Создать оптимизатор
save_model()Сохранить контрольные точки обучения
get_model()Возвратить экземпляр модели
get_validator()Возвращает экземпляр валидатора
get_dataloader()Создать загрузчик данных
preprocess_batch()Предварительная обработка входного пакета
label_loss_items()Форматирование элементов потери для регистрации

Полное описание API см. в BaseTrainer документацию.

Можно ли использовать обратные вызовы вместо создания подкласса тренера?

Да, для более простых настроек, обратные вызовы часто бывают достаточными. Доступные события обратного вызова включают on_train_start, on_train_epoch_start, on_train_epoch_end, on_fit_epoch_endи on_model_save. Они позволяют подключиться к циклу обучения без создания подклассов. Пример замораживания основного алгоритма, приведенный выше, демонстрирует этот подход.

Как настроить функцию потерь без создания подкласса модели?

Если ваше изменение более простое (например, корректировка потерь и прибыли), вы можете изменить гиперпараметры напрямую:

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

Для структурных изменений потери (таких как добавление весов классов) необходимо создать подкласс потери и модели, как показано в разделе «Веса классов».



📅 Создано 6 дней назад ✏️ Обновлено 0 дней назад
raimbekovmonuralpszr

Комментарии