Janus-Pro: 통합 멀티모달 모델을 위한 분리형 인코더(Decoupled Encoders for Unified Multimodal Models)
통합 멀티모달 모델(Unified Multimodal Model)에는 피할 수 없는 긴장이 존재합니다. 이해(Understanding) 과제는 의미 특징(semantic feature)을 원합니다. SigLIP이나 DINOv2가 내놓는 벡터처럼 개념 수준의 정보(concept-level information)가 풍부한 표현입니다. 반면 생성(Generation) 과제는 재구성에 친화적인 코드(reconstruction-friendly code)를 원합니다. 선명한 픽셀로 다시 조립될 수 있는 벡터 양자화(VQ) 토큰이 그것입니다. 두 목표는 하나의 인코더(encoder) 안에서 잘 양립하지 않습니다. Janus(DeepSeek, 2024년 10월)와 Janus-Pro(DeepSeek, 2025년 1월)는 해결책은 굳이 하나로 통합하려 하지 않는 것이라고 주장합니다. 두 인코더를 분리(decouple)하자는 것입니다. 과제 사이에서 트랜스포머 본체(transformer body)는 공유하되, 이해 경로는 SigLIP으로 라우팅(routing)하고 생성 경로는 VQ 토크나이저(VQ tokenizer)로 라우팅합니다. 7B 규모에서 Janus-Pro는 GenEval 벤치마크에서 DALL-E 3를 이기면서 MMMU 벤치마크에서는 LLaVA와 맞먹는 점수를 냅니다. 이 강의에서는 하나의 인코더가 실패하는 지점에서 왜 두 개의 인코더가 동작하는지를 읽어 봅니다.
유형: Build
언어: Python (표준 라이브러리, 이중 인코더 라우팅 + 공유 본체 신호)
선수 지식: Phase 12 · 13 (Transfusion), Phase 12 · 14 (Show-o)
예상 시간: 약 120분
학습 목표
- 단일 공유 인코더가 이해 품질과 생성 품질 중 하나를 왜 희생(compromise)할 수밖에 없는지 설명합니다.
- Janus-Pro의 라우팅 구조를 설명합니다. 이해에는 입력 측에 SigLIP 특징을, 생성에는 입력과 출력 양쪽에 VQ 토큰을 사용합니다.
- Janus가 실패했던 곳에서 Janus-Pro를 성공시킨 데이터 혼합 스케일링(data-mix scaling)을 추적합니다.
- 분리형(Janus-Pro), 결합-연속형(coupled-continuous; Transfusion), 결합-이산형(coupled-discrete; Show-o) 아키텍처를 비교합니다.
문제
통합 모델은 이해와 생성 사이에서 트랜스포머 본체를 공유합니다. 이전 시도인 Chameleon, Show-o, Transfusion은 모두 양방향에 하나의 시각 토크나이저(visual tokenizer)를 사용했습니다. 이 토크나이저는 일종의 타협입니다.
- 재구성(reconstruction)에 최적화된 경우(생성용): VQ-VAE는 픽셀 수준의 세밀한 디테일을 포착하지만, 의미적 응집력(semantic coherence)이 약한 토큰을 만들어 냅니다.
- 의미(semantics)에 최적화된 경우(이해용): SigLIP 임베딩은 "고양이" 이미지를 "고양이" 토큰 근처에 배치하지만, 좋은 재구성을 허용하지는 않습니다.
Show-o와 Transfusion은 이 타협을 한쪽 방향의 눈에 띄는 품질 손실(visible quality tax)로 치릅니다. Janus-Pro는 질문을 바꿉니다. 두 과제가 서로 다른 것을 필요로 하는데 왜 하나의 토크나이저를 강제해야 할까요?
개념
분리형 시각 인코딩(Decoupled visual encoding)
Janus-Pro의 아키텍처는 두 인코더를 분리합니다.
- 이해 경로(Understanding path): 입력 이미지 → SigLIP-SO400m → 2-layer MLP → 트랜스포머 본체.
- 생성 경로(Generation path): 기존 이미지를 조건(conditioning)으로 사용하는 경우 입력 이미지 → VQ 토크나이저 → 토큰 ID → 트랜스포머 본체.
- 출력 생성(Output generation): 트랜스포머가 예측한 이미지 토큰 → VQ 디코더(VQ decoder) → 픽셀.
트랜스포머 본체는 공유됩니다. 본체의 상류(upstream)와 하류(downstream)에 있는 모든 것은 과제별로 분리되어 있습니다.
입력은 프롬프트 형식으로 구분(disambiguate)합니다. <understand> 태그는 SigLIP 경로로 라우팅하고, <generate> 태그는 VQ 경로로 라우팅합니다. 또는 과제로부터 암묵적으로 라우팅이 결정될 수도 있습니다.
왜 동작하는가
이해 손실(understanding loss)은 SigLIP 특징을 받습니다. CLIP 계열의 사전학습(pretraining)이 의미 유사도(semantic similarity)에 맞춰 특징을 조정해 두었기 때문에, 인식(perception) 벤치마크에서 Show-o나 Transfusion보다 좋은 점수가 나옵니다. 입력 특징이 해당 과제에 더 적합하기 때문입니다.
생성 손실(generation loss)은 VQ 토큰을 받습니다. 토크나이저가 재구성에 맞춰 학습되었기 때문에, VQ 코드는 깔끔하게 픽셀로 되돌아갑니다. 이미지 품질은 Show-o보다 향상됩니다.
공유 트랜스포머 본체는 두 가지 입력 분포(SigLIP과 VQ)를 모두 보면서 둘 다 다루는 법을 배웁니다. 주장은 이렇습니다. 충분한 데이터와 충분한 파라미터가 있으면 본체는 두 입력 사이의 전환(switching)을 흡수할 수 있다는 것입니다.
데이터 스케일링 — Janus vs Janus-Pro
Janus(원본, arXiv 2410.13848)는 인코더 분리(decoupling)라는 아이디어를 도입했지만 작은 규모였습니다. 파라미터 1.3B에 제한된 데이터를 사용했습니다. Janus-Pro(arXiv 2501.17811)는 규모를 키웠습니다.
- 7B 파라미터입니다. 기존 1.3B와 비교됩니다.
- Stage 1(정렬; alignment)에 9천만 개의 image-text 쌍을 사용했습니다. 기존 7천 2백만 개에서 늘었습니다.
- Stage 2(통합; unified)에 7천 2백만 개를 사용했습니다. 기존 2천 6백만 개에서 늘었습니다.
- Stage 3에는 20만 개의 이미지 생성 instruction 샘플을 추가했습니다.
결과적으로 Janus-Pro-7B는 MMMU에서 LLaVA와 맞먹고(60.3 vs 약 58), GenEval에서는 DALL-E 3를 이겼습니다(0.80 vs 0.67). 하나의 오픈 모델이 통합 스펙트럼의 양쪽 모두에서 경쟁력을 확보한 것입니다.
JanusFlow — 정류 흐름(rectified flow) 변형
JanusFlow(arXiv 2411.07975)는 VQ 기반 생성 경로를 정류 흐름(rectified flow) 기반 생성 경로(연속형)로 바꿉니다. 분할 구조는 이해용 SigLIP + 생성용 정류 흐름이 됩니다. 품질 상한(quality ceiling)은 더 끌어올려집니다. 아키텍처는 여전히 분리형 인코더 + 공유 본체(decoupled-encoders-shared-body) 구조입니다.
공유 본체가 하는 일
트랜스포머 본체는 통합 시퀀스를 처리하되 두 가지 입력 분포를 받습니다. 본체가 맡는 역할은 다음과 같습니다.
- 이해 시: SigLIP 특징 + 텍스트 토큰을 소비하고, 텍스트를 자기회귀적으로(autoregressively) 출력합니다.
- 생성 시: 텍스트 토큰 + (선택적으로) 이미지 VQ 토큰을 소비하고, 이미지 VQ 토큰을 자기회귀적으로 출력합니다.
본체에는 블록별로 모달리티별 전용 가중치(modality-specific weight)가 없습니다. Qwen이나 Llama 안에서 흔히 볼 수 있는 텍스트 스타일의 트랜스포머에 두 개의 입력 어댑터(input adapter)가 붙은 형태입니다.
흥미로운 점은 Janus-Pro의 본체를 사전학습된 대규모 언어 모델(LLM)로부터 초기화할 수 있다는 것입니다. Janus-Pro는 DeepSeek-MoE-7B로부터 초기화합니다. 이 선택은 중요합니다. 대규모 언어 모델은 처음부터 학습한 순수(from-scratch) 통합 모델로는 도달하기 어려운 추론 능력(reasoning ability)을 제공하기 때문입니다.
InternVL-U와의 비교
InternVL-U(Lesson 12.10)는 2026년의 후속 작업입니다. 다음 요소들을 결합합니다.
- 네이티브 멀티모달 사전학습(InternVL3 백본 기반).
- 분리형 인코더 라우팅(입력은 SigLIP, 출력은 VQ + 확산 헤드).
- 이해 + 생성 + 편집을 통합한 모델.
InternVL-U는 Janus-Pro의 아키텍처 선택을 더 큰 프레임워크 안에 흡수합니다. 분리형 인코더라는 아이디어는 이제 대규모 통합 모델에서 사실상 기본값이 되었습니다.
한계
분리형 인코더는 아키텍처 복잡도를 더합니다. 학습해야 할 토크나이저가 두 개이고, 유지해야 할 입력 경로(input path)도 두 개이며, 실패 모드(failure mode) 역시 두 가지 집합으로 늘어납니다. 생성이 필요 없는 제품이라면 Janus-Pro는 과한 설계(over-engineered)입니다. LLaVA 계열의 이해 모델을 고르는 편이 낫습니다.
이해가 필요 없고 생성만 필요한 제품이라면 Janus-Pro는 과한 자격(overqualified)입니다. Stable Diffusion 3나 Flux 모델을 고르면 됩니다.
이해와 생성이 모두 필요한 제품이라면, Janus-Pro는 현 시점에서 참고할 만한 오픈 아키텍처(reference open architecture)입니다.
사용해보기
code/main.py는 Janus-Pro 라우팅을 시뮬레이션합니다.
- 두 개의 가짜(mock) 인코더가 있습니다. SigLIP 유사 인코더는 256차원 의미 벡터(semantic vector)를 만들고, VQ 유사 인코더는 정수 코드(integer code)를 만듭니다.
- 과제 태그(task tag)에 따라 인코더를 고르는 프롬프트 라우터(prompt router)가 있습니다.
- 공유 본체 대역(stand-in)은 어떤 인코더가 만들었든 토큰 시퀀스를 처리합니다.
- Stage 1(정렬)에서 Stage 3(instruction tune)으로 넘어가는 가중 샘플링 스케줄(weighted-sample schedule)을 보여줍니다.
이미지 QA, 텍스트-투-이미지(T2I), 이미지 편집(image editing) 세 가지 예시에 대해 라우팅 경로를 출력합니다.
산출물 만들기
이 강의는 outputs/skill-decoupled-encoder-picker.md를 만듭니다. 프런티어에 근접한 품질로 통합 생성 + 이해를 원하는 제품이 주어지면 Janus-Pro, JanusFlow, InternVL-U 중 하나를 선택하고, 구체적인 데이터 스케일 권장안(data-scale recommendation)을 제공합니다.
연습문제
-
쉬움: Janus-Pro-7B는 GenEval에서 DALL-E 3를 이깁니다. 왜 7B 규모의 오픈 모델이 생성에서는 프런티어 사유(proprietary) 모델과 맞먹을 수 있지만, 이해에서는 그렇지 못한지 설명해 봅니다.
-
중간: 라우터 함수(router function)를 구현합니다. 프롬프트 텍스트가 주어지면 understand 또는 generate로 분류합니다. "describe and then sketch"처럼 모호한(ambiguous) 프롬프트는 어떻게 처리하시겠습니까?
-
중간: JanusFlow는 VQ 경로를 정류 흐름으로 대체합니다. 이때 트랜스포머 본체는 무엇을 출력하게 되며, 손실 함수는 어떻게 바뀝니까?
-
어려움: Janus-Pro 아키텍처가 인코더를 하나 더 분리해 처리할 수 있는 네 번째 과제를 제안해 봅니다. 예시: 이미지 분할(image segmentation; DINO 스타일), 깊이 추정(depth; MiDaS 스타일).
-
어려움: 데이터 스케일링에 관한 Janus-Pro 논문의 Section 4.2를 읽습니다. Janus 대비 T2I 품질 향상에 가장 크게 기여한 데이터 단계(data stage)는 어디입니까?
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 분리형 인코딩(Decoupled encoding) | "시각 인코더가 두 개" | 방향별로 다른 토크나이저나 인코더를 쓰는 방식. 이해에는 의미 인코더, 생성에는 재구성 토크나이저를 사용한다. |
| 공유 본체(Shared body) | "트랜스포머 하나" | 어느 인코더의 출력이든 처리하는 단일 트랜스포머. 모달리티별 전용 가중치를 두지 않는다. |
| 이해용 SigLIP(SigLIP for understanding) | "의미 특징(semantic features)" | 개념 정보가 풍부한 특징을 제공하지만 재구성에는 약한 CLIP 계열의 비전 타워. |
| 생성용 VQ(VQ for generation) | "재구성 코드(reconstruction codes)" | 픽셀로 깔끔하게 디코딩되는 벡터 양자화 토큰. |
| JanusFlow | "정류 흐름 변형(rectified-flow variant)" | VQ 대신 연속 흐름 매칭(continuous flow-matching) 생성 헤드를 사용하는 Janus-Pro 변형. |
| 라우팅 태그(Routing tag) | "과제 태그(task tag)" | 입력 인코더를 고르는 프롬프트 마커. 예: <understand> / <generate>. |
더 읽을거리