Étiqueter et entraîner des modèles de pose
Rédige des étiquettes de points clés avec visibilité, définis correctement kpt_shape et flip_idx, entraîne le modèle sur COCO8-pose et convertis les points clés en angles articulaires soumis à un seuil de confiance.
L’estimation de pose représente chaque personne par 17 points numérotés. Ces points permettent de calculer les valeurs que les produits proposent : angles des coudes pour l’ergonomie, angles des genoux pour la rééducation, hauteur des hanches pour la détection des chutes. Deux clés du jeu de données déterminent si ces points sont fiables ; une erreur sur l’une ou l’autre ne génère aucune erreur — seulement un modèle qui confond la gauche et la droite, ou qui s’entraîne discrètement sans utiliser son augmentation la moins coûteuse.
Pratique
La ligne d’étiquette des points clés#

Une ligne d’étiquette de pose est une ligne de détection à laquelle on ajoute des points clés. Chaque coordonnée est normalisée entre 0 et 1, et les points clés suivent le même ordre fixe dans chaque ligne. Voici une vraie ligne COCO8-pose, tronquée après quatre points clés :
0 0.671279 0.617945 0.645759 0.726859 0.519751 0.381250 2.000000 0.550936 0.348438 2.000000 0.488565 0.367188 2.000000 0.642412 0.354687 2.000000 ...| Valeurs | Signification |
|---|---|
0 | indice de classe (personne) |
0.671279 0.617945 0.645759 0.726859 | centre x de la boîte, centre y, largeur, hauteur |
0.519751 0.381250 2 | point clé 0 (nez) : x, y, visibilité |
| … | 16 points clés supplémentaires selon le même format : 5 + 17 × 3 = 56 valeurs par ligne |
L’indicateur de visibilité suit la convention COCO : 2 visible, 1 étiqueté mais occulté, 0 non étiqueté. Ultralytics masque les points clés dont la valeur est v = 0 dans la fonction de perte, afin que l’absence d’un poignet n’apprenne pas au modèle à prédire les poignets en (0, 0).

Utilise 1 pour une articulation occultée que tu peux localiser avec assurance — une hanche derrière une table, un poignet derrière le dos ; elle contribue toujours à l'entraînement. Réserve 0 aux articulations que personne ne pourrait localiser. Étiqueter chaque articulation cachée avec 0 élimine précisément les cas difficiles dont le modèle a besoin.
Le guide sur les jeux de données de pose couvre également la variante à deux valeurs (x y uniquement, kpt_shape: [K, 2]) destinée aux jeux de données sans visibilité.
kpt_shape et flip_idx#
Le YAML du jeu de données ajoute deux clés aux clés habituelles path, train, val et names :
kpt_shape: [17, 3] # 17 keypoints, each x, y, visibility
flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]
flip_idx indique à l’augmentation par retournement horizontal quel point clé correspond à chacun dans l’image miroir. Quand on retourne une personne, son poignet gauche (9) se retrouve à la place de son poignet droit (10) ; la position 9 de flip_idx contient donc 10, et la position 10 contient 9. Le nez (0) correspond à lui-même. Chaque augmentation déplace le squelette avec les pixels, et le retournement est la seule qui nécessite aussi la table de correspondance des indices :

Que se passe-t-il lorsque flip_idx est incorrect, comme le confirme le code source installé :
- Manquant : l’entraînement affiche un avertissement et définit
fliplretflipudsur 0. Tu perds ainsi ta augmentation la moins coûteuse. - Longueur incorrecte : l’entraînement s’arrête avec un
ValueErrorqui signale l’incompatibilité — le seul échec explicite. - Longueur correcte, paires incorrectes : aucun avertissement. La moitié des exemples retournés apprend au modèle que la gauche est la droite, et les images de validation montrent des inversions gauche-droite sur les poses symétriques.
Entraîner sur COCO8-pose#

