Özelleştirme Eğitmeni
Ultralytics süreci şu unsurlar etrafında şekillenmiştir: BaseTrainer ve göreve özel eğitmenler gibi DetectionTrainer. Bu sınıflar, eğitim döngüsünü, doğrulamayı, kontrol noktasını ve oturum kapatmayı hazır olarak ele alır. Daha fazla kontrol gerektiğinde (özel metrikleri izleme, kayıp ağırlığını ayarlama veya öğrenme oranı programlarını uygulama gibi), eğiticiyi alt sınıfa alabilir ve belirli yöntemleri geçersiz kılabilirsiniz.
Bu kılavuz, beş yaygın özelleştirme işlemini adım adım açıklamaktadır:
- Her dönemin sonunda özel metrikleri (F1 puanı) kaydetme
- Sınıf dengesizliğini gidermek için sınıf ağırlıkları ekleme
- Farklı bir metrik temelinde en iyi modeli kaydetme
- İlk N dönem boyunca backbone dondurmak, ardından dondurmayı kaldırmak
- Katman başına öğrenme oranlarını belirleme
Ön koşullar
Bu kılavuzu okumadan önce, temel bilgilere aşina olduğunuzdan emin olun. YOLO eğitimi ve Gelişmiş Özelleştirme sayfası, aşağıdakileri kapsar: BaseTrainer mimarlık.
Özel Antrenörler Nasıl Çalışır?
YOLO model sınıfı bir trainer parametresi içinde train() yöntemi. Bu, varsayılan davranışı genişleten kendi eğitmen sınıfınızı aktarmanıza 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ğitmeniniz tüm işlevleri miras alır. DetectionTrainer, bu nedenle yalnızca özelleştirmek istediğiniz belirli yöntemleri geçersiz kılmanız yeterlidir.
Ö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 metriklere ihtiyacınız varsa F1 skoru, geçersiz kıl 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ışmasından sonra tüm sınıflar için ortalama F1 puanını ve sınıf başına dağılımı kaydeder.
Mevcut Metrikler
Doğrulayıcı, birçok metriğe erişim sağlar. self.validator.metrics.box:
| Özellik | Açıklama |
|---|---|
f1 | Sınıf başına F1 puanı |
p | Sınıf başına hassasiyet |
r | Sınıf başına geri çağırma |
ap50 | Sınıf AP IoU ,5 IoU |
ap | AP IoU ,5:0,95 AP sınıf |
mp, mr | Ortalama hassasiyet ve geri çağırma |
map50, map | Ortalama AP |
Sınıf Ağırlıkları Ekleme
Veri kümenizde dengesiz sınıflar varsa (örneğin, üretim denetiminde nadir görülen bir kusur), kayıp fonksiyonunda yeterince temsil edilmeyen sınıfların ağırlığını artırabilirsiniz. Bu, modelin nadir görülen 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ıkları Hesaplama
Veri kümenizin etiket dağılımından sınıf ağırlıklarını 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 Metrik ile En İyi Modeli Kaydetme
Eğitmen kaydeder best.pt fitness temelinde, varsayılan olarak 0.9 × mAP@0.5:0.95 + 0.1 × mAP@0.5. Farklı bir ölçü birimi kullanmak için (örneğin mAP@0.5 veya geri çağırma), geçersiz kılma validate() ve seçtiğiniz metriği uygunluk değeri olarak döndürür. 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
Yaygın olarak kullanılan ölçütler self.metrics doğrulama sonrasında şunları içerir:
| Anahtar | Açıklama |
|---|---|
metrics/precision(B) | Kesinlik |
metrics/recall(B) | Duyarlılık (Recall) |
metrics/mAP50(B) | IoU ,5mAP |
metrics/mAP50-95(B) | IoU ,5:0,95'te mAP |
Omurganın Backboneondurulması ve Dondurmanın Kaldırılması
Transfer öğrenimi iş akışları genellikle ilk N dönem backbone önceden eğitilmiş backbone dondurmaktan yararlanır, böylece algılama başlığı uyum sağlayabilir. ince ayar tüm ağ. Ultralytics bir freeze parametresini kullanarak eğitimin başlangıcında katmanları dondurmak ve geri çağırma (callback) N dönemden sonra onları çö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 parametresi, eğitim başlangıcında ilk 10 katmanı ( backbone) dondurur. on_train_epoch_start geri arama her dönemin başında tetiklenir ve dondurma süresi tamamlandığında tüm parametreleri çözdürür.
Ne Dondurulacağını Seçmek
freeze=10ilk 10 katmanı dondurur (genellikle YOLO backbone )freeze=[0, 1, 2, 3]belirli katmanları indekse göre dondurur- Daha Yüksek
FREEZE_EPOCHSdeğerler, backbone önce kafanın uyum sağlaması için daha fazla zaman tanır
Katman Başına Öğrenme Oranları
Ağın farklı bölümleri farklı öğrenme hızlarından yararlanabilir. Yaygın bir strateji, öğrenilen özellikleri korumak backbone önceden eğitilmiş backbone için daha düşük bir öğrenme hızı kullanırken, algılama başlığının daha yüksek bir hızla daha hızlı uyum sağlamasına izin vermektir:
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 Hızı Planlayıcı
Yerleşik öğrenme oranı zamanlayıcı (cosine veya linear) grup başına temel öğrenme oranlarının üzerine eklenir. Hem backbone baş öğrenme oranları aynı azalma programını takip eder ve eğitim boyunca aralarındaki oranı korur.
Teknikleri Birleştirme
Bu özelleştirmeler, birden fazla yöntemi geçersiz kılarak ve gerektiğinde geri aramalar ekleyerek tek bir eğitmen sınıfında birleştirilebilir.
SSS
YOLO'ya özel bir eğitmen nasıl aktarılır?
Özel eğitmen sınıfınızı (örnek değil) 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ğitmen örneklenmesini dahili olarak yönetir. Bkz. Gelişmiş Özelleştirme Eğitmen mimarisi hakkında daha fazla bilgi için sayfasına bakın.
Hangi BaseTrainer yöntemlerini geçersiz kılabilirim?
Özelleştirme için kullanılabilen temel yöntemler:
| Metot | Amaç |
|---|---|
validate() | Doğrulama çalıştır ve metrikleri döndür |
build_optimizer() | Optimize ediciyi oluşturun |
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() | Veri yükleyiciyi oluşturun |
preprocess_batch() | Giriş grubunu önceden işleme |
label_loss_items() | Günlüğe kaydetmek için biçim kaybı öğelerini biçimlendirin |
API referansının tamamı için bkz. BaseTrainer belgeleme.
Eğiticiyi alt sınıflandırmak yerine geri aramaları kullanabilir miyim?
Evet, daha basit özelleştirmeler için, geri aramalar genellikle yeterlidir. Kullanılabilir geri arama olayları şunlardır: on_train_start, on_train_epoch_start, on_train_epoch_end, on_fit_epoch_endve on_model_saveBunlar, alt sınıf oluşturmadan eğitim döngüsüne bağlanmanıza olanak tanır. Yukarıdaki backbone örneği bu yaklaşımı göstermektedir.
Modeli alt sınıflandırmadan kayıp fonksiyonunu nasıl özelleştirebilirim?
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)
Kayıpta yapısal değişiklikler yapmak için (sınıf ağırlıkları eklemek gibi), sınıf ağırlıkları bölümünde gösterildiği gibi kayıp ve modeli alt sınıfa ayırmanız gerekir.