Qwen3-VL-2B (VLM)¶
개요¶
이 가이드는 optimum-rbln 기본 사용 경험이 있는 사용자를 대상으로, Vision-Language Model(VLM)을 RBLN NPU에서 효과적으로 활용하기 위한 두 가지 핵심 기법을 소개합니다.
- 다양한 입력 해상도와 디코더 배치 크기를 버케팅(bucketing)을 사용해 한 번의 컴파일로 효율적으로 처리하기
- 각 submodule별
rbln_config를 구성하여 제어하기
예제 모델은 Qwen/Qwen3-VL-2B-Instruct이며, 코드 예제의 기본값은 Model Zoo의 compile.py, inference.py와 일치합니다.
Note
에러 상황 디버깅(예: Failed to create RBLN runtime, No memory blocks are available)은 이 문서의 범위가 아닙니다. 메모리, runtime 생성 관련 에러가 발생한 경우 다중 모듈 모델 트러블슈팅을 참고하세요.
submodule을 가지는 모델 구조¶
VLM은 비전 인코더, 언어 모델 등 여러 신경망 구성요소로 이루어진 모델입니다. optimum-rbln은 이러한 구성요소 중 주축이 되는 하나를 최상위(top-level) 모델로 두고, 나머지를 submodule로 선언하여 각각 별개의 그래프로 컴파일하고 별개의 runtime으로 실행합니다. 이러한 구조에서는 각 submodule마다 원하는 설정을 독립적으로 지정할 수 있습니다.
Qwen3-VL(Qwen3VLForConditionalGeneration)의 모델 구조는 다음과 같습니다.
최상위 모델은 causal Language Model(LM)이며, 이 모델은 이미지와 비디오 프레임을 인코딩하는 Vision Transformer를 visual이라는 단일 submodule로 가지고 있습니다. 각 구성요소의 역할은 다음과 같습니다.
- 최상위(top) LM -
visual이 만든 이미지 임베딩을 받아 텍스트 토큰을 auto-regressive하게 생성합니다. visualsubmodule - Vision Transformer. 이미지 또는 비디오 프레임 단위로 실행됩니다.
이 트리 구조는 컴파일 시에 넘기는 rbln_config의 키 구조와 그대로 대응합니다. 최상위 키는 LM에, "visual" 아래에 중첩한 키는 visual submodule에 적용됩니다.
Tip
각 필드의 정의는 해당 모델의 RBLN config 파일에서 찾을 수 있습니다. 예를 들어 Qwen3-VL의 visual submodule 필드는 RBLNQwen3VLVisionModelConfig에 정의되어 있습니다.
Note
어떤 구성요소가 최상위 모델이고 어떤 것이 submodule인지는 모델마다 다릅니다. 예를 들어 LLaVA, Gemma3는 최상위가 Conditional Generation 래퍼이고 vision_tower와 language_model이 둘 다 submodule이며, Idefics3는 vision_model, text_model이 함께 submodule로 선언되어 있습니다. 사용하려는 모델의 구조는 해당 모델의 RBLN config 파일(configuration_<model>.py)에서 submodules = [...] 선언으로 확인할 수 있습니다.
submodule의 rbln_config 사용하기¶
이 섹션에서는 rbln_config로 optimum-rbln의 VLM을 운용할 때 자주 사용하는 패턴을 정리했습니다. submodule과 top 모델에 device를 분리 배치하는 단순 예제로 시작해, 두 축의 버케팅, 그 외 메모리 및 워크플로우 시나리오로 이어집니다.
Qwen3-VL에서 주로 사용하는 rbln_config 필드를 정리하면 다음과 같습니다.
| 필드 | 레벨 | 용도 |
|---|---|---|
batch_size |
공통 | 모델 전체의 동시 시퀀스 수 - submodule별로 따로 줄 수 없음 |
visual.max_seq_lens |
submodule (visual) |
ViT 그래프가 이미지, 프레임당 받아들이는 최대 패치(patch) 수. int 하나 또는 List[int](bucketing) |
visual.tensor_parallel_size |
submodule (visual) |
visual의 tensor parallel size |
visual.device |
submodule (visual) |
visual runtime이 올라갈 device(들) |
visual.create_runtimes |
submodule (visual) |
컴파일 시 visual runtime 생성 여부 |
tensor_parallel_size |
top (LM) | LM의 tensor parallel size |
kvcache_partition_len |
top (LM) | Flash attention 파티션 크기 - max_seq_len을 나누어 떨어져야 함 |
max_seq_len |
top (LM) | LM의 최대 position embedding - kvcache_partition_len의 배수 |
decoder_batch_sizes |
top (LM) | 디코더 배치 크기 목록(bucketing) - 모든 값이 batch_size 이하 |
device |
top (LM) | LM runtime이 올라갈 device 리스트 |
create_runtimes |
top (LM) | 컴파일 시 LM runtime 생성 여부 |
Caution
두 레벨에 동시에 등장할 수 있는 키에 대해 주의할 점이 있습니다.
tensor_parallel_size는 submodule별로 다르게 줄 수 있지만, 각 submodule의device리스트 길이와 반드시 일치해야 합니다.batch_size는 모델 전체에 단일 값으로 적용되며, submodule별로 따로 지정할 수 없습니다.visual과 LM은 배치를 다른 방식으로 실행하지만optimum-rbln이 내부에서 조율합니다.
submodule과 top 모델의 device 분리 배치¶
rbln_config의 2-레벨 구조 덕분에 submodule과 top 모델의 device를 나누어 배치할 수 있습니다. 예를 들어 16 device 서버에서 rbln_config의 device를 사용하여 visual을 앞쪽 8개에, LM을 뒤쪽 8개에 분리해 두면, 어떤 device도 두 submodule의 메모리를 동시에 보유하지 않아 큰 배치와 긴 컨텍스트에서도 메모리 부족을 피할 수 있습니다.
8 device 환경에서 두 모델이 같은 pool을 공유하는 baseline이나 device, 메모리 압박이 발생할 때의 진단 흐름은 그 외 시나리오에서 트러블슈팅 가이드로 안내합니다.
ViT 입력 길이 버케팅: visual.max_seq_lens¶
Qwen3-VL의 ViT는 이미지(또는 비디오 프레임) 한 장 단위로 실행되며, 그래프 모양은 컴파일 타임에 고정됩니다. 즉 max_seq_lens=16384로 컴파일한 ViT는 실제 입력이 1,024 패치든 16,384 패치든 항상 16,384 패치 분량을 계산합니다. 실제 서빙에서는 한 요청에 크기가 다양한 여러 이미지, 혹은 프레임 수가 다른 비디오가 섞여 들어오므로, 가장 큰 입력 기준으로 단일 max_seq_lens를 설정하면 작은 입력에서도 그 용량을 그대로 소모하게 되어 지연, 메모리가 낭비됩니다.
optimum-rbln은 이 문제를 multi-size 입력에 대한 버케팅 전략으로 해결합니다. visual.max_seq_lens는 int 하나뿐 아니라 List[int]도 받으며, 동작은 다음과 같습니다.
- 리스트로 지정하면
optimum-rbln은 각 길이에 대해 여러 개의 ViT 그래프를 한꺼번에 컴파일합니다. - 추론 시 실제 패치 수에 맞춰 그 값을 담을 수 있는 가장 적당한 버켓이 자동으로 선택됩니다.
- 모든 버켓보다 큰 입력이 들어오면 에러가 발생합니다. 리스트의 가장 큰 값은 워크로드 상한을 포함할 수 있도록 설정해야 합니다.
Model Zoo compile.py의 단일 값(max_seq_lens: 16384)을 리스트로 바꿔 세 개의 버켓을 함께 컴파일하는 예제입니다.
이렇게 컴파일한 모델은 런타임에 다음과 같이 동작합니다.
| 입력 패치 수 | 선택되는 버켓 |
|---|---|
| 800 | 1024 |
| 2000 | 3136 |
| 5000 | 16384 |
| 20000 | 에러: 모든 버켓 초과 |
Note
max_seq_lens의 각 값은 ViT가 이미지 한 장(또는 비디오 프레임 한 장)에서 처리할 패치 수의 상한입니다. Qwen3-VL은 patch_size=16, spatial_merge_size=2이므로, 해상도 H × W 이미지의 패치 수는 다음과 같이 계산할 수 있습니다.
예를 들어 1024×1024는 1,024개, 1792×1792는 3,136개, 4096×4096는 16,384개입니다. 앞선 예제의 버켓 값(1024, 3136, 16384)은 각각 이 세 해상도까지를 커버합니다.
Caution
버켓 수가 많을수록 컴파일 시간과 디바이스 메모리 사용량이 함께 늘어납니다. 너무 많은 가짓수를 넣으면 디바이스 메모리가 부족해질 수 있으니, 실제 트래픽의 해상도 분포를 보고 2–4개 정도를 고르는 것을 추천합니다.
Tip
버케팅의 일반적인 동작 원리, 트레이드오프, 모델 zoo 예제는 Bucketing 일반 가이드에 정리되어 있습니다. ViT 입력 길이 버케팅은 이를 지원하는 모델에서만 리스트 입력이 유효하며, 지원 여부는 각 모델의 RBLN config 파일에서 max_seq_len가 아닌 max_seq_lens 필드가 Union[int, List[int]]로 선언되어 있는지 확인하세요.
디코더 배치 크기 버케팅: decoder_batch_sizes¶
Qwen3-VL의 언어 모델 디코더 역시 그래프 모양이 컴파일 타임에 고정됩니다. batch_size=8로 컴파일된 디코더는 실제 배치가 3이든 8이든 항상 8 슬롯 분량을 계산합니다. 그런데 실제 서빙(continuous batching, in-flight batching)에서는 시간차로 끝나는 요청들 때문에 실제 배치 크기가 자주 줄어듭니다. 이때마다 사용되지 않는 슬롯에 대한 계산이 그대로 낭비됩니다.
decoder_batch_sizes는 이 문제를 디코더 그래프 차원의 버케팅으로 해결합니다. LM 측 top-level 필드(RBLNDecoderOnlyModelConfig에 정의)이며 List[int]를 받고, 동작은 다음과 같습니다.
- 리스트로 주면
optimum-rbln은 각 배치 크기에 대해 별도의 디코더 그래프(decoder_batch_{N})를 함께 컴파일합니다. - 모든 값은
batch_size이하여야 하며, 최댓값은batch_size와 같아야 합니다(작으면 자동으로 append되며 경고가 출력됩니다). - 추론 시에는 실제 배치 크기를 기준으로 가장 적합한 디코더 그래프가 선택되어 디코딩 단계에서 사용됩니다.
batch_size=8로 컴파일하면서 1, 2, 4, 8 네 개의 디코더 그래프를 함께 만드는 예제입니다.
이렇게 컴파일한 모델은 런타임에 입력으로 들어오는 실제 배치 크기에 따라 다음과 같은 디코더 그래프를 사용합니다.
| 실제 배치 | 선택되는 디코더 그래프 |
|---|---|
| 1 | decoder_batch_1 |
| 2 | decoder_batch_2 |
| 3–4 | decoder_batch_4 |
| 5–8 | decoder_batch_8 |
Caution
디코더 그래프 수가 많을수록 컴파일 시간과 디바이스 메모리 사용량이 늘어납니다. 보통 1, batch_size//2, batch_size처럼 2–4개 정도를 고르는 것이 일반적이며, 트래픽 패턴에 맞춰 조정하세요.
Tip
ViT 입력 길이 버케팅(visual.max_seq_lens)과 디코더 배치 크기 버케팅(decoder_batch_sizes)은 서로 독립적이며, 한 컴파일 안에서 함께 사용할 수 있습니다.
그 외 시나리오¶
특정 메모리, device 환경이나 컴파일, 추론 워크플로우는 다중 모듈 모델 트러블슈팅에서 단계별로 다룹니다. 자주 마주치는 시나리오와 해당 가이드는 다음과 같습니다.
| 시나리오 | 가이드 |
|---|---|
컴파일과 runtime 생성 분리 (create_runtimes: False) |
Step 1 |
visual과 LM을 disjoint device pool로 분리 (16 ATOM™) |
Step 2 |
큰 batch에서 KV cache 블록 pool 고갈 (kvcache_num_blocks 조정) |
Step 3 |
| 실제 워크로드에 맞춘 LM 컨텍스트 길이 제한 | Step 4 |
| 타겟 해상도에 맞춘 ViT 입력 적정화 | Step 5 |