Eğiticiyi Özelleştirme
Ultralytics eğitim hattı şunun etrafında inşa edilmiştir: BaseTrainer ve şunun gibi göreve özel eğiticiler: DetectionTrainer. Bu sınıflar, eğitim döngüsünü, doğrulamayı, kontrol noktası oluşturmayı ve günlük kaydını hazır olarak yönetir. Daha fazla kontrole ihtiyacınız olduğunda — özel metrikleri track etme, kayıp ağırlığını ayarlama veya öğrenme oranı çizelgeleri uygulama — eğiticiyi alt sınıflandırabilir ve belirli yöntemleri geçersiz kılabilirsiniz.
Bu kılavuz, beş yaygın özelleştirmeyi ele almaktadır:
- Özel metrikleri (F1 skoru) kaydetme her epoch sonunda
- Sınıf ağırlıkları ekleme sınıf dengesizliğini ele almak için
- En iyi modeli kaydetme farklı bir metriğe göre
- Backbone'u dondurma ilk N epoch için, sonra çözme
- Katman başına öğrenme oranlarını belirtme
Ön koşullar
Bu kılavuzu okumadan önce, şunların temellerine aşina olduğunuzdan emin olun: YOLO modellerini eğitme ve Gelişmiş Özelleştirme sayfası, şunları kapsar: BaseTrainer mimarisi.
Özel Eğiticiler Nasıl Çalışır?
YOLO model sınıfı bir trainer parametresi içinde train() metodunu kabul eder. Bu, varsayılan davranışı genişleten kendi eğitici sınıfınızı geçirmenize olanak tanır:
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)
Özel eğiticiniz tüm işlevselliği şuradan devralır: DetectionTrainer, bu nedenle yalnızca özelleştirmek istediğiniz belirli metotları geçersiz kılmanız gerekir.
Özel Metrikleri Günlüğe Kaydetme
doğrulama adımı hesaplar kesinlik, duyarlılık (recall)ve mAP. Sınıf başına gibi ek metrikler gerekiyorsa F1 skoru, geçersiz kılın 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)
Bu, her doğrulama çalıştırmasından sonra tüm sınıflar genelindeki ortalama F1 skorunu ve sınıf başına dökümü kaydeder.
Mevcut Metrikler
Doğrulayıcı, birçok metriğe şunlar aracılığıyla erişim sağlar: self.validator.metrics.box:
| Özellik | Açıklama |
|---|---|
f1 | Sınıf başına F1 skoru |
p | Sınıf başına kesinlik (Precision) |
r | Sınıf başına duyarlılık (Recall) |
ap50 | Sınıf başına IoU 0.5'teki AP |
ap | Sınıf başına IoU 0.5:0.95'teki AP |
mp, mr | Ortalama kesinlik ve duyarlılık |
map50, map | Ortalama AP metrikleri |
Sınıf Ağırlıkları Ekleme
Veri setiniz dengesiz sınıflara sahipse (örneğin, üretim denetiminde nadir bir kusur), az temsil edilen sınıfları kayıp fonksiyonunda ağırlıklandırabilirsiniz. Bu, modelin nadir sınıflardaki yanlış sınıflandırmaları daha ağır bir şekilde cezalandırmasını sağlar.
Kaybı özelleştirmek için kayıp sınıflarını, modeli ve eğiticiyi alt sınıflara ayırı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)
Veri Setinden Ağırlık Hesaplama
Sınıf ağırlıklarını veri setinizin etiket dağılımından otomatik olarak hesaplayabilirsiniz. Yaygın bir yaklaşım ters frekans ağırlıklandırmasıdır:
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]
Özel Metriğe Göre En İyi Modeli Kaydetme
Eğitici kaydeder best.pt varsayılan olarak uygunluğa göre 0.9 × mAP@0.5:0.95 + 0.1 × mAP@0.5. Farklı bir metrik kullanmak için (örneğin mAP@0.5 veya duyarlılık), geçersiz kılın validate() ve seçtiğiniz metriği uygunluk değeri olarak döndürün. Yerleşik save_model() daha sonra otomatik olarak kullanacaktır:
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)
Mevcut Metrikler
Şurada bulunan yaygın metrikler: self.metrics doğrulama sonrası şunları içerir:
| Anahtar | Açıklama |
|---|---|
metrics/precision(B) | Kesinlik |
metrics/recall(B) | Duyarlılık (Recall) |
metrics/mAP50(B) | 0.5 IoU'da mAP |
metrics/mAP50-95(B) | 0.5:0.95 IoU'da mAP |
Backbone'u Dondurma ve Çözme
Aktarım öğrenimi İş akışları, genellikle önceden eğitilmiş backbone'u ilk N epoch boyunca dondurmaktan faydalanır; bu, detection head'in uyum sağlamasına olanak tanır, ardından ince ayar tüm ağı. Ultralytics bir freeze parametre sağlar ve bir geri çağırma (callback) N epoch sonra dondurmayı çözmek için:
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 parametre, eğitimin başlangıcında ilk 10 katmanı (backbone'u) dondurur. Bu on_train_epoch_start callback, her epoch'un başlangıcında tetiklenir ve dondurma süresi tamamlandığında tüm parametrelerin dondurmasını çözer.
Neyi Donduracağınızı Seçmek
freeze=10ilk 10 katmanı (genellikle YOLO mimarilerindeki backbone'u) dondururfreeze=[0, 1, 2, 3]belirli katmanları indekse göre dondurur- Daha Yüksek
FREEZE_EPOCHSdeğerleri, backbone değişmeden önce head'e uyum sağlaması için daha fazla zaman tanır
Katman Başına Öğrenme Oranları
Ağın farklı bölümleri, farklı öğrenme oranlarından faydalanabilir. Yaygın bir strateji, öğrenilmiş özellikleri korumak için önceden eğitilmiş backbone için daha düşük bir öğrenme oranı kullanırken, detection head'in daha yüksek bir oranla daha hızlı uyum sağlamasına olanak tanımaktır:
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)
Öğrenme Oranı Zamanlayıcısı
Dahili öğrenme oranı zamanlayıcısı (cosine veya linear) grup başına temel öğrenme oranlarının üzerinde hala uygulanır. Hem backbone hem de head öğrenme oranları aynı düşüş programını takip edecek ve eğitim boyunca aralarındaki oranı koruyacaktır.
Teknikleri Birleştirmek
Bu özelleştirmeler, birden fazla yöntemi geçersiz kılarak ve gerektiğinde callback'ler ekleyerek tek bir eğitici sınıfında birleştirilebilir.
SSS
YOLO'ya özel bir eğiticiyi nasıl iletirim?
Özel eğitici sınıfınızı (bir örnek değil) şuraya iletin: trainer parametresi içinde model.train():
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", trainer=MyCustomTrainer)
YOLO sınıfı, eğitici örneklendirmesini dahili olarak yönetir. Daha fazla bilgi için Gelişmiş Özelleştirme eğitici mimarisi hakkında daha fazla ayrıntı için sayfasına bakın.
Hangi BaseTrainer yöntemlerini geçersiz kılabilirim?
Özelleştirme için mevcut temel yöntemler:
| Metot | Amaç |
|---|---|
validate() | Doğrulamayı çalıştırın ve metrikleri döndürün |
build_optimizer() | Optimizer'ı oluştur |
save_model() | Eğitim kontrol noktalarını kaydet |
get_model() | Model örneğini döndür |
get_validator() | Doğrulayıcı örneğini döndür |
get_dataloader() | Dataloader'ı oluştur |
preprocess_batch() | Giriş batch'ini ön işle |
label_loss_items() | Loglama için kayıp öğelerini biçimlendir |
Tam API referansı için şuraya bakın: BaseTrainer belgeleme.
Eğiticiyi alt sınıflandırmak yerine geri çağırmaları (callbacks) kullanabilir miyim?
Evet, daha basit özelleştirmeler için, callback'ler genellikle yeterlidir. Mevcut callback olayları şunları içerir: on_train_start, on_train_epoch_start, on_train_epoch_end, on_fit_epoch_endve on_model_save. Bunlar, alt sınıflandırma yapmadan eğitim döngüsüne müdahale etmenizi sağlar. Yukarıdaki backbone dondurma örneği bu yaklaşımı göstermektedir.
Modeli alt sınıflandırmadan kayıp fonksiyonunu nasıl özelleştiririm?
Değişikliğiniz daha basitse (kayıp kazançlarını ayarlamak gibi), hiperparametreleri doğrudan değiştirebilirsiniz:
model.train(data="coco8.yaml", box=10.0, cls=1.5, dfl=2.0)
Kayba yapısal değişiklikler için (sınıf ağırlıkları eklemek gibi), sınıf ağırlıkları bölümünde gösterildiği gibi kaybı ve modeli alt sınıflamanız gerekir.