Chuyển đến nội dung

Tùy chỉnh huấn luyện viên

Cái Ultralytics Quy trình đào tạo được xây dựng dựa trên... BaseTrainer và các huấn luyện viên chuyên trách nhiệm vụ như DetectionTrainerCác lớp này xử lý vòng lặp huấn luyện, xác thực, điểm kiểm tra và ghi nhật ký một cách mặc định. Khi bạn cần kiểm soát nhiều hơn — theo dõi các chỉ số tùy chỉnh, điều chỉnh trọng số mất mát hoặc triển khai lịch trình tốc độ học — bạn có thể tạo lớp con của lớp huấn luyện và ghi đè các phương thức cụ thể.

Hướng dẫn này sẽ trình bày chi tiết năm tùy chỉnh phổ biến:

  1. Ghi nhật ký các chỉ số tùy chỉnh (điểm F1) vào cuối mỗi kỷ nguyên.
  2. Thêm trọng số cho các lớp để xử lý tình trạng mất cân bằng lớp.
  3. Lưu lại mô hình tốt nhất dựa trên một chỉ số khác.
  4. Đóng băng chuỗi xương sống trong N kỷ nguyên đầu tiên, sau đó bỏ đóng băng.
  5. Xác định tốc độ học cho từng lớp

Điều Kiện Tiên Quyết

Trước khi đọc hướng dẫn này, hãy đảm bảo bạn đã nắm vững những kiến ​​thức cơ bản về đào tạo YOLO mô hìnhTùy chỉnh nâng cao trang này bao gồm BaseTrainer ngành kiến ​​​​trúc.

Cách thức hoạt động của huấn luyện viên cá nhân

Hàm YOLO Lớp mô hình chấp nhận một trainer tham số trong train() Phương pháp này cho phép bạn truyền vào lớp huấn luyện viên của riêng mình, lớp này mở rộng hành vi mặc định:

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)

Công cụ huấn luyện tùy chỉnh của bạn kế thừa tất cả chức năng từ công cụ gốc. DetectionTrainerVì vậy, bạn chỉ cần ghi đè lên các phương thức cụ thể mà bạn muốn tùy chỉnh.

Ghi nhật ký các Số liệu Tùy chỉnh

Hàm xác thực bước tính toán độ chính xác, độ chính xác, và mAPNếu bạn cần thêm các số liệu khác như số liệu theo từng lớp. Điểm F1ghi đè 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)

Nhật ký này ghi lại điểm F1 trung bình trên tất cả các lớp và phân tích chi tiết theo từng lớp sau mỗi lần chạy kiểm định.

Các chỉ số khả dụng

Trình xác thực cung cấp quyền truy cập vào nhiều chỉ số thông qua self.validator.metrics.box:

Thuộc tínhMô tả
f1Điểm F1 cho mỗi hạng
pĐộ chính xác trên mỗi lớp
rNhớ lại theo từng lớp
ap50AP Tại IoU 0,5 điểm mỗi lớp
apAP Tại IoU 0,5:0,95 mỗi lớp
mp, mrĐộ chính xác và độ thu hồi trung bình
map50, mapNghĩa là AP số liệu

Thêm trọng số lớp

Nếu tập dữ liệu của bạn có các lớp không cân bằng (ví dụ: một lỗi hiếm gặp trong quá trình kiểm tra sản xuất), bạn có thể tăng trọng số cho các lớp ít được đại diện trong hàm mất mát . Điều này khiến mô hình phạt nặng hơn đối với các lỗi phân loại sai trên các lớp hiếm gặp.

Để tùy chỉnh hàm mất mát, hãy tạo lớp con cho các lớp mất mát, mô hình và trình huấn luyện:

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)

Tính toán trọng số từ tập dữ liệu

Bạn có thể tự động tính toán trọng số lớp từ phân bố nhãn của tập dữ liệu. Một phương pháp phổ biến là trọng số tần suất nghịch đảo:

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]

Lưu mô hình tốt nhất theo số liệu tùy chỉnh

Huấn luyện viên tiết kiệm được best.pt dựa trên thể lực, mặc định là 0.9 × mAP@0.5:0.95 + 0.1 × mAP@0.5Để sử dụng một thước đo khác (như...), mAP@0.5 hoặc thu hồi), ghi đè validate() và trả về chỉ số bạn đã chọn làm giá trị đánh giá độ phù hợp. Chức năng tích hợp sẵn save_model() Sau đó, nó sẽ tự động sử dụng:

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)

Các chỉ số khả dụng

Các chỉ số phổ biến có sẵn trong self.metrics Sau khi xác thực bao gồm:

KhóaMô tả
metrics/precision(B)Độ chính xác
metrics/recall(B)Độ nhớ lại
metrics/mAP50(B)mAP Tại IoU 0,5
metrics/mAP50-95(B)mAP Tại IoU 0,5:0,95

