vLLM Neuron 모델 온보딩 가이드¶
신규 모델 아키텍처를 vLLM Neuron Plugin에 추가하여 AWS AI Chip 에서 서빙하기 위한 전체 프로세스를 다룹니다.
온보딩의 본질: 2-Phase 접근¶
Phase 1: NF 함수로 모델 "교체" (필수)¶
NF.flash_attention,NF.mlp등 Plugin 빌딩블록 사용- 이것만으로도 NKI 최적화가 자동 적용됩니다. (NF 내부에 이미 NKI 커널 내장)
- 목표: 정확하게 동작하는 모델
Phase 2: 커스텀 NKI 커널 추가 (선택, 성능 최적화)¶
- NF 기본 함수로 커버 안 되는 부분에 직접 NKI 커널 투입
- 예: Gemma4
attention_cte→ ≤16k TTFT 40% 개선 - 목표: 경쟁력 있는 성능
전체 흐름 (5단계)¶
┌───────────────────────────────────────────────────────────────────────────────┐
│ 1. Implement 2. Register 3. Compile & 4. Validate 5. Benchmark │
│ (config/model/ (ModelRegistry) Smoke Test Accuracy & Tune │
│ factory/weights) │
└───────────────────────────────────────────────────────────────────────────────┘
Step 0: 아키텍처 Diff 분석¶
온보딩 전 필수 작업 — 내 모델이 기존 Reference 구현(Llama)과 어디가 다른지 파악합니다.
모델링 코드 위치
HuggingFace Hub의 모델 레포에는 weight + config.json + tokenizer만 존재합니다.
모델링 로직(forward 구현)은 transformers 패키지 안에 있습니다:
transformers/models/{model_name}/modeling_{model_name}.py
분석 스크립트¶
임의의 두 모델을 비교할 수 있습니다 (MODEL_A, MODEL_B만 변경):
#!/usr/bin/env python3
"""
vLLM Neuron 모델 온보딩 — Step 0: 아키텍처 Diff 분석
임의의 두 모델 비교 가능 (MODEL_A, MODEL_B만 변경)
사용법: python3 arch_diff_analysis.py
필요: pip install transformers torch (weight 다운로드 없음, config만 fetch)
"""
import json
import torch
from transformers import AutoConfig, AutoModelForCausalLM
MODEL_A = "meta-llama/Llama-3.1-8B"
MODEL_B = "google/gemma-4-31B-it"
print("=" * 80)
print(" 1. config.json 비교")
print("=" * 80)
config_a = AutoConfig.from_pretrained(MODEL_A)
config_b = AutoConfig.from_pretrained(MODEL_B)
dict_a = config_a.to_dict()
dict_b = config_b.to_dict()
all_keys = sorted(set(list(dict_a.keys()) + list(dict_b.keys())))
print(f"\n{'Key':<40} {MODEL_A:<30} {MODEL_B:<30}")
print("-" * 100)
only_a = []
only_b = []
different = []
for key in all_keys:
if key in ['_name_or_path', 'transformers_version', 'tokenizer_class', '_attn_implementation_autoset']:
continue
val_a = dict_a.get(key, "—")
val_b = dict_b.get(key, "—")
if val_a == "—":
only_b.append((key, val_b))
elif val_b == "—":
only_a.append((key, val_a))
elif val_a != val_b:
different.append((key, val_a, val_b))
print("\n### 값이 다른 필드 ###")
for key, va, vb in different:
print(f" {key:<38} {str(va)[:28]:<30} {str(vb)[:28]}")
print(f"\n### {MODEL_B}에만 있는 필드 ({len(only_b)}개) ###")
for key, val in only_b[:20]:
print(f" {key:<38} = {str(val)[:50]}")
print(f"\n### {MODEL_A}에만 있는 필드 ({len(only_a)}개) ###")
for key, val in only_a[:20]:
print(f" {key:<38} = {str(val)[:50]}")
print("\n\n")
print("=" * 80)
print(" 2. 모델 구조 출력 (weight 로드 없음, 메모리 0)")
print("=" * 80)
print(f"\n### {MODEL_A} 구조 ###\n")
try:
with torch.device("meta"):
model_a = AutoModelForCausalLM.from_config(config_a)
model_str = str(model_a)
lines = model_str.split('\n')
for line in lines[:60]:
print(f" {line}")
if len(lines) > 60:
print(f" ... ({len(lines) - 60} more lines)")
del model_a
except Exception as e:
print(f" [ERROR] {e}")
print(f"\n### {MODEL_B} 구조 ###\n")
try:
with torch.device("meta"):
model_b = AutoModelForCausalLM.from_config(config_b)
model_str = str(model_b)
lines = model_str.split('\n')
for line in lines[:60]:
print(f" {line}")
if len(lines) > 60:
print(f" ... ({len(lines) - 60} more lines)")
del model_b
except Exception as e:
print(f" [ERROR] {e}")
Diff 결과 해석¶
스크립트 결과가 나오면, 아래 체크리스트로 온보딩 작업 항목을 도출합니다:
| # | 비교 항목 | config에서 보이는 필드 | 다르면 영향받는 코드 |
|---|---|---|---|
| 1 | Attention 방식 | num_attention_heads, num_key_value_heads, layer_types, sliding_window |
model.py — attention forward 전체 |
| 2 | Heterogeneous Layers | per_layer_config, layer_types 배열 |
model.py — 레이어별 분기, weight 초기화 |
| 3 | Position Encoding | rope_theta, rope_scaling, rope_parameters, partial_rotary_factor |
model.py — RoPE 계산 로직 |
| 4 | MLP/Activation | hidden_act, hidden_activation, intermediate_size |
NF.mlp 호환 확인, 안 맞으면 직접 구현 |
| 5 | Normalization | rms_norm_eps + modeling 소스 확인 |
위치/타입(RMS vs Layer) 차이 |
| 6 | Config 구조 | top-level fields vs text_config/vision_config nested |
config.py — from_configs() 파싱 로직 |
| 7 | Embedding | tie_word_embeddings, vocab_size |
weight sharing 로직, vocab 크기 |
| 8 | Special features | final_logit_softcapping, enable_moe_block, vision_config |
추가 구현 필요 여부 |
Stage 1: Implement (모델 구현)¶
디렉토리 구조¶
src/model/your_model/
├── __init__.py
├── config.py # HuggingFace PretrainedConfig → 자체 dataclass
├── factory.py # vLLM ModelRegistry 인터페이스
└── model.py # 전체 모델 구현 (Attention, MLP, Embedding, LMHead)
핵심 구현 요소¶
| 파일 | 역할 | 핵심 |
|---|---|---|
config.py |
HF config 파싱 | from_configs(hf_config, neuron_config) classmethod |
model.py |
실제 모델 | forward(), get_kv_spec(), bind_kv_cache(), load_weights(), from_configs() |
factory.py |
팩토리 패턴 | variant 선택 (BF16 vs quantized), config 검증 |
사용 가능한 Building Blocks¶
| Component | Utility |
|---|---|
| Attention | NF.qkv_proj, NF.flash_attention, NF.segmented_attention, NF.attention_decode, NF.o_proj |
| MLP | NF.mlp — fused gate/up/down + NKI kernel |
| Embedding | vllm_neuron.nn.VocabDimShardedEmbedding |
| LM Head | vllm_neuron.nn.ColumnParallelLinear |
| Weight Loading | sharding_weight_loader, fused_qkv_weight_loader, with_rank_override, EP loaders |
| KV Cache | KVSpec, LayerSpec dataclasses |
| Collectives | get_tp_group() → all_gather, reduce_scatter, all_reduce |
NF (vllm_neuron.functional) 소스 코드
vllm_neuron/functional — Neuron HW에서는 NKI 커널로 dispatch되고, CPU에서는 PyTorch fallback으로 동작합니다. 새 모델 구현 시 이 함수들의 입출력 shape과 지원 파라미터를 소스에서 직접 확인하세요.
Reference 구현
- Dense model →
vllm_neuron/model/llama3/(porting template) - MoE model →
vllm_neuron/model/gpt_oss/(expert parallel)
Stage 2: Register (모델 등록)¶
from vllm import ModelRegistry
from .factory import MyCustomModelForCausalLM
ModelRegistry.register_model(
"MyCustomModelForCausalLM", # ← HF config.json의 "architectures" 필드와 일치
MyCustomModelForCausalLM
)
Stage 3: Compile & Smoke Test¶
from vllm import LLM, SamplingParams
llm = LLM(model="/path/to/model", max_num_seqs=4, max_model_len=2048, tensor_parallel_size=8)
output = llm.generate(["Hello, my name is"], SamplingParams(max_tokens=32, temperature=0.0))
print(output[0].outputs[0].text)
- 첫 실행 시 모든 bucket에 대해 NEFF 컴파일 (수 분~수십 분)
- CPU Compile Mode:
VLLM_NEURON_CPU_COMPILE=1 NEURON_PLATFORM_TARGET_OVERRIDE=trn2→ HW 없이 NEFF 사전 생성
Stage 4: Validate Accuracy¶
3-Level 검증 프레임워크¶
| Level | 대상 | 방법 | Pass 기준 |
|---|---|---|---|
| L1: Task-level | 전체 모델 | lm_eval, longbench | 사용자 정의 threshold |
| L2: Prompt-level | 토큰 단위 | teacher-forcing logit 비교 (HF FP32 vs BF16 vs Neuron) | divergence tolerance + KV BC ≥ 0.99 |
| L3: Module-level | 개별 컴포넌트 | attention/MLP/RMSNorm 단위 테스트 | HF reference 텐서 매치 |
Three-Way Comparison¶
from vllm_neuron.accuracy.testing import assert_close_three_way
assert_close_three_way(
target=neuron_output, # 테스트 대상
expected=hf_fp32_output, # 금본위 (FP32)
baseline=hf_bf16_output, # BF16 수치 오차 기준
rtol=0.01, name="attn_prefill",
)
해석: Neuron 오차 ≈ BF16 오차 → 정상. Neuron 오차 >> BF16 → 버그.
Stage 5: Benchmark & Tune¶
vllm serve /path/to/model --tensor-parallel-size 8 --max-num-seqs 32
vllm bench serve --model /path/to/model --dataset-name random \
--random-input-len 1024 --random-output-len 128 --num-prompts 100
Key Tuning Parameters¶
| Parameter | 효과 |
|---|---|
num_batched_tokens_buckets |
Prefill bucket 수 |
num_seqs_buckets |
Decode bucket 수 |
attention_dp_size / mlp_dp_size |
Data parallelism |
ep_degree |
Expert parallelism (MoE) |
| On-device sampling | CPU↔NC 왕복 제거 |
| FP8 KV cache | 메모리 절감 → 더 큰 batch |
Phase 2 상세: NKI 커널 적용 경로¶
NF 함수로 충분하지 않을 때, 추가 NKI 커널을 적용하는 순서:
1. nkilib에서 기존 커널 검색 (from nkilib.core.attention import attention_cte 등)
2. 맞는 커널이 있으면 → wrap_nki()로 감싸서 model.py에서 호출
3. 없으면 → @nki.jit으로 직접 커널 작성 후 동일하게 wrap_nki()
vLLM Plugin 내 NKI 실행 경로¶
model.py forward() 호출
→ torch.compile (전체 forward를 graph로 캡처)
→ Neuron Graph Compiler가 그래프 컴파일
→ NKI 커널 부분: wrap_nki() → NKI Compiler가 별도 컴파일
→ 나머지 부분: Graph Compiler가 자동 최적화
→ 결과: 하나의 NEFF 바이너리 안에 두 컴파일러 결과가 공존
NKI 커널 등록 방법 비교¶
| PyTorch Native (연구/학습용) | vLLM Plugin (서빙/프로덕션) | |
|---|---|---|
| 커널 정의 | @nki.jit |
@nki.jit (동일) |
| PyTorch 연동 | @nki_op + wrap_nki → custom op 등록 |
wrap_nki() → Plugin HOP dispatch |
| 컴파일 단위 | op별 또는 torch.compile graph | 전체 forward를 단일 NEFF로 |
| 필요 환경 | PyTorch Native Beta (torch-neuronx) | vLLM Neuron Plugin (Beta 불필요) |
코드 예시¶
# 1. NKI 커널 정의
@nki.jit
def my_custom_attention(q_ref, k_ref, v_ref, ...):
q = nl.load(q_ref[...])
# ... SBUF 연산 ...
nl.store(out_ref[...], result)
# 2. vLLM Plugin에서 등록
from vllm_neuron.utils.nki_utils import wrap_nki
wrapped_attn = wrap_nki(my_custom_attention)
# 3. model.py에서 호출
def forward_prefill(self, q, k, v, ...):
return wrapped_attn[grid](q, k, v, ...)