다중 모듈 모델 트러블슈팅¶
이 가이드는 다중 모듈 Vision-Language 모델을 RBLN NPU에서 컴파일·배포할 때 자주 발생하는 이슈를 Qwen3-VL-2B Model Zoo 예제를 기준으로 단계별로 다룹니다. 독립적인 submodule로 구성된 Optimum RBLN 모델 전반에 같은 패턴이 적용됩니다.
전제 조건
아래 예제는 tensor_parallel_size=8을 사용하며, Qwen3-VL-2B Model Zoo 컴파일 설정과 일치합니다. 이 가이드는 두 가지 device layout을 사용합니다.
- 8 ATOM™ (device 0–7) — Model Zoo baseline. 두 submodule이 8 device pool을 공유합니다. 기본 컴파일 설정과 소규모 batch에서 동작합니다.
- 16 ATOM™ (device 0–15) —
visual과 LM을 disjoint pool (각각 8–15, 0–7)로 분리합니다. 이 가이드 전반의batch_size=8시나리오처럼 메모리 부담이 큰 설정에서 요구됩니다.
rbln-smi로 사용 가능한 device를 확인하고, 하드웨어에 맞게 tensor_parallel_size와 device 할당을 조정하세요.
요약¶
| 증상 | 근본 원인 | 해결 |
|---|---|---|
컴파일 직후 Failed to create RBLN runtime |
기본값 create_runtimes=True가 컴파일 중 runtime 생성을 수행하며 실패 |
Step 1 |
저장된 모델 로딩 중 Failed to create RBLN runtime |
모든 submodule이 같은 [0..TP-1] pool로 기본 배치되며 device당 합산 footprint가 device memory를 초과 |
Step 2 |
No memory blocks are available for allocation |
큰 batch에서 paged attention 블록 pool 고갈 | Step 3 |
Memory is not enough for full sequence length |
KV 캐시가 실제 워크로드가 아닌 아키텍처 최대치로 설정됨 | Step 4 |
| 작은 이미지에 비해 ViT 지연이 높음 | max_seq_lens가 실제 해상도 대비 과다 할당됨 |
Step 5 |
Step 1: Runtime 생성을 분리해서 컴파일하기¶
증상¶
batch_size=8로 컴파일하면 컴파일 완료 직후 크래시가 발생합니다:
근본 원인¶
from_pretrained(..., export=True)는 두 단계를 순차적으로 수행합니다:
- 모델을 RBLN IR로 컴파일 — 성공.
- 컴파일된 모듈을 device memory에 올려 runtime을 생성 — 실패.
Qwen3-VL은 독립적인 두 submodule로 구성됩니다.
visual— 이미지·비디오 프레임을 인코딩하는 Vision Transformermodel— 텍스트를 생성하는 causal language model
runtime 생성 시 각 submodule의 device는 list(range(tensor_parallel_size)) — TP=8이면 [0, 1, …, 7] — 로 기본 배치됩니다. 따라서 visual과 LM 모두 동일한 8 device pool에 샤딩되며, 각 device는 ViT 슬라이스, LM tensor-parallel shard, KV 캐시의 일부를 함께 보유합니다. device당 합산 footprint가 device memory를 초과하면 runtime 생성이 실패합니다.
해결¶
create_runtimes의 기본값은 True이므로 from_pretrained는 컴파일 직후 runtime 생성 단계로 진행하며 이 단계에서 실패합니다. submodule 레벨과 최상위 레벨 양쪽에 create_runtimes: False를 설정하면 from_pretrained가 컴파일 직후 종료되어 실패하는 runtime 생성 단계를 건너뜁니다. 이는 Model Zoo compile.py의 표준 패턴입니다.
컴파일이 완료되고 산출물이 디스크에 저장됩니다. 근본 메모리 압박은 해소되지 않으며 로드 시점에 다시 드러납니다. 이는 Step 2에서 명시적 device 배치로 처리합니다.
레벨별 필드 정리:
| 필드 | 레벨 | 용도 |
|---|---|---|
visual.max_seq_lens |
submodule (ViT) | ViT 그래프가 이미지·프레임당 받아들이는 최대 merged patch 수 |
visual.tensor_parallel_size |
submodule (ViT) | ViT의 TP degree |
visual.create_runtimes |
submodule (ViT) | 컴파일 시 ViT runtime 생성을 건너뜀 |
tensor_parallel_size |
top (LM) | Language model의 TP degree |
kvcache_partition_len |
top (LM) | Flash attention 파티션 크기 — max_seq_len을 나누어 떨어져야 함 |
max_seq_len |
top (LM) | LM의 최대 position embedding — kvcache_partition_len의 배수여야 함 |
create_runtimes |
top (LM) | 컴파일 시 LM runtime 생성을 건너뜀 |
Tip
다중 모듈 모델을 컴파일할 때는 항상 create_runtimes: False를 사용하세요.
Step 2: 로드 시점에 submodule을 device별로 분리 배치하기¶
증상¶
Step 1에서 batch_size=8로 컴파일한 모델을 device 힌트 없이 로드합니다:
이번에도 로드 중에 Failed to create RBLN runtime이 발생합니다.
근본 원인¶
Step 1과 동일한 메모리 압박입니다. 명시적인 device 설정이 없으면 visual과 LM이 기본 pool [0..TP-1]을 공유하며, 각 device의 누적 ViT + LM + KV 캐시 footprint가 device memory를 초과합니다. Step 1은 runtime 생성을 건너뛰어 이를 우회했지만, 저장된 산출물을 로드하는 시점에 다시 드러납니다.
해결¶
submodule마다 device 리스트를 명시합니다. Step 1의 batch_size=8 컴파일에 대해서는 visual과 LM을 16 ATOM™에 걸친 disjoint pool로 배치하여 어떤 device도 ViT와 LM 메모리를 동시에 담지 않도록 합니다.
8 ATOM™만 가용할 때는 Model Zoo inference.py의 공유 pool 구성(visual과 LM 모두 device 0–7)을 사용합니다. 합산 footprint가 각 device에 들어맞아야 동작하므로 Step 1의 batch_size=8 컴파일은 이 layout으로 로드되지 않습니다. Step 3 또는 Step 4로 워크로드를 먼저 줄이세요.
Step 3: 더 큰 batch는 vllm-rbln으로 서빙¶
증상¶
batch가 커지면 paged attention 블록 pool이 runtime에서 고갈됩니다:
에러 메시지 자체가 vllm-rbln 사용을 권고하며, 본 step은 그 마이그레이션 경로를 단계별로 안내합니다.
근본 원인¶
KV 캐시는 paged attention을 사용합니다. optimum-rbln만 단독 사용할 경우 블록 pool은 컴파일 타임에 고정된 kvcache_num_blocks로 선할당되며, 활성 batch가 예약된 블록 수를 초과하면 생성 중에 pool이 고갈되어 위 에러가 발생합니다.
해결¶
컴파일된 산출물을 직접 호출하지 말고 vllm-rbln을 통해 서빙하세요. vllm-rbln은 paged-attention 블록 pool을 동적으로 관리합니다. 블록 할당, eviction, admission control이 엔진 차원에서 처리되므로 pool을 초과하는 워크로드는 큐잉되거나 안전하게 거부될 뿐 생성 도중 크래시되지 않습니다.
optimum-rbln 단독 운용 — kvcache_num_blocks 조정¶
optimum-rbln 단독으로 운용해야 한다면 batch 전체가 들어가도록 kvcache_num_blocks를 명시합니다.
flash attention 모드에서는 kvcache_block_size가 kvcache_partition_len과 같으므로, 위 식에서 두 값은 동일하게 다룰 수 있습니다.
유효 범위:
이 구성의 kvcache_num_blocks 최소 유효값은 (max_seq_len / kvcache_block_size) + 1 = 17입니다. 유효 범위 안에서 그보다 작은 값을 쓰면 추론이 정상적으로 시작되더라도 max_seq_len에 도달하기 전에 pool이 고갈되어 OOM이 발생합니다.
Step 4: 실제 워크로드에 맞춘 LM 컨텍스트 길이 제한¶
증상¶
batch가 커질 때 컴파일이 다음 에러로 거부됩니다:
근본 원인¶
flash attention 모드(kvcache_partition_len 설정 시 활성화)에서 KV 캐시는 컴파일 타임에 미리 할당됩니다. 메모리 footprint는 다음과 같이 스케일합니다:
Model Zoo compile.py는 max_seq_len을 262,144 — Qwen3-VL의 아키텍처 최대치 — 로 설정합니다. batch_size > 1이면 엔진이 시퀀스마다 256K 토큰 크기의 슬롯을 예약하므로, 실제 요청이 훨씬 짧더라도 device memory가 빠르게 고갈됩니다.
해결¶
max_seq_len을 타겟 워크로드의 현실적 상한으로 줄입니다. 요청이 32K 토큰 이하라면:
| 파라미터 | 제약 | 효과 |
|---|---|---|
max_seq_len |
kvcache_partition_len의 배수 |
시퀀스당 KV 메모리 상한 결정 |
kvcache_partition_len |
Flash attention 파티션 크기 | 작을수록 유연, 클수록 오버헤드 적음 |
batch_size |
동시 시퀀스 수 | KV 메모리에 선형 비례 |
max_seq_len을 절반으로 줄이면 KV 캐시 메모리도 대략 절반이 됩니다. 긴 컨텍스트와 큰 batch가 동시에 필요하다면 Step 3과 조합하세요. 먼저 max_seq_len을 줄이고, 남은 메모리 예산 안에서 kvcache_num_blocks를 조정합니다.
Step 5: 타겟 해상도에 맞춰 ViT 입력 적정화¶
증상¶
배치 추론은 정상 동작하지만, 작은 이미지에서도 ViT 지연이 예상보다 높게 나타납니다.
근본 원인¶
visual.max_seq_lens는 ViT 그래프가 이미지·비디오 프레임당 받아들이는 최대 merged patch 수를 결정합니다. Model Zoo compile.py는 기본값 16,384를 사용하며, 이는 대략 4096×4096까지의 모든 입력을 커버하는 보수적 상한입니다. 실제 배포나 서빙 시나리오의 최대 해상도가 그보다 작다면, max_seq_lens를 해당 시나리오의 상한에 맞춰 축소해 ViT 연산·메모리 낭비를 줄일 수 있습니다.
merged patch 수는 모델의 patch_size와 spatial_merge_size (두 값 모두 config.json에 정의)에 따라 결정됩니다.
Qwen3-VL의 경우 (patch_size=16, spatial_merge_size=2):
| 이미지 해상도 | Merged patches |
|---|---|
| 1792 × 1792 | 3,136 |
| 4096 × 4096 | 16,384 |
1792×1792 배포 환경에서 max_seq_lens: 16384로 컴파일하면 실제 사용량 대비 약 5배의 patch 슬롯을 예약합니다 (16,384 vs. 3,136). ViT는 실제 입력과 무관하게 컴파일된 용량 그대로 실행되므로, 초과분은 그대로 지연과 메모리 낭비로 이어집니다.
해결¶
두 파라미터는 파이프라인으로 연결되며 반드시 일치해야 합니다.
processor.max_pixels— patch 추출 전에 이미지 크기를 제한합니다.rbln_config["visual"]["max_seq_lens"]— 컴파일된 ViT 그래프가 받아들이는 patch 수를 제한합니다.
| 관계 | 결과 |
|---|---|
max_pixels가 만드는 patch 수가 max_seq_lens 범위 안에 들어감 |
추론 정상 동작 |
max_pixels가 만드는 patch 수가 max_seq_lens와 근접함 |
연산·메모리 효율 유지 |
1792×1792을 상한으로 하는 배포의 경우는 다음과 같이 설정합니다.
컴파일
로드
Note
max_seq_lens는 컴파일된 그래프의 모양을 고정합니다. 모델이 이론적으로 지원하는 최대치가 아니라 해당 배포의 현실적 최대치에 맞춰 설정하세요. 배포 해상도가 커지면 다시 컴파일해야 합니다.
Bucketing — 가변 이미지 크기¶
배포가 단일 해상도가 아니라 다양한 이미지 크기를 처리해야 한다면, ViT를 여러 max_seq_lens bucket으로 컴파일합니다. runtime이 요청마다 가장 작은 적합 그래프를 선택하므로, 큰 입력에 필요한 그래프를 유지하면서도 작은 입력에서 앞서 설명한 연산 낭비를 피할 수 있습니다.
이미지 크기 bucketing 적용 예시는 VLM 튜토리얼을 참고하세요.