CNN에서 출발해 Point Transformer V3까지
합성곱 신경망만 알고 있는 사람을 위한, 점군(point cloud) 위에서 동작하는 가장 강력한 트랜스포머 — PTv3 — 의 처음부터 끝까지. 도식과 수식, 그리고 그 사이를 잇는 직관을 모두.
들어가며 — 왜 이 글인가
2024년 CVPR에서 발표된 Point Transformer V3 (이하 PTv3) 는, 점군(point cloud) 위에서 동작하는 모델이 도달할 수 있는 “좋은 기본값(default)”의 새로운 기준선이 되었다. 단일 모델로 옥내 분할(ScanNet, S3DIS), 옥외 자율주행 분할(nuScenes, SemanticKITTI), 객체 분류(ModelNet), 부품 분할(ShapeNetPart) 등 20여 개의 벤치마크를 동시에 갈아치웠고, PTv2보다 3배 이상 빠르며 10배 이상 메모리를 적게 쓴다. 그러면서도 정확도는 더 높다. 이 “더 빠르고, 더 가볍고, 더 정확한” 보기 드문 조합은 어디서 왔는가?
대답은 PTv3 논문의 부제에서 이미 드러난다 — “Simpler, Faster, Stronger.” 정확하게 자르고 정밀하게 이웃을 묶는 대신, 점들을 1차원 시퀀스로 정렬하고, 그 시퀀스를 단순한 패치로 자른 뒤, 패치 안에서만 어텐션을 돌린다. CNN과 Transformer의 사고방식을 모두 한참 배운 사람이 보면 “고작 그게?” 싶지만, 바로 그 “고작”이 점군 위 트랜스포머의 운명을 바꿨다.
이 글은 CNN 한 가지만 아는 사람이 PTv3를 완전히 이해하도록 만드는 것을 목표로 한다. 그래서 우리는 다소 멀리서부터 출발한다. CNN의 어떤 성질이 점군에서 무너지는지 보고, 그 빈자리를 채우러 들어온 Attention을 만나고, 어텐션을 쌓아 Transformer를 짓고, 트랜스포머가 점군을 만나며 거쳐온 PointNet → PointNet++ → PT v1 → PT v2 → PT v3 의 다섯 정거장을 차례로 통과한다.
각 정거장에서 우리는 (1) 왜 이 모델이 등장했는가, (2) 핵심 아이디어와 수식은 무엇인가, (3) 다음 모델은 무엇을 고치려 했는가 를 묻는다. 모든 수식은 풀이 과정을 함께 적었고, 모든 다이어그램은 SVG로 인라인 삽입되어 인쇄해도 깨지지 않는다.
“Scaling principle is the most important factor for advancing deep learning. Simplicity allows us to scale; precision tries to stop us.”
— PTv3, CVPR 2024 (의역)
읽는 방법
좌측 사이드바의 목차에서 어디로든 점프할 수 있다. 모바일에서는 우측 상단의 햄버거 버튼으로 같은 목차를 펼친다. 다크/라이트 모드 토글은 그 옆에 있다. 수식은 KaTeX로, 도식은 인라인 SVG로 그려져 있어 별도의 네트워크 없이도 글의 본문은 모두 읽힌다(폰트와 KaTeX는 CDN을 사용한다).
CNN 복습 — 우리가 떠나는 출발점
이미 잘 아는 내용을 짧게 짚자. 모든 다음 이야기가 이 “CNN의 어떤 성질을 잃었는가”에서 시작된다.
2.1합성곱이라는 약속
2D 영상에 대한 합성곱(convolution)은 다음과 같이 정의된다. 입력 특징 맵 $X \in \mathbb{R}^{H\times W\times C_\text{in}}$ 와 커널 $W \in \mathbb{R}^{k\times k\times C_\text{in}\times C_\text{out}}$가 있을 때, 출력의 $(i,j,c)$ 위치는
이 한 줄짜리 식 안에 CNN이 의존하는 네 가지 강력한 가정이 들어 있다.
2.2왜 이 4개가 함께 등장하는가
이미지에서는 픽셀이 격자에 “놓여” 있다. $X[i+1, j]$는 “왼쪽에서 한 칸 오른쪽”이라는 명확한 의미를 갖는다. 그래서 “3×3 이웃”이 잘 정의된다(지역성). 그리고 “같은 패턴(가령 가로 엣지)이 어디에 있든 똑같이 검출되어야 한다”는 직관이 자연스럽기 때문에 가중치를 공유한다(공유). 그 결과 입력의 평행이동에 대해 출력이 평행이동하는 등변성이 따라온다.
여기서 우리가 기억할 핵심은 — CNN은 입력이 격자라는 사실에 모든 것을 의존한다는 점이다.
CNN의 한계 — Attention이 필요한 이유
CNN은 “격자 + 좁은 이웃”에 의존한다고 했다. 두 가지가 모두 무너지는 두 상황을 보자.
3.1먼 거리 의존성 (long-range dependency)
이미지 안에서도 멀리 떨어진 두 영역이 의미적으로 강하게 연결될 수 있다 — 한쪽 끝의 “신호등 색”과 반대쪽 끝의 “보행자 자세” 같은 것. CNN은 한 층에서 k×k만 보기 때문에, 거리를 좁히려면 층을 깊게 쌓거나 stride·풀링으로 해상도를 줄여야 한다. 멀리 있는 한 쌍의 픽셀이 서로 영향을 주려면 “충분히 깊은 층”이 필요하고, 정보가 그 사이를 지나며 흐려진다.
3.2입력이 격자가 아닐 때 (point cloud!)
점군은 $\{x_1, x_2, \dots, x_N\}$ 인 좌표(과 특징)의 집합(set) 이다. 다음 세 성질이 CNN과 충돌한다.
이 세 가지 때문에 — 점들 위에 “3×3 윈도우”를 정의하는 일이 처음부터 막힌다. 위·아래·왼쪽·오른쪽이라는 개념 자체가 점군에는 없다.
3.3두 한계를 한 번에 해소하는 한 가지 발상
“좁은 이웃”의 가정을 버리고, 모든 원소가 모든 원소를 본다. 그 대신, 모든 쌍을 동등하게 보지 않고 중요한 쌍에 가중치를 더 준다. 그 가중치를 데이터로부터 학습한다. 이 발상이 곧 Attention이다.
CNN의 강점이었던 “격자 + 좁은 이웃 + 공유 가중치”의 자리에, 어텐션은 “집합 + 모든 쌍 + 데이터-적응적 가중치”를 둔다. 이는 점군처럼 격자가 없는 데이터에 자연스럽게 어울린다.
Attention 메커니즘의 핵심
어텐션을 처음 만나는 사람을 위해, 도서관 비유로 한 번만 짚고 들어간다. 그러고 나서 곧바로 수식으로 들어간다.
4.1도서관 비유 — Query, Key, Value
당신이 도서관에 가서 “1980년대 한국 SF”에 관한 책을 찾는다고 하자. 머릿속에 떠올린 검색어가 Query(질의) 다. 책장에는 책마다 라벨이 붙어 있는데, 그 라벨이 Key(열쇠)다. 그리고 책의 실제 내용물이 Value(값)다.
당신이 하는 일은 (1) 내 Query와 각 책의 Key가 얼마나 비슷한지를 점수로 매기고 (2) 그 점수를 정규화해 “주의의 비중”으로 바꾼 뒤 (3) 모든 책의 Value를 그 비중으로 가중평균하는 것이다. 잘 맞는 책의 내용은 많이, 안 맞는 책의 내용은 적게 가져온다.
4.2가장 단순한 형태 — Dot-Product Attention
유사도 함수로 가장 단순한 것은 내적(dot product) 이다. 두 벡터가 같은 방향으로 클수록 내적이 커진다. 한 개의 Query $q \in \mathbb{R}^{d_k}$와 N개의 Key $k_i \in \mathbb{R}^{d_k}$, Value $v_i \in \mathbb{R}^{d_v}$가 주어졌을 때, 어텐션 출력은
여기서 softmax는 (a) 모든 출력을 양수로 만들고 (b) 모두 더하면 1이 되도록 한다. 그래서 $\alpha_i$를 “주의의 비중”으로 해석할 수 있다.
4.3왜 √d_k로 나눠야 하는가 — Scaled Dot-Product
이 부분은 보통 천천히 설명되지 않고 지나가는데, PTv3까지 가는 길에 어텐션의 “크기”와 “스케일”이 반복 등장하므로 한 번 제대로 짚고 가자.
$q$와 $k$의 각 성분이 평균 0, 분산 1인 독립 확률변수라고 가정하자. 그러면
풀이 — 분산이 d_k가 되는 이유
- 독립이고 평균 0이므로 $\mathbb{E}[q_i k_i] = \mathbb{E}[q_i]\mathbb{E}[k_i] = 0$. 따라서 $\mathbb{E}[q^\top k] = 0$.
- 같은 가정에서 $\mathrm{Var}(q_i k_i) = \mathbb{E}[q_i^2 k_i^2] - 0 = \mathbb{E}[q_i^2]\mathbb{E}[k_i^2] = 1\cdot 1 = 1$.
- 독립인 항들의 합의 분산은 각 항 분산의 합. 항이 $d_k$개이므로 $\mathrm{Var}(q^\top k) = d_k$.
차원 $d_k$가 커질수록 내적의 분산이 함께 커진다. softmax는 입력값이 커지면 — 곧 가장 큰 항이 다른 항을 압도하기 시작하면 — 출력 분포가 거의 one-hot에 가까워진다. 그 영역에서는 softmax의 그래디언트가 거의 0에 수렴해 학습이 멈춰버린다(vanishing gradient). 따라서 분산을 1로 정규화해 주려고 $\sqrt{d_k}$로 나눈다.
여러 개의 Query를 동시에 처리하면 행렬로 정리된다. $Q \in \mathbb{R}^{N\times d_k}$, $K \in \mathbb{R}^{N\times d_k}$, $V \in \mathbb{R}^{N\times d_v}$ 일 때:
이때 softmax는 각 행에 대해 독립적으로 적용된다(행마다 한 개의 Query).
Self-Attention 완전 해부
지금까지의 어텐션에서는 Q, K, V가 서로 다른 곳에서 “이미 주어졌다”고 가정했다. Self-Attention은 — 이름 그대로 — Q, K, V를 같은 입력으로부터 만든다. 입력 시퀀스의 각 원소가 다른 모든 원소를 어떻게 “돌아볼지” 학습한다.
5.1입력에서 Q, K, V 만들기
입력을 $X \in \mathbb{R}^{N \times d_\text{model}}$ 이라 하자(N개의 토큰, 각 토큰의 차원이 $d_\text{model}$). 학습 가능한 세 개의 가중치 행렬
로 입력을 세 가지 다른 “관점”으로 투영한다.
핵심 — 같은 토큰 $x_i$가 “질문하는 입장(Q)”, “답이 될 후보로 묻혀 있는 입장(K)”, “실제로 다른 토큰에 전달할 내용(V)”의 세 가지 모드로 동시에 변환된다.
5.2한 줄 요약 — 핵심 수식
5.3한 행의 의미를 풀어 쓰면
출력의 $i$번째 행, 곧 $x_i$가 만든 새 벡터 $y_i$를 풀면:
이 식이 모든 다음 이야기의 토대다. 한 토큰 $x_i$는 자기 자신을 포함한 N개 모든 토큰의 정보 $v_j$를, 학습된 가중치 $\alpha_{ij}$로 “골라 모은” 결과로 갱신된다.
5.4두 가지 결정적 성질
(a) 순열 불변·등변성
입력 토큰들의 순서를 바꾸면 — 곧 $X$의 행을 임의의 순열 $\pi$로 재배열하면 — Q, K, V도 같은 순열로 재배열되고, 출력 행도 같은 순열로 재배열된다(permutation-equivariant). 만약 출력을 평균 풀링 같은 대칭 함수로 모은다면 그 결과는 순열을 무시한다(permutation-invariant). 이 성질이 점군과 어텐션의 결혼식이다 — 점군은 순서가 없지 않은가.
(b) 임의-거리 의존성
한 층의 어텐션이 모든 쌍을 본다. CNN이 깊이로 쌓아야 했던 “먼 영역 사이 의존성”을 한 층에서 직접 만든다.
5.5대가 — 비용은 N²이다
출력의 모든 N개 행마다 N개의 점수를 계산하므로, 시간·메모리 비용 모두 $O(N^2 d)$ 가 된다. 이미지에서는 N이 많아야 수만, 점군에서는 N이 수십만~수백만이 된다. 우리가 PT v1 → v2 → v3로 가며 마주칠 모든 어려움의 진원지가 바로 이 N² 항이다.
Multi-Head Attention
한 번의 self-attention은 “하나의 관계 패턴”을 학습한다. 그런데 토큰들 사이에는 동시에 여러 종류의 관계가 있다 — 문법적 관계, 의미적 유사성, 위치적 인접 등. 이를 분담하기 위해, 여러 개의 어텐션을 병렬로 돌리고 결과를 합친다. 이를 multi-head attention이라 부른다.
6.1핵심 아이디어 — 작은 어텐션 여러 개
전체 차원 $d_\text{model}$을 $h$개의 헤드로 잘라, 헤드마다 차원이 $d_k = d_v = d_\text{model}/h$ 인 self-attention을 따로 돌린다. 그리고 결과를 옆으로 이어 붙인 뒤 마지막으로 한 번 더 선형 변환한다.
차원이 줄어 헤드 하나가 더 가벼워지고, 동시에 여러 “주의의 종류”를 학습할 수 있다. 모든 헤드를 합쳐도 파라미터 수와 연산량은 단일 헤드와 거의 같은 규모로 유지된다.
원 Transformer 논문(Vaswani 2017)은 d_model = 512, h = 8을 사용한다. 헤드마다 d_k = d_v = 64가 되는 셈이다. PTv3에서도 stage별로 헤드 수가 달라지지만 “부분 차원으로 잘라 병렬”이라는 골격은 같다.
Transformer 아키텍처 (Vaswani 2017)
이제 self-attention과 multi-head attention을 ‘부품’으로 갖고 있다. 이를 어떻게 쌓아 “블록”과 “네트워크”로 만드는지가 — 그리고 그 블록의 어떤 부분이 PTv3에까지 살아남는지가 — 이 절의 주제다.
Attention Is All You Need
RNN과 CNN 없이, 오직 어텐션과 잔차 연결·LayerNorm만으로 시퀀스를 처리할 수 있음을 보였다. 이후 모든 분야의 “Transformer”의 부모.
7.1인코더 블록 — 두 개의 부품, 두 개의 잔차 연결
Transformer 인코더의 한 블록은 다음 두 단계를 차례로 적용한다.
여기서 LN은 LayerNorm, FFN은 토큰별로 독립적으로 적용되는 2층 MLP다. 즉
이때 $\sigma$는 ReLU 또는 GELU. 첫 행렬 $W_1 \in \mathbb{R}^{d_\text{model}\times d_\text{ff}}$은 차원을 일시적으로 4배 정도(가령 512→2048) 늘리고, $W_2$가 다시 줄인다. FFN의 핵심은 “토큰끼리는 절대 섞지 않는다”는 것이다 — 토큰 간 정보 교환은 오직 MHA에서만 일어난다. FFN은 각 토큰이 자기 안에서 비선형 변환을 거치는 단계다.
잔차 연결(residual)과 LN의 역할은 깊은 네트워크에서의 학습 안정성과 그래디언트 흐름이다. 이 블록 구조는 PT v1·v2·v3에서도 거의 그대로 살아남는다.
7.2위치 정보의 주입 — Positional Encoding
self-attention은 순열 등변이라 했다. 그런데 자연어, 이미지, 점군 모두에 “위치”라는 정보가 의미를 갖는다. 따라서 입력에 위치 정보를 직접 더해 모델이 그것을 활용하게 한다. 원 논문은 사인·코사인의 결합으로 정의된 절대 위치 인코딩을 사용했다.
여기서 $p$는 시퀀스 내 위치, $i$는 차원 인덱스. 차원이 다른 두 위치는 서로 다른 주파수의 사인·코사인을 갖게 되어, 모델이 “두 위치의 차이”를 선형 변환만으로 표현 가능하다.
점군에서는 위치가 연속 좌표(x, y, z)이므로 사정이 다르다. PT v1은 두 점의 좌표 차이 $p_i - p_j$를 작은 MLP에 통과시킨 상대 위치 인코딩을 사용하고, PTv3는 직렬화 자체에 위치 정보를 녹여낸다(뒤에서 본다).
잠시 — Vision Transformer
Transformer가 자연어에서 이미지로 옮겨가는 가장 단순한 방법이 ViT(Dosovitskiy 2020)다. PTv3가 ViT의 한 핵심 아이디어 — 입력을 “패치”로 잘라 시퀀스로 다룬다 — 를 점군에 옮겨오므로, 30초만 짚고 가자.
An Image is Worth 16×16 Words
이미지를 16×16 patch로 나눠 각 patch를 “토큰”으로 보고, 표준 Transformer 인코더에 통과시킨다. 큰 데이터에서 CNN을 능가함을 보였다.
이미지가 “격자에서 잘라낸 16×16 패치들의 시퀀스”라면, 점군은 “1차원으로 정렬된 점들의 시퀀스” — 가 될 수 있을까? 이 생각이 PTv3의 출발점이다.
포인트 클라우드의 기초
이제 시각을 점군으로 옮긴다. PTv3가 다루는 데이터의 모양을 또렷이 해두자.
9.1형식적 정의
점군은 $N$개의 점을 모은 집합으로, 각 점은 좌표와 (선택적으로) 특징을 갖는다.
$f_i$에는 색(RGB), 반사도(intensity), 법선 벡터, 기타 측정값이 포함될 수 있다. 우리가 학습으로 알고 싶은 양은 보통 두 가지다.
9.2점군이 어디서 오는가
실용적 맥락 한 줄. LiDAR(자율주행), RGB-D 카메라(실내 매핑), 구조광·라인 트라이앵귤레이션 센서(예: Micro-Epsilon scanCONTROL — 용접 검사에 흔히 쓰인다), MVS(다중뷰 스테레오), 3D 스캐너 등이 모두 점군을 만든다. 이미지가 “격자 위에 놓인 색”인 것에 대해 — 점군은 “3D 공간 어디든 흩뿌려진 측정”이다.
9.3점군 학습의 핵심 요구
모델이 다음 셋을 만족하도록 설계해야 한다.
PointNet — 모든 것의 시작
점군 위에서 작동하는 “신경망다운 신경망”의 첫 모델이다. PT 시리즈로 가는 길의 첫 정류장이며, 핵심 아이디어 — 대칭 함수로 순서를 지운다 — 가 이후 모든 모델에서 변주된다.
PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation
“집합”에 적용 가능한 신경망의 보편 근사 정리(universal approximation)를 제시. PointNet++로, 이후의 모든 점군 학습으로 이어졌다.
10.1핵심 정리 — 대칭 함수의 보편 근사
저자들은 다음을 증명했다(요지). 어떤 연속이고 순열 불변인 집합 함수 $f: 2^{\mathbb{R}^3} \to \mathbb{R}$도, 충분히 큰 신경망 $h$와 $\gamma$에 대해
여기 MAX는 채널별 최댓값(element-wise max). $h$는 점 하나하나에 똑같이 적용되는 MLP, $\gamma$는 풀링 결과를 받는 또 다른 MLP. 이 식은 다음 두 의미가 있다.
10.2왜 MAX인가 — 다른 대칭 함수와의 비교
대칭 함수의 후보로는 SUM, AVG, MAX 등이 있다. 그런데 신경망의 근사 능력을 살리려면 하나의 점만으로도 충분히 강한 “시그널”이 다른 점에 의해 가려지지 않는 풀링이 좋다. MAX는 “가장 두드러진 특징”을 그대로 보존한다. 실제 실험에서도 SUM·AVG보다 분류 정확도가 안정적으로 높았다.
10.3점별 분할(segmentation)은 어떻게 만드나
분류는 “집합 → 단일 라벨”이지만 분할은 “집합 → 점마다의 라벨”이다. PointNet은 풀링 직전의 “전역 1024차원 벡터”를 모든 점에 다시 붙여(concat) 점별 MLP로 라벨을 낸다. 즉, 각 점의 결정에 “나만의 특징 + 전체의 요약”이 모두 들어간다.
10.4한계 — “지역(local) 구조”가 없다
여기서 끝나면 좋겠지만 — PointNet은 점 하나에서 곧장 “전체 요약”으로 점프한다. 중간 단계의 지역 패턴 — “이 작은 영역의 점들이 모여 모서리를 이룬다”거나 “이 표면 조각이 평면이다” — 이 없다. 이것이 CNN의 “계층적 수용장(receptive field)”에 비해 약한 이유였다.
PointNet은 “점 → 전체”의 두 단계만 있다. 다음 모델 PointNet++는 그 사이에 여러 단계의 지역 PointNet을 끼워 넣는다 — CNN이 깊이를 쌓아 수용장을 키운 것과 정확히 같은 발상이다.
PointNet++ — 계층적 학습
아이디어 한 줄: “PointNet을 작은 영역에 반복 적용하고, 그 결과를 다시 더 큰 영역에 적용한다.” CNN의 점진적 풀링과 동일한 효과를 점군에서 만든다.
PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space
Set Abstraction (SA) 모듈 — Sampling → Grouping → PointNet — 을 반복 쌓아 계층 표현을 학습한다.
11.1Set Abstraction (SA) — 세 단계
한 SA 모듈은 입력 점들 $\mathcal{P}_\text{in}$ 을 받아, 더 적은 수의 “센터 점”과 그 점들의 더 풍부한 특징 벡터로 이루어진 $\mathcal{P}_\text{out}$ 을 만든다.
단계 — Sampling → Grouping → PointNet
- Sampling — Farthest Point Sampling (FPS). 입력에서 가장 “고르게 퍼진” $M$개의 센터 점을 뽑는다. 첫 점은 임의, 그 다음부터는 “지금까지 뽑힌 점들로부터 가장 먼 점”을 반복적으로 선택. 결과는 점군의 형상에 적응한 균일한 다운샘플링.
- Grouping — Ball Query. 각 센터 점 주변 반경 $r$ 안의 점들을 묶어 “지역 패치”를 구성. 패치당 점의 수는 K로 잘라(필요 시 패딩) 텐서 형식으로 통일.
- PointNet on Patch. 각 패치(K개의 점, 각각 d차원 특징)에 PointNet을 적용해 단일 벡터로 요약. 이 벡터가 그 센터 점의 새로운 특징이 된다.
11.2다단 SA로 수용장이 자라난다
SA 모듈을 여러 번 쌓으면, 매 단계마다 (a) 점의 수가 줄고 (b) 점의 특징이 더 큰 영역을 요약한다 — CNN에서 stride 2 conv를 반복할 때와 동일한 “수용장 키우기”다. 분할 작업에서는 그 후 거꾸로 점 수를 다시 키워 원래 해상도로 돌아간다(Feature Propagation 단계).
11.3그래도 남은 한계
PointNet++는 지역 구조를 잡지만, 한 패치 내부에서 일어나는 일은 여전히 “점마다 동일한 MLP + MAX”다. 각 점이 이웃 안에서 누구를 더 비중 있게 볼지는 학습되지 않는다. CNN의 한계와 똑같은 문제 — “이웃 안에서 모두를 똑같이 본다”. 이 자리에 어텐션을 넣으면? 그 답이 바로 Point Transformer다.
Point Transformer V1
“PointNet++의 ‘이웃 안에서 동일 가중치’ 자리에 어텐션을 넣자.” 정확히 그 한 줄짜리 발상을 깔끔하게 구현한 모델이다.
Point Transformer
점의 k-NN 이웃에 대해 벡터 어텐션을 정의해 점군 위 트랜스포머의 표준 구조를 제시. 다음에 본다.
12.1스칼라 어텐션과 벡터 어텐션
지금까지 우리가 본 어텐션은 “Q·K → 스칼라 점수”를 만든 뒤 그것을 V에 곱했다. 점군에서는 그 대신 벡터 어텐션을 쓴다 — 점수가 채널마다 다르게 나오는 “채널별 가중치 벡터”다.
두 점 $x_i$ (질문하는 점)와 $x_j$ (그 이웃)에 대해, PT v1은 다음과 같이 출력 $y_i$를 계산한다.
표기를 풀면:
12.2한 줄로 풀어 쓰면
“i와 j의 차이”를 작은 MLP에 넣어 “채널마다 다른 비중”을 뽑고, 그것으로 j의 값을 채널별로 골라 모은다. 거기에 두 점의 위치 차이도 함께 더해 넣는다.
12.3왜 “벡터”인가 — 채널별 가중치의 의미
표준 어텐션은 한 쌍에 대해 “하나의 점수”를 낸다. 벡터 어텐션은 한 쌍에 대해 “채널 수만큼의 점수”를 낸다. 점군에서 두 점의 관계는 채널마다 다르게 의미를 가질 수 있다 — 예를 들어 한 채널은 “이 둘이 같은 표면 위에 있는가”, 다른 채널은 “이 둘 사이에 모서리가 있는가”를 학습할 수 있다. 채널별 가중치는 이런 다양한 관계를 동시에 인코딩한다.
12.4전체 네트워크 — U-Net의 점군판
PT v1의 전체 네트워크는 PointNet++가 그랬듯 인코더-디코더 (U-Net) 구조다. 차이는 “Set Abstraction에서 PointNet 대신 Point Transformer Block을 쓴다”는 것뿐이다. 인코더로 내려가며 점 수가 줄고, 디코더로 올라오며 점 수가 회복된다. 분할에서는 디코더 끝단에서 점별 라벨을 낸다.
12.5비용 — k-NN과 N의 곱
한 점이 k-NN 이웃만 보므로 어텐션 자체는 $O(N k)$로 줄었다. 그런데 두 가지 무거운 비용이 남아 있다.
이 두 항이 PTv2와 PTv3가 각자 다른 방식으로 공격하는 표적이다.
Point Transformer V2 — 짧은 다리
PTv2는 PT v1과 PTv3 사이의 중요한 디딤돌이다. 본격은 PTv3이지만, “v3가 무엇을 버리고 무엇을 단순화했는가”를 이해하려면 v2가 시도한 것을 알아야 한다.
Point Transformer V2: Grouped Vector Attention and Partition-based Pooling
벡터 어텐션의 채널을 그룹으로 묶어 효율을 올리고(GVA), 이웃 풀링을 “격자 분할” 기반으로 바꿔 안정성을 개선했다.
13.1핵심 변경 1 — Grouped Vector Attention (GVA)
PT v1의 벡터 어텐션은 채널 수만큼의 가중치를 만들어 메모리·연산이 비싸다. GVA는 채널을 g개의 그룹으로 묶고, 그룹 내부 채널은 같은 가중치를 공유하게 한다. 즉 가중치 벡터의 길이를 d → g로 줄인 셈이다.
이는 standard scalar attention($g=1$)과 v1 vector attention($g=d$) 사이의 절충점이다. 속도와 표현력의 균형을 보다 잘 맞춘다.
13.2핵심 변경 2 — Partition-based Pooling
PointNet++의 FPS+Ball-Query는 학습 가능하지 않고 비용이 크다. PTv2는 공간을 균일한 격자(voxel grid)로 분할해, 같은 voxel에 떨어진 점들을 한 번에 풀링한다. 격자 분할은 (a) 결정적이고 (b) 메모리 접근이 정렬된 패턴을 가진다 — GPU 친화적.
13.3그러나 여전히 — k-NN의 그림자
풀링은 격자로 바뀌었지만, 어텐션 자체는 여전히 각 점의 k-NN 이웃에서 일어난다. PT v1의 가장 무거운 비용 — 매 layer마다 k-NN을 다시 만들어야 한다 — 이 그대로 남아 있다. PTv3는 이 마지막 못을 뽑는다.
지금까지 본 모든 점군 트랜스포머의 비용은 “이웃을 어떻게 정의하느냐” 에서 발생한다. PTv3의 통찰은 — “점들을 1차원으로 정렬해 두면, ‘이웃’은 그냥 시퀀스 상의 인접 구간이 된다”는 것.
Point Transformer V3 — 본론
Point Transformer V3: Simpler, Faster, Stronger
점들을 공간 채움 곡선으로 정렬한 뒤 패치 단위로 어텐션을 적용해, 단순함·속도·정확도를 동시에 거머쥔 설계.
14.1출발점 — “정밀도가 아니라 규모(Scale)”
PT v1·v2는 “이웃을 더 정밀하게 정의하면 더 좋은 모델이 된다”는 가정을 따랐다. PTv3 저자들은 이를 정면으로 뒤집는다.
정밀한 이웃 한 줌보다, 단순한 이웃 한 무더기가 낫다. 단순해야 더 큰 모델·더 큰 데이터로 확장된다(scale).
이 철학을 따라 PTv3는 k-NN을 완전히 폐기하고, 그 자리를 “직렬화 + 패치 어텐션”이라는 두 단어로 메운다. 결과 — PTv2 대비 처리량 약 3배, 메모리 약 1/10. 그러면서 정확도는 더 높다.
14.2아이디어 1 — 직렬화 (Serialization)
점군의 각 점에는 3D 좌표 $(x, y, z)$ 가 있다. 이를 1차원 정수 인덱스로 바꾸어 점들을 한 줄로 세운다. 단, 무작정 세우면 의미가 없다 — 3D 공간에서 가까웠던 점들이 1D에서도 가까워야 한다. 이것이 가능한가? 가능하다. 공간 채움 곡선(space-filling curve)이 그 도구다.
14.2.1 Z-order curve (Morton order)
가장 단순한 공간 채움 곡선. 좌표의 비트(bit)를 교차로 끼워 넣어(interleave) 단일 정수로 만든다. 2D 좌표 $(x, y) = (5, 3)$ 을 예로 보자.
예제 — (x, y) = (5, 3) 의 Z-order 인덱스 계산
- 각 좌표를 이진수로 적는다. $x = 5 = 101_2$, $y = 3 = 011_2$.
- 비트를 교차로 배치. 결과의 비트 위치를 낮은 자리부터 적으면: $y_0,\, x_0,\, y_1,\, x_1,\, y_2,\, x_2$.
- 대입: $y_0=1,\, x_0=1,\, y_1=1,\, x_1=0,\, y_2=0,\, x_2=1$ → 결과 $= 101101_2$.
- 10진수 변환: $1\cdot32 + 0\cdot16 + 1\cdot8 + 1\cdot4 + 0\cdot2 + 1\cdot1 = 45$.
- 3D에서는 비트가 $z, y, x$ 의 순서로 교차된다 — 같은 원리.
이 방식의 장점은 (a) 인덱스 계산이 비트 연산만으로 $O(\log_2 \text{coord})$ 시간이고, (b) 인덱스가 가까운 점들이 공간상 같은 “Z 모양” 셀에 들어 있다는 점이다. 단점은 셀 경계에서 “급격한 점프”가 일어날 수 있다는 것 — 두 점이 공간상 매우 가깝지만 Z-인덱스가 멀어질 수 있다.
14.2.2 Hilbert curve
Hilbert curve는 임의의 두 점이 곡선 상에서 가까우면 공간상에서도 거의 항상 가깝다는 더 강한 국지성을 갖는다. 계산은 Z-order보다 비싸지만 — 미리 한 번 계산해 인덱스를 캐싱해두면 학습 내내 비용은 0이다.
14.2.3 PTv3가 사용하는 4종류의 직렬화
PTv3는 4가지 직렬화를 번갈아 사용한다 — Z-order, Z-order-trans (좌표 축을 회전한 버전), Hilbert, Hilbert-trans. 매 어텐션 layer마다 이 중 하나를 선택해 적용한다. 한 layer에서 같은 패치에 속하지 못한 두 점이 다음 layer에서는 같은 시퀀스에 묶일 수 있도록 — 정보 흐름의 다양성을 만든다. ViT의 Swin Transformer가 “shifted window”로 한 일을, PTv3는 “shifted curve”로 한다.
# 의사 코드 — 한 layer의 직렬화 / Pseudocode for per-layer serialization
def serialize(points, mode):
# points: (N, 3) raw xyz
# mode : 'z' | 'z_trans' | 'hilbert' | 'hilbert_trans'
if mode.startswith('z'):
idx = morton_encode(points, transposed=mode.endswith('trans'))
else:
idx = hilbert_encode(points, transposed=mode.endswith('trans'))
order = argsort(idx) # 정렬 순서 / sorted order
return points[order], order # 정렬된 점과 원-인덱스 매핑
한 종류의 곡선만 사용하면 그 곡선이 깨는 자리(cell boundary)의 두 점은 영원히 한 패치에 들어가지 못한다. 매 layer마다 다른 곡선을 돌려쓰면, 깊이가 쌓이면서 모든 가능한 “이웃 관계”가 결국에는 정보로 연결된다. 다양한 시각의 지역성을 시간에 따라 결합한다고 봐도 좋다.
14.3아이디어 2 — Patch Attention
점들이 1차원 시퀀스로 정렬되었다. 이제 어텐션은 어떻게 적용하나? 답은 단순하다 — 시퀀스를 고정 길이의 패치로 자르고, 패치 안에서만 어텐션을 한다. ViT가 이미지 패치 안에서 어텐션을 했던 것의 1D 버전이다.
한 패치 안의 K개 점들 사이에 표준 multi-head self-attention을 한 번 돌린다. 비용을 따져보자 — 패치 수는 $N/K$, 패치마다의 비용은 $O(K^2 d)$, 따라서 총 비용은
K는 보통 1024로 고정. 즉 비용은 $N$에 대해 선형이다. PT v1·v2의 어텐션이 가졌던 “k-NN 이웃 수 × 차원” 비용과 같은 차수지만, 차이가 둘 있다.
14.3.1 패치 그룹화 — Shifted Patches와 곡선 회전
같은 패치 분할을 모든 layer에서 쓰면 패치 경계의 두 점은 영원히 만나지 못한다. PTv3는 두 가지 방법을 결합한다 — (a) 매 layer마다 다른 직렬화 곡선을 사용 (4종류 회전), (b) 시퀀스 시작점을 layer마다 시프트. 여러 layer를 거치며 점들 사이의 모든 가능한 “이웃 관계”가 결국 직간접으로 연결된다.
14.3.2 패딩 처리
현실에서 $N$이 $K$의 배수가 아닐 때, 마지막 패치는 더미 토큰으로 채워진다. attention mask로 더미 위치의 영향을 0으로 만들면 된다. 토큰 수 N이 들쭉날쭉한 점군에서 흔히 일어나는 일이라, PTv3 구현은 이 처리를 한 번에 묶어 GPU 친화적인 batched attention으로 돌린다(flash-attn나 xformers와 결합 가능).
14.4아이디어 3 — Conditional Position Encoding (xCPE)
패치 어텐션은 시퀀스 인덱스만으로는 “3D 위치”를 충분히 알지 못한다. PT v1·v2는 이를 어텐션 안에 $\delta_{ij} = \text{MLP}(p_i - p_j)$ 로 넣었다. PTv3는 더 단순한 방법을 쓴다 — sparse convolution 한 번을 어텐션 앞에 둔다. 이 sparse conv가 “3D 좌표 정보”를 특징 안으로 자연스럽게 주입하는 위치 인코딩 역할을 한다(conditional — 입력에 따라 적응한다).
sparse conv는 voxel 격자 위에서만 동작하므로, 점들을 voxel로 양자화한 표현 위에서 한 번 돌린다. 점 자체는 그대로 유지되고, voxel feature가 다시 점에 매핑된다. PTv3가 의존하는 sparse conv 라이브러리는 spconv 또는 torchsparse다.
이 한 줄짜리 변경의 의미가 크다 — PT v1·v2의 비싼 상대 위치 인코딩($\delta_{ij}$ 계산)을 통째로 대체한다. “가까운 점들의 정보 한 번 섞기”를 sparse conv가 빠르게 해주고, 그 다음 패치 어텐션이 “더 넓고 다양한 관계”를 책임진다. conv와 attention의 깔끔한 분업이다.
14.5전체 아키텍처 — U-Net 백본
PTv3의 전체 네트워크는 PointNet++가 그랬듯, 5단계의 인코더와 4단계의 디코더로 이루어진 U-Net이다. 각 단계는 [Block × N] 으로 구성되며, Block은 위에서 본 “xCPE → Patch Attn → FFN”의 세 부품이다. 단계 사이의 다운/업샘플링은 voxel 격자 단위의 풀링으로 수행된다(공간을 (2×2×2) voxel로 합치며 점 수가 ~1/2~1/8로 줄어든다).
14.5.1 하이퍼파라미터 한 묶음
| 요소 | 값 | 비고 |
|---|---|---|
| Patch size K | 1024 | 한 attention 내부의 토큰 수 |
| Stage 별 채널 수 | 32 → 64 → 128 → 256 → 512 | 전형적 U-Net |
| Stage 별 Block 수 | 2 → 2 → 6 → 2 → 2 | 중간 stage가 가장 두텁다 |
| Heads 수 | 2 → 4 → 8 → 16 → 32 | 채널당 64 유지 |
| FFN 확장 | 4× | 표준 Transformer와 동일 |
| Activation | GELU | BatchNorm 대신 LayerNorm |
| Voxel size | 2 cm (실내) / 5 cm (실외) | xCPE의 sparse conv 기준 |
| 직렬화 4종 순환 | z → z' → h → h' | 매 layer마다 변경 |
14.5.2 핵심을 30줄로 — 학습용 PyTorch 의사 코드
# PTv3의 한 Block을 단순화한 의사 코드 / Simplified PTv3 block
import torch, torch.nn as nn
class PTv3Block(nn.Module):
def __init__(self, dim, heads, patch_size=1024, ffn_ratio=4):
super().__init__()
# 위치 정보 주입용 sparse conv / position-encoding sparse conv
self.xcpe = SparseConv3d(dim, dim, kernel_size=3)
self.norm1 = nn.LayerNorm(dim)
self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)
self.norm2 = nn.LayerNorm(dim)
self.ffn = nn.Sequential(
nn.Linear(dim, dim*ffn_ratio), nn.GELU(),
nn.Linear(dim*ffn_ratio, dim))
self.K = patch_size
def forward(self, x, coords, order):
# x: (N, C) features, coords: (N, 3) xyz, order: (N,) serialization indices
# 1) xCPE — 가까운 점끼리 한 번 섞기 / mix nearby points once
x = x + self.xcpe(x, coords)
# 2) 직렬화 순서로 정렬 / sort by curve order
x_sorted = x[order] # (N, C)
N, C = x_sorted.shape
K = self.K
# 3) 패딩 후 패치 단위로 자르기 / pad to multiple of K, reshape
pad = (-N) % K
x_pad = torch.cat([x_sorted, x_sorted.new_zeros(pad, C)], 0)
mask = torch.arange(N+pad) >= N # True = padded
x_pad = x_pad.view(-1, K, C) # (P, K, C)
mask = mask.view(-1, K) # (P, K)
# 4) 패치 안에서 attention / attention inside each patch
h = self.norm1(x_pad)
h, _ = self.attn(h, h, h, key_padding_mask=mask, need_weights=False)
x_pad = x_pad + h
x_pad = x_pad + self.ffn(self.norm2(x_pad))
# 5) 평탄화 → 원래 순서로 복원 / flatten and unsort
x_sorted = x_pad.view(-1, C)[:N]
out = torch.empty_like(x_sorted); out[order] = x_sorted
return out
실제 구현은 flash-attn이나 xformers의 메모리 효율 attention을 끼워 넣고, 직렬화 인덱스는 torch_scatter의 정렬 보조 함수로 빠르게 만든다. 그리고 인코더-디코더의 풀링·언풀링은 voxel 그리드 인덱스를 통해 처리되므로 여기서는 생략했다.
14.6성능 — “더 빠르고 더 가볍고 더 정확한” 것의 의미
PTv3 논문의 핵심 비교(요약). 동일 모델(또는 그에 가까운 변종) 하나로 옥내·옥외·합성·실측을 모두 휩쓴다.
| 벤치마크 | 지표 | 이전 SOTA | PTv3 |
|---|---|---|---|
| ScanNet v2 (val) | mIoU | ~76.4 (PTv2) | 77.5 |
| S3DIS Area-5 | mIoU | ~71.6 | 73.4 |
| nuScenes (val) | mIoU | ~80.2 | 80.4 |
| SemanticKITTI | mIoU | ~70.3 | 71.2 |
| 처리량(throughput) vs PTv2 | — | 1× | ~3.3× |
| 최대 메모리 vs PTv2 | — | 1× | ~0.1× |
특히 “여러 데이터셋을 하나로 묶어 함께 학습할 때(multi-dataset joint training)” 정확도가 더 오른다는 점이 중요하다. 이는 PTv3의 단순한 골격이 데이터의 다양성을 그대로 흡수할 수 있다는 증거다 — 곧 scale의 약속이 실현되었음을 보여준다.
옥외 자율주행 데이터(nuScenes, SemanticKITTI)에서는 한 프레임당 점 수가 수십만에 달한다. PTv2까지는 이 규모에서 학습이 매우 무거웠지만, PTv3는 단일 GPU 24GB에서도 batch size를 의미 있게 키울 수 있어 학습 사이클이 짧아진다. 산업 검사 데이터(예: 라인 트라이앵귤레이션 LVS 센서로 얻은 용접 비드 점군)도 같은 이유로 다루기 좋다.
14.7왜 그렇게 단순한 변경이 그렇게 큰 차이를 만드는가
마지막으로 한 번 응축한다.
정리 — 한 장으로 보는 흐름
다섯 정거장이었다. 각각이 이전 모델의 어떤 한계를 정확히 짚어 풀었는지 되짚어보자.
15.1한 줄 요약
CNN의 “격자 + 좁은 이웃”은 self-attention의 “집합 + 모든 쌍”으로 일반화되었고, 그 결과는 점군에서 “k-NN으로 좁힌 이웃”으로 한 번 다시 좁혀졌다가, PTv3에서 “직렬화로 만든 1D 시퀀스의 패치”로 한 번 더 일반화되었다. 매 단계의 진보는 단순함을 향했다.
15.2다음 단계 — 더 읽으면 좋은 것
15.3생각해볼 만한 질문
- 왜 “정렬”이 어텐션에 도움이 되는가? 토큰 위치만이 아니라 “어떤 토큰끼리 가까이 놓이는가”가 표현 학습에 영향을 준다는 점.
- 패치 크기 K와 모델 깊이 사이의 균형은? 큰 K는 한 번에 더 많은 점을 보지만 K²의 비용. 작은 K는 더 깊은 층이 필요.
- 4가지 직렬화 외에 다른 곡선은? Gilbert curve, Peano curve, learned curve … 아직 열린 문제.
- 비정형(unstructured) 메쉬에서도 같은 발상이 통할까? 메쉬는 위상 구조가 있어 직렬화가 자명하지 않다. 그래프 spectral 정렬이 후보.
— 끝까지 함께 와줘서 감사. 좋은 점군과 좋은 모델이 있기를.