TT Lab
요금제

한국어

시작하기
블로그

블로그

사람 자세 2: ViTPose, RTMPose 의 SimCC, RTMO — 실시간과 정확도 사이에서 고르기

상태: 초안 (2026-10-13). 논문 수치는 원문을 열어 조건과 함께 확인했습니다. 직접 해 본 것은 노트북 CPU 의 numpy 예제(code/08_simcc.py, 1초 미만)입니다. TT Score 가 RTMPose·MediaPipe 를 실제로 쓴 결과는 TT Score 모델 해부 4편을 인용했고, ViTPose·RTMO 는 돌려 보지 않았습니다(미실측). 시리즈 「비전 모델 읽기 2」 8편. 앞 편: 사람 자세 1. RTMPose 의 디코딩 코드와 우리 쓰임새는 TT Score 4편이 자세히 다뤘으니, 이 편은 「왜 그렇게 학습시키나」에 집중합니다.

사람 자세 2: 실시간과 정확도 사이

목차

1. 직관: 한쪽은 키우고, 한쪽은 덜어 낸다

7편 끝의 표를 보면 2019–2020년의 강자들은 HRNet 같은 정교한 망에 정교한 디코딩을 얹은 구조였습니다. 그 뒤로 길이 둘로 갈렸습니다.

관절 하나의 출력: 2D 지도 대 1D 벡터 두 개 히트맵(¼ 해상도, 48×64) 칸 하나 = 입력 4화소 값 3,072개 손실: 칸마다 제곱 오차 SimCC(나누기 배율 k=2) x: 384칸(192 × 2), 칸 하나 = ½ 화소 … y: 512칸(256 × 2) … 값 896개 정답 = 가우시안으로 번진 분포 손실 = 분포 사이의 KL 발산 디코딩 = argmax 두 번 RTMPose 사람 상자(검출기) → CSPNeXt 몸통 → SimCC 두 분류기 RTMO 검출기 없이 한 번에: 상자 안에 칸을 펼친 1D 분류
그림: 2D 히트맵과 SimCC 의 1D 벡터 두 개, RTMPose 와 RTMO 의 구조

2. ViTPose: 디코더가 거의 필요 없다

ViTPose(2022)는 평범한 ViT 를 몸통으로 쓰고(그림의 1/16 해상도 특징), 그 위에 아주 단순한 디코더를 얹습니다. 「4배로 키우기(쌍선형 보간) → ReLU → 3×3 합성곱」 하나로 히트맵을 냅니다. 학습은 7편과 같은 히트맵 제곱 오차입니다.

이 단순한 디코더가 통하는지 몸통별로 비교한 결과가 흥미롭습니다(COCO val). ResNet 몸통에서는 단순 디코더로 바꾸면 약 18점이 떨어졌는데(ResNet-50 71.8 → 53.1), ViT 몸통에서는 0.3점 이내였습니다(ViT-B 75.8 → 75.5). 주의 층이 이미 넓은 맥락을 섞어 주니, 디코더가 할 일이 거의 남지 않는다는 뜻입니다(논문).

정확도는 몸통 크기를 따라 오릅니다. 256×192 입력에서 ViTPose-B 75.8, -L 78.3, -H 79.1 이고, 10억 파라미터급 몸통으로 COCO test-dev 80.9(단일 모델, 576×432, 외부 데이터 추가)까지 갔습니다(논문). 속도도 생각만큼 나쁘지 않습니다. ViTPose-B 가 초당 944장, HRNet-W32 가 916장이었습니다(데이터센터급 GPU, 배치 64, 논문).

사전학습 데이터에 대한 실험도 있습니다. MAE 사전학습을 ImageNet 대신 COCO·AI Challenger 의 사람 자르기 50만 장으로 해도 같은 75.8 이었고, COCO 만으로 하면 74.5 였습니다(논문). 자세 데이터만으로도 꽤 된다는 뜻이고, 2·3편의 「라벨 없는 우리 영상」 이야기와 이어집니다.

ViTPose 는 4편의 증류도 씁니다. 큰 모델(L)을 얼린 채 학습 가능한 「지식 토큰」 하나만 학습시키고, 그 토큰을 작은 모델(B)에 끼워 넣었습니다. 토큰만으로 +0.2, 출력 증류와 함께 +0.8(75.8 → 76.6)이었습니다(논문).

