Point Transformer의 2번째 version이다. 논문의 인용수는 2024.04.02, google scholar 기준으로 124회이다. v2는 v1에서의 한계점을 분석하고 이를 해결하기 위해 group vector attention을 제안한다. 멀티헤드 어텐션에서 영감을 얻은것으로 보인다. 또한 positional encoding을 수정하였고 partition-based pooling을 새로 제안한다.
코드는 https://github.com/Pointcept/PointTransformerV2 에서 확인가능하다.
v1에서의 문제점
1. MLPs로 이루어진 vector attention은큰 computational cost가 발생한다.
2. v1에서의 positional encoding은 3d point에서의 geometric정보를 충분히 담지 못한다.
3. point cloud pooling을 위해 사용했던 kNN, farthest point sampling은 time-consuming이고 not spatially well-alined(공간 정보를 담지 못한다)
Problem Formulation and Background
shifted-grid attention은 ViT에서 이미지를 격자형태로 나누는것과 같이 3d point에서도 가능하다. 하지만 격자마다 포인트의 밀집도가 다르면 잘 작동하지 않는다. 그렇기에 우리는 v1에서 kNN을 사용하였고 이는 실험에서 가장 효과적이였다.
Grouped Vector Attention
Grouped Vector Attention(GVA)는 c차원의 채널을 입력받아 g차원에서 어텐션을 진행하고 다시 c차원으로 출력한다. 필자는 쉽게 생각해서 multihead attention에 squeeze and excitation에서 사용한 채널의 축소, 확장을 적용한 것으로 이해했다.
그림으로 보면 다음과 같다.
Position Encoding Multiplier
(논문에 오타가 상당히 많은듯..multipler이라고 써져있고 GVA를 VGA로 써놓고..)
v2에서는 과적합을 방지하고 일반화 성능을 향상시키기 위해 attention의 계산비용을 조정하였기 때문에 position encoding에 좀 더 힘을 실어주기 위해 추가적인 multiplier를 적용했다.
Figure 1의 왼쪽그림이 이를 표현하고 있고 식은 다음과 같다.
\(\odot\) : Hadamard product -> 일반적인 행렬 곱셈과 달리 동일한 크기의 두 행렬에 대해 각 원소끼리 곱하는 연산이다
하나의 position에 대해 2개의 MLP를 적용하고 이를 product하여 조금 더 많은 정보를 포함한 것 같다.
partition-based Pooling
v1에서는 farthest point sampling - kNN 을 사용하였는데 이는 주변 픽셀의 정보들을 종합하기 힘들다. 쿼리 셋(한개의 포인트에 대한 주변 정보)에 따라 밀집도가 다르고 중복된 면적이 있을 경우 이를 컨트롤하기 힘들다.
Pooling 연산에 대한 수식
M = (P, F) 라는 포인트셋을 \(M_i\) = \([M_1, M_2, ..., M_n]\)의 중복되지 않은 subset으로 나눈다.
이후 (8)과 같이 p는 MeanPool을 적용하고 f는 U=Linear Projection 인 MaxPool을 적용한다.
Unpooling의 경우 Pooling에서 연산된 정보들을 이용하여 subset을 되돌리는 형태로 진행된다고 하는데 무슨말인지 몰라서 깃허브에 있는 코드를 참고해보니 학습 가능한 weight들을 이용하여 값을 복원시키는 형태로 진행되는 것 같다.
class UnpoolWithSkip(nn.Module):
"""
Map Unpooling with skip connection
"""
def __init__(self,
in_channels,
skip_channels,
out_channels,
bias=True,
skip=True,
backend="map"
):
super(UnpoolWithSkip, self).__init__()
self.in_channels = in_channels
self.skip_channels = skip_channels
self.out_channels = out_channels
self.skip = skip
self.backend = backend
assert self.backend in ["map", "interp"]
self.proj = nn.Sequential(nn.Linear(in_channels, out_channels, bias=bias),
PointBatchNorm(out_channels),
nn.ReLU(inplace=True))
self.proj_skip = nn.Sequential(nn.Linear(skip_channels, out_channels, bias=bias),
PointBatchNorm(out_channels),
nn.ReLU(inplace=True))
def forward(self, points, skip_points, cluster=None):
coord, feat, offset = points
skip_coord, skip_feat, skip_offset = skip_points
if self.backend == "map" and cluster is not None:
feat = self.proj(feat)[cluster]
else:
feat = pointops.interpolation(coord, skip_coord, self.proj(feat), offset, skip_offset)
if self.skip:
feat = feat + self.proj_skip(skip_feat)
return [skip_coord, feat, skip_offset]
제안된 방법론은 여기까지고 실험결과는 v1보다 조금 더 좋은 성능을 냈다. 결과는 논문 참고 바람
'논문 리뷰' 카테고리의 다른 글
Emerging Properties in Self-Supervised Vision Transformers 리뷰 (0) | 2024.04.04 |
---|---|
Point Transformer V3 : Simpler, Faster, Stronger (0) | 2024.04.03 |
Point Transformer v1 리뷰 (0) | 2024.04.01 |
YOLOP 논문 요약 - 2021 (0) | 2022.09.26 |
Faster R-CNN 논문 번역/리뷰 - 2016 (0) | 2022.08.16 |