Giới thiệu
Trong lĩnh vực thị giác máy tính, phát hiện điểm chính và ước tính tư thế (pose estimation) là một nhiệm vụ quan trọng, liên quan đến việc xác định vị trí của các điểm đặc trưng trên cơ thể người trong một hình ảnh. Các điểm này thường bao gồm các khớp xương, mắt, mũi, và các bộ phận khác. YOLO12, phiên bản mới nhất của chuỗi mô hình YOLO, đã giới thiệu các cải tiến trong kiến trúc, bao gồm cơ chế chú ý (attention mechanism) và các lớp tích hợp hiệu quả, giúp cải thiện độ chính xác và tốc độ xử lý. Bài viết này sẽ hướng dẫn bạn cách sử dụng YOLO12 để phát hiện các điểm chính trên cơ thể và ước tính tư thế, sử dụng bộ dữ liệu COCO Pose.
Cấu trúc của bài viết
- Phần 1: Cơ sở lý thuyết - Giới thiệu về pose estimation và các điểm chính trên cơ thể.
- Phần 2: Kiến trúc YOLO12 - Phân tích các cải tiến trong YOLO12, đặc biệt là cơ chế chú ý vùng (Area Attention).
- Phần 3: Cài đặt và huấn luyện - Hướng dẫn cài đặt môi trường, cấu hình dữ liệu và huấn luyện mô hình.
- Phần 4: Thực thi và đánh giá - Cách sử dụng mô hình đã huấn luyện để dự đoán và phân tích kết quả.
- Phần 5: Mở rộng và cải tiến - Các hướng cải tiến mô hình như CBAM và GhostConv.
Phần 1: Cơ sở lý thuyết
Ước tính tư thế là một nhiệm vụ trong thị giác máy tính, liên quan đến việc xác định vị trí của các điểm đặc trưng (gọi là điểm chính hoặc keypoints) trên cơ thể người trong một hình ảnh. Các điểm chính này thường bao gồm các khớp xương, mắt, mũi, và các bộ phận khác. Mô hình ước tính tư thế sẽ trả về một tập hợp các điểm, mỗi điểm có tọa độ 2D hoặc 3D và một độ tin cậy (confidence score).
Bộ dữ liệu COCO Pose chứa 17 điểm chính trên cơ thể, mỗi điểm tương ứng với một bộ phận cụ thể. Dưới đây là danh sách các điểm chính và bộ phận tương ứng:
0: Mũi
1: Mắt trái
2: Mắt phải
3: Tai trái
4: Tai phải
5: Vai trái
6: Vai phải
7: Cẳng tay trái
8: Cẳng tay phải
9: Cổ tay trái
10: Cổ tay phải
11: Hông trái
12: Hông phải
13: Gối trái
14: Gối phải
15: Mắt cá chân trái
16: Mắt cá chân phải
Phần 2: Kiến trúc YOLO12
YOLO12 đã cải tiến kiến trúc so với các phiên bản trước, tập trung vào việc tối ưu hóa hiệu suất và độ chính xác. Một trong những cải tiến chính là việc sử dụng cơ chế chú ý vùng (Area Attention).
Cơ chế chú ý vùng (Area Attention)
Cơ chế chú ý vùng được thiết kế để xử lý các vùng quan tâm lớn mà không làm tăng chi phí tính toán. Nó chia feature map thành các vùng nhỏ hơn, giúp giảm độ phức tạp tính toán từ O(n²) xuống O(n²/2) trong khi vẫn duy trì một trường quan sát lớn.
Lớp A2C2f
Lớp A2C2f là một module tích hợp cơ chế chú ý vùng vào kiến trúc C2f. Dưới đây là một ví dụ về triển khai lớp A2C2f:
import torch
import torch.nn as nn
class A2C2f(nn.Module):
"""
Module A2C2f tích hợp cơ chế chú ý vùng (Area Attention) để trích xuất đặc trưng.
"""
def __init__(self, c1, c2, n=1, a2=True, area=1, residual=False, mlp_ratio=2.0, e=0.5, g=1, shortcut=True):
super().__init__()
c_ = int(c2 * e)
assert c_ % 32 == 0, "Kích thước của ABlock phải là bội số của 32."
self.cv1 = nn.Conv2d(c1, c_, 1, 1)
self.cv2 = nn.Conv2d((1 + n) * c_, c2, 1)
self.gamma = nn.Parameter(0.01 * torch.ones(c2), requires_grad=True) if a2 and residual else None
self.m = nn.ModuleList(
nn.Sequential(*(ABlock(c_, c_ // 32, mlp_ratio, area) for _ in range(2)))
if a2
else C3k(c_, c_, 2, shortcut, g)
for _ in range(n)
)
def forward(self, x):
y = [self.cv1(x)]
y.extend(m(y[-1]) for m in self.m)
y = self.cv2(torch.cat(y, 1))
if self.gamma is not None:
return x + self.gamma.view(-1, len(self.gamma), 1, 1) * y
return y
Phần 3: Cài đặt và huấn luyện
Cài đặt môi trường
Để bắt đầu, bạn cần cài đặt PyTorch và Miniconda. Sau đó, bạn có thể sử dụng các lệnh sau để cài đặt các thư viện cần thiết:
pip install -v -e .
Cấu hình dữ liệu
Bạn cần cấu hình đường dẫn đến bộ dữ liệu COCO Pose trong tệp cấu hình. Dưới đây là ví dụ về tệp cấu hình `my_data.yaml`:
# Đường dẫn đến bộ dữ liệu
path: /đường/dẫn/đến/bộ/dữ/liệu/coco-pose
train: train2017.txt
val: val2017.txt
test: test-dev2017.txt
# Điểm chính
kpt_shape: [17, 3] # 17 điểm chính, 3 chiều (x, y, visible)
flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]
# Lớp
names:
0: person
Huấn luyện mô hình
Sử dụng script `train.py` để huấn luyện mô hình. Bạn cần chỉ định đường dẫn đến tệp cấu hình dữ liệu và các tham số huấn luyện khác.
python train.py --data my_data.yaml --cfg yolov12-pose.yaml --weights yolov12n-pose.pt --epochs 100
Phần 4: Thực thi và đánh giá
Dự đoán trên hình ảnh
Sau khi huấn luyện, bạn có thể sử dụng mô hình để dự đoán trên hình ảnh. Dưới đây là một ví dụ về script sử dụng Gradio để tạo giao diện web:
import gradio as gr
import torch
from PIL import Image
from yolov12 import YOLO12Pose
model = YOLO12Pose("runs/yolov12n-pose/weights/best.pt")
def predict(image):
results = model(image)
annotated_image = results[0].plot()
return Image.fromarray(annotated_image[..., ::-1])
iface = gr.Interface(
fn=predict,
inputs=gr.Image(type="pil"),
outputs=gr.Image(type="pil"),
title="YOLO12 Pose Estimation",
description="Upload an image to perform pose estimation."
)
iface.launch()
Đánh giá mô hình
Để đánh giá mô hình, bạn có thể sử dụng các chỉ số như mAP (mean Average Precision), Precision, và Recall. Các chỉ số này thường được lưu trong thư mục `runs` sau khi huấn luyện.
Phần 5: Mở rộng và cải tiến
Cải tiến bằng CBAM
CBAM (Convolutional Block Attention Module) là một module chú ý nhẹ, có thể được thêm vào mô hình để cải thiện độ chính xác.
class ChannelAttention(nn.Module):
def __init__(self, in_channels, reduction=16):
super().__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
self.fc = nn.Sequential(
nn.Linear(in_channels, in_channels // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(in_channels // reduction, in_channels, bias=False)
)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avg_out = self.fc(self.avg_pool(x).view(x.size(0), -1))
max_out = self.fc(self.max_pool(x).view(x.size(0), -1))
out = avg_out + max_out
return x * self.sigmoid(out).view(x.size(0), -1, 1, 1)
class SpatialAttention(nn.Module):
def __init__(self, kernel_size=7):
super().__init__()
self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avg_out = torch.mean(x, dim=1, keepdim=True)
max_out, _ = torch.max(x, dim=1, keepdim=True)
x = torch.cat([avg_out, max_out], dim=1)
x = self.conv(x)
return x * self.sigmoid(x)
Cải tiến bằng GhostConv
GhostConv là một phép toán tích chập nhẹ, giúp giảm số lượng tính toán và tham số.
class GhostConv(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=1, stride=1, padding=0, ratio=2, dw_kernel_size=3):
super().__init__()
self.out_channels = out_channels
self.primary_channels = out_channels // ratio
self.ghost_channels = out_channels - self.primary_channels
self.primary_conv = nn.Conv2d(in_channels, self.primary_channels, kernel_size, stride, padding, bias=False)
self.ghost_conv = nn.Conv2d(self.primary_channels, self.ghost_channels, dw_kernel_size, 1, dw_kernel_size//2, groups=self.primary_channels, bias=False)
def forward(self, x):
primary = self.primary_conv(x)
ghost = self.ghost_conv(primary)
return torch.cat([primary, ghost], dim=1)