트레이너 사용자 정의하기#
Ultralytics 학습 파이프라인은 BaseTrainer 및 DetectionTrainer과 같은 태스크별 트레이너를 중심으로 구축되어 있습니다. 이 클래스들은 학습 루프, 검증, 체크포인트 저장, 로깅을 기본적으로 처리합니다. 커스텀 메트릭 추적, 손실 가중치 조정, 학습률 스케줄 구현 등 더 세밀한 제어가 필요할 때는 트레이너를 상속하여 특정 메서드를 오버라이드할 수 있습니다.
이 가이드에서는 7가지 일반적인 사용자 정의 방법을 다룹니다:
- 각 epoch 종료 시점에 커스텀 메트릭(F1 score) 로깅
- 클래스 불균형 처리를 위한 클래스 가중치 추가
- 다른 메트릭을 기반으로 한 최적 모델 저장
- 처음 N 에폭 동안 백본 동결 후 해제
- 레이어별 학습률 지정
- 다중 GPU 학습을 위한 GPU 간 BatchNorm 동기화
- 안정성 튜닝을 위한 그래디언트 클리핑 설정
이 가이드를 읽기 전에 YOLO 모델 학습의 기본 사항과 BaseTrainer 아키텍처를 다루는 고급 커스터마이징(Advanced Customization) 페이지에 익숙해지시기 바랍니다.
사용자 지정 트레이너 작동 방식#
YOLO 모델 클래스는 train() 메서드에서 trainer 파라미터를 허용합니다. 이를 통해 기본 동작을 확장하는 사용자 정의 트레이너 클래스를 전달할 수 있습니다:
from ultralytics import YOLO
from ultralytics.models.yolo.detect import DetectionTrainer
class CustomTrainer(DetectionTrainer):
"""A custom trainer that extends DetectionTrainer with additional functionality."""
# Add your customizations here
model = YOLO("yolo26n.pt")
model.train(data="coco8.yaml", epochs=10, trainer=CustomTrainer)사용자 정의 트레이너는 DetectionTrainer의 모든 기능을 상속하므로 커스터마이즈하려는 특정 메서드만 오버라이드하면 됩니다.
사용자 지정 메트릭 로깅#
검증(validation) 단계는 정밀도(precision), 재현율(recall), 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 점수 |
image_metrics | 정밀도, 재현율, F1, TP, FP, FN을 포함하는 이미지별 메트릭 딕셔너리 |
p | 클래스별 정밀도 |
r | 클래스별 재현율 |
ap50 | 클래스별 IoU 0.5에서의 AP |
ap | 클래스별 IoU 0.5:0.95에서의 AP |
mp, mr | 평균 정밀도 및 재현율 |
map50, map | 평균 AP 메트릭 |
클래스 가중치 추가#
데이터셋의 클래스가 불균형한 경우(예: 제조 공정 검사의 희귀 결함), 손실 함수(loss function)에서 과소대표된 클래스의 가중치를 높일 수 있습니다. 이렇게 하면 모델이 희귀 클래스의 오분류에 더 큰 페널티를 부과하게 됩니다.
손실을 사용자 정의하려면 손실 클래스, 모델 및 트레이너를 상속받아야 합니다:
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]WeightedDetectionModel과 같은 커스텀 클래스는 참조를 통해 체크포인트에 저장됩니다. 학습 스크립트 내에 정의될 때 이들은 __main__ 모듈에 속하므로, 다른 스크립트에서 best.pt를 로드하면 AttributeError: Can't get attribute 'WeightedDetectionModel' on <module '__main__'>이 발생합니다.
임포트가 유지될 수 있도록 전용 모듈에 커스텀 클래스를 정의하고, 로드 시점에 해당 모듈이 PYTHONPATH에 포함되어 있는지 확인하세요.
# weighted_model.py
from ultralytics.nn.tasks import DetectionModel
class WeightedDetectionModel(DetectionModel):
"""Detection model that uses class-weighted loss."""# inference script
from weighted_model import WeightedDetectionModel # noqa: F401 - must be importable at checkpoint load time
from ultralytics import YOLO
model = YOLO("runs/detect/train/weights/best.pt")
metrics = model.val()사용자 지정 메트릭으로 최적의 모델 저장#
트레이너는 적합도(fitness)를 기준으로 best.pt을 저장하며, 탐지의 경우 기본값은 mAP@0.5:0.95입니다([P, R, mAP@0.5, mAP@0.5:0.95]에 대한 가중치 [0.0, 0.0, 0.0, 1.0]). (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 0.5에서의 mAP |
metrics/mAP50-95(B) | IoU 0.5:0.95에서의 mAP |
백본 동결 및 해제#
전이 학습(Transfer learning) 워크플로우는 처음 N 에폭 동안 사전 학습된 백본을 동결하여, 전체 네트워크를 파인튜닝(fine-tuning)하기 전에 탐지 헤드가 적응할 수 있도록 시간을 주는 방식이 유용한 경우가 많습니다. Ultralytics는 학습 시작 시 레이어를 동결하는 freeze 파라미터를 제공하며, N 에폭 후에 이를 해제하기 위해 콜백(callback)을 사용할 수 있습니다:
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개 레이어(인덱스 0-9)를 동결하며, 이는 YOLO26 백본의 대부분을 다룹니다. 백본은 레이어 0-10에 걸쳐 있으므로 freeze=10은 마지막 C2PSA 블록(레이어 10)을 학습 가능한 상태로 둡니다. 전체 백본을 동결하려면 freeze=11를 사용하세요. on_train_epoch_start 콜백은 각 에폭의 시작 부분에서 실행되며 동결 기간이 완료되면 모든 파라미터를 해제합니다.
freeze=10은 처음 10개 레이어, 인덱스 0-9를 동결합니다(YOLO26 백본의 대부분이며, 레이어 10의 마지막 C2PSA 블록을 포함하려면freeze=11을 사용하세요).freeze=[0, 1, 2, 3]은 인덱스별로 특정 레이어를 동결합니다.- 더 높은
FREEZE_EPOCHS값은 백본이 변경되기 전에 헤드가 적응할 수 있는 시간을 더 많이 제공합니다.
레이어별 학습률#
네트워크의 서로 다른 부분은 서로 다른 학습률(learning rates)의 혜택을 받을 수 있습니다. 일반적인 전략은 학습된 특징을 보존하기 위해 사전 학습된 백본에는 더 낮은 학습률을 사용하고, 탐지 헤드는 더 높은 학습률로 더 빠르게 적응할 수 있도록 하는 것입니다:
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 = []
unwrapped = unwrap_model(model)
backbone_len = len(unwrapped.yaml["backbone"]) # YOLO26 backbone spans layers 0-10 (C2PSA at layer 10)
for k, v in unwrapped.named_parameters():
if not v.requires_grad:
continue
is_backbone = any(k.startswith(f"model.{i}.") for i in range(backbone_len))
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)RT-DETR 변형#
RT-DETR의 경우 두 가지 세부 조정을 거치면 패턴은 동일합니다. 백본 길이는 model.yaml["backbone"]에서 읽어오므로 하드코딩된 레이어 수 없이도 동일한 트레이너가 RT-DETR 변형(RT-DETR-L, RT-DETR-X, ResNet-50/101 백본)에서 작동합니다. 또한 각 섹션 내에서 파라미터가 weight, BatchNorm, bias 그룹으로 분할되어 BatchNorm 파라미터 및 바이어스에서 가중치 감쇠(weight decay)가 제외되며, 이는 기본 트레이너의 정책과 일치합니다. 이는 디코더 헤드가 일반적으로 무작위로 초기화되는 반면 백본은 더 낮은 학습률의 이점을 받는 사전 학습된 특징을 유지하는 RT-DETR 파인튜닝에 특히 유용합니다:
import torch
from torch import nn
from ultralytics import RTDETR
from ultralytics.models.rtdetr.train import RTDETRTrainer
from ultralytics.utils import LOGGER, colorstr
from ultralytics.utils.torch_utils import unwrap_model
class RTDETRBackboneLRTrainer(RTDETRTrainer):
"""RT-DETR trainer with a lower learning rate for backbone parameters."""
backbone_lr_ratio = 0.1 # backbone learning rate as a fraction of head learning rate
def build_optimizer(self, model, name="auto", lr=0.001, momentum=0.9, decay=1e-5, iterations=1e5):
"""Build an AdamW optimizer with six param groups: head and backbone x {weight, bn, bias}."""
# Resolve optimizer name; "auto" maps to AdamW with RT-DETR-style defaults
canonical = {"Adam", "Adamax", "AdamW", "NAdam", "RAdam", "auto"}
name = {x.lower(): x for x in canonical}.get(name.lower(), name)
if name == "auto":
name, lr, momentum = "AdamW", 1e-4, 0.9
self.args.warmup_bias_lr = 0.0 # RT-DETR warms biases from 0, unlike YOLO's 0.1
if name not in {"Adam", "Adamax", "AdamW", "NAdam", "RAdam"}:
raise NotImplementedError(f"This trainer only supports AdamW-family optimizers; got {name}")
# Identify backbone parameters from model.yaml and route each param into a (section, kind) group
unwrapped = unwrap_model(model)
backbone_len = len(unwrapped.yaml["backbone"])
norm_types = tuple(v for k, v in nn.__dict__.items() if "Norm" in k)
groups = {f"{s}_{k}": [] for s in ("head", "backbone") for k in ("weight", "bn", "bias")}
for module_name, module in unwrapped.named_modules():
for param_name, param in module.named_parameters(recurse=False):
if not param.requires_grad:
continue
fullname = f"{module_name}.{param_name}" if module_name else param_name
parts = fullname.split(".")
section = (
"backbone"
if len(parts) > 1 and parts[0] == "model" and parts[1].isdigit() and int(parts[1]) < backbone_len
else "head"
)
if "bias" in param_name:
kind = "bias"
elif isinstance(module, norm_types) or "logit_scale" in fullname:
kind = "bn"
else:
kind = "weight"
groups[f"{section}_{kind}"].append(param)
# Build the optimizer with per-group lr and weight decay; backbone groups use lr * backbone_lr_ratio
backbone_lr = lr * self.backbone_lr_ratio
param_groups = [
{"params": groups["head_weight"], "lr": lr, "weight_decay": decay, "param_group": "weight"},
{"params": groups["head_bn"], "lr": lr, "weight_decay": 0.0, "param_group": "bn"},
{"params": groups["head_bias"], "lr": lr, "weight_decay": 0.0, "param_group": "bias"},
{"params": groups["backbone_weight"], "lr": backbone_lr, "weight_decay": decay, "param_group": "weight"},
{"params": groups["backbone_bn"], "lr": backbone_lr, "weight_decay": 0.0, "param_group": "bn"},
{"params": groups["backbone_bias"], "lr": backbone_lr, "weight_decay": 0.0, "param_group": "bias"},
]
param_groups = [pg for pg in param_groups if pg["params"]] # drop empty groups
optimizer = getattr(torch.optim, name)(param_groups, betas=(momentum, 0.999))
LOGGER.info(
f"{colorstr('optimizer:')} {name}(lr={lr}, backbone_lr={backbone_lr}) with parameter groups\n"
f" Head: {len(groups['head_bn'])} bn, {len(groups['head_weight'])} weight(decay={decay}), "
f"{len(groups['head_bias'])} bias (lr={lr})\n"
f" Backbone: {len(groups['backbone_bn'])} bn, {len(groups['backbone_weight'])} weight(decay={decay}), "
f"{len(groups['backbone_bias'])} bias (lr={backbone_lr})"
)
return optimizer
model = RTDETR("rtdetr-l.pt")
model.train(data="coco8.yaml", epochs=20, trainer=RTDETRBackboneLRTrainer)일반적인 시작점은 HGNetV2 백본을 사용하는 원래 RT-DETR 설정과 일치하는 backbone_lr_ratio = 0.1입니다. 문헌에 따르면 백본 크기 및 사전 학습 데이터 스케일에 반비례하여 비율을 조정하는 것이 좋습니다. 수억 장의 이미지로 DINO, CLIP, 또는 MAE를 사용하여 학습된 ViT-L/H와 같이 매우 큰 데이터셋에서 사전 학습된 대형 백본은 잘 학습된 특징을 보존하기 위해 일반적으로 0.01 이하의 더 작은 비율을 사용하며, 가벼운 사전 학습을 거친 소형 백본은 0.5 이상의 더 큰 비율을 허용합니다.
내장된 학습률 스케줄러(cosine 또는 linear)는 그룹별 기본 학습률 위에 계속 적용됩니다. 백본과 헤드의 학습률 모두 동일한 감쇠 스케줄을 따르며, 학습 과정 내내 두 학습률 간의 비율을 유지합니다.
이러한 사용자 정의들은 여러 메서드를 재정의하고 필요에 따라 콜백을 추가함으로써 단일 트레이너 클래스로 결합할 수 있습니다.
멀티 GPU 학습을 위한 동기화된 BatchNorm#
DistributedDataParallel을 사용하여 여러 GPU에서 학습할 때 기본 BatchNorm2d 레이어는 각 GPU에서 독립적으로 통계치를 계산합니다. GPU당 작은 배치 크기를 사용하는 RT-DETR 파인튜닝 및 기타 레시피의 경우, GPU별 배치 통계치가 불안정할 수 있습니다. PyTorch의 SyncBatchNorm은 단일 전역 배치 통계를 위해 모든 랭크에 걸쳐 평균과 분산을 동결하며, 이는 약간의 GPU 간 통신 오버헤드 비용으로 수렴성을 종종 향상시킵니다.
변환은 모델이 GPU에 올라간 후 DDP가 이를 감싸기 전에 이루어져야 합니다. 이를 위한 가장 깔끔한 훅은 set_model_attributes()이며, BaseTrainer은 정확히 그 시점에 이를 호출합니다:
from torch import nn
from ultralytics import RTDETR
from ultralytics.models.rtdetr.train import RTDETRTrainer
class SyncBNTrainer(RTDETRTrainer):
"""RT-DETR trainer that converts BatchNorm to SyncBatchNorm for multi-GPU training."""
def set_model_attributes(self):
"""Run the parent setup, then convert BN to SyncBatchNorm when training on multiple GPUs."""
super().set_model_attributes()
if self.world_size > 1:
self.model = nn.SyncBatchNorm.convert_sync_batchnorm(self.model)
model = RTDETR("rtdetr-l.pt")
model.train(data="coco8.yaml", epochs=20, device=[0, 1], trainer=SyncBNTrainer)world_size > 1 가드는 트레이너가 단일 GPU 실행에서도 안전하게 사용될 수 있도록 보장합니다. 단일 GPU에서는 변환이 건너뛰어지고 일반 BatchNorm2d로 학습이 진행됩니다. 부모 클래스를 DetectionTrainer로 전환하면 YOLO에서도 동일한 패턴이 작동합니다.
| 시나리오 | 권장 사항 |
|---|---|
| 멀티 GPU 학습, 작은 GPU당 배치 (≤ 16) | 활성화 |
| 멀티 GPU 학습, 큰 GPU당 배치 (≥ 32) | 선택 사항; 경미한 이점 |
| 단일 GPU 학습 | 해당 없음 (건너뜀) |
구성 가능한 그래디언트 클리핑#
기본 트레이너는 그라디언트가 이를 초과하는 경우가 드문 YOLO 모델에 맞춰진 헐거운 값인 optimizer_step()에서 max_norm=10.0(으)로 그라디언트를 클리핑합니다. RT-DETR, DEIM, DINO와 같은 DETR 계열 검출기는 그라디언트 크기가 급증할 수 있는 디코더의 크로스 어텐션 레이어를 안정화하기 위해 일반적으로 0.1와 같이 훨씬 더 엄격한 값을 사용합니다. 클립 값을 재정의하려면 트레이너를 서브클래싱하고 optimizer_step()(을)를 재정의하십시오:
import torch
from ultralytics import RTDETR
from ultralytics.models.rtdetr.train import RTDETRTrainer
class CustomClipTrainer(RTDETRTrainer):
"""RT-DETR trainer with configurable gradient clipping."""
clip_grad_norm = 0.1 # max gradient norm; set to 0 to disable clipping
def optimizer_step(self):
"""Run an optimizer step with a configurable gradient-norm clip."""
self.scaler.unscale_(self.optimizer)
if self.clip_grad_norm > 0:
torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=self.clip_grad_norm)
self.scaler.step(self.optimizer)
self.scaler.update()
self.optimizer.zero_grad()
if self.ema:
self.ema.update(self.model)
model = RTDETR("rtdetr-l.pt")
model.train(data="coco8.yaml", epochs=20, trainer=CustomClipTrainer)부모 클래스를 DetectionTrainer (from ultralytics.models.yolo.detect import DetectionTrainer)으로 전환하고 YOLO("yolo26n.pt")로 YOLO 체크포인트를 로드하면 동일한 트레이너가 YOLO에서도 작동합니다. optimizer_step 본문은 변경되지 않습니다.
| 아키텍처 제품군 | 일반적인 max_norm |
|---|---|
| RT-DETR / DEIM / DETR 제품군 | 0.1 |
| YOLO (Ultralytics 기본값) | 10.0 |
| 클리핑 비활성화 | 0 |
FAQ#
인스턴스가 아닌 커스텀 트레이너 클래스를
model.train()의trainer파라미터에 전달하십시오:from ultralytics import YOLO model = YOLO("yolo26n.pt") model.train(data="coco8.yaml", trainer=MyCustomTrainer)YOLO클래스는 내부적으로 트레이너 인스턴스화를 처리합니다. 트레이너 아키텍처에 대한 자세한 내용은 고급 커스터마이징(Advanced Customization) 페이지를 참조하세요.사용자 정의를 위해 제공되는 주요 메서드:
메서드 목적 validate()검증을 실행하고 메트릭을 반환합니다 build_optimizer()옵티마이저를 구성합니다 save_model()학습 체크포인트를 저장합니다 get_model()모델 인스턴스를 반환합니다 get_validator()검증기 인스턴스를 반환합니다 get_dataloader()데이터로더를 빌드합니다 preprocess_batch()입력 배치를 전처리합니다 label_loss_items()로깅을 위해 손실 항목의 형식을 지정합니다 전체 API 레퍼런스는
BaseTrainer문서를 참조하세요.그렇습니다. 더 간단한 커스터마이징의 경우 콜백(callbacks)만으로 충분한 경우가 많습니다. 사용 가능한 콜백 이벤트에는
on_train_start,on_train_epoch_start,on_train_epoch_end,on_fit_epoch_end,on_model_save가 있습니다. 이를 통해 서브클래싱 없이 학습 루프에 훅을 걸 수 있습니다. 위의 백본 동결 예시가 이 접근 방식을 보여줍니다.변경 사항이 더 간단한 경우(예: 손실 게인 조정) 하이퍼파라미터(hyperparameters)를 직접 수정할 수 있습니다:
model.train(data="coco8.yaml", box=10.0, cls=1.5, dfl=2.0)손실에 대한 구조적 변경(예: 클래스 가중치 추가)의 경우 클래스 가중치 섹션에 표시된 대로 손실과 모델을 서브클래싱해야 합니다.