YOLO a detekce objektů: Od teorie k praxi s YOLOv8
V lednu 2026 vyšlo Ultralytics YOLO26, nejnovější vývoj rodiny YOLO, která nově definovala detekci objektů v reálném čase. Ale abyste pochopili YOLO26, musíte nejprve pochopit YOLO: čím je mimořádně rychlý, jak funguje jeho architektura, proč se stal de facto standard pro detekci objektů v průmyslových, automobilových a sledovacích aplikacích a robotiky. V tomto článku vybudujeme úplné pochopení moderní detekce objektů, od YOLOv1 po praktickou implementaci s YOLOv8 a YOLO26.
Poznámka: Toto je první kompletní výukový program v italštině na YOLO26. Seriál Počítač Vision with Deep Learning na federicocalo.dev a italský referenční zdroj pro tato témata.
Co se naučíte
- Jak funguje detekce objektů: ohraničující rámečky, skóre spolehlivosti, třídy
- Architektura YOLO: páteř, krk, hlava - od teorie k realizaci
- Příběh YOLO: Od v1 po YOLO26, klíčová vylepšení v každé verzi
- Základní metriky: IoU, mAP, křivky přesnosti vyvolání
- Kompletní školení o přizpůsobeném datovém souboru s YOLOv8 (Ultralytics)
- Odvozování videa a webové kamery v reálném čase
- Export a nasazení: ONNX, TensorRT, OpenVINO
- YOLO26: architektonické novinky ledna 2026
- Osvědčené postupy pro přípravu datové sady a rozšiřování dat
1. Detekce objektů: Základní pojmy
L'detekce objektu a úkol současně identifikovat a klasifikovat jeden nebo více objekty uvnitř obrázku. Na rozdíl od klasifikace (jeden štítek pro celý obrázek), Detekce musí odpovědět na tři otázky: Co je to na obrázku, Kde je nalezen (ohraničující rámeček) a se kterým důvěra zjistil to.
1.1 Výstupní reprezentace
Každý detekovaný objekt je reprezentován a ohraničující krabice s 5 základními hodnotami plus vektor pravděpodobnosti pro třídy:
# 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 Non-Maximum Supression (NMS)
Detekční modely generují stovky překrývajících se návrhů ohraničovacích rámečků. Tam Non-Maximum Supression (NMS) a algoritmus, který vybere nejlepší box odstranění duplicit na základě metriky Průsečík nad Unií (IoU).
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 Metriky hodnocení
Základní metriky pro detekci objektů
| Metrický | Vzorec | Význam |
|---|---|---|
| Dlužní úpis | Křižovatka / Unie | Překrývání mezi předpovězenou krabicí a základní pravdou |
| Přesnost | TP / (TP + FP) | Kolik předpovědí je správných |
| Odvolání | TP / (TP + FN) | Kolik skutečných předmětů bylo nalezeno |
| AP@0,5 | Plocha pod PR křivkou při IoU=0,5 | Přesnost pro jednu třídu |
| mAP@0,5 | Průměr AP ve všech třídách | Hlavní metrika pro srovnání modelů |
| mAP@0,5:0,95 | Průměrný mAP při IoU 0,5-0,95 (krok 0,05) | Přesnější metrika (standardní COCO) |
2. Architektura YOLO: Jak to funguje
YOLO (Podíváš se jen jednou) zavedli Redmon et al. v roce 2016 s revolučním nápadem: považovat detekci objektů za a jediný regresní problém, předchozí přímo ohraničující boxy a pravděpodobnosti tříd z jednoho dopředného průchodu sítí. Žádné návrhy regionů, žádné dvě fáze: jediná síť, jediný závěr, extrémní rychlost.
2.1 Třístupňová architektura: páteř, krk, hlava
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 Evolution: Od verze 1 po YOLO26
Historie verzí YOLO
| Verze | Rok | Vedoucí inovace | mAP (COCO) |
|---|---|---|---|
| YOLOv1 | 2016 | Jednostupňová detekce, mřížka SxS | 63,4 (VOC) |
| YOLOv3 | 2018 | Víceúrovňová detekce, Darknet-53 | 33,0 |
| YOLOv5 | 2020 | Páteř CSP, augmentace mozaiky | 48,2 |
| YOLOv7 | 2022 | Prodloužený ELAN, pomocné hlavice | 51.4 |
| YOLOv8 | 2023 | Bezkotevní, oddělená hlava, blok C2f | 53,9 |
| YOLOv9 | 2024 | GELAN, Informace o programovatelném gradientu | 55,6 |
| YOLOv10 | 2024 | Přiřazení dvou značek bez NMS | 54,4 |
| YOLO26 | ledna 2026 | Páteř Hybrid Attention, Dynamic NMS | 57,2 |
2.3 Detekce bez kotvy: revoluce YOLOv8
Jednou z nejvýznamnějších inovací YOLOv8 je opuštění kotevní boxy. Předchozí verze YOLO používaly výchozí kotvy: krabice pevné velikosti počítané přes k-znamená shlukování na datové sadě. To vyžadovalo pečlivý výběr kotev pro každou datovou sadu. YOLOv8 (a YOLO26) mají jeden přístup bez kotvy: model předpovídá přímo souřadnice středu a rozměry krabice, eliminující vychýlení výchozích kotev.
3. Školení o vlastní datové sadě s YOLOv8
3.1 Příprava datové sady
YOLOv8 používá formát YOLO TXT pro anotace: soubor .txt pro každý obrázek
s jedním řádkem pro každý objekt ve formátu:
<class_id> <x_center> <y_center> <width> <height>
(normalizované souřadnice 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 Trénink s 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 Vyvození a vizualizace
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. Export a nasazení: ONNX, TensorRT, OpenVINO
4.1 Exportní formáty
Exportní formáty YOLOv8
| Formát | Cíl | Zrychlení vs PyTorch | Use Case |
|---|---|---|---|
| ONNX | Multiplatformní | 1,5-2x | Maximální přenosnost |
| TensorRT | GPU NVIDIA | 5-8x | Maximální rychlost na GPU NVIDIA |
| OpenVINO | CPU/GPU Intel | 3-4x | Servery Intel, hrany Intel |
| CoreML | Apple Silicon | 3-5x | Nasazení iOS/macOS |
| TFLite | Mobile/Edge | 2-3x | Android, Raspberry Pi |
| NCNN | CPU ARM | 2-4x | Raspberry Pi, ARM embedded |
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: Co je nového v lednu 2026
Vydáno společností Ultralytics v lednu 2026, YOLO26 zavádí inovace významné architektonické prvky, které jej staví jako referenční model v reálném čase detekce objektů v roce 2026.
5.1 Klíčové inovace
YOLO26 vs YOLOv8: Technické srovnání
| Charakteristický | YOLOv8 | YOLO26 |
|---|---|---|
| Páteř | CSP-DarkNet s C2f | Hybridní pozornost + C3k2 |
| Krk | PANet | Vylepšený PANet s SCDown |
| Hlava | Odpojené bez kotvy | Bez ukotvení s dvojitou hlavou |
| NMS | standardy NMS | Dynamické NMS (naučené) |
| mAP@0,5 (COCO) | 53,9 | 57,2 (+3,3) |
| Závěr (ms) | 4,1 ms (A100) | 3,8 ms (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. Ladění hyperparametrů a pokročilé tréninkové strategie
Úspěch YOLO závisí jak na architektuře, tak na optimalizaci procesů školení. Od výběru plánovače rychlosti učení až po řešení nerovnováhy ve třídě, tyto techniky dělají rozdíl mezi průměrným modelem a modelem připraveným k výrobě.
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 Strategie sběru dat pro robustní školení
Kvalita datové sady je důležitější než architektura. A YOLO26n trénoval na vynikající data předčí YOLO26x trénovaný na špatných datech. Zde jsou zlatá pravidla pro sběr dat:
Zlatá pravidla pro kvalitní datové sady YOLO
| čekám | Minimální požadavek | Optimální | Motivace |
|---|---|---|---|
| Obrázky podle třídy | 500 | 2000+ | Více rozmanitosti = více zobecnění |
| Ohraničovací rámeček pro obrázek | 1-10 | 5-50 (skutečné scény) | Příliš řídké = model se neučí kontext |
| Různorodost podmínek | 2 světelné podmínky | Den/noc/vnitřní/venkovní | Robustnost k posunu domény |
| Vyvážení třídy | Maximální poměr 5:1 | 2:1 nebo lepší | Vyhněte se třídní dominanci |
| Split vlak/val/test | 70/20/10 | 80/10/10 | Testovací sada nebyla během vývoje nikdy použita |
| kvalita anotace | Mezianotační dohoda > 0,8 | Konsensus 2+ anotátorů | Hluk štítků zhoršuje trénink |
7. Nejlepší postupy pro detekci objektů
Průvodce výběrem modelu YOLO
| Scénář | Doporučený model | Motivace |
|---|---|---|
| Rychlé prototypování | YOLOv8n / YOLO26n | Rychlý výcvik, snadné ladění |
| Produkce (GPU serveru) | YOLO26m / YOLO26l | Lepší vyvážení přesnosti a rychlosti |
| Edge (Raspberry Pi, Jetson Nano) | YOLOv8n s INT8 | Minimální využití paměti a počítače |
| Maximální přesnost | YOLO26x | Nejmodernější, vyžaduje výkonný GPU |
| Malé předměty s vysokou hustotou | YOLOv8m s imgsz=1280 | Větší vstup zachovává detaily |
Časté chyby
- Nevyvážená datová sada: Pokud má jedna třída 10x více obrázků než ostatní, model se na ni specializuje. Použijte vážené vzorkování nebo převzorkování/podvzorkování.
- Příliš nízký práh: Konf. práh 0,1 vytváří mnoho falešně pozitivních výsledků. Začněte s 0,25 a zvyšujte, dokud nezískáte přijatelnou přesnost.
- Příliš nízký práh IoU NMS: 0,3 eliminuje platné rámečky v hustých scénách. Pro scény s překrývajícími se objekty použijte 0,45-0,5.
- Tréninkové obrázky jsou příliš malé: YOLO je optimalizováno pro 640x640. Trénink s obrázky 320x320 výrazně snižuje přesnost na malých objektech.
- Agresivní augmentace v průmyslových doménách: Mozaikové zvětšení může být kontraproduktivní na průmyslových snímcích, kde je důležitý prostorový kontext.
- Zapomeňte na normalizaci pro doménu: YOLO předtrénováno na COCO. Pro extrémní posuny domén (např. infračervené zobrazování, mikroskopie) zvažte trénink od začátku.
Závěry
V tomto článku jsme vybudovali komplexní porozumění moderní detekci objektů s YOLO, pokrývající teorii, praxi a nasazení:
- Základy detekce objektů: hraniční boxy, IoU, NMS, metriky mAP
- Třístupňová architektura YOLO: páteř, krk FPN+PAN, hlava bez kotvení
- Vývoj od YOLOv1 k YOLO26 s klíčovými příspěvky každé verze
- Kompletní školení na přizpůsobené datové sadě s pokročilými strategiemi: zahřívání LR, kosinové žíhání, třída vážená ztráta
- Automatizované ladění hyperparametrů pomocí Ray Tune pro nalezení optimálního nastavení
- Export do ONNX, TensorRT, OpenVINO, CoreML, NCNN pro nasazení na jakýkoli hardware
- Osvědčené postupy datové sady: množství, rozmanitost, vyváženost tříd a kvalita anotací
- Co je nového v YOLO26: Páteř Hybrid Attention, Dynamic NMS, +3,3 mAP vs YOLOv8
Navigace série
Meziřadové zdroje
- MLOps: Model Serving in Production - potřebujete svůj model YOLO ve výrobě
- Počítačové vidění na hraně: Optimalizováno pro mobilní zařízení - nasadit YOLO26 na Raspberry Pi a Jetson







