Ultralytics YOLO27: Tham gia danh sách chờ.
Get Started
Bài họctrung cấp

Gán nhãn và huấn luyện model pose

Viết nhãn keypoint kèm thông tin visibility, thiết lập chính xác kpt_shape và flip_idx, huấn luyện trên COCO8-pose, rồi chuyển keypoint thành góc khớp có áp dụng ngưỡng confidence.

Ước lượng pose chuyển mỗi người thành 17 điểm được đánh số; từ các điểm này, ta tính được những chỉ số có giá trị thương mại: góc khuỷu tay cho công thái học, góc đầu gối cho phục hồi chức năng, chiều cao hông để phát hiện té ngã. Hai khóa trong tập dữ liệu quyết định liệu có thể tin cậy các điểm đó hay không, và nếu thiết lập sai một trong hai thì sẽ không có lỗi nào xuất hiện — chỉ có một model nhầm trái với phải hoặc âm thầm huấn luyện mà không dùng phép tăng cường dữ liệu đơn giản nhất.

Kết quả

Huấn luyện yolo26n-pose.pt trên coco8-pose.yaml và tính góc khuỷu tay trái, phải của từng người từ result.keypoints, bỏ qua các khớp mà model không đủ confidence.

Lộ trình cấp tốc
Nếu bạn đã nắm rõ cách thực hiện, đây là phiên bản tóm tắt.
  1. Một hàng nhãn: lớp, box (4 giá trị), rồi x y v cho mỗi keypoint, tất cả đều được chuẩn hóa; v là 0 (không được gán nhãn), 1 (bị che khuất) hoặc 2 (nhìn thấy).

  2. Thiết lập kpt_shape: [K, 3] và một flip_idx hoán đổi mọi cặp tương ứng bên trái và bên phải.

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

  4. Đọc result.keypoints.xy và .conf; bỏ qua mọi phép tính có các khớp thấp hơn ngưỡng confidence.

Thực hành

Hàng nhãn keypoint#

Ước lượng pose Ultralytics YOLO cho ba người chạy bộ, mỗi người có một box người và bộ xương gồm 17 keypoint

Một hàng nhãn pose là một hàng detection có thêm keypoint. Mọi tọa độ đều được chuẩn hóa về 0–1 và keypoint xuất hiện theo cùng một thứ tự cố định trên mọi hàng. Một hàng thực tế của COCO8-pose, đã lược bớt sau bốn keypoint:

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 ...
Giá trịÝ nghĩa
0chỉ số lớp (người)
0.671279 0.617945 0.645759 0.726859tâm box x, tâm y, chiều rộng, chiều cao
0.519751 0.381250 2keypoint 0 (mũi): x, y, visibility
…16 keypoint còn lại, theo cùng cấu trúc: 5 + 17 × 3 = 56 giá trị mỗi hàng

Cờ visibility tuân theo quy ước COCO: 2 là nhìn thấy, 1 là đã gán nhãn nhưng bị che khuất, 0 là không được gán nhãn. Ultralytics loại các keypoint có v = 0 khỏi loss, để cổ tay bị thiếu không khiến model học cách dự đoán cổ tay tại (0, 0).

Trình biên tập gán nhãn của Ultralytics Platform trên ảnh COCO8-pose, với bộ xương 17 keypoint được vẽ trên những người đang cắt bánh

Gán nhãn các khớp bị che khuất mà bạn có thể xác định vị trí

Dùng 1 cho khớp bị che khuất mà bạn có thể xác định vị trí một cách chắc chắn — chẳng hạn hông phía sau bàn, cổ tay phía sau lưng; các khớp này vẫn được dùng để huấn luyện. Chỉ dùng 0 cho các khớp không ai có thể xác định vị trí. Gán 0 cho mọi khớp bị che khuất sẽ loại bỏ chính những trường hợp khó mà model cần học.

Hướng dẫn về tập dữ liệu pose cũng trình bày biến thể hai giá trị (x y chỉ có, kpt_shape: [K, 2]) dành cho tập dữ liệu không có thông tin visibility.

kpt_shape và flip_idx#

YAML của tập dữ liệu bổ sung hai khóa vào các khóa thông thường path, train, val và 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]

Thứ tự 17 keypoint COCO trên người đang đứng, được đánh số từ 0 là mũi đến 16 là mắt cá chân phải

flip_idx cho phép phép tăng cường lật ngang biết mỗi keypoint sẽ trở thành keypoint nào trong ảnh phản chiếu. Khi lật gương một người, cổ tay trái (9) sẽ nằm ở vị trí cổ tay phải (10), vì vậy vị trí 9 trong flip_idx chứa giá trị 10 và vị trí 10 chứa giá trị 9; mũi (0) ánh xạ về chính nó. Mọi phép tăng cường đều dịch chuyển bộ xương cùng với các pixel; phép lật là phép duy nhất cần thêm bản đồ chỉ số:

