Ultralytics YOLO27: 대기자 명단에 등록하세요.
Get Started
강의중급

포즈 모델 레이블 지정 및 학습

가시성 정보와 함께 키포인트 레이블을 작성하고, kpt_shape와 flip_idx를 올바르게 설정한 다음, COCO8-pose로 학습하고, 키포인트를 신뢰도 기준을 적용한 관절 각도로 변환합니다.

포즈 추정은 사람 한 명을 번호가 지정된 점 17개로 표현하며, 이 점으로부터 제품에 활용되는 수치가 나옵니다. 인체공학을 위한 팔꿈치 각도, 재활을 위한 무릎 각도, 낙상 감지를 위한 엉덩이 높이 등이 그 예입니다. 데이터셋의 두 가지 키가 해당 점을 신뢰할 수 있는지 결정합니다. 둘 중 하나라도 잘못 설정하면 오류가 발생하지 않고, 모델이 좌우를 혼동하거나 가장 비용이 적게 드는 증강을 조용히 적용하지 않은 채 학습됩니다.

학습 성과

coco8-pose.yaml로 yolo26n-pose.pt을 학습하고, result.keypoints를 사용해 사람마다 왼쪽 및 오른쪽 팔꿈치 각도를 계산하되 모델의 신뢰도가 낮은 관절은 건너뜁니다.

속성 과정
이미 사용법을 알고 계신다면, 간단히 요약해 드리겠습니다.
  1. 레이블 행: 클래스, 박스(값 4개), 키포인트마다 x y v 순서이며 모두 정규화된 값입니다. v은 0(레이블 없음), 1(가려짐), 2(보임)입니다.

  2. kpt_shape: [K, 3]과 모든 좌우 대응점을 서로 바꾸는 flip_idx을 설정합니다.

  3. yolo pose train data=coco8-pose.yaml model=yolo26n-pose.pt epochs=1 imgsz=320.

  4. result.keypoints.xy과 .conf을 읽고, 관절의 신뢰도가 기준값보다 낮은 계산은 건너뜁니다.

실습형

키포인트 레이블 행#

사람 박스와 17개 키포인트로 이루어진 신체 스켈레톤이 각각 표시된 세 명의 달리기 선수에 대한 Ultralytics YOLO 포즈 추정

포즈 레이블 행은 키포인트를 추가한 검출 행입니다. 모든 좌표는 0–1 범위로 정규화되며, 모든 행에서 키포인트 순서는 동일하게 고정됩니다. 실제 COCO8-pose 행에서 키포인트 네 개 뒤를 생략한 예:

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 ...
값의미
0클래스 인덱스(사람)
0.671279 0.617945 0.645759 0.726859박스 중심 x, 중심 y, 너비, 높이
0.519751 0.381250 2키포인트 0(코): x, y, 가시성
…키포인트 16개 추가, 동일한 패턴: 행마다 값 5 + 17 × 3 = 56개

가시성 플래그는 COCO 규칙을 따릅니다. 2는 보임, 1은 레이블이 지정되었지만 가려짐, 0은 레이블이 지정되지 않음을 의미합니다. Ultralytics는 손실 계산에서 v = 0인 키포인트를 마스킹하므로, 손목이 누락되어도 모델이 손목을 (0, 0)으로 예측하도록 학습되지 않습니다.

케이크를 자르는 사람들에게 17개 키포인트 스켈레톤을 표시한 COCO8-pose 이미지와 Ultralytics Platform 어노테이션 편집기

위치를 추정할 수 있는 가려진 관절에 레이블 지정하기

자신 있게 위치를 추정할 수 있는 가려진 관절(탁자 뒤의 엉덩이, 등 뒤의 손목)에는 1을 사용하세요. 이런 관절도 학습에 사용됩니다. 아무도 위치를 파악할 수 없는 관절에만 0을 사용하세요. 보이지 않는 관절을 모두 0으로 지정하면 모델에 필요한 어려운 사례를 정확히 버리게 됩니다.

포즈 데이터셋 가이드에서는 가시성 정보가 없는 데이터셋을 위한 두 값 형식(x y만 사용, kpt_shape: [K, 2])도 설명합니다.

kpt_shape 및 flip_idx#

데이터셋 YAML에는 일반적인 path, train, val, 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]

서 있는 사람의 COCO 17개 키포인트 순서. 0번 코부터 16번 오른쪽 발목까지 번호가 표시되어 있음

flip_idx은 좌우 반전 증강 시 각 키포인트가 거울 이미지에서 어떤 키포인트로 대응하는지 지정합니다. 사람을 반전하면 왼쪽 손목(9)이 오른쪽 손목(10)이 있던 위치로 이동합니다. 따라서 flip_idx의 9번 위치에는 10이, 10번 위치에는 9가 들어가며 코(0)는 그대로 0에 대응합니다. 모든 증강은 픽셀과 함께 스켈레톤을 이동시키며, 인덱스 맵이 필요한 증강은 좌우 반전뿐입니다:

한 러너의 포즈 증강: 원본, 좌우 반전, 회전, 스케일, HSV 색상 및 모자이크와 각 변환을 따라가는 키포인트 스켈레톤

설치된 소스에서 확인한 결과, flip_idx이 잘못되면 다음과 같은 일이 발생합니다:

  • 누락: 학습 시 경고가 기록되고 fliplr과 flipud이 0으로 설정되므로, 가장 비용이 적게 드는 증강을 사용할 수 없게 됩니다.
  • 길이가 잘못됨: 불일치를 설명하는 ValueError과 함께 학습이 중단됩니다. 소리 내어 알리는 유일한 오류입니다.
  • 길이는 맞지만 쌍이 잘못됨: 아무런 경고도 표시되지 않습니다. 반전된 샘플의 절반은 모델에 왼쪽이 오른쪽이라고 학습시키고, 검증 이미지의 대칭 포즈에서는 좌우가 뒤바뀐 결과가 나타납니다.

COCO8-pose로 학습하기#

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.326

세그멘테이션과 마찬가지로 표는 두 블록으로 구성됩니다. Box는 사람 바운딩 박스를 평가하고, Pose는 동일한 mAP 계산 체계 안에서 Object Keypoint Similarity(OKS)를 사용해 키포인트 배치를 평가합니다. 포즈 제품의 출시 기준은 Pose mAP50-95이며, 다음 강의에서는 OKS가 키포인트의 정답 여부를 판정하는 방법을 살펴봅니다.

키포인트에서 관절 각도까지#

result.keypoints에는 COCO 순서에 따라 xy(N × K × 2, 픽셀 단위), xyn(정규화됨), conf(N × K)이 저장됩니다. 관절의 각도는 그 지점에서 만나는 두 팔다리 벡터 사이의 각도이며, 내적을 사용해 계산합니다:

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 enough

한 줄씩 살펴보면, kpts.xy[p]은 사람 p의 픽셀 단위 17개 포인트이고, a - b와 c - b은 팔꿈치에서 뻗어 나온 위팔과 아래팔 벡터입니다. 두 벡터의 정규화된 내적은 두 벡터 사이 각도의 코사인입니다. 팔을 곧게 펴면 약 180°, 완전히 구부리면 약 30°로 표시됩니다. 사람 3과 4는 왼팔 관절 중 하나 이상이 0.5 미만의 신뢰도를 가지므로 스크립트가 측정을 거부합니다.

모든 관절을 신뢰도로 판단하기

증상: 팔이 프레임 밖에 있는 사람의 각도가 확신에 찬 것처럼 보입니다. 원인: 모델은 가려졌는지 여부와 관계없이 모든 키포인트의 좌표를 반환합니다. 해결 방법: 관절의 신뢰도가 기준값(여기서는 0.5)보다 낮으면 해당 계산을 건너뛰고, 건너뛴 횟수를 기록하세요. 건너뛰는 비율이 증가하면 카메라나 조명 변화가 시작되었다는 초기 신호일 수 있습니다.

사용해 보기

coco8-pose.yaml으로 한 에폭을 학습하고 box 및 pose mAP50-95를 기록하세요. 그런 다음 pose.py을 확장해 오른쪽 팔꿈치 각도도 보고하고(키포인트 6, 8, 10), 버스 이미지에서 양팔을 모두 측정할 수 있는 사람이 누구인지 나열하세요.

완료 조건
다음 조건을 모두 충족하면 레슨을 완료한 것입니다.
  • 학습이 완료되었고 box 및 pose mAP50-95를 기록했습니다.

  • 스크립트는 사람별 양쪽 팔꿈치 각도를 모두 출력하고, 신뢰도 기준 미만인 팔은 제외합니다.

  • flip_idx이 누락되거나 길이가 잘못되었거나 쌍이 잘못된 경우 어떤 일이 발생하는지 설명할 수 있습니다.

정답 보기
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 deg

사람 0과 2는 양팔을 모두 측정할 수 있습니다. 사람 1의 오른팔과 사람 3 및 4의 양팔은 0.5 신뢰도 기준 미만이므로 스크립트는 추측하는 대신 해당 팔을 제외합니다.

flip_idx이 누락되면 경고와 함께 좌우 반전 증강이 비활성화됩니다. 길이가 잘못되면 ValueError과 함께 학습이 중단됩니다. 형식상 유효해 보이지만 쌍이 잘못된 flip_idx는 반전된 라벨로 조용히 학습되며 검증 시 좌우가 뒤바뀐 결과로 나타납니다.

다음 단계

다음으로 사람 이외의 객체에 사용할 키포인트를 설계하고, OKS가 키포인트의 정답 여부를 판정하는 방법을 알아봅니다.