왜 4비트 추론이 지금 화두일까요?

확산 트랜스포머(Diffusion Transformer)를 로컬에서 돌려본 분이라면 아실 거예요. 모델 하나 로드하는 데 VRAM이 20~30GB씩 잡아먹고, 1024x1024 이미지 한 장 뽑는 데 3초 넘게 걸리는 상황 말이죠. 특히 FLUX나 ERNIE-Image 같은 최신 아키텍처는 소비자용 GPU에서 돌리기 상당히 부담스러운 수준입니다.

기존 양자화 백엔드 대부분은 가중치만(weight-only) 낮은 정밀도로 저장하고, 연산 시점에 다시 고정밀로 복원(dequantize)하는 방식을 씁니다. 메모리는 줄지만 속도는 그대로거나, 오히려 역양자화 오버헤드로 미세하게 느려지기도 해요.

여기서 등장하는 게 SVDQuant입니다. 가중치와 활성값을 **둘 다 4비트(W4A4)**로 돌리면서 메모리와 속도를 동시에 잡는 방식이에요. 그동안은 별도 추론 엔진(Nunchaku)을 써야 했는데, 이제 Diffusers에서 from_pretrained() 한 줄로 네이티브 로딩이 가능해졌습니다. 로컬 CUDA 컴파일도 필요 없어요.

이 글은 Hugging Face 공식 블로그(근거자료: Nunchaku Diffusers 통합 발표)를 기반으로, 국내 개발 환경에 맞게 재구성한 실전 가이드입니다.

Developer running 4-bit diffusion transformer inference with Nunchaku Lite on terminal Technical Structure Concept

일단 돌려보기: 설치와 첫 추론

1. 패키지 설치

최신 Diffusers와 kernels 패키지가 필요합니다. kernels는 NVFP4/INT4 CUDA 커널을 Hub에서 자동으로 받아오는 역할을 해요.

pip install -U diffusers transformers accelerate kernels bitsandbytes

2. 사전 양자화된 파이프라인 로드

커스텀 파이프라인 클래스도, 별도 추론 엔진도 필요 없습니다. 일반 Diffusers 모델처럼 불러오면 끝이에요.

import torch
from diffusers import ErnieImagePipeline

# 4비트 양자화된 체크포인트를 일반 Diffusers 모델처럼 로드
pipe = ErnieImagePipeline.from_pretrained(
    "lite-infer/ERNIE-Image-Turbo-nunchaku-lite-nvfp4_r32-bnb4-text-encoder",
    torch_dtype=torch.bfloat16,
).to("cuda")

image = pipe(
    prompt="A cinematic portrait of a red fox in a misty forest at sunrise, "
           "detailed fur, volumetric light",
    height=1024,
    width=1024,
    num_inference_steps=8,
    guidance_scale=1.0,
    generator=torch.Generator("cuda").manual_seed(42),
).images[0]

image.save("output.png")

RTX 5090 기준으로 1024x1024 이미지가 약 1.7초, 피크 메모리는 12GB 정도입니다. BF16 파이프라인이 24GB를 잡아먹는 것과 비교하면 체감이 확실하죠.

3. 하드웨어 주의사항

여기서 중요한 함정이 하나 있어요. NVFP4 체크포인트는 Blackwell GPU(RTX 50 시리즈, RTX PRO 6000, B200)에서만 동작합니다. RTX 30/40 시리즈나 A100, L40S를 쓰신다면 반드시 INT4 변형을 선택하세요.

스킴정밀도지원 GPU
svdq_w4a4nvfp4Blackwell (RTX 50 시리즈, RTX PRO 6000, B200)
svdq_w4a4int4Turing / Ampere / Ada (RTX 30·40 시리즈, A100, L40S)
awq_w4a16int4Turing / Ampere / Ada (RTX 30·40 시리즈, A100, L40S)

Volta와 Hopper는 현재 4비트 커널이 지원되지 않습니다. 로드 시점에 CUDA capability를 검증하고 명확한 에러를 던져주니, 이상한 결과물이 나오기 전에 미리 걸러진다고 보시면 됩니다.

GPU server rack displaying VRAM usage comparison between BF16 and NVFP4 quantized diffusion models Software Concept Art

속도와 메모리, 어디까지 짜낼 수 있나

torch.compile과의 조합

Nunchaku Lite는 기존 Diffusers 최적화와 자유롭게 조합할 수 있습니다. 특히 torch.compile을 붙이면 엔드투엔드 속도 향상이 1.35배 → 1.8배로 뛰어요.

# 트랜스포머 컴파일로 커널 런치 오버헤드 감소
pipe.transformer.compile(fullgraph=True)
# 또는 컴파일 시간을 줄이고 싶다면:
# pipe.transformer.compile_repeated_blocks(fullgraph=True)

텍스트 인코더도 양자화하자

트랜스포머만 메모리를 먹는 게 아닙니다. T5나 Qwen3 같은 텍스트 인코더는 그 자체로 수 GB를 차지해요. bitsandbytes NF4로 텍스트 인코더까지 양자화하면 피크 VRAM이 약 22% 추가 절감됩니다.

벤치마크 (RTX PRO 6000, 1024x1024, ERNIE-Image-Turbo)

구성전체 파이프라인디노이즈 루프피크 VRAM속도 향상
BF16 베이스라인3.00 s2.86 s31.1 GB1.0x
Nunchaku Lite NVFP42.27 s2.13 s20.6 GB1.35x
NVFP4 + torch.compile1.68 s1.53 s20.6 GB1.8x
NVFP4 + NF4 텍스트 인코더2.29 s2.13 s16.0 GB1.35x

