YOLO 및 객체 감지: YOLOv8을 사용한 이론에서 실습까지
2026년 1월 Ultralytics 출시 욜로26, 가족의 최신 진화 실시간 객체 감지를 재정의한 YOLO. 하지만 YOLO26을 이해하려면 먼저 이해해야 합니다. YOLO: 무엇이 엄청나게 빠른지, 아키텍처가 어떻게 작동하는지, 왜 그렇게 되었는지 산업, 자동차 및 감시 애플리케이션의 물체 감지를 위한 사실상의 표준 그리고 로봇 공학. 이 글에서 우리는 최신 객체 감지에 대한 완전한 이해를 구축할 것입니다. YOLOv1부터 YOLOv8 및 YOLO26을 사용한 실제 구현까지.
메모: 이것은 YOLO26의 첫 번째 이탈리아어 완전한 튜토리얼입니다. 컴퓨터 시리즈 federicocalo.dev 및 해당 주제에 대한 이탈리아어 참조 소스의 딥 러닝을 통한 비전.
무엇을 배울 것인가
- 객체 감지 작동 방식: 경계 상자, 신뢰도 점수, 클래스
- YOLO 아키텍처: 백본, 넥, 헤드 - 이론에서 구현까지
- YOLO의 이야기: v1부터 YOLO26까지, 각 버전의 주요 개선사항
- 기본 측정항목: IoU, mAP, 정밀도-재현율 곡선
- YOLOv8(Ultralytics)을 사용하여 맞춤형 데이터 세트에 대한 교육 완료
- 비디오 및 웹캠에 대한 실시간 추론
- 내보내기 및 배포: ONNX, TensorRT, OpenVINO
- YOLO26: 2026년 1월의 건축뉴스
- 데이터세트 준비 및 데이터 증대 모범 사례
1. 객체 감지: 기본 개념
L'물체 감지 하나 이상의 항목을 동시에 식별하고 분류하는 작업 이미지 내의 개체. 분류(전체 이미지에 하나의 라벨)와 달리, 탐지는 세 가지 질문에 답해야 합니다. 무엇 이미지에 있어요. 어디 발견되다 (경계 상자) 및 어느 것 신뢰 그는 그것을 감지했습니다.
1.1 출력 표현
감지된 각 개체는 경계 상자 5가지 기본가치로 클래스에 대한 확률 벡터를 추가합니다.
# Ogni detection e rappresentata da:
# [x_center, y_center, width, height, confidence] + [p_class1, p_class2, ..., p_classN]
# Esempio: detection di un gatto (classe 0) in un'immagine 640x640
detection = {
'bbox': (0.45, 0.60, 0.30, 0.40), # x_c, y_c, w, h (normalizzati 0-1)
'confidence': 0.94, # confidence score del box
'class_id': 0, # indice classe
'class_name': 'gatto',
'class_prob': 0.96 # probabilità condizionale della classe
}
# Il "final score" e: confidence * class_prob = 0.94 * 0.96 = 0.90
# Coordinate in pixel (immagine 640x640):
x_c_px = 0.45 * 640 # = 288
y_c_px = 0.60 * 640 # = 384
w_px = 0.30 * 640 # = 192
h_px = 0.40 * 640 # = 256
# Conversione a [x1, y1, x2, y2]
x1 = x_c_px - w_px / 2 # = 192
y1 = y_c_px - h_px / 2 # = 256
x2 = x_c_px + w_px / 2 # = 384
y2 = y_c_px + h_px / 2 # = 512
1.2 비최대 억제(NMS)
감지 모델은 수백 개의 겹치는 경계 상자 제안을 생성합니다. 거기 비최대 억제(NMS) 그리고 최고의 상자를 선택하는 알고리즘 측정항목을 기반으로 중복 제거 IoU(Intersection over Union).
import numpy as np
def compute_iou(box1: np.ndarray, box2: np.ndarray) -> float:
"""
Calcola Intersection over Union tra due bounding boxes.
Input: [x1, y1, x2, y2] per entrambi i box.
Output: IoU in [0, 1]
"""
# Coordinate dell'intersezione
x_left = max(box1[0], box2[0])
y_top = max(box1[1], box2[1])
x_right = min(box1[2], box2[2])
y_bottom = min(box1[3], box2[3])
if x_right < x_left or y_bottom < y_top:
return 0.0 # Nessuna intersezione
intersection = (x_right - x_left) * (y_bottom - y_top)
area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
union = area1 + area2 - intersection
return intersection / union
def non_maximum_suppression(
boxes: np.ndarray,
scores: np.ndarray,
iou_threshold: float = 0.45,
score_threshold: float = 0.25
) -> list[int]:
"""
Applica NMS per eliminare bounding box sovrapposti.
Args:
boxes: [N, 4] array di box [x1, y1, x2, y2]
scores: [N] array di confidence scores
iou_threshold: soglia IoU per considerare due box duplicati
score_threshold: filtra box sotto questa confidenza
Returns:
Lista di indici dei box selezionati
"""
# Filtra box sotto la soglia di confidenza
valid_mask = scores >= score_threshold
boxes = boxes[valid_mask]
scores = scores[valid_mask]
indices = np.where(valid_mask)[0]
# Ordina per score decrescente
order = np.argsort(scores)[::-1]
selected_indices = []
while len(order) > 0:
# Prendi il box con score più alto
best_idx = order[0]
selected_indices.append(indices[best_idx])
order = order[1:]
if len(order) == 0:
break
# Calcola IoU del box selezionato con tutti i restanti
ious = np.array([
compute_iou(boxes[best_idx], boxes[i])
for i in order
])
# Mantieni solo i box con IoU basso (non sovrapposti)
order = order[ious < iou_threshold]
return selected_indices
# Test
boxes = np.array([
[100, 100, 300, 300], # box principale
[110, 105, 310, 305], # quasi identico - da eliminare
[500, 200, 700, 400], # box diverso - da mantenere
])
scores = np.array([0.92, 0.88, 0.75])
kept = non_maximum_suppression(boxes, scores, iou_threshold=0.5)
print(f"Box mantenuti: {kept}") # [0, 2] - elimina il duplicato
1.3 평가 지표
객체 감지를 위한 기본 측정항목
| 미터법 | 공식 | 의미 |
|---|---|---|
| 아이유 | 교차로/합중국 | 예측 상자와 지상 진실 간의 중첩 |
| 정도 | TP / (TP + FP) | 얼마나 많은 예측이 정확합니까? |
| 상기하다 | TP / (TP + FN) | 실제 물체가 얼마나 많이 발견되었는가 |
| AP@0.5 | IoU=0.5에서 PR 곡선 아래 면적 | 단일 클래스의 정확도 |
| mAP@0.5 | 모든 클래스의 AP 평균 | 모델 비교를 위한 주요 지표 |
| mAP@0.5:0.95 | IoU 0.5-0.95에서 평균 mAP(0.05단계) | 더욱 엄격한 측정 기준(표준 COCO) |
2. YOLO 아키텍처: 작동 방식
욜로(한 번만 보세요)는 Redmon et al.에 의해 소개되었습니다. 2016년에는 혁신적인 아이디어로 객체 감지를 다음과 같이 처리합니다. 단일 회귀 문제, 이전 네트워크를 통한 단일 정방향 전달의 직접 경계 상자 및 클래스 확률입니다. 지역 제안도 없고 두 단계도 없습니다. 단일 네트워크, 단일 추론, 극도의 속도입니다.
2.1 3단계 아키텍처: 백본, 넥, 헤드
Input Immagine (640x640x3)
|
v
+------------------+
| BACKBONE | Estrazione feature multi-scala
| (CSPDarkNet / | Output: feature maps a scale diverse
| EfficientRep) | P3: 80x80 (oggetti piccoli)
| | P4: 40x40 (oggetti medi)
| | P5: 20x20 (oggetti grandi)
+------------------+
|
v
+------------------+
| NECK | Aggregazione multi-scala
| (PANet / BiFPN)| Feature Pyramid Network
| | Fonde informazioni semantiche (deep)
| | con informazioni spaziali (shallow)
+------------------+
|
v
+------------------+
| HEAD | Predizioni finali
| (Decoupled | Per ogni cella della griglia:
| Detection) | - Box regression: [x, y, w, h]
| | - Objectness: p(oggetto)
| | - Classification: [p_c1, ..., p_cN]
+------------------+
|
v
Output: [batch, num_predictions, 4 + 1 + num_classes]
# Per YOLOv8 nano su 640x640: 8400 predizioni totali
# (80x80 + 40x40 + 20x20 = 6400 + 1600 + 400 = 8400)
2.2 YOLO 진화: v1에서 YOLO26으로
YOLO 버전 기록
| 버전 | 년도 | 혁신을 선도하다 | 지도(코코) |
|---|---|---|---|
| YOLOv1 | 2016년 | 단일 단계 감지, SxS 그리드 | 63.4 (VOC) |
| YOLOv3 | 2018 | 다중 규모 탐지, Darknet-53 | 33.0 |
| YOLOv5 | 2020 | CSP 백본, 모자이크 확대 | 48.2 |
| YOLOv7 | 2022년 | 확장 ELAN, 보조 헤드 | 51.4 |
| YOLOv8 | 2023년 | 앵커가 없고 분리된 헤드, C2f 블록 | 53.9 |
| YOLOv9 | 2024년 | GELAN, 프로그래밍 가능한 그라데이션 정보 | 55.6 |
| YOLOv10 | 2024년 | NMS가 필요 없는 이중 라벨 할당 | 54.4 |
| 욜로26 | 2026년 1월 | 하이브리드 주의 백본, 동적 NMS | 57.2 |
2.3 앵커 없는 감지: YOLOv8 혁명
YOLOv8의 가장 중요한 혁신 중 하나는 앵커 박스. 이전 버전의 YOLO는 기본 앵커를 사용했습니다. 고정 크기 상자는 다음을 통해 계산되었습니다. k-는 데이터 세트의 클러스터링을 의미합니다. 이를 위해서는 각 데이터세트에 대한 앵커를 신중하게 선택해야 했습니다. YOLOv8(및 YOLO26)은 한 가지 접근 방식을 취합니다. 앵커가 없는: 모델이 직접 예측함 중심 좌표와 상자 크기를 조정하여 기본 앵커의 편향을 제거합니다.
3. YOLOv8을 사용한 사용자 정의 데이터 세트 훈련
3.1 데이터세트 준비
YOLOv8은 다음 형식을 사용합니다. 욜로TXT 주석용: 각 이미지에 대한 .txt 파일
다음 형식으로 각 개체에 대해 한 줄을 사용합니다.
<class_id> <x_center> <y_center> <width> <height>
(정규화된 좌표 0-1).
dataset/
├── images/
│ ├── train/ # Immagini di training
│ │ ├── img001.jpg
│ │ ├── img002.jpg
│ │ └── ...
│ ├── val/ # Immagini di validazione (20%)
│ │ └── ...
│ └── test/ # Immagini di test (opzionale)
│ └── ...
├── labels/
│ ├── train/ # Annotazioni training
│ │ ├── img001.txt # Una riga per oggetto
│ │ ├── img002.txt
│ │ └── ...
│ ├── val/
│ │ └── ...
│ └── test/
│ └── ...
└── dataset.yaml # Configurazione dataset
# Contenuto di img001.txt (due oggetti):
# class_id x_c y_c w h
# 0 0.45 0.60 0.30 0.40 # gatto al centro
# 1 0.85 0.25 0.20 0.35 # cane in alto a destra
# Contenuto di dataset.yaml:
# path: /path/to/dataset
# train: images/train
# val: images/val
# test: images/test # opzionale
# nc: 2 # numero classi
# names: ['gatto', 'cane']
import json
import os
from pathlib import Path
def convert_coco_to_yolo(coco_json_path: str, output_dir: str) -> None:
"""
Converte annotazioni COCO JSON in formato YOLO TXT.
Utile per dataset pubblici (COCO, Open Images, etc.)
"""
with open(coco_json_path) as f:
coco_data = json.load(f)
# Mappa image_id -> info immagine
images = {img['id']: img for img in coco_data['images']}
# Mappa category_id -> indice YOLO (0-based)
cat_id_to_yolo = {
cat['id']: i
for i, cat in enumerate(coco_data['categories'])
}
# Raggruppa annotazioni per immagine
annotations_by_image: dict[int, list] = {}
for ann in coco_data['annotations']:
img_id = ann['image_id']
if img_id not in annotations_by_image:
annotations_by_image[img_id] = []
annotations_by_image[img_id].append(ann)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
for img_id, anns in annotations_by_image.items():
img_info = images[img_id]
img_w = img_info['width']
img_h = img_info['height']
img_name = Path(img_info['file_name']).stem
txt_lines = []
for ann in anns:
# COCO: [x_top_left, y_top_left, width, height]
x_tl, y_tl, w, h = ann['bbox']
# Converti a formato YOLO (normalizzato)
x_c = (x_tl + w / 2) / img_w
y_c = (y_tl + h / 2) / img_h
w_n = w / img_w
h_n = h / img_h
class_idx = cat_id_to_yolo[ann['category_id']]
txt_lines.append(f"{class_idx} {x_c:.6f} {y_c:.6f} {w_n:.6f} {h_n:.6f}")
with open(output_path / f"{img_name}.txt", 'w') as f:
f.write('\n'.join(txt_lines))
print(f"Convertite {len(annotations_by_image)} immagini in {output_dir}")
3.2 Ultralytics YOLOv8을 사용한 훈련
from ultralytics import YOLO
import yaml
from pathlib import Path
# ---- Configurazione dataset ----
dataset_config = {
'path': '/path/to/dataset',
'train': 'images/train',
'val': 'images/val',
'nc': 80, # numero classi (es. COCO)
'names': ['person', 'bicycle', 'car', '...'] # lista classi
}
config_path = 'dataset.yaml'
with open(config_path, 'w') as f:
yaml.dump(dataset_config, f, allow_unicode=True)
# ---- Scelta del modello ----
# Varianti disponibili (velocità vs accuratezza):
# yolov8n.pt - nano (fastest, lowest accuracy)
# yolov8s.pt - small
# yolov8m.pt - medium
# yolov8l.pt - large
# yolov8x.pt - xlarge (slowest, highest accuracy)
model = YOLO('yolov8m.pt') # carica pesi pre-addestrati su COCO
# ---- Training ----
results = model.train(
data=config_path,
epochs=100,
imgsz=640, # dimensione input immagine
batch=16, # batch size (riduci se OOM)
workers=8, # CPU workers per data loading
device='0', # GPU index ('cpu' per CPU-only)
# Ottimizzazione
optimizer='AdamW',
lr0=0.001, # learning rate iniziale
lrf=0.01, # lr finale = lr0 * lrf
momentum=0.937,
weight_decay=0.0005,
warmup_epochs=3,
cos_lr=True, # cosine LR schedule
# Augmentation
mosaic=1.0, # mosaic augmentation (YOLOv5+ feature)
mixup=0.1, # mixup augmentation
copy_paste=0.1, # copy-paste augmentation
degrees=10.0, # rotation
translate=0.1, # translation
scale=0.5, # scaling
fliplr=0.5, # horizontal flip
flipud=0.0, # vertical flip
# Regularization
dropout=0.0,
label_smoothing=0.0,
# Checkpointing
save=True,
save_period=10, # salva ogni N epoche
project='runs/train',
name='yolov8m_custom',
# Early stopping
patience=50, # stop se mAP non migliora per N epoche
# Logging
plots=True,
verbose=True
)
print(f"Best mAP@0.5: {results.results_dict['metrics/mAP50(B)']:.3f}")
print(f"Best model salvato in: runs/train/yolov8m_custom/weights/best.pt")
3.3 추론과 시각화
from ultralytics import YOLO
import cv2
import numpy as np
from pathlib import Path
class YOLOInferenceEngine:
"""
Engine di inference per YOLOv8/YOLO26 con supporto
per immagini, video e stream live.
"""
def __init__(
self,
model_path: str = 'yolov8m.pt',
conf_threshold: float = 0.25,
iou_threshold: float = 0.45,
device: str = 'auto'
):
self.model = YOLO(model_path)
self.conf = conf_threshold
self.iou = iou_threshold
# Palette colori per classi
np.random.seed(42)
self.colors = np.random.randint(0, 255, size=(100, 3), dtype=np.uint8)
def predict_image(self, image_path: str, save_path: str | None = None) -> list[dict]:
"""Inference su singola immagine con visualizzazione opzionale."""
results = self.model.predict(
source=image_path,
conf=self.conf,
iou=self.iou,
verbose=False
)
detections = []
for r in results:
for box in r.boxes:
det = {
'bbox': box.xyxy[0].tolist(), # [x1, y1, x2, y2]
'confidence': float(box.conf[0]),
'class_id': int(box.cls[0]),
'class_name': r.names[int(box.cls[0])]
}
detections.append(det)
if save_path:
annotated = r.plot()
cv2.imwrite(save_path, annotated)
return detections
def process_video(self, video_path: str, output_path: str | None = None) -> None:
"""
Processa un video file con detection frame per frame.
Mostra FPS e statistiche in tempo reale.
"""
cap = cv2.VideoCapture(video_path)
if output_path:
fps = cap.get(cv2.CAP_PROP_FPS)
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
writer = cv2.VideoWriter(output_path, fourcc, fps, (w, h))
frame_count = 0
import time
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
start = time.perf_counter()
results = self.model(frame, conf=self.conf, iou=self.iou, verbose=False)
elapsed = time.perf_counter() - start
# Annota il frame
annotated = results[0].plot()
# Overlay FPS
fps_text = f"FPS: {1/elapsed:.1f}"
cv2.putText(annotated, fps_text, (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
if output_path:
writer.write(annotated)
cv2.imshow('YOLO Detection', annotated)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
frame_count += 1
cap.release()
if output_path:
writer.release()
cv2.destroyAllWindows()
print(f"Processati {frame_count} frame")
def run_webcam(self, camera_id: int = 0) -> None:
"""Detection live da webcam."""
print("Avvio webcam detection. Premi 'q' per uscire.")
self.process_video(camera_id, output_path=None)
# Uso pratico
engine = YOLOInferenceEngine('yolov8m.pt', conf_threshold=0.4)
# Inference su immagine
dets = engine.predict_image('test.jpg', save_path='result.jpg')
for d in dets:
print(f"{d['class_name']}: {d['confidence']:.2f} @ {[int(c) for c in d['bbox']]}")
# Inference su video
engine.process_video('traffic.mp4', 'traffic_detected.mp4')
# Webcam live
engine.run_webcam(camera_id=0)
4. 내보내기 및 배포: ONNX, TensorRT, OpenVINO
4.1 내보내기 형식
YOLOv8 내보내기 형식
| 체재 | 목표 | 속도 향상 대 PyTorch | 사용 사례 |
|---|---|---|---|
| ONNX | 다중 플랫폼 | 1.5-2x | 최대의 휴대성 |
| 텐서RT | 엔비디아 GPU | 5-8x | NVIDIA GPU의 최대 속도 |
| 오픈비노 | 인텔 CPU/GPU | 3-4배 | 인텔 서버, 인텔 엣지 |
| CoreML | 애플실리콘 | 3-5배 | iOS/macOS 배포 |
| TFLite | 모바일/엣지 | 2-3배 | 안드로이드, 라즈베리파이 |
| NCNN | ARM CPU | 2-4배 | 라즈베리 파이, ARM 내장 |
from ultralytics import YOLO
model = YOLO('runs/train/yolov8m_custom/weights/best.pt')
# Export ONNX (più portabile)
model.export(format='onnx', imgsz=640, opset=17, simplify=True)
# Export TensorRT (massima velocità su GPU NVIDIA)
# Richiede: NVIDIA GPU + CUDA + TensorRT installato
model.export(
format='engine', # TensorRT Engine
imgsz=640,
half=True, # FP16 per maggiore velocità
workspace=4, # GB di workspace GPU
batch=1, # batch size fisso per TensorRT
device=0
)
# Export OpenVINO (CPU Intel ottimizzata)
model.export(format='openvino', imgsz=640, half=False)
# Export TFLite (mobile/edge)
model.export(format='tflite', imgsz=640, int8=False)
# Carica e usa il modello esportato
# ONNX Runtime (CPU/GPU, no PyTorch)
model_onnx = YOLO('best.onnx')
results = model_onnx.predict('image.jpg', conf=0.25)
# TensorRT (GPU NVIDIA)
model_trt = YOLO('best.engine')
results = model_trt.predict('image.jpg', conf=0.25)
print(f"Inferenza TensorRT completata: {len(results[0].boxes)} oggetti rilevati")
5. YOLO26: 2026년 1월의 새로운 기능
2026년 1월 Ultralytics에서 출시된 욜로26 혁신을 소개하다 실시간 참조 모델로 자리매김하는 중요한 아키텍처 기능 2026년에는 물체 감지.
5.1 주요 혁신
YOLO26 대 YOLOv8: 기술 비교
| 특성 | YOLOv8 | 욜로26 |
|---|---|---|
| 등뼈 | C2f를 사용하는 CSP-DarkNet | 하이브리드 어텐션 + C3k2 |
| Neck | 파넷 | SCDown으로 향상된 PANet |
| 머리 | 앵커가 없는 분리형 | 듀얼 헤드로 앵커가 필요 없음 |
| NMS | NMS 표준 | 동적 NMS(학습됨) |
| mAP@0.5 (코코) | 53.9 | 57.2 (+3.3) |
| 추론(밀리초) | 4.1ms(A100) | 3.8ms(A100) |
# YOLO26 richiede ultralytics >= 8.3.0 (gennaio 2026)
# pip install ultralytics --upgrade
from ultralytics import YOLO
# Carica modello YOLO26
model = YOLO('yolo26n.pt') # nano - massima velocità
model = YOLO('yolo26s.pt') # small
model = YOLO('yolo26m.pt') # medium - bilanciato
model = YOLO('yolo26l.pt') # large
model = YOLO('yolo26x.pt') # xlarge - massima accuratezza
# Training su dataset personalizzato (stessa API di YOLOv8)
results = model.train(
data='dataset.yaml',
epochs=100,
imgsz=640,
batch=16,
device='0',
# YOLO26 aggiunge: self-calibrating augmentation
auto_augment='yolo26', # NEW: augmentation policy ottimizzata
# Dynamic NMS threshold scheduling
nms_schedule=True # NEW: NMS adattivo durante training
)
# Inference (identica a YOLOv8)
results = model.predict('image.jpg', conf=0.25, iou=0.45)
# Validazione sul validation set
metrics = model.val(data='dataset.yaml')
print(f"mAP@0.5: {metrics.box.map50:.3f}")
print(f"mAP@0.5:0.95: {metrics.box.map:.3f}")
6. 초매개변수 조정 및 고급 훈련 전략
YOLO의 성공은 프로세스 최적화만큼 아키텍처에 달려 있습니다. 훈련의. 학습률 스케줄러 선택부터 클래스 불균형 처리까지, 이러한 기술은 평범한 모델과 생산 준비가 완료된 모델의 차이를 만듭니다.
from ultralytics import YOLO
from ultralytics.utils.callbacks import on_train_epoch_end
import torch
import yaml
from pathlib import Path
# ---- Dataset YAML con class weights per bilanciamento ----
dataset_config = {
'path': './datasets/custom',
'train': 'images/train',
'val': 'images/val',
'nc': 5,
'names': ['person', 'car', 'bicycle', 'dog', 'cat'],
# Pesi per classe: compensa sbilanciamento (persona 5x più frequente)
'cls_weights': [1.0, 1.0, 2.0, 2.5, 2.5]
}
with open('dataset.yaml', 'w') as f:
yaml.dump(dataset_config, f)
# ---- Training con hyperparameters ottimizzati ----
model = YOLO('yolo26m.pt') # Pre-trained YOLO26 medium
results = model.train(
data='dataset.yaml',
epochs=300,
imgsz=640,
batch=16,
device='0',
# ---- Optimizer ----
optimizer='AdamW', # AdamW migliore di SGD per fine-tuning
lr0=0.001, # Learning rate iniziale
lrf=0.01, # LR finale = lr0 * lrf (1/100 dell'iniziale)
momentum=0.937, # Momentum per SGD (ignorato per AdamW)
weight_decay=0.0005, # L2 regularization
# ---- Learning Rate Scheduling ----
cos_lr=True, # Cosine annealing invece di step decay
warmup_epochs=3, # Warmup lineare da lr0/10 a lr0
warmup_momentum=0.8, # Momentum durante warmup
# ---- Augmentation ----
auto_augment='yolo26', # Policy self-calibrating di YOLO26
mosaic=1.0, # Mosaic augmentation (0.0 per disabilitare)
mixup=0.2, # MixUp probability
copy_paste=0.1, # Copy-Paste probability
degrees=10.0, # Rotazione max +/- gradi
translate=0.2, # Traslazione max (fraction of image size)
scale=0.9, # Scale augmentation range [1-scale, 1+scale]
flipud=0.0, # Flip verticale (0 se non sensato)
fliplr=0.5, # Flip orizzontale
# ---- Loss weights ----
box=7.5, # Peso perdita bounding box
cls=0.5, # Peso perdita classificazione
dfl=1.5, # Peso Distribution Focal Loss
# ---- Early stopping e checkpointing ----
patience=50, # Ferma se non migliora per 50 epoch
save_period=25, # Salva checkpoint ogni 25 epoch
val=True, # Valida ad ogni epoch
# ---- Output ----
project='runs/train',
name='yolo26m_custom',
exist_ok=True,
plots=True, # Genera grafici training
verbose=True
)
print(f"Best mAP@0.5: {results.results_dict['metrics/mAP50(B)']:.4f}")
print(f"Best mAP@0.5:0.95: {results.results_dict['metrics/mAP50-95(B)']:.4f}")
# ---- Hyperparameter Auto-Tuning con Ray Tune ----
def tune_hyperparameters(model_path: str, data_path: str) -> None:
"""
Usa Ray Tune per ottimizzare automaticamente gli hyperparameter.
Richiede: pip install ray[tune]
Esplora learning rate, augmentation intensity, loss weights.
"""
model = YOLO(model_path)
# Spazio di ricerca degli hyperparameters
space = {
'lr0': (1e-5, 1e-1), # log-uniform
'lrf': (0.01, 1.0),
'momentum': (0.6, 0.98),
'weight_decay': (0.0, 0.001),
'warmup_epochs': (0, 5),
'warmup_momentum': (0.0, 0.95),
'box': (0.02, 0.2),
'cls': (0.2, 4.0),
'mosaic': (0.0, 1.0),
'mixup': (0.0, 0.5),
'copy_paste': (0.0, 0.3),
}
result = model.tune(
data=data_path,
space=space,
epochs=50, # Epoch per ogni trial
iterations=100, # Numero di configurazioni da provare
optimizer='AdamW',
plots=True,
save=True
)
print("Migliori hyperparameter trovati:")
for k, v in result.items():
print(f" {k}: {v}")
# ---- Custom callback per monitoraggio avanzato ----
class YOLOTrainingMonitor:
"""
Callback per monitoring avanzato durante il training.
Traccia metriche custom e genera alert se necessario.
"""
def __init__(self, alert_threshold: float = 0.3):
self.best_map = 0.0
self.no_improve_count = 0
self.alert_threshold = alert_threshold
self.history = []
def on_train_epoch_end(self, trainer) -> None:
"""Chiamato alla fine di ogni epoch di training."""
metrics = trainer.metrics
current_map = metrics.get('metrics/mAP50(B)', 0.0)
self.history.append({
'epoch': trainer.epoch,
'map50': current_map,
'loss': trainer.loss.item()
})
if current_map > self.best_map:
self.best_map = current_map
self.no_improve_count = 0
else:
self.no_improve_count += 1
# Alert se il modello non migliora dopo 30 epoch
if self.no_improve_count == 30:
print(f"[WARN] Nessun miglioramento per 30 epoch. Best mAP: {self.best_map:.4f}")
# Utilizzo del monitor
model = YOLO('yolo26m.pt')
monitor = YOLOTrainingMonitor()
model.add_callback('on_train_epoch_end', monitor.on_train_epoch_end)
# model.train(data='dataset.yaml', epochs=200)
6.1 강력한 훈련을 위한 데이터 수집 전략
데이터 세트의 품질은 아키텍처보다 더 중요합니다. 훈련받은 YOLO26n 우수한 데이터는 열악한 데이터로 훈련된 YOLO26x보다 성능이 뛰어납니다. 여기에 황금률이 있습니다 데이터 수집을 위해:
고품질 YOLO 데이터세트를 위한 황금률
| 나는 기다린다 | 최소 요구 사항 | 최적 | 동기 부여 |
|---|---|---|---|
| 수업별 사진 | 500 | 2000+ | 더 많은 다양성 = 더 많은 일반화 |
| 이미지의 경계 상자 | 1-10 | 5-50 (실제 장면) | 너무 희박함 = 모델이 컨텍스트를 학습하지 않음 |
| 다양한 조건 | 2가지 조명 조건 | 낮/밤/실내/실외 | 도메인 이동에 대한 견고성 |
| 클래스 밸런싱 | 최대 5:1 비율 | 2:1 이상 | 계급 지배를 피하라 |
| 열차/발행/테스트 분할 | 70/20/10 | 80/10/10 | 개발 중에 사용된 적이 없는 테스트 세트 |
| 주석 품질 | 주석자 간 합의 > 0.8 | 2명 이상의 주석자의 합의 | 라벨 노이즈로 인해 학습 성능이 저하됩니다. |
7. 객체 감지 모범 사례
YOLO 모델 선택 가이드
| 대본 | 추천 모델 | 동기 부여 |
|---|---|---|
| 신속한 프로토타이핑 | YOLOv8n / YOLO26n | 빠른 훈련, 쉬운 디버깅 |
| 프로덕션(서버 GPU) | 욜로26m / 욜로26l | 더 나은 정확도/속도 균형 |
| 엣지(라즈베리 파이, 젯슨 나노) | INT8을 사용한 YOLOv8n | 최소 메모리 및 컴퓨터 사용량 |
| 최대 정확도 | 욜로26x | 최첨단, 강력한 GPU 필요 |
| 밀도가 높은 작은 물체 | imgsz=1280인 YOLOv8m | 입력이 클수록 세부 정보가 유지됩니다. |
일반적인 실수
- 불균형 데이터세트: 한 클래스에 다른 클래스보다 10배 더 많은 이미지가 있는 경우 모델은 해당 클래스에 특화됩니다. 가중 샘플링 또는 초과/과소 샘플링을 사용합니다.
- 임계값이 너무 낮음: Conf 임계값 0.1은 많은 거짓양성을 생성합니다. 0.25부터 시작하여 허용 가능한 정밀도를 얻을 때까지 늘립니다.
- IoU 임계값 NMS가 너무 낮음: 0.3은 밀집된 장면에서 유효한 상자를 제거합니다. 객체가 겹치는 장면에는 0.45-0.5를 사용합니다.
- 학습 이미지가 너무 작음: YOLO는 640x640에 최적화되어 있습니다. 320x320 이미지로 훈련하면 작은 물체의 정확도가 크게 떨어집니다.
- 산업 영역의 공격적인 확장: 공간적 맥락이 중요한 산업 이미지에서는 모자이크 확대가 비생산적일 수 있습니다.
- 도메인 정규화를 잊어버리세요: YOLO는 COCO로 사전 훈련되었습니다. 극단적인 영역 이동(예: 적외선 이미징, 현미경 검사)의 경우 처음부터 교육하는 것을 고려하세요.
결론
이 기사에서 우리는 최신 객체 감지에 대한 포괄적인 이해를 구축했습니다. YOLO와 함께 이론, 실습 및 배포를 다룹니다.
- 객체 감지의 기본 사항: 경계 상자, IoU, NMS, mAP 측정항목
- 3단계 YOLO 아키텍처: 백본, 넥 FPN+PAN, 헤드 앵커 없음
- 각 버전의 주요 기여를 통해 YOLOv1에서 YOLO26으로의 진화
- 고급 전략을 갖춘 맞춤형 데이터 세트에 대한 완벽한 교육: LR 준비, 코사인 어닐링, 클래스 가중 손실
- 최적의 설정을 찾기 위해 Ray Tune을 사용한 자동화된 하이퍼파라미터 튜닝
- 모든 하드웨어에 배포하기 위해 ONNX, TensorRT, OpenVINO, CoreML, NCNN으로 내보내기
- 데이터 세트 모범 사례: 주석의 수량, 다양성, 클래스 균형 및 품질
- YOLO26의 새로운 기능: Hybrid Attention 백본, 동적 NMS, +3.3 mAP 대 YOLOv8
시리즈 탐색
시리즈 간 리소스
- MLOps: 프로덕션에서 모델 제공 - 생산에 YOLO 모델이 필요합니다.
- 엣지의 컴퓨터 비전: 모바일 장치에 최적화됨 - Raspberry Pi 및 Jetson에 YOLO26 배포







