맞춤형 트레이너
Ultralytics 파이프라인은 다음을 중심으로 구축되었습니다. BaseTrainer 그리고 작업별 트레이너들처럼 DetectionTrainer이러한 클래스는 기본적으로 훈련 루프, 검증, 체크포인트 저장, 로깅을 처리합니다. 사용자 정의 메트릭 추적, 손실 가중치 조정, 학습률 스케줄 구현 등 더 많은 제어가 필요할 때는 트레이너를 상속받아 특정 메서드를 재정의할 수 있습니다.
이 가이드는 다섯 가지 일반적인 사용자 지정 방법을 안내합니다:
- 각 에포크 종료 시 사용자 정의 메트릭(F1 점수) 기록
- 클래스 불균형을 처리하기 위한 클래스 가중치 추가
- 다른 지표를 기반으로 최적 모델 저장하기
- 첫 N 에포크 동안 백본을 고정시킨 후, 고정 해제
- 레이어별 학습률 지정
필수 조건
이 가이드를 읽기 전에, 기본 사항에 대해 잘 알고 있는지 확인하십시오. YOLO 훈련 그리고 고급 사용자 정의 페이지, 이는 다음을 다루는 BaseTrainer 건축학.
맞춤형 트레이너의 작동 방식
에 지정되어 있습니다. YOLO 모델 클래스는 trainer 의 parameter 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따라서 사용자 정의하려는 특정 메서드만 재정의하면 됩니다.
사용자 정의 메트릭 로깅
에 지정되어 있습니다. 유효성 검사 단계 계산 정밀도, 재현율및 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 점수와 각 검증 실행 후 클래스별 세부 내역을 기록합니다.
사용 가능한 지표
검증기는 다양한 메트릭에 대한 접근을 제공합니다. self.validator.metrics.box:
| 속성 | 설명 |
|---|---|
f1 | 클래스별 F1 점수 |
p | 클래스별 정밀도 |
r | 클래스별 리콜 |
ap50 | AP 평균 학점 IoU .5 |
ap | AP IoU .5:0.95 per class |
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) | 재현율 |
metrics/mAP50(B) | IoU .5mAP |
metrics/mAP50-95(B) | IoU .5mAP : 0.95 |
백본 동결 및 해동
전이 학습 워크플로는 종종 처음 N 에포크 동안 사전 훈련된 백본을 고정함으로써 이점을 얻으며, 이는 탐지 헤드가 적응할 수 있도록 허용합니다. 미세 조정 전체 네트워크. Ultralytics freeze 훈련 시작 시 레이어를 고정하는 매개변수를 사용할 수 있으며, 콜백 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그룹별 기본 학습률에 추가로 적용됩니다. 백본과 헤드 학습률은 동일한 감쇠 일정을 따르며, 훈련 전반에 걸쳐 둘 사이의 비율을 유지합니다.
기법 결합
이러한 사용자 정의는 여러 메서드를 재정의하고 필요에 따라 콜백을 추가함으로써 단일 트레이너 클래스로 통합될 수 있습니다.
FAQ
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)
손실 함수의 구조적 변경(예: 클래스 가중치 추가)을 위해서는 클래스 가중치 섹션에 설명된 대로 손실 함수와 모델을 서브클래싱해야 합니다.