Comment affiner YOLO sur un jeu de données personnalisé#
Le fine-tuning adapte un modèle pré-entraîné pour reconnaître de nouvelles classes en partant de poids déjà appris plutôt que d'une initialisation aléatoire. Au lieu d'entraîner à partir de zéro pendant des centaines d'époques, le fine-tuning exploite des caractéristiques pré-entraînées COCO et converge sur des données personnalisées en une fraction du temps.
Ce guide couvre le fine-tuning de YOLO26 sur des jeux de données personnalisés, de l'utilisation de base aux techniques avancées comme le gel des couches et l'entraînement en deux étapes.
Fine-tuning vs entraînement à partir de zéro#
Un modèle pré-entraîné a déjà appris des caractéristiques visuelles générales — détection de contours, reconnaissance de textures, compréhension des formes — à partir de millions d'images. Le transfer learning par le biais du fine-tuning réutilise ces connaissances et enseigne uniquement au modèle à quoi ressemblent les nouvelles classes, ce qui explique pourquoi il converge plus rapidement et nécessite moins de données. S'entraîner à partir de zéro jette tout cela aux oubliettes et force le modèle à tout apprendre depuis les motifs au niveau des pixels, ce qui demande nettement plus de ressources.
| Fine-Tuning | Entraînement à partir de zéro | |
|---|---|---|
| Poids de départ | Pré-entraîné sur COCO (80 classes) | Initialisation aléatoire |
| Commande | YOLO("yolo26n.pt") | YOLO("yolo26n.yaml") |
| Convergence | Plus rapide - le backbone est déjà entraîné | Plus lente - toutes les couches apprennent de zéro |
| Exigences en données | Plus faibles - les caractéristiques pré-entraînées compensent le manque de données | Plus élevées - le modèle doit apprendre toutes les caractéristiques à partir du jeu de données seul |
| Quand utiliser | Classes personnalisées avec des images naturelles | Domaines fondamentalement différents de COCO (médical, satellite, radar) |
Lorsqu'un fichier .pt est chargé avec YOLO("yolo26n.pt"), les poids pré-entraînés sont stockés dans le modèle. L'appel de .train(data="custom.yaml") par la suite transfère automatiquement tous les poids compatibles vers la nouvelle architecture du modèle, réinitialise toutes les couches qui ne correspondent pas (telles que la tête de détection lorsque le nombre de classes diffère) et lance l'entraînement. Aucun chargement manuel des poids, manipulation de couches ou code de transfer learning personnalisé n'est requis.
Comment fonctionne le transfert de poids pré-entraînés#
Lorsqu'un modèle pré-entraîné est affiné sur un jeu de données avec un nombre différent de classes (par exemple, 80 classes COCO vers 5 classes personnalisées), Ultralytics effectue un transfert de poids conscient de la forme :
- Le backbone et le neck sont entièrement transférés - ces couches extraient des caractéristiques visuelles générales et leurs formes sont indépendantes du nombre de classes.
- La tête de détection est partiellement réinitialisée : les couches de sortie de classification (
cv3,one2one_cv3) ont des formes liées au nombre de classes (80 contre 5), elles ne peuvent donc pas être transférées et sont initialisées aléatoirement. Les couches de régression de boîtes englobantes (cv2,one2one_cv2) dans la tête ont des formes fixes quel que soit le nombre de classes, elles se transfèrent donc normalement. - La grande majorité des poids sont transférés lors du changement de nombre de classes. Par exemple, l'affinage de YOLO26n depuis COCO (80 classes) vers un jeu de données à 5 classes transfère 606 des 708 tenseurs de poids : seules les couches de classification dépendantes du nombre de classes sont réinitialisées, tandis que le backbone, le neck et les branches de régression de boîte restent intacts.
Pour les jeux de données ayant le même nombre de classes que le modèle pré-entraîné (par exemple, l'affinage de poids pré-entraînés sur COCO sur un autre jeu de données à 80 classes), 100 % des poids sont transférés, y compris la tête de détection.
Transférer des classes avec des alias de noms#
Ultralytics transfère les lignes de classification correspondantes par nom de classe entre les jeux de données, en ignorant la casse et les espaces environnants. Lorsque des classes équivalentes utilisent des noms différents, renomme les classes du checkpoint source en mémoire avant le chargement. Cela préserve les poids de classification pré-entraînés pour les concepts partagés qui seraient autrement traités comme non correspondants et initialisés aléatoirement.
Cet exemple Objects365 v2 vers COCO renomme les classes source sur le point de contrôle chargé, que train() transmet ensuite en tant que poids pré-entraînés :
from ultralytics import YOLO
# Source Objects365 v2 name -> target COCO name
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")
model.model.names = {i: ALIASES.get(name, name) for i, name in model.model.names.items()}
model.train(data="coco.yaml", epochs=100, imgsz=640)Exemple de fine-tuning de base#
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # load pretrained model
model.train(data="custom.yaml", epochs=50, imgsz=640)Choisir une taille de modèle#
Les modèles plus grands offrent plus de capacité mais aussi davantage de paramètres à mettre à jour, ce qui peut accroître le risque de surapprentissage lorsque les données d'entraînement sont limitées. Commencer avec un modèle plus petit (YOLO26n ou YOLO26s) et monter en puissance uniquement si les métriques de validation stagnent est une approche pragmatique. La taille de modèle optimale dépend de la complexité de la tâche, du nombre de classes, de la diversité du jeu de données et du matériel disponible pour le déploiement. Consulte la page du modèle YOLO26 complète pour connaître les tailles disponibles et les benchmarks de performance.
Sélection de l'optimiseur et du taux d'apprentissage#
Le paramètre par défaut optimizer=auto sélectionne l'optimiseur et le taux d'apprentissage en fonction du nombre total d'itérations d'entraînement :
- < 10 000 itérations (petits jeux de données ou peu d'époques) : AdamW avec un taux d'apprentissage faible et calculé automatiquement
- > 10 000 itérations (grands jeux de données) : MuSGD (un optimiseur hybride Muon+SGD) avec lr=0,01
Pour la plupart des tâches de fine-tuning, le paramètre par défaut fonctionne bien sans aucun réglage manuel. Envisage de définir l'optimiseur explicitement lorsque :
- L'entraînement est instable (pics de perte ou divergence) : essaie
optimizer=AdamW, lr0=0.001pour une convergence plus stable - Fine-tuning d'un grand modèle sur un petit jeu de données : un taux d'apprentissage plus faible comme
lr0=0.001aide à préserver les caractéristiques pré-entraînées
Quand optimizer=auto, les valeurs lr0 et momentum sont ignorées. Pour contrôler manuellement le taux d'apprentissage, définis explicitement l'optimiseur : optimizer=SGD, lr0=0.005.
Geler les couches#
Le gel empêche des couches spécifiques de se mettre à jour pendant l'entraînement. Cela accélère l'entraînement et réduit le surapprentissage lorsque le jeu de données est petit par rapport à la capacité du modèle.
Le paramètre freeze accepte soit un entier, soit une liste. Un entier freeze=10 gèle les 10 premières couches (indices 0 à 9), ce qui couvre l'essentiel du backbone YOLO26. Le backbone s'étend des couches 0 à 10, donc freeze=10 laisse le dernier bloc C2PSA (couche 10) entraînable ; utilise freeze=11 pour geler l'intégralité du backbone. Une liste peut contenir des indices de couches comme freeze=[0, 3, 5] pour un gel partiel du backbone, ou des chaînes de caractères de noms de modules comme freeze=["23.cv2", "23.one2one_cv2"] pour un contrôle fin sur des branches spécifiques au sein d'une couche (ici, les deux branches de régression de boîtes englobantes de la tête de détection).
model.train(data="custom.yaml", epochs=50, freeze=10)La profondeur de gel appropriée dépend de la similitude du domaine cible avec les données pré-entraînées et de la quantité de données d'entraînement disponibles :
| Scénario | Recommandation | Justification |
|---|---|---|
| Grand jeu de données, domaine similaire | freeze=None (par défaut) | Assez de données pour adapter toutes les couches sans surapprentissage |
| Petit jeu de données, domaine similaire | freeze=10 | Préserve les caractéristiques du backbone, réduit les paramètres entraînables |
| Très petit jeu de données | freeze=23 | Seule la tête de détection s'entraîne, minimisant le risque de surapprentissage |
| Domaine éloigné de COCO | freeze=None | Les caractéristiques du backbone peuvent ne pas bien se transférer et nécessitent un réentraînement |
La profondeur de gel peut aussi être traitée comme un hyperparamètre - essayer quelques valeurs (0, 5, 10) et comparer le mAP de validation est un moyen pratique de trouver le meilleur réglage pour un jeu de données spécifique.
Hyperparamètres clés pour le fine-tuning#
Le fine-tuning nécessite généralement moins d'ajustements d'hyperparamètres qu'un entraînement à partir de zéro. Les paramètres qui comptent le plus sont :
epochs: Le fine-tuning converge plus rapidement qu'un entraînement à partir de zéro. Commence avec une valeur modérée et utilisepatiencepour arrêter l'entraînement prématurément lorsque les métriques de validation stagnent.patience: La valeur par défaut de 100 est conçue pour de longs cycles d'entraînement. La réduire à 10-20 évite de perdre du temps sur des exécutions qui ont déjà convergé.warmup_epochs: L'échauffement (warmup) par défaut (3 époques) augmente progressivement le taux d'apprentissage à partir de zéro, ce qui évite que de grandes mises à jour de gradients n'endommagent les caractéristiques pré-entraînées lors des premières itérations. Conserver la valeur par défaut est recommandé, même pour le fine-tuning.
Pour consulter la liste complète des paramètres d'entraînement, reporte-toi à la référence de la configuration d'entraînement.
Fine-tuning en deux étapes#
Le fine-tuning en deux étapes divise l'entraînement en deux phases. La première étape gèle le backbone et n'entraîne que le neck et la tête, permettant aux couches de détection de s'adapter aux nouvelles classes sans perturber les caractéristiques pré-entraînées. La seconde étape dégèle toutes les couches et entraîne le modèle complet avec un taux d'apprentissage plus faible pour affiner le backbone pour le domaine cible.
Cette approche est particulièrement utile lorsque le domaine cible diffère significativement de COCO (images médicales, imagerie aérienne, microscopie), cas dans lesquels le backbone peut nécessiter une adaptation, mais où tout entraîner en même temps provoque de l'instabilité. Pour un dégel automatique avec une approche basée sur un callback, consulte Gel et dégel du backbone.
from ultralytics import YOLO
# Stage 1: freeze backbone, train head and neck
model = YOLO("yolo26n.pt")
model.train(data="custom.yaml", epochs=20, freeze=10, name="stage1", exist_ok=True)
# Stage 2: unfreeze all, fine-tune with lower lr
model = YOLO("runs/detect/stage1/weights/best.pt")
model.train(data="custom.yaml", epochs=30, lr0=0.001, name="stage2", exist_ok=True)Pièges courants#
Le modèle ne produit aucune prédiction#
- Données d'entraînement insuffisantes : l'entraînement avec très peu d'échantillons est la cause la plus fréquente - le modèle ne peut pas apprendre ou généraliser à partir de trop peu de données. Assure-toi d'avoir suffisamment d'exemples diversifiés par classe avant d'enquêter sur d'autres causes.
- Vérifie les chemins des jeux de données : des chemins incorrects dans
data.yamlgénèrent silencieusement zéro étiquette. Exécuteyolo detect val model=yolo26n.pt data=custom.yamlavant l'entraînement pour confirmer que les étiquettes se chargent correctement. - Abaisse le seuil de confiance : si des prédictions existent mais sont filtrées, essaie
conf=0.1pendant l'inférence. - Vérifie le nombre de classes : assure-toi que
ncdansdata.yamlcorrespond au nombre réel de classes dans les fichiers d'étiquettes.
Le mAP de validation stagne tôt#
- Ajoute plus de données : le fine-tuning bénéficie considérablement de données d'entraînement supplémentaires, surtout d'exemples diversifiés avec des angles, éclairages et arrière-plans variés.
- Vérifie l'équilibre des classes : les classes sous-représentées auront un AP faible. Utilise
cls_pwpour appliquer une pondération de classe par fréquence inverse (commence aveccls_pw=0.25pour un déséquilibre modéré, augmente à1.0pour un déséquilibre sévère). - Réduis l'augmentation : pour de très petits jeux de données, une forte augmentation peut nuire plus qu'elle ne aide. Essaie
mosaic=0.5oumosaic=0.0. - Augmente la résolution : pour les jeux de données comportant de petits objets, essaie
imgsz=1280pour préserver les détails.
La performance se dégrade sur les classes originales après le fine-tuning#
Ceci est connu sous le nom d'oubli catastrophique - le modèle perd des connaissances précédemment apprises lorsqu'il est affiné exclusivement sur de nouvelles données. L'oubli est pratiquement inévitable sans inclure des images du jeu de données original avec les nouvelles données. Pour atténuer cela :
- Fusionne les jeux de données : inclus des exemples des classes originales avec les nouvelles classes lors du fine-tuning. C'est le seul moyen fiable d'éviter l'oubli.
- Gèle le backbone et le neck : geler à la fois le backbone et le neck pour que seule la tête de détection s'entraîne aide pour les sessions de fine-tuning courtes avec un taux d'apprentissage très faible.
- Entraîne sur moins d'époques : plus le modèle s'entraîne exclusivement sur de nouvelles données, plus l'oubli augmente.
FAQ#
Il n'y a pas de minimum fixe - les résultats dépendent de la complexité de la tâche, du nombre de classes et de la similitude du domaine avec COCO. Des images plus diversifiées (éclairage, angles, arrière-plans variés) comptent plus que la quantité brute. Commence avec ce que tu as et augmente si les métriques de validation sont insuffisantes.
Les causes les plus fréquentes sont des chemins incorrects dans
data.yaml(ce qui produit silencieusement zéro étiquette), une non-correspondance entrencdans le fichier YAML et les fichiers d'étiquettes réels, ou un seuil de confiance trop élevé. Consulte les Pièges courants pour une liste de contrôle complète de dépannage.Cela dépend de la taille du jeu de données et de la similarité des domaines. Pour les petits jeux de données avec un domaine similaire à COCO, geler le backbone (
freeze=10) évite le surapprentissage. Pour des domaines très différents de COCO, laisser toutes les couches non gelées (freeze=None) permet au backbone de s'adapter. Consulte Gel des couches pour des recommandations détaillées.Inclus des exemples des classes d'origine dans les données d'entraînement aux côtés des nouvelles classes. Si ce n'est pas possible, geler davantage de couches (
freeze=10ou plus) et utiliser un taux d'apprentissage plus faible aide à préserver les connaissances pré-entraînées. Consulte Les performances se dégradent sur les classes d'origine pour plus de détails.
Charge un fichier
.ptpré-entraîné et appelle.train()avec le chemin d'un fichierdata.yamlpersonnalisé. Ultralytics gère automatiquement le transfert des poids, la réinitialisation de la tête de détection et la sélection de l'optimiseur. Consulte la section Fine-Tuning de Base pour l'exemple de code complet.