Qwen3-VL-2B (VLM)¶
개요¶
이 가이드는 optimum-rbln 기본 사용 경험이 있는 사용자를 대상으로, Vision-Language Model(VLM)을 RBLN NPU에서 효과적으로 활용하기 위한 두 가지 핵심 기법을 소개합니다.
- 다양한 입력 해상도와 디코더 배치 크기를 버케팅(bucketing)을 사용해 한 번의 컴파일로 효율적으로 처리하기
- 각 submodule별
rbln_config를 구성하여 제어하기
예제 모델은 Qwen/Qwen3-VL-2B-Instruct이며, 코드 예제의 기본값은 RBLN 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_len |
submodule (visual) |
ViT 그래프가 이미지, 프레임당 받아들이는 최대 패치(patch) 수. int 하나 또는 List[int](bucketing) |
visual.num_devices |
submodule (visual) |
visual에 사용할 device 수 |
visual.device |
submodule (visual) |
visual runtime이 올라갈 device(들) |
visual.create_runtimes |
submodule (visual) |
컴파일 시 visual runtime 생성 여부 |
num_devices |
top (LM) | LM에 사용할 device 수 |
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
두 레벨에 동시에 등장할 수 있는 키에 대해 주의할 점이 있습니다.
num_devices는 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_len¶
Qwen3-VL의 ViT는 이미지(또는 비디오 프레임) 한 장 단위로 실행되며, 그래프 모양은 컴파일 타임에 고정됩니다. 즉 max_seq_len=16384로 컴파일한 ViT는 실제 입력이 1,024 패치든 16,384 패치든 항상 16,384 패치 분량을 계산합니다. 실제 서빙에서는 한 요청에 크기가 다양한 여러 이미지, 혹은 프레임 수가 다른 비디오가 섞여 들어오므로, 가장 큰 입력 기준으로 단일 max_seq_len을 설정하면 작은 입력에서도 그 용량을 그대로 소모하게 되어 지연, 메모리가 낭비됩니다.
optimum-rbln은 이 문제를 multi-size 입력에 대한 버케팅 전략으로 해결합니다. visual.max_seq_len은 int 하나뿐 아니라 List[int]도 받으며, 동작은 다음과 같습니다.
- 리스트로 지정하면
optimum-rbln은 각 길이에 대해 여러 개의 ViT 그래프를 한꺼번에 컴파일합니다. - 추론 시 실제 패치 수에 맞춰 그 값을 담을 수 있는 가장 적당한 버켓이 자동으로 선택됩니다.
- 모든 버켓보다 큰 입력이 들어오면 에러가 발생합니다. 리스트의 가장 큰 값은 워크로드 상한을 포함할 수 있도록 설정해야 합니다.
Model Zoo compile.py의 단일 값(max_seq_len: 16384)을 리스트로 바꿔 세 개의 버켓을 함께 컴파일하는 예제입니다.
이렇게 컴파일한 모델은 런타임에 다음과 같이 동작합니다.
| 입력 패치 수 | 선택되는 버켓 |
|---|---|
| 800 | 1024 |
| 2000 | 3136 |
| 5000 | 16384 |
| 20000 | 에러: 모든 버켓 초과 |
Note
max_seq_len의 각 값은 ViT가 이미지 한 장(또는 비디오 프레임 한 장)에서 처리할 패치 수의 상한입니다. Qwen3-VL은 patch_size=16, spatial_merge_size=2이므로, 해상도 H × W 이미지의 패치 수는 다음과 같이 계산할 수 있습니다.
이렇게 컴파일한 모델은 런타임에 입력으로 들어오는 실제 배치 크기에 따라 다음과 같은 디코더 그래프를 사용합니다.
| 실제 배치 | 선택되는 디코더 그래프 |
|---|---|
| 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_len)과 디코더 배치 크기 버케팅(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 |