from ultralytics import YOLO
model = YOLO("yolo26n-pose.pt")
model.train(data="coco8-pose.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box mAP50-95: {metrics.box.map:.3f}")
print(f"pose mAP50-95: {metrics.pose.map:.3f}") Class Images Instances Box(P R mAP50 mAP50-95) Pose(P R mAP50 mAP50-95)
all 4 14 1 0.783 0.893 0.643 0.966 0.571 0.575 0.326
box mAP50-95: 0.643
pose mAP50-95: 0.326Comme pour la segmentation, le tableau comporte deux blocs. Box évalue les boîtes des personnes ; Pose évalue le placement des points clés à l’aide de la similarité des points clés d’objet (OKS), dans le même mécanisme de mAP. La mAP50-95 de pose est le critère de validation d’un produit de pose — la prochaine leçon montre comment l’OKS détermine si un point clé est correct.
Des points clés aux angles des articulations#
result.keypoints contient xy (N × K × 2, en pixels), xyn (normalisés) et conf (N × K), dans l’ordre COCO indiqué ci-dessus. L’angle d’une articulation est l’angle entre les deux vecteurs des membres qui s’y rejoignent — un produit scalaire :
import numpy as np
from ultralytics import YOLO
model = YOLO("yolo26n-pose.pt")
result = model("https://ultralytics.com/images/bus.jpg")[0]
kpts = result.keypoints.cpu().numpy() # people x 17 x (x, y, conf)
SHOULDER, ELBOW, WRIST = 5, 7, 9 # left arm, COCO keypoint order
for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
if conf[[SHOULDER, ELBOW, WRIST]].min() < 0.5:
print(f"person {person}: left arm not visible enough")
continue
a, b, c = xy[SHOULDER], xy[ELBOW], xy[WRIST]
cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
print(f"person {person}: left elbow angle {np.degrees(np.arccos(cos)):.0f} deg")person 0: left elbow angle 71 deg
person 1: left elbow angle 96 deg
person 2: left elbow angle 59 deg
person 3: left arm not visible enough
person 4: left arm not visible enoughLigne par ligne : kpts.xy[p] correspond aux 17 points en pixels de la personne p ; a - b et c - b sont les vecteurs du bras et de l’avant-bras partant du coude ; leur produit scalaire normalisé est le cosinus de l’angle qui les sépare. Un bras tendu donne une valeur proche de 180°, un bras complètement plié une valeur proche de 30°. Les personnes 3 et 4 ont au moins une articulation du bras gauche sous le seuil de confiance de 0.5, donc le script refuse de les mesurer.
Symptôme : des angles qui semblent fiables pour des personnes dont les bras sont hors champ. Cause : le modèle renvoie des coordonnées pour chaque point clé, qu'il soit occulté ou non. Solution : ignore tout calcul dont les articulations se trouvent sous un seuil de confiance (0.5 ici) et consigne la fréquence de ces omissions — une hausse de ce taux est un signe précoce de changement de caméra ou d'éclairage.
Afficher la solution
import numpy as np
from ultralytics import YOLO
ARMS = {"left": (5, 7, 9), "right": (6, 8, 10)} # shoulder, elbow, wrist
kpts = YOLO("yolo26n-pose.pt")("https://ultralytics.com/images/bus.jpg")[0].keypoints.cpu().numpy()
for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
for side, (s, e, w) in ARMS.items():
if conf[[s, e, w]].min() < 0.5:
continue
a, b, c = xy[s], xy[e], xy[w]
cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
print(f"person {person}: {side} elbow {np.degrees(np.arccos(cos)):.0f} deg")person 0: left elbow 71 deg
person 0: right elbow 94 deg
person 1: left elbow 96 deg
person 2: left elbow 59 deg
person 2: right elbow 135 degLes personnes 0 et 2 ont les deux bras mesurables. Le bras droit de la personne 1 et les deux bras des personnes 3 et 4 sont sous le seuil de confiance de 0.5 ; le script les ignore donc au lieu de faire des suppositions.
Un flip_idx manquant désactive l’augmentation par retournement avec un avertissement ; une longueur incorrecte arrête l’entraînement avec un ValueError ; un flip_idx d’apparence valide mais contenant des paires incorrectes entraîne silencieusement le modèle sur des étiquettes inversées et provoque des inversions gauche-droite lors de la validation.