Trainer 사용자 지정
Ultralytics 파이프라인은 다음을 중심으로 구축되었습니다. BaseTrainer 및 다음과 같은 작업별 트레이너 DetectionTrainer. 이 클래스들은 훈련 루프, 검증, 체크포인트 저장 및 로깅을 즉시 처리합니다. 사용자 정의 메트릭 추적, 손실 가중치 조정 또는 학습률 스케줄 구현과 같이 더 많은 제어가 필요한 경우 트레이너를 서브클래싱하고 특정 메서드를 오버라이드할 수 있습니다.
이 가이드는 다섯 가지 일반적인 사용자 지정 방법을 안내합니다:
- 사용자 정의 지표(F1 스코어) 로깅: 각 에폭의 끝에 사용자 정의 지표(F1 스코어)를 로깅합니다.
- 클래스 불균형 처리를 위한 클래스 가중치 추가
- 최적 모델 저장: 다른 지표를 기반으로 최적 모델을 저장합니다.
- 백본 동결: 처음 N 에폭 동안 백본을 동결한 다음, 동결 해제합니다.
- 레이어별 학습률 지정
맞춤형 트레이너의 작동 방식
에 지정되어 있습니다. 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 | 클래스별 IoU 0.5에서의 AP |
ap | 클래스별 IoU 0.5:0.95에서의 AP |
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 기본적으로 다음으로 설정되는 적합도(fitness) 기반 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)
손실 함수의 구조적 변경(예: 클래스 가중치 추가)을 위해서는 클래스 가중치 섹션에 설명된 대로 손실 함수와 모델을 서브클래싱해야 합니다.