Các phép tăng cường pose cho một vận động viên chạy: ảnh gốc, lật ngang, xoay, thay đổi tỷ lệ, màu HSV và mosaic; bộ khung keypoint theo từng phép biến đổi

Điều gì xảy ra khi flip_idx không đúng, được xác minh trong source đã cài đặt:

  • Thiếu: quá trình huấn luyện ghi cảnh báo và đặt fliplr cùng flipud thành 0, khiến bạn mất phép tăng cường ít tốn kém nhất.
  • Sai độ dài: quá trình huấn luyện dừng với một ValueError nêu rõ điểm không khớp — đây là lỗi duy nhất được báo rõ ràng.
  • Đúng độ dài, sai cặp: không có cảnh báo nào. Một nửa số mẫu được lật khiến model học rằng trái là phải, còn ảnh validation cho thấy các bên trái-phải bị hoán đổi ở những pose đối xứng.

Huấn luyện trên COCO8-pose#

Mosaic huấn luyện COCO8-pose: người trên xe máy, dưới chiếc ô màu hồng, tại công viên trượt ván và trên lưng ngựa; mỗi người có một bounding box và bộ khung keypoint nhiều màu

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

Tương tự segmentation, bảng có hai phần. Box đánh giá bounding box người; Pose đánh giá vị trí keypoint bằng Độ tương đồng điểm khóa đối tượng (OKS) trong cùng cơ chế mAP. Pose mAP50-95 là tiêu chí để phát hành sản phẩm pose — bài tiếp theo sẽ cho thấy OKS quyết định một keypoint có chính xác hay không như thế nào.

Từ keypoint đến góc khớp#

result.keypoints chứa xy (N × K × 2, pixel), xyn (đã chuẩn hóa) và conf (N × K), theo thứ tự COCO được trình bày ở trên. Góc tại một khớp là góc giữa hai vector chi đang gặp nhau tại đó — tính bằng tích vô hướng:

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

Từng dòng: kpts.xy[p] là 17 điểm của người p tính bằng pixel; a - b và c - b là cánh tay trên và cẳng tay, biểu diễn dưới dạng vector xuất phát từ khuỷu tay; tích vô hướng đã chuẩn hóa của hai vector là cosine của góc giữa chúng. Cánh tay duỗi thẳng cho giá trị gần 180°, còn cánh tay gập hết cho giá trị gần 30°. Người số 3 và 4 có ít nhất một khớp tay trái với độ tin cậy dưới 0.5, vì vậy script không đo các khớp đó.

Kiểm tra độ tin cậy của từng khớp

Triệu chứng: góc trông có vẻ đáng tin cậy đối với người có cánh tay nằm ngoài khung hình. Nguyên nhân: model trả về tọa độ cho mọi keypoint, dù bị che khuất hay không. Cách khắc phục: bỏ qua mọi phép tính có các khớp thấp hơn ngưỡng độ tin cậy (ở đây là 0.5), đồng thời ghi log tần suất bỏ qua — tỷ lệ bỏ qua tăng là dấu hiệu sớm cho thấy camera hoặc ánh sáng đã thay đổi.

Dùng thử

Huấn luyện một epoch trên coco8-pose.yaml và ghi lại box mAP50-95 cùng pose mAP50-95. Sau đó mở rộng pose.py để báo cáo cả góc khuỷu tay phải (keypoint 6, 8, 10), đồng thời liệt kê những người trong ảnh xe buýt có thể đo được cả hai cánh tay.

Điều kiện hoàn thành
Bạn đã hoàn thành bài học khi tất cả các điều kiện sau đều đúng.
  • Quá trình huấn luyện đã hoàn tất và bạn đã ghi lại box mAP50-95 cùng pose mAP50-95.

  • Script in ra góc của cả hai khuỷu tay cho mỗi người, bỏ qua mọi cánh tay có độ tin cậy dưới ngưỡng.

  • Bạn có thể giải thích điều gì xảy ra khi flip_idx bị thiếu, sai độ dài hoặc sai cặp.

Hiển thị lời giải
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

Có thể đo được cả hai cánh tay của người số 0 và 2. Cánh tay phải của người số 1 và cả hai cánh tay của người số 3 và 4 đều thấp hơn ngưỡng độ tin cậy 0.5, vì vậy script bỏ qua thay vì đoán.

Thiếu flip_idx sẽ tắt phép tăng cường lật kèm cảnh báo; sai độ dài sẽ khiến quá trình huấn luyện dừng với một ValueError; flip_idx trông có vẻ hợp lệ nhưng chứa các cặp sai sẽ khiến model âm thầm huấn luyện trên nhãn bị đảo gương và gây ra hiện tượng hoán đổi trái-phải khi validation.

Tiếp theo là gì

Tiếp theo, chúng ta sẽ thiết kế keypoint cho các đối tượng không phải người và tìm hiểu cách OKS quyết định một keypoint có chính xác hay không.

Đăng nhập để lưu tiến độ