정리하면 VRAM은 최대 50% 절감, 레이턴시는 약 30% 개선입니다. 남은 오버헤드는 대부분 커널 런치에서 오는데, torch.compile이 이를 상당 부분 상쇄해줍니다.

국내 환경에서의 주의점

국내 SI/스타트업 환경에서는 아직 RTX 30/40 시리즈 비중이 높아서, NVFP4 체크포인트를 그대로 쓰면 로드 단계에서 에러가 납니다. 반드시 INT4 변형 + awq_w4a16 조합으로 시작하시고, Blackwell 워크스테이션(RTX PRO 6000 등)이 확보된 팀이라면 NVFP4로 넘어가는 게 좋아요. 또한 운영 서버가 Hopper(H100) 기반이라면 현재 4비트 커널이 지원되지 않으니, 학습은 H100, 추론은 Ada/Blackwell로 분리하는 전략을 권합니다.

Benchmark chart visualization of Nunchaku Lite quantization speedup and memory reduction on RTX GPU System Abstract Visual

내 모델을 직접 양자화하고 싶다면

diffuse-compressor 툴킷을 쓰면 Diffusers 모델을 캘리브레이션 → 양자화 → 패키징 → Hub 푸시까지 한 번에 처리할 수 있습니다. FLUX.2 Klein 4B를 예로 들면 흐름은 이렇습니다.

1. 양자화 대상 스캔

python examples/text_to_image/quantize_hf.py black-forest-labs/FLUX.2-klein-4B \
    --precision int4 --rank 32 --inspect-config

FLUX.2 Klein 4B의 경우 SVDQ 100개, AWQ 3개, dense 6개가 나오는 게 정상입니다. 이 리포트를 반드시 먼저 읽어보세요.

2. SVDQuant 실행

python examples/text_to_image/quantize_hf.py black-forest-labs/FLUX.2-klein-4B \
    --precision int4 \
    --output outputs/checkpoints/svdq-int4_r32-flux-2-klein-4b.safetensors

Blackwell 네이티브로 만들고 싶다면 --precision nvfp4로 바꾸면 됩니다.

3. Diffusers 파이프라인으로 패키징

python examples/convert_nunchaku_lite_diffusers.py \
    --checkpoint outputs/checkpoints/svdq-int4_r32-flux-2-klein-4b.safetensors \
    --model-id black-forest-labs/FLUX.2-klein-4B \
    --bnb4-text-encoder text_encoder \
    --compute-dtype bfloat16 \
    --output-dir outputs/diffusers/FLUX.2-klein-4B-nunchaku-lite-int4-bnb4-text-encoder

4. 검증 후 Hub에 푸시

import torch
from diffusers import DiffusionPipeline

pipe = DiffusionPipeline.from_pretrained(
    "outputs/diffusers/FLUX.2-klein-4B-nunchaku-lite-int4-bnb4-text-encoder",
    device_map="cuda",
)
image = pipe(
    "A glass robot in a greenhouse, cinematic lighting",
    num_inference_steps=4, guidance_scale=1.0,
    generator=torch.Generator("cuda").manual_seed(12345),
).images[0]

pipe.push_to_hub("your-name/your-model-nunchaku-lite-int4")

구조적 재작성이 필요한 모델은?

한 가지 한계를 짚고 갑시다. 일반 경로는 구조적 재작성 없이 양자화 가능한 아키텍처를 전제로 합니다. Nunchaku 원본 엔진은 Q/K/V 프로젝션을 하나의 to_qkv 모듈로 합치고, Q/K 정규화와 rotary embedding까지 하나의 fused operator로 묶어서 처리해요. 이런 fused 경로는 일반 경로가 자동으로 추론할 수 없습니다.

# Diffusers 기본 경로: Q, K, V를 개별로 실행
query = attn.to_q(hidden_states)
key   = attn.to_k(hidden_states)
value = attn.to_v(hidden_states)
query = attn.norm_q(query.unflatten(-1, (attn.heads, -1)))
key   = attn.norm_k(key.unflatten(-1, (attn.heads, -1)))
query = apply_rotary_emb(query, image_rotary_emb, sequence_dim=1)
key   = apply_rotary_emb(key, image_rotary_emb, sequence_dim=1)

# Nunchaku 경로: 하나의 fused operator로 묶음
qkv = fused_qkv_norm_rottary(
    hidden_states, attn.to_qkv, attn.norm_q, attn.norm_k, image_rotary_emb
)

이런 모델은 양자화 시 모델별 target config로 재작성 방식을 명시하고, 런타임에는 소형 adapter가 로드 시점에 처리합니다. FLUX.2 Klein 4B 스크립트와 rootonchair/nunchaku-lite 저장소가 좋은 참고 예시예요.

다음 단계 학습 방향

  • 1단계: 사전 양자화 체크포인트(lite-infer, rootonchair)로 감을 잡기
  • 2단계: torch.compile + NF4 텍스트 인코더 조합으로 VRAM 프로파일링
  • 3단계: diffuse-compressor로 자체 모델 양자화 및 Hub 배포
  • 4단계: QKV fusion 같은 구조적 재작성 어댑터 직접 작성

함께 보면 좋은 글

마무리

Nunchaku Lite는 "4비트 추론 = 별도 엔진"이라는 공식을 깨버렸습니다. from_pretrained() 한 줄, 로컬 컴파일 없음, 기존 Diffusers 생태계(scheduler, LoRA, offloading, torch.compile)와 완전 호환. 소비자용 GPU에서 FLUX급 모델을 돌리는 게 현실적인 선택지가 된 셈이에요.

직접 양자화한 모델이 있다면 Hub에 공유해보세요. 생각보다 많은 사람들이 같은 삽질을 반복하고 있습니다 😅

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.