Quá trình đóng băng và rã đông xương sống

Học chuyển giao Các quy trình làm việc thường được hưởng lợi từ việc đóng băng mạng xương sống được huấn luyện trước trong N epoch đầu tiên, cho phép đầu phát hiện thích nghi trước khi tinh chỉnh toàn bộ mạng lưới. Ultralytics cung cấp một freeze tham số để đóng băng các lớp khi bắt đầu huấn luyện, và bạn có thể sử dụng một callback để rã đông chúng sau N kỷ nguyê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)

Hàm freeze=10 Tham số này đóng băng 10 lớp đầu tiên (phần xương sống) khi bắt đầu huấn luyện. on_train_epoch_start Hàm callback được kích hoạt vào đầu mỗi kỷ nguyên và sẽ bỏ đóng băng tất cả các tham số sau khi giai đoạn đóng băng kết thúc.

Lựa chọn thực phẩm để đông lạnh

  • freeze=10 Đóng băng 10 lớp đầu tiên (thường là xương sống trong YOLO kiến trúc)
  • freeze=[0, 1, 2, 3] Đóng băng các lớp cụ thể theo chỉ mục
  • Cao hơn FREEZE_EPOCHS các giá trị này giúp đầu có thêm thời gian thích nghi trước khi cột sống thay đổi.

Tốc độ học trên mỗi lớp

Các phần khác nhau của mạng có thể hưởng lợi từ các tốc độ học khác nhau. Một chiến lược phổ biến là sử dụng tốc độ học thấp hơn cho phần xương sống được huấn luyện trước để bảo toàn các đặc trưng đã học, đồng thời cho phép phần đầu phát hiện thích nghi nhanh hơn với tốc độ cao hơn:

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)

Bộ lập lịch tốc độ học

Bộ lập lịch tốc độ học tích hợp (cosine hoặc linear(Điều này vẫn được áp dụng trên tỷ lệ học tập cơ bản của mỗi nhóm. Cả tỷ lệ học tập của hệ thống xương sống và hệ thống đầu sẽ tuân theo cùng một lịch trình suy giảm, duy trì tỷ lệ giữa chúng trong suốt quá trình huấn luyện.)

Kết hợp các kỹ thuật

Các tùy chỉnh này có thể được kết hợp thành một lớp huấn luyện viên duy nhất bằng cách ghi đè nhiều phương thức và thêm các hàm gọi lại khi cần thiết.

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

Làm thế nào để tôi chuyển một huấn luyện viên tùy chỉnh cho... YOLO ?

Truyền lớp huấn luyện viên tùy chỉnh của bạn (không phải một thể hiện) cho trainer trong model.train():

from ultralytics import YOLO

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

Hàm YOLO Lớp này tự động xử lý việc khởi tạo đối tượng huấn luyện. Xem thêm Tùy chỉnh nâng cao Xem thêm chi tiết về kiến ​​trúc của hệ thống huấn luyện.

Tôi có thể ghi đè những phương thức nào của BaseTrainer?

Các phương pháp chính có sẵn để tùy chỉnh:

Phương thứcMục đích
validate()Chạy quá trình xác thực và trả về các chỉ số.
build_optimizer()Xây dựng trình tối ưu hóa
save_model()Lưu lại các điểm kiểm tra huấn luyện
get_model()Trả về thể hiện của mô hình
get_validator()Trả về thể hiện của trình xác thực
get_dataloader()Xây dựng trình tải dữ liệu
preprocess_batch()Xử lý trước lô dữ liệu
label_loss_items()Định dạng các mục bị mất để ghi nhật ký

Để xem toàn bộ tài liệu tham khảo API, vui lòng xem [liên kết]. BaseTrainer tài liệu.

Tôi có thể sử dụng hàm gọi lại (callback) thay vì kế thừa lớp huấn luyện viên được không?

Vâng, để tùy chỉnh đơn giản hơn, gọi lại thường là đủ. Các sự kiện gọi lại có sẵn bao gồm on_train_start, on_train_epoch_start, on_train_epoch_end, on_fit_epoch_end, và on_model_saveChúng cho phép bạn tham gia vào vòng lặp huấn luyện mà không cần tạo lớp con. Ví dụ về việc đóng băng Backbone ở trên minh họa cách tiếp cận này.

Làm thế nào để tùy chỉnh hàm mất mát mà không cần tạo lớp con cho mô hình?

Nếu thay đổi của bạn đơn giản hơn (chẳng hạn như điều chỉnh lãi lỗ), bạn có thể sửa đổi trực tiếp các siêu tham số :

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

Để thực hiện các thay đổi cấu trúc đối với hàm mất mát (chẳng hạn như thêm trọng số lớp), bạn cần tạo lớp con cho hàm mất mát và mô hình như được trình bày trong phần trọng số lớp .



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

Bình luận