3. SimCC: 좌표를 두 개의 분류로

생각

히트맵의 문제는 두 가지였습니다(7편). 관절마다 2D 지도를 그려야 해서 비싸고, 정밀도가 지도 해상도에 묶입니다. SimCC(2022)는 질문을 바꿉니다. 「x 좌표는 몇 번 칸인가」와 「y 좌표는 몇 번 칸인가」를 각각 분류 문제로 풉니다.

지도를 키우지 않고도 화소보다 고운 해상도를 얻습니다.

라벨과 손실

정답 칸 하나만 1인 원-핫 라벨을 쓰면, 한 칸 옆도 열 칸 옆도 똑같이 「틀림」입니다. SimCC 는 정답 둘레로 확률을 나눠 주는 라벨 다듬기(label smoothing) 를 쓰고, 예측 분포와 정답 분포 사이의 KL 발산을 손실로 씁니다. 다듬는 방식 비교(ResNet-50, 384×288)에서 다듬지 않음 71.3, 고르게 나눔 73.0, 가우시안 73.4, 라플라스 73.0 이었습니다(논문). 가까운 칸일수록 덜 틀린 것으로 보는 가우시안이 가장 좋았습니다.

직접 해 보기

code/08_simcc.py 의 출력입니다(code/08_simcc.out.txt).

[1] x 정답 101.3px → 칸 202.6/384, 정답 분포의 최대 칸 203, 폭(σ) 6.0칸
[2] τ=1.0: 예측 분포 최댓값 0.012, KL(정답‖예측) 1.420, 디코딩 105.5px
[2] τ=0.1: 예측 분포 최댓값 0.171, KL(정답‖예측) 0.499, 디코딩 105.5px

[2] 는 온도의 역할을 보여 줍니다. 망이 내는 로짓은 보통 값의 폭이 좁습니다. 그대로(τ=1) 소프트맥스를 걸면 분포가 납작해서 정답 분포와 멀고(KL 1.42), τ=0.1 로 나누면 뾰족해져 가까워집니다(KL 0.50). RTMPose 는 이 온도를 0.1 로 두어 COCO val 이 72.7 이 됐습니다(아래 표의 단계). 그리고 온도는 argmax 를 바꾸지 않습니다(두 경우 모두 105.5px). 온도는 학습의 기울기를 바꾸는 손잡이이지 디코딩 결과를 바꾸는 손잡이가 아닙니다. 이 예시의 4px 오차는 일부러 넣은 잡음 때문입니다.

양자화 오차를 x 한 축에서 비교했습니다(완벽한 예측, 무작위 위치 2만 개).

출력 칸 크기 평균 오차
SimCC k=1 1 화소 0.249px
SimCC k=2 ½ 화소 0.125px
SimCC k=3 ⅓ 화소 0.084px
2D 히트맵 ¼ 해상도, argmax 4 화소 1.012px

로짓에 잡음을 섞으면(1천 번) k=1·2·3 의 평균 오차가 2.97·2.54·2.37px 로, 칸을 잘게 쪼개는 이득이 잡음에 묻혀 작아집니다. SimCC 논문도 k 를 1–4 로 시험해 2–3 이 가장 좋다고 했습니다. 너무 잘게 쪼개면 칸마다 학습 신호가 얇아집니다.

출력 크기(관절 하나당 값의 개수)는 이렇습니다.

2D 히트맵 ¼ 해상도 48×64 = 3,072  → 17관절 52,224
SimCC k=2: 384 + 512 = 896 → 17관절 15,232

낮은 해상도에서 특히 강하다

SimCC 의 이득은 입력이 작을 때 큽니다(논문, COCO val, HRNet-W48).

입력 히트맵 + ¼ 칸 밀기 SimCC
64×64 48.5 59.7
128×128 68.9 72.0
256×192 75.1 75.9

7편의 결론과 같습니다. 칸이 거칠수록 디코딩의 양자화가 크게 문제가 되고, SimCC 는 그 양자화를 출력 구조에서 없앱니다.

4. RTMPose: 학습 요령을 하나씩 쌓는다

RTMPose(2023)는 위에서 아래로(top-down) 방식입니다. 사람 검출기(RTMDet)가 상자를 찾고, CSPNeXt 몸통 → 큰 합성곱(7×7) → 관절별 특징 → 주의 층(GAU) → SimCC 두 분류기로 관절을 찾습니다. 이 논문의 가치는 구조보다 학습 요령을 하나씩 더하며 그 효과를 다 적어 둔 표에 있습니다(COCO val, RTMPose-m).

단계 AP
출발점 69.7
몸통을 히트맵(UDP)으로 먼저 학습 70.3
+ 가중치 지수 이동 평균(EMA) 70.4
+ 학습률 스케줄(평평하다가 코사인) 70.7
+ 두 단계 증강(강하게 180에폭 → 약하게 30에폭) 등 …
+ 주의 층(GAU) 71.9
+ 온도 τ=0.1 72.7
+ x·y 따로 σ 72.8
+ 7×7 합성곱 73.3
+ 420에폭 73.7
+ 데이터 추가(AI Challenger) 75.3

라벨은 「가우시안 거리로 만든 분포에 소프트맥스」를 씌운 꼴이고, 예측과 라벨 양쪽에 온도를 씁니다(논문). 강한 증강은 크기 0.6–1.4배, 회전 80도, 일부 지우기(Cutout)를 늘 거는 정도였습니다. 학습 데이터 조건이 다른 판이 여러 개라(COCO 만 / AI Challenger 추가 / 뒤집기 시험 포함), 75.8 같은 숫자를 인용할 때는 어느 판인지 맞춰야 합니다.

속도(논문, RTMPose-m): 데스크톱 CPU(ONNX Runtime) 11.06ms, 데스크톱급 GPU(TensorRT) 2.29ms, 휴대폰 SoC 에서 35fps 이상. 작은 판 RTMPose-s 는 휴대폰 SoC 에서 13.89ms(70fps 이상)에 72.2 AP 입니다. 사람 검출기를 매 프레임 돌리지 않고 몇 프레임마다 돌리며, 그 사이에는 앞 프레임의 자세로 상자를 만듭니다.

5. RTMO: 사람 검출기를 없앤다

top-down 의 약점은 사람 수에 비례하는 시간과, 검출기를 따로 돌리는 부담입니다. RTMO(CVPR 2024)는 물체 검출기(YOLO 계열)처럼 그림을 격자로 나눠 칸마다 「여기 사람이 있나, 있다면 상자와 관절은?」을 한 번에 냅니다.

관절 좌표는 SimCC 처럼 1D 분류로 내되, 칸을 그림 전체가 아니라 그 칸이 예측한 사람 상자(1.25배로 넓힘) 안에만 펼칩니다(동적 좌표 분류기, DCC). 사람이 작든 크든 상자 안을 x 192칸·y 256칸으로 나누니 해상도가 사람 크기에 맞춰집니다.

손실도 바꿨습니다. 정답 분포와의 KL 대신, 「예측 위치 둘레의 가우시안 분포에서 정답이 나올 우도」를 최대화하되 분포의 폭(불확실도)도 망이 함께 배우게 했습니다(최대 우도, MLE). 같은 RTMO-s 에서 KL 64.4, 좌표 회귀(OKS 손실) 65.6, MLE 67.6 이었습니다(COCO val, 논문). 5편의 SEA-RAFT 처럼, 원래 애매한 경우를 덜 벌하는 손실이 이긴 예입니다.

항목 값(논문)
COCO val(COCO 만 학습) RTMO-s 67.6 · -m 71.2 · -l 72.5
초록의 대표 수치 RTMO-l val 74.8, 데이터센터급 GPU 한 장에서 141fps(학습 데이터·실행기 조건은 초록에 없음)
COCO test-dev s 66.9 · m 70.1 · l 71.6(외부 데이터 추가 시 67.7 · 71.5 · 73.3)
CrowdPose(붐비는 장면) RTMO-l 73.2(CrowdPose 만 학습)
top-down(RTMPose) 대비 사람 4명 안팎부터 RTMO 가 더 빠름(ONNX Runtime). 1명에서 10명 넘게 늘어도 지연 증가 약 0.1ms

6. 한계

7. 우리 프로젝트와의 연결

티티 한마디. 좌표를 「몇 번째 칸?」이라는 분류 문제로 바꾸니, 지도를 그리던 3,072칸이 896칸으로 줄었어요. 그리고 온도는 정답을 바꾸는 게 아니라 배우는 방식을 바꾸는 손잡이라는 것도 직접 계산해서 확인했어요.

참고 문헌

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다