FP8 W8A8 양자화

FP8 W8A8 양자화 (FP8 W8A8)

FP8(8비트 부동소수점)은 NVIDIA H100, AMD MI300X 같은 GPU의 하드웨어 가속을 활용해 웨이트와 활성화를 모두 8비트로 압축하는 방식이에요. 이 페이지에서는 vLLM에서 FP8 W8A8 모델을 만들어 배포하는 과정을 단계별로 살펴볼게요.

출처: vLLM 공식 문서 — FP8 W8A8

FP8의 두 표현 (Two FP8 representations)

하드웨어에서 일반적으로 지원되는 FP8 타입은 상황에 따라 쓰임새가 다른 두 가지가 있어요.

  • E4M3: 부호 1비트, 지수 4비트, 가수 3비트. 최대 ±448과 nan 값을 표현해요.
  • E5M2: 부호 1비트, 지수 5비트, 가수 2비트. 최대 ±57344, ±inf, nan을 표현해요. 동적 범위(dynamic range)가 커진 대신 저장되는 값의 정밀도는 낮아지죠.

FP8 양자화는 모델 메모리 요구량을 약 2배 줄이고, 정확도 영향은 최소화하면서 처리량을 최대 1.6배까지 개선할 수 있어요. vLLM에서 바로 쓸 수 있는 인기 LLM의 FP8 체크포인트 컬렉션도 HuggingFace에 올라와 있으니 참고하세요.

참고: FP8 연산은 NVIDIA compute capability ≥ 8.9(Ada Lovelace, Hopper, Blackwell) GPU에서 지원돼요. compute capability ≥ 7.5(Turing) GPU에서는 FP8 Marlin 커널로 웨이트 온리(W8A16) 방식으로 돌아갑니다.

GEMM 커널 선택 (GEMM kernel selection)

vLLM은 로드 시점에 FP8 GEMM 커널을 자동으로 선택해요. 블록 양자화 체크포인트에서 CUDA라면 순서대로 FlashInfer/DeepGEMM 하이브리드(Hopper 전용), DeepGEMM, CUTLASS, Marlin, Triton, Humming, 그리고 PyTorch 폴백을 시도하죠. 네이티브 FP8을 지원하지 않는 GPU(Turing/Ampere 등)는 웨이트 온리(W8A16) Marlin으로 떨어집니다.

추론이 오류 없이 멈추면 VLLM_USE_DEEP_GEMM=0 또는 --linear-backend cutlass를 시도해 보세요. --linear-backend는 양자화된 선형 레이어에만 영향을 주고, MoE 전문가는 별도의 --moe-backend로 제어해요. 명시적으로 지정한 백엔드가 하드웨어에서 지원되지 않으면 폴백 없이 오류를 냅니다.

설치 (Installation)

성능 좋은 FP8 양자화 모델을 만들려면 llm-compressor 라이브러리를 설치해야 해요.

(venv-llm-compressor) pip install llmcompressor

평가를 위해 vllm과 lm-evaluation-harness도 설치해요.

(venv-vllm) pip install vllm "lm-eval[api]>=0.4.12"

참고: vLLM과 llm-compressor는 호환되지 않을 수 있으니 별도 환경에서 쓰는 걸 권장해요.

양자화 과정 (Quantization process)

양자화 과정은 세 단계로 나뉘어요.

1. 모델 로딩 (Loading the model)

표준 transformers AutoModel 클래스로 모델과 토크나이저를 로딩해요.

from transformers import AutoTokenizer, AutoModelForCausalLM

MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"

model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    device_map="auto",
    dtype="auto",
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)

2. 양자화 적용 (Applying quantization)

FP8에서는 단순한 RTN 양자화로도 정확도를 회복할 수 있어요. 웨이트는 정적·채널별(per-channel), 활성화는 동적·토큰별(per-token) 방식인 FP8_DYNAMIC 스킴으로 모든 Linear 레이어를 타겟팅하는 걸 권장해요. RTN은 웨이트 양자화에 데이터가 필요 없고 활성화도 동적으로 양자화되니, 캘리브레이션 데이터가 필요 없어요.

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import QuantizationModifier

# 간단한 PTQ 양자화 구성
recipe = QuantizationModifier(
    targets="Linear",
    scheme="FP8_DYNAMIC",
    ignore=["lm_head"],
)

# 양자화 알고리즘 적용
oneshot(model=model, recipe=recipe)

# Meta-Llama-3-8B-Instruct-FP8-Dynamic 저장
SAVE_DIR = MODEL_ID.split("/")[1] + "-FP8-Dynamic"
model.save_pretrained(SAVE_DIR)
tokenizer.save_pretrained(SAVE_DIR)

3. 정확도 평가 (Evaluating accuracy)

vLLM으로 모델을 로딩해 실행해요.

from vllm import LLM

llm = LLM("./Meta-Llama-3-8B-Instruct-FP8-Dynamic")

더 알아보기 (Learn more)