어떤 멀티모달(Multimodal) 작업을 하기 전에도, 이미지는 먼저 트랜스포머(Transformer)가 받아들일 수 있는 토큰 시퀀스(token sequence)가 되어야 합니다. 2020년 ViT 논문(paper)은 이 문제를 16x16 픽셀(pixel) 패치(patch), 선형 사영(linear projection), 위치 임베딩(position embedding)으로 풀었습니다. 5년이 흐른 2026년에도 모든 프런티어(frontier) 모델, 예를 들어 2576px 네이티브(native) 비전을 가진 Claude Opus 4.7, Gemini 3.1 Pro, Qwen3.5-Omni가 여전히 같은 방식에서 시작합니다. 인코더(encoder)는 ViT에서 DINOv2를 거쳐 SigLIP 2로 바뀌었고, 레지스터 토큰(register token)이 추가되었으며, 위치 부여 방식은 2D-RoPE로 옮겨갔지만 핵심 기본 단위(primitive)는 그대로 유지되었습니다. 이 강의는 패치-토큰 파이프라인(patch-token pipeline)을 처음부터 끝까지 읽어 내고, 표준 라이브러리(stdlib) 파이썬으로 직접 구현해 봄으로써, Phase 12의 나머지 강의에서 "시각 토큰(visual tokens)"이 무엇인지에 대한 구체적인 머릿속 모델을 갖추게 합니다.
유형: Learn
언어: Python (stdlib, 패치 토크나이저(patch tokenizer)와 지오메트리 계산기(geometry calculator))
선수 지식: Phase 7 (Transformers), Phase 4 (Computer Vision)
예상 시간: 약 120분
학습 목표
- HxWx3 형태의 이미지를 올바른 위치 인코딩(positional encoding)이 부여된 패치 토큰(patch token) 시퀀스로 변환합니다.
- 주어진 패치 크기(patch size), 해상도(resolution), 은닉 차원(hidden dim), 깊이(depth) 조합에 대해 ViT의 시퀀스 길이, 파라미터(parameter) 수, FLOPs를 계산합니다.
- ViT를 2020년 연구물에서 2026년 프로덕션(production) 표준으로 끌어올린 세 가지 업그레이드, 즉 자기지도 사전학습(self-supervised pretraining; DINO / MAE), 레지스터 토큰(register tokens), 네이티브 해상도 패킹(native-resolution packing)을 설명합니다.
- 다운스트림 태스크(downstream task)에 맞추어 CLS 풀링(CLS pooling), 평균 풀링(mean pooling), 레지스터 토큰 중 무엇을 사용할지 판단합니다.
문제
트랜스포머는 벡터 시퀀스 위에서 동작합니다. 텍스트는 그 자체로 이미 시퀀스이며, 바이트(byte) 또는 토큰(token)의 나열입니다. 그러나 이미지는 세 개의 색 채널(color channel)을 가진 2차원 픽셀 격자입니다. 시퀀스가 아닙니다. 모든 픽셀을 단순히 평탄화(flatten)하면 224x224 RGB 이미지는 150,528개의 토큰이 되며, 이 길이에서의 셀프-어텐션(self-attention)은 사실상 불가능에 가깝습니다. 시퀀스 길이에 대해 비용이 이차(quadratic)로 증가하기 때문입니다.
2020년 이전의 접근은 합성곱 신경망(CNN) 특성 추출기(feature extractor)를 앞단에 붙이는 방식이었습니다. 예를 들어 ResNet은 2048차원 벡터로 구성된 7x7 특성 맵(feature map)을 만들어 내고, 그 49개의 토큰을 트랜스포머에 넣었습니다. 이 방식은 동작하기는 했지만 CNN이 가진 편향(bias) — 평행이동 등가성(translation equivariance)과 지역 수용 영역(local receptive field) — 을 그대로 물려받았고, 트랜스포머가 본래 가진 "데이터 규모를 먹고 자라는" 성질을 충분히 살리지 못했습니다.
Dosovitskiy 외(2020)는 단순하지만 과감한 질문을 던졌습니다. "CNN을 아예 건너뛰면 어떻게 될까?" 이미지를 고정 크기 패치(예: 16x16 픽셀)로 나누고, 각 패치를 선형 사영을 통해 벡터로 옮긴 뒤, 위치 임베딩을 더해 평범한(vanilla) 트랜스포머에 그대로 넣어 보자는 제안이었습니다. 당시에는 합성곱 없는 비전이라는 점에서 이단처럼 받아들여졌지만, 충분히 큰 데이터(JFT-300M, 이후 LAION)가 주어지면 ImageNet에서 ResNet을 넘어섰고 이후로도 꾸준히 개선되었습니다.
2026년 현재 ViT 기본 단위는 의심의 여지가 없는 시각 모델의 토대입니다. 모든 공개 가중치(open-weights) 비전-언어 모델(VLM)의 비전 타워(vision tower)는 DINOv2, SigLIP 2, CLIP, EVA, InternViT 같은 ViT 후손들 가운데 하나입니다. 이제 질문은 더 이상 "패치를 써야 하나?"가 아니라, "어떤 패치 크기, 어떤 해상도 스케줄, 어떤 사전학습 목표(pretraining objective), 어떤 위치 인코딩을 쓸 것인가"입니다.
개념
토큰으로서의 패치(Patches as tokens)
형태가 (H, W, 3)인 이미지 x와 패치 크기 P가 주어졌을 때, 이미지를 (H/P) x (W/P) 크기의 겹치지 않는(non-overlapping) 패치 격자로 잘라 냅니다. 각 패치는 P x P x 3 크기의 픽셀 큐브(cube)이며, 이를 평탄화하면 길이 3 P^2인 벡터가 됩니다. 모든 패치는 형태 (3 P^2, D)인 공통(shared) 선형 사영 행렬 W_E를 통해 모델의 은닉 차원 D로 사상(mapping)됩니다.
표준 구성인 ViT-B/16의 경우는 다음과 같습니다.
- 해상도 224, 패치 크기 16 → 격자 14x14 → 196개의 패치 토큰
- 각 패치는
16 x 16 x 3 = 768개의 픽셀 값으로 이루어지며 D = 768로 사영됨
- 학습 가능한(learnable)
[CLS] 토큰을 앞에 붙이면 시퀀스 길이는 197
패치 사영은 수학적으로 커널 크기(kernel size) P, 스트라이드(stride) P, 출력 채널 수 D인 2D 합성곱과 완전히 동일합니다. 실제 프로덕션 코드는 이를 그대로 구현합니다. nn.Conv2d(3, D, kernel_size=P, stride=P)입니다. "선형 사영"이라는 표현은 개념적 묘사이고, 커널 관점의 구현이 효율적인 실제 구현입니다.
위치 임베딩(Positional embeddings)
패치 자체에는 본래의 순서가 없습니다. 트랜스포머는 패치들을 순서 없는 가방(bag)처럼 다룹니다. 초기 ViT는 학습 가능한 1차원 위치 임베딩을 더했습니다. 위치마다 768차원 벡터 하나씩, 총 197개입니다. 잘 동작하기는 하지만 모델이 학습 해상도에 묶이게 됩니다. 추론(inference) 단계에서 격자 크기를 바꾸려면 위치 테이블을 보간(interpolate)해야 합니다.
현대의 비전 백본(vision backbone)은 2D-RoPE(Qwen2-VL의 M-RoPE, SigLIP 2 기본값)나 분해된(factorized) 2D 위치 표현을 사용합니다. 2D-RoPE는 패치의 (row, column) 인덱스를 기준으로 쿼리(query)와 키(key) 벡터를 회전시키며, 모델은 그 회전각으로부터 상대적인 2차원 위치를 유추합니다. 별도의 위치 테이블이 없으므로 추론 시 임의의 격자 크기를 자연스럽게 처리할 수 있습니다.
CLS 토큰, 풀링된 출력, 레지스터 토큰(CLS token, pooled output, register tokens)
이미지 수준(image-level)의 표현은 무엇으로 삼아야 할까요? 세 가지 선택지가 공존합니다.
[CLS] 토큰. 학습 가능한 벡터를 패치 시퀀스 맨 앞에 붙입니다. 모든 트랜스포머 블록(block)을 거친 뒤 CLS 토큰의 은닉 상태(hidden state)가 이미지의 대표 표현이 됩니다. BERT에서 물려받은 방식이며, 원조 ViT와 CLIP이 사용합니다.
- 평균 풀링(Mean pool). 패치 토큰들의 출력 은닉 상태를 평균 냅니다. SigLIP, DINOv2를 비롯한 대부분의 현대 VLM이 사용합니다.
- 레지스터 토큰(Register tokens). Darcet 외(2023)는 명시적인 싱크 토큰(sink token) 없이 학습된 ViT가 셀프-어텐션을 가로채는 "아티팩트(artifact)" 패치, 즉 비정상적으로 큰 노름(norm)을 가지는 패치를 만들어 낸다는 사실을 관찰했습니다. 학습 가능한 레지스터 토큰을 4~16개 정도 추가하면 이 부담을 흡수해, 세그멘테이션(segmentation), 깊이(depth) 같은 밀집 예측(dense-prediction) 품질이 좋아집니다. DINOv2와 SigLIP 2는 모두 레지스터 토큰을 함께 제공합니다.
어떤 선택을 하느냐는 다운스트림 태스크에 영향을 줍니다. 분류 문제에는 CLS만으로도 충분합니다. 패치 토큰을 LLM에 직접 흘려보내는 VLM에서는 풀링을 아예 하지 않습니다. 모든 패치가 LLM의 입력 토큰이 됩니다. 레지스터 토큰은 LLM으로 넘기기 전에 버립니다. 그 자체가 내용이 아니라, 학습을 도와주는 비계(scaffolding)일 뿐이기 때문입니다.
사전학습: 지도, 대조, 마스킹, 자기증류(Pretraining: supervised, contrastive, masked, self-distilled)
2020년 ViT는 JFT-300M 위에서 지도 분류(supervised classification)로 사전학습되었습니다. 곧 다음 방식들이 그 자리를 대체했습니다.
- CLIP(2021): 4억 개 이미지-텍스트 쌍 위에서의 대조(contrastive) 학습입니다. 강의 12.02에서 다룹니다.
- MAE(2021, He 외): 패치의 75%를 가리고(mask) 픽셀을 복원합니다. 순수한 이미지만으로 동작하는 자기지도 방식입니다.
- DINO(2021) / DINOv2(2023): 학생-교사(student-teacher) 자기증류(self-distillation) 방식입니다. 라벨도, 캡션도 사용하지 않습니다. 2023년의 DINOv2 ViT-g/14는 순수 시각 백본 가운데 가장 강하며, 밀집 특성(dense features) 활용 사례의 기본 선택지입니다.
- SigLIP / SigLIP 2(2023, 2025): 시그모이드(sigmoid) 손실과 네이티브 종횡비(aspect ratio)를 위한 NaFlex를 도입한 CLIP 변형입니다. 2026년 공개 VLM(Qwen, Idefics2, LLaVA-OneVision)의 사실상 표준 비전 타워입니다.
어떤 사전학습을 선택하느냐가 백본이 무엇에 강한지를 결정합니다. CLIP/SigLIP은 텍스트와의 의미적 매칭(semantic matching)에 강하고, DINOv2는 밀집 시각 특성에 강하며, MAE는 다운스트림 파인튜닝(finetuning)의 출발점으로 좋습니다.
스케일링 법칙(Scaling laws)
ViT 스케일링 연구(Zhai 외, 2022)는 ViT의 품질이 모델 크기, 데이터 크기, 연산량(compute)에 대해 예측 가능한 법칙을 따른다는 사실을 보였습니다. 고정된 연산량 아래에서는 다음이 성립합니다.
- 더 큰 모델 + 더 많은 데이터 → 더 좋은 품질
- 패치 크기는 시퀀스 길이와 충실도(fidelity) 사이를 조절하는 손잡이입니다. DINOv2 / SigLIP SO400m에서 흔히 쓰이는 패치 14는 패치 16보다 이미지당 더 많은 토큰을 만들어 냅니다. OCR과 밀집 태스크에는 더 좋지만 속도(speed)에는 불리합니다.
- 해상도 역시 또 하나의 큰 손잡이입니다. 224에서 384, 다시 512로 올리면 거의 항상 품질이 좋아지지만 FLOPs는 이차로 늘어나는 비용을 치러야 합니다.
ViT-g/14(파라미터 10억, 패치 14, 해상도 224 → 토큰 256개)와 SigLIP SO400m/14(파라미터 4억, 패치 14)는 2026년 공개 VLM의 두 주력 인코더입니다.
ViT 파라미터 수 계산(Parameter count for a ViT)
전체 계산은 code/main.py에 있습니다. ViT-B/16 at 224 기준으로 정리하면 다음과 같습니다.
patch_embed = 3 * 16 * 16 * 768 + 768 = 591k
cls + pos = 768 + 197 * 768 = 152k
block = 4 * 768^2 (QKVO) + 2 * 4 * 768^2 (MLP) + 2 * 2*768 (LN)
= 12 * 768^2 + 3k = 7.1M
12 blocks = 85M
final LN = 1.5k
total ≈ 86M
체크포인트를 내려받기 전에 모든 ViT를 이런 식으로 대략(ball-park) 계산해 보아야 합니다. 백본 크기는 다운스트림 VLM의 VRAM 하한선을 그대로 결정하기 때문입니다.
2026년 프로덕션 구성(2026 production config)
2026년 대부분의 공개 VLM이 탑재하는 인코더는 네이티브 해상도(NaFlex) 모드의 SigLIP 2 SO400m/14입니다. 그 구성은 다음과 같습니다.
- 4억 개의 파라미터.
- 패치 크기 14, 기본 해상도 384 → 이미지당 729개의 패치 토큰.
- 이미지 수준 태스크에는 평균 풀링을 사용하고, 시각 질의응답(VQA)에는 729개 패치 모두가 LLM으로 흘러갑니다.
- 4개의 레지스터 토큰, LLM으로 넘기기 전에 폐기.
- 네이티브 종횡비를 위한 이미지 단위 스케일링이 적용된 2D-RoPE.
이 구성의 모든 결정은 출처가 되는 논문으로 거슬러 올라가 직접 확인할 수 있습니다.
사용해보기
code/main.py는 패치 토크나이저이자 지오메트리 계산기입니다. (image H, W, patch P, hidden D, depth L)을 입력으로 받아 다음을 보고합니다.
- 패치 처리 후의 격자 모양(grid shape)과 시퀀스 길이
- 합성된(synthetic) 8x8 픽셀 토이(toy) 이미지의 토큰 시퀀스 — 평탄화와 사영 과정을 한 단계씩 따라갑니다.
- 패치 임베딩, 위치 임베딩, 트랜스포머 블록, 헤드(head)별로 나눈 파라미터 수
- 목표 해상도에서의 순전파(forward pass)당 FLOPs
- ViT-B/16 @ 224, ViT-L/14 @ 336, DINOv2 ViT-g/14 @ 224, SigLIP SO400m/14 @ 384를 비교하는 표
실제로 실행해 보고, 파라미터 수가 공개된 수치와 맞는지 맞춰 보세요. 패치 크기와 해상도를 바꿔 가며 토큰 수가 늘어나는 비용을 직접 체감해 봅니다.
산출물 만들기
이 강의는 outputs/skill-patch-geometry-reader.md를 만들어 냅니다. ViT 구성(패치 크기, 해상도, 은닉 차원, 깊이)을 받으면, 그에 따른 토큰 수, 파라미터 수, VRAM 추정값을 근거와 함께 산출해 주는 스킬(skill)입니다. VLM에 쓸 비전 백본을 고를 때마다 이 스킬을 사용하세요. "토큰이 폭발해서 LLM 컨텍스트(context)가 가득 차 버렸다"는 사고를 미리 막아 줍니다.
연습문제
- 쉬움. 네이티브 1280x720 입력, 패치 크기 14인 Qwen2.5-VL의 패치 토큰 시퀀스 길이를 계산해 보세요. CLS만 사용하는 표현과 비교했을 때 차이가 얼마나 됩니까?
- 중간. 1080p 프레임(1920x1080)을 패치 14로 처리하면 토큰이 몇 개 생깁니까? 30 FPS의 5분짜리 영상에서는 시각 토큰이 총 몇 개입니까? 풀링, 프레임 샘플링(frame sampling), 토큰 병합(token merging) 가운데 어떤 비용 절감 전략이 가장 큰 효과를 냅니까?
- 중간. 순수 파이썬으로 패치 토큰의 평균 풀링을 구현해 보세요. DINOv2 출력의 196개 토큰을 평균 풀링한 값이, 모델의
forward에 풀링된 임베딩(pooled embedding)을 요청했을 때 반환되는 값과 일치하는지 확인합니다.
- 어려움. "Vision Transformers Need Registers"(arXiv:2309.16588)의 Section 3을 읽으세요. 레지스터가 어떤 종류의 아티팩트를 흡수하며, 그것이 다운스트림 밀집 예측에 왜 중요한지를 두 문장으로 설명합니다.
- 어려움.
code/main.py를 수정해 patch-n'-pack을 지원하도록 만드세요. 서로 다른 해상도의 이미지 리스트를 받아, 하나의 패킹된 시퀀스(packed sequence)와 블록-대각(block-diagonal) 어텐션 마스크를 만들어 냅니다. 강의 12.06에 도달하면 그 구현과 비교해 검증합니다.
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 패치(Patch) | "16x16 픽셀 정사각형" | 입력 이미지의 고정 크기, 겹치지 않는 영역이다. 하나의 토큰이 된다. |
| 패치 임베딩(Patch embedding) | "선형 사영" | 평탄화된 패치 픽셀을 D차원 벡터로 사상하는 공통 학습 행렬이다. 실제 프로덕션에서는 stride=P인 Conv2d로 구현한다. |
| CLS 토큰(CLS token) | "클래스 토큰" | 이미지 전체를 대표하도록 시퀀스 앞에 붙는 학습 가능한 벡터다. 2026년 시점에서는 선택 사항이다. |
| 레지스터 토큰(Register token) | "싱크 토큰" | ViT가 사전학습 과정에서 만들어 내는 비정상적으로 큰 노름의 어텐션 아티팩트를 흡수하는 보조 학습 가능 토큰이다. |
| 위치 임베딩(Position embedding) | "위치 정보" | 시퀀스가 순서를 인식하도록 만드는 위치별 벡터 또는 회전이다. 현대의 기본 선택은 2D-RoPE다. |
| 격자(Grid) | "패치 격자" | 주어진 해상도와 패치 크기에서 만들어지는 (H/P) x (W/P)의 2차원 패치 배열이다. |
| NaFlex | "네이티브 가변 해상도" | 재학습 없이 여러 종횡비와 해상도를 한 모델로 처리하는 SigLIP 2의 기능이다. |
| 백본(Backbone) | "비전 타워" | 패치 토큰 출력을 만들어 LLM에 넘기는 사전학습된 이미지 인코더다. |
| 풀링(Pooling) | "이미지 수준 요약" | 패치 토큰들을 하나의 벡터로 바꾸는 전략이다. CLS, 평균, 어텐션 풀(attention pool), 레지스터 기반 방식이 있다. |
| 패치 14 vs 16(Patch 14 vs 16) | "더 미세한 격자 vs 더 거친 격자" | 패치 14는 이미지당 토큰이 더 많아 OCR 충실도에 유리하지만 느리다. 패치 16은 고전적인 기본값이다. |
더 읽을거리