Come ottimizzare YOLO su un dataset personalizzato
La messa a punto adatta un modello preaddestrato al riconoscimento di nuove classi partendo dai pesi appresi anziché da un'inizializzazione casuale. Invece di addestrare da zero per centinaia di epoche, la messa a punto riutilizza le caratteristiche preaddestrate di COCO e converge sui dati personalizzati in una frazione del tempo.
Questa guida illustra l'ottimizzazione fine di YOLO26 su dataset personalizzati, dall'uso di base a tecniche avanzate come il congelamento dei layer e l'addestramento in due fasi.
Ottimizzazione fine o addestramento da zero#
Un modello pretrained ha già appreso caratteristiche visive generali — rilevamento dei bordi, riconoscimento delle texture e comprensione delle forme — da milioni di immagini. Il transfer learning tramite ottimizzazione fine riutilizza queste conoscenze e insegna al modello soltanto l'aspetto delle nuove classi, consentendo una convergenza più rapida e richiedendo meno dati. L'addestramento da zero elimina tutto questo e costringe il modello ad apprendere ogni aspetto partendo dai pattern a livello di pixel, richiedendo molte più risorse. Consulta la guida alla configurazione YAML del modello per scoprire in che modo i file .yaml che definiscono solo l'architettura differiscono dai checkpoint.
| Ottimizzazione fine | Addestramento da zero | |
|---|---|---|
| Pesi iniziali | Pretrained su COCO (80 classi) | Inizializzazione casuale |
| Comando | YOLO("yolo26n.pt") | YOLO("yolo26n.yaml") |
| Convergenza | Più rapida: il backbone è già addestrato | Più lenta: tutti i layer apprendono da zero |
| Requisiti dei dati | Inferiori: le caratteristiche pretrained compensano la minore quantità di dati | Superiori: il modello deve apprendere tutte le caratteristiche solo dal dataset |
| Quando usarla | Classi personalizzate con immagini naturali | Domini fondamentalmente diversi da COCO (medicina, satellite, radar) |
Quando un file .pt viene caricato con YOLO("yolo26n.pt"), i pesi preaddestrati vengono memorizzati nel modello. La chiamata a .train(data="custom.yaml") trasferisce automaticamente tutti i pesi compatibili alla nuova architettura del modello, reinizializza i layer non corrispondenti (come il layer di classificazione finale quando il numero di classi è diverso) e avvia l'addestramento. Non è necessario caricare manualmente i pesi, manipolare i layer o scrivere codice personalizzato per il transfer learning.
Come funziona il trasferimento dei pesi pretrained#
Quando si ottimizza un modello pretrained su un dataset con un numero diverso di classi (ad esempio, passando dalle 80 classi di COCO a 5 classi personalizzate), Ultralytics esegue il trasferimento dei pesi tenendo conto delle forme:
- Il backbone e il neck vengono trasferiti completamente: questi layer estraggono caratteristiche visive generali e le loro forme non dipendono dal numero di classi.
- La head di rilevamento viene reinizializzata parzialmente - solo la convoluzione finale di ciascun ramo di classificazione (
cv3,one2one_cv3) ha una forma legata al numero di classi (80 contro 5); i layer nascosti del ramo vengono trasferiti quando entrambi i modelli condividono una scala. Le righe compatibili i cui nomi delle classi corrispondono vengono rimappate prima di inizializzare quelle non corrispondenti. I layer di regressione delle box (cv2,one2one_cv2) nella head hanno forme fisse indipendentemente dal numero di classi, quindi vengono trasferiti normalmente. - La stragrande maggioranza dei pesi viene trasferita quando si cambia il numero di classi. Ad esempio, il fine-tuning di YOLO26n da COCO (80 classi) a un dataset con 5 classi trasferisce 696 dei 708 tensori dei pesi, oltre alle righe di classificazione compatibili abbinate in base al nome.
Per i dataset con lo stesso numero di classi del modello pretrained (ad esempio, ottimizzando pesi pretrained su COCO su un altro dataset di 80 classi), viene trasferito il 100% dei pesi, inclusa la testa di rilevamento.
Trasferire classi con nomi alternativi#
Ultralytics trasferisce le righe corrispondenti della testa di classificazione tra dataset in base al nome della classe, ignorando maiuscole, minuscole e spazi iniziali o finali. Se classi equivalenti hanno nomi diversi, rinomina in memoria le classi del checkpoint sorgente prima di caricarlo. In questo modo si preservano i pesi di classificazione pretrained per i concetti condivisi, che altrimenti verrebbero considerati senza corrispondenza e inizializzati casualmente.
Questo esempio di passaggio da Objects365 v2 a COCO rinomina le classi sorgente nel checkpoint caricato; train() le passa quindi come pesi pretrained:
from ultralytics import YOLO
# # Nome della classe sorgente Objects365 v2 (in minuscolo) -> nome della classe di destinazione COCO
ALIASES = {
"wild bird": "bird",
"handbag/satchel": "handbag",
"luggage": "suitcase",
"bowl/basin": "bowl",
"orange/tangerine": "orange",
"monitor/tv": "tv",
"stuffed toy": "teddy bear",
"hair dryer": "hair drier",
}
model = YOLO("path/to/yolo26s-objects365.pt")
# # I nomi delle classi Objects365 sono in formato Titolo, quindi confrontali in minuscolo
model.model.names = {i: ALIASES.get(name.lower(), name) for i, name in model.model.names.items()}
model.train(data="coco.yaml", epochs=100, imgsz=640)L'impostazione cls_remap abilita per impostazione predefinita questo trasferimento basato sui nomi. Durante l'addestramento viene stampato Remapped N/M cls head rows from pretrained weights by class name quando vengono copiate righe compatibili; imposta cls_remap=False per disabilitarlo.
Esempio di ottimizzazione fine di base#
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # carica il modello preaddestrato
model.train(data="custom.yaml", epochs=50, imgsz=640)Scegliere la dimensione del modello#
I modelli più grandi hanno maggiore capacità, ma anche più parametri da aggiornare, il che può aumentare il rischio di overfitting quando i dati di addestramento sono limitati. Un approccio pratico consiste nell'iniziare con un modello più piccolo (YOLO26n o YOLO26s e aumentare le dimensioni solo se le metriche di convalida si stabilizzano. Le dimensioni del modello ottimali dipendono dalla complessità del compito, dal numero di classi, dalla diversità del dataset e dall'hardware disponibile per la distribuzione.
Scelta dell'ottimizzatore e del tasso di apprendimento#
L'impostazione predefinita optimizer=auto seleziona l'ottimizzatore e il tasso di apprendimento in base al numero totale di iterazioni di addestramento:
- 10.000 iterazioni o meno (dataset piccoli o poche epoche): AdamW con un tasso di apprendimento basso, calcolato automaticamente
- Oltre 10.000 iterazioni (dataset di grandi dimensioni): MuSGD (un ottimizzatore ibrido Muon+SGD) con lr=0.01
Per la maggior parte delle attività di ottimizzazione fine, l'impostazione predefinita funziona bene senza regolazioni manuali. Valuta di impostare esplicitamente l'ottimizzatore nei seguenti casi:
- Addestramento instabile (la perdita aumenta improvvisamente o diverge): prova
optimizer=AdamW, lr0=0.001per una convergenza più stabile - Ottimizzazione fine di un modello grande su un dataset piccolo: un ottimizzatore esplicito con un tasso di apprendimento più basso, come
optimizer=AdamW, lr0=0.001, può aiutare a preservare le caratteristiche pretrained
Quando è impostato optimizer=auto, il valore lr0 viene ignorato. Per controllare manualmente il tasso di apprendimento, imposta esplicitamente l'ottimizzatore: optimizer=SGD, lr0=0.005.
Congelamento dei layer#
Il congelamento impedisce a determinati layer di aggiornarsi durante l'addestramento. Questo accelera l'addestramento e riduce l'overfitting quando il dataset è piccolo rispetto alla capacità del modello.
Il parametro freeze accetta un intero o un elenco. Un intero freeze=10 congela i primi 10 layer (indici 0-9), che comprendono gran parte del backbone di YOLO26. Il backbone si estende dai layer 0 a 10, quindi freeze=10 lascia addestrabile il blocco C2PSA finale (layer 10); usa freeze=11 per congelare l'intero backbone. Un elenco può contenere indici di layer, come freeze=[0, 3, 5], per congelare parzialmente il backbone, oppure stringhe con i nomi dei moduli, come freeze=["23.cv2", "23.one2one_cv2"], per controllare nel dettaglio rami specifici all'interno di un layer (in questo caso, entrambi i rami di regressione dei box della testa di rilevamento).
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.train(data="custom.yaml", epochs=50, freeze=10)La profondità di congelamento ideale dipende da quanto il dominio di destinazione è simile ai dati di preaddestramento e dalla quantità di dati di addestramento disponibile:
| Scenario | Consiglio | Motivazione |
|---|---|---|
| Dataset ampio, dominio simile | freeze=None (predefinito) | Dati sufficienti per adattare tutti i layer senza overfitting |
| Dataset piccolo, dominio simile | freeze=10 | Mantiene le feature del backbone e riduce i parametri addestrabili |
| Dataset molto piccolo | freeze=23 | Si addestra solo la testa di rilevamento, riducendo al minimo il rischio di overfitting |
| Dominio molto diverso da COCO | freeze=None | Le feature del backbone potrebbero non essere trasferibili in modo efficace e potrebbero richiedere un nuovo addestramento |
Anche la profondità di congelamento può essere trattata come un iperparametro: provare alcuni valori (0, 5, 10) e confrontare mAP di convalida è un modo pratico per trovare l'impostazione migliore per un dataset specifico.
Iperparametri chiave per il fine-tuning#
In genere, il fine-tuning richiede meno modifiche agli iperparametri rispetto all'addestramento da zero. I parametri più importanti sono:
epochs: il fine-tuning converge più rapidamente dell'addestramento da zero. Inizia con un valore moderato e usapatienceper interrompere in anticipo quando le metriche di validazione si stabilizzano.patience: il valore predefinito di 100 è pensato per addestramenti lunghi. Ridurlo a 10-20 evita di sprecare tempo in esecuzioni già convergenti.warmup_epochs: il warmup porta gradualmente il learning rate al valore previsto dal programma durante le prime epoche, così i batch iniziali hanno meno probabilità di alterare le feature preaddestrate. Mantienilo diverso da zero durante il fine-tuning, ma non è necessario usare il valore predefinito di 3 epoche: la ricerca evolutiva alla base del fine-tuning ufficiale di YOLO26 su COCO — una continuazione di più epoche a partire dai pesi di Objects365 — ha stabilito circa un'epoca per ogni dimensione del modello.
Per l'elenco completo dei parametri di addestramento, consulta il riferimento alla configurazione di addestramento. Per controllare comportamenti non esposti dai parametri — learning rate per layer, gradient clipping o metriche di validazione personalizzate — crea una sottoclasse del trainer.
Fine-tuning in due fasi#
Il fine-tuning in due fasi suddivide l'addestramento in due fasi. Nella prima fase, il backbone viene congelato e si addestrano solo neck e head, consentendo ai layer di rilevamento di adattarsi alle nuove classi senza alterare le feature preaddestrate. Nella seconda fase, si scongelano tutti i layer e si addestra il modello completo con un learning rate inferiore, per perfezionare il backbone in funzione del dominio di destinazione.
Questo approccio è particolarmente utile quando il dominio di destinazione è molto diverso da COCO (immagini mediche, immagini aeree, microscopia), poiché il backbone potrebbe richiedere un adattamento, mentre addestrare tutto in una volta può causare instabilità. Per scongelare automaticamente i layer con un approccio basato su callback, consulta Congelare e scongelare il backbone.
from ultralytics import YOLO
# Fase 1: congela il backbone, addestra head e neck
model = YOLO("yolo26n.pt")
model.train(data="custom.yaml", epochs=20, freeze=10, name="stage1", exist_ok=True)
# Fase 2: scongela tutto, esegui il fine-tuning con un lr inferiore
model = YOLO("runs/detect/stage1/weights/best.pt")
model.train(data="custom.yaml", epochs=30, optimizer="AdamW", lr0=0.001, name="stage2", exist_ok=True)Problemi comuni#
Il modello non produce predizioni#
-
Dati di addestramento insufficienti: addestrare con pochissimi campioni è la causa più comune; il modello non può apprendere né generalizzare con così pochi dati. Prima di indagare altre cause, assicurati di avere abbastanza esempi diversificati per ogni classe.
-
Controlla i percorsi del dataset: i percorsi delle immagini non validi generano un errore del dataset. I singoli file di etichette mancanti o vuoti vengono trattati come immagini di sfondo e segnalati durante la scansione; una suddivisione di addestramento senza etichette genera un errore. Convalida il dataset prima dell'addestramento:
yolo detect val model=yolo26n.pt data=custom.yaml -
Abbassa la soglia di confidenza: se le predizioni sono presenti ma vengono filtrate, prova
conf=0.1durante l'inferenza. -
Verifica il numero di classi: assicurati che
ncindata.yamlcorrisponda al numero effettivo di classi nei file di etichette.
La mAP di validazione si stabilizza presto#
- Aggiungi altri dati: il fine-tuning trae notevole beneficio da ulteriori dati di addestramento, soprattutto da esempi diversificati con angolazioni, illuminazione e sfondi vari.
- Controlla il bilanciamento delle classi: le classi sottorappresentate avranno AP bassi. Aggiungi altri esempi oppure ottimizza
cls_pwsul set di validazione. - Riduci l'augmentation: con dataset molto piccoli, un'augmentation intensa può essere più dannosa che utile. Prova
mosaic=0.5oppuremosaic=0.0. - Aumenta la risoluzione: per i dataset con oggetti piccoli, prova
imgsz=1280per preservare i dettagli.
Le prestazioni sulle classi originali peggiorano dopo il fine-tuning#
Questo fenomeno è noto come oblio catastrofico: il modello perde le conoscenze apprese in precedenza quando viene messo a punto esclusivamente su nuovi dati. L'oblio è perlopiù inevitabile se non si includono immagini del dataset originale insieme ai nuovi dati. Per ridurne l'impatto:
- Unisci i dataset: durante il fine-tuning, includi esempi delle classi originali insieme a quelli delle nuove classi. È l'unico metodo affidabile per evitare l'oblio.
- Congela backbone e neck: congelare sia il backbone sia il neck, in modo da addestrare solo la testa di rilevamento, è utile per brevi sessioni di fine-tuning con un learning rate molto basso.
- Riduci il numero di epoche: più a lungo il modello viene addestrato esclusivamente sui nuovi dati, più aumenta l'oblio.
Domande frequenti#
Non esiste un minimo fisso: i risultati dipendono dalla complessità dell'attività, dal numero di classi e dalla somiglianza del dominio con COCO. Le immagini più diversificate (con illuminazione, angolazioni e sfondi vari) contano più della quantità grezza. Inizia con i dati che hai e aumentali se le metriche di validazione non sono sufficienti.
Carica un file
.ptpreaddestrato e chiama.train()indicando il percorso di undata.yamlpersonalizzato. Ultralytics gestisce automaticamente il trasferimento dei pesi, la reinizializzazione del layer di classificazione e la selezione dell'ottimizzatore. Per l'esempio di codice completo, consulta la sezione Fine-tuning di base.Le cause più comuni sono percorsi delle immagini non validi, file di etichette mancanti o vuoti, una mancata corrispondenza tra
ncnel file YAML e i file di etichette effettivi, oppure una soglia di confidenza troppo alta. Consulta Problemi comuni per una checklist completa di risoluzione dei problemi.Dipende dalle dimensioni del dataset e dalla somiglianza del dominio. Per dataset piccoli con un dominio simile a COCO, congelare il backbone (
freeze=10) previene l'overfitting. Per domini molto diversi da COCO, lasciare scongelati tutti i layer (freeze=None) consente al backbone di adattarsi. Consulta Congelamento dei layer per consigli dettagliati.Includi nei dati di addestramento esempi delle classi originali insieme a quelli delle nuove classi. Se non è possibile, congelare più layer (
freeze=10o un valore superiore) e usare un learning rate inferiore aiuta a preservare le conoscenze preaddestrate. Per maggiori dettagli, consulta Le prestazioni sulle classi originali peggiorano.
Commenti4
Fai una domanda, condividi i tuoi risultati o aiuta qualcun altro.
Yuan Yao(modificato)这是我在训练时遇到的一个问题,
Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 81/1000 6.68G 0 0 0 195 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.1s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 82/1000 6.68G 0 0 0 184 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.0it/s 11.3s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 83/1000 6.68G 0 0 0 221 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:19 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.1s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 84/1000 6.68G 0 0 0 212 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.2s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 85/1000 6.68G 0 0 0 221 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.2s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 86/1000 6.68G 0 0 0 216 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.0s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 87/1000 6.68G 0 0 0 198 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:19 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.0it/s 11.4s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 88/1000 6.68G 0 0 0 229 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.2s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 89/1000 6.68G 0 0 0 182 640: 100% ━━━━━━━━━━━━ 121/121 1.1s/it 2:18 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.0s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 90/1000 6.68G 0 0 0 218 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.0s all 1449 8364 0 0 0 0 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 91/1000 6.68G 0 0 0 181 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:19 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.1it/s 11.1s all 1449 8364 0 0 0 0mAP一直是0。当时我用的是yaml文件用于训练模型,
model_name = r"H:\1_yaoyuan\code\01_yolo26\yolo_Project\ultralytics-main\ultralytics\cfg\models\11\yolo11.yaml"
之后改成了,
model_name = 'yolo11n.pt' # 首次运行会自动下载
然后训练才变的正常,
Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 2/1000 5.47G 2.825 1.856 0.8776 131 640: 100% ━━━━━━━━━━━━ 121/121 1.2s/it 2:20 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 2.0it/s 11.5s all 1449 8364 0.554 0.314 0.32 0.117 Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 3/1000 5.47G 2.904 1.726 0.8738 206 640: 100% ━━━━━━━━━━━━ 121/121 1.1s/it 2:19 Class Images Instances Box(P R mAP50 mAP50-95): 100% ━━━━━━━━━━━━ 23/23 1.9it/s 11.9s all 1449 8364 0.496 0.29 0.269 0.085这是为什么?微调和从头开始训练差别很大吗? 我问了deepseek,他说:“.yaml 路径迫使模型在毫无知识的情况下承受初始的梯度冲击,导致了数值崩溃;而 .pt 路径让模型从一个稳定的知识基础开始,顺利进入了正常的学习轨道。”
可如果是这样,如果这个世界还没有yolo的pt文件,那么是不是mAP永远都是0了?
Yuan Yao我还是刚学习的阶段,如果我问的问题很幼稚我非常抱歉
Paula DerrengerUltralytics不用道歉,这是很合理的问题!不是的:从
.yaml构建模型是随机初始化,从yolo11n.pt开始则利用预训练特征,通常收敛更快、需要的数据更少;YOLO11 官方示例也支持从零训练,并不需要已有.pt才能学会检测。你这里不仅 mAP 为 0,三个 loss 也连续为 0,这不能简单解释为“从零训练比较慢”,更应优先排查训练数值异常;仅凭这些日志,还不能确认 DeepSeek 所说的“梯度冲击”。建议保留 YAML,在原来的model.train(...)中加入amp=False,重新启动训练以排查混合精度问题(AMP 检查说明)。如果仍然全为 0,请贴出完整训练代码、启动到首个异常 epoch 的文本日志,以及 GPU 型号、Ultralytics 和 PyTorch 版本。
somerongit
Thank You, It's Really a great elaborate guide.