Skip to content
Published on

LLM 양자화 기법 완벽 비교: GPTQ, AWQ, GGUF 실전 적용 가이드

Authors
  • Name
    Twitter
LLM 양자화 기법 비교

들어가며

70B 파라미터 LLM을 FP16으로 로딩하면 약 140GB의 VRAM이 필요하다. A100 80GB 2장으로도 빠듯한 수준이다. 하지만 **양자화(Quantization)**를 적용하면 동일한 모델을 단일 GPU에서 운용할 수 있다. INT4 양자화를 적용하면 70B 모델이 약 35GB로 줄어 A100 한 장에 올릴 수 있게 된다.

2024~2026년 사이에 양자화 기법은 급격히 발전했다. GPTQ, AWQ, GGUF, BitsAndBytes 등 다양한 방법이 등장하며, 각각 정확도-속도-메모리의 트레이드오프가 다르다. 이 글에서는 각 기법의 원리를 깊이 있게 분석하고, 실전 코드와 벤치마크를 통해 최적의 선택지를 안내한다.

양자화 기본 원리

FP16에서 INT4까지의 여정

양자화란 높은 정밀도의 부동소수점(FP16/FP32) 가중치를 낮은 비트의 정수(INT8/INT4)로 변환하는 과정이다.

데이터 타입비트 수표현 범위70B 모델 메모리
FP3232bit약 ±3.4×10^38~280GB
FP1616bit약 ±6.5×10^4~140GB
INT88bit-128 ~ 127~70GB
INT44bit-8 ~ 7~35GB

Absmax 양자화

가장 단순한 양자화 방법으로, 텐서의 절대 최대값을 기준으로 스케일링한다.

import torch

def absmax_quantize(tensor: torch.Tensor, bits: int = 8) -> tuple:
    """Absmax 양자화: 절대 최대값 기준 스케일링"""
    qmax = 2 ** (bits - 1) - 1  # INT8이면 127
    scale = tensor.abs().max() / qmax
    quantized = torch.round(tensor / scale).clamp(-qmax, qmax).to(torch.int8)
    return quantized, scale

def absmax_dequantize(quantized: torch.Tensor, scale: float) -> torch.Tensor:
    """역양자화: 원래 스케일로 복원"""
    return quantized.float() * scale

# 예시: FP16 가중치 양자화
weight = torch.randn(4096, 4096, dtype=torch.float16)
q_weight, scale = absmax_quantize(weight.float())
restored = absmax_dequantize(q_weight, scale)
error = (weight.float() - restored).abs().mean()
print(f"평균 양자화 오차: {error:.6f}")

Zero-Point 양자화

Absmax는 분포가 대칭이 아닐 때 비효율적이다. Zero-Point 양자화는 비대칭 분포를 처리한다.

import torch

def zeropoint_quantize(tensor: torch.Tensor, bits: int = 8) -> tuple:
    """Zero-Point 양자화: 비대칭 분포 대응"""
    qmin = -(2 ** (bits - 1))
    qmax = 2 ** (bits - 1) - 1

    rmin, rmax = tensor.min(), tensor.max()
    scale = (rmax - rmin) / (qmax - qmin)
    zero_point = torch.round(qmin - rmin / scale).clamp(qmin, qmax)

    quantized = torch.round(tensor / scale + zero_point).clamp(qmin, qmax).to(torch.int8)
    return quantized, scale, zero_point

def zeropoint_dequantize(quantized: torch.Tensor, scale: float, zero_point: float) -> torch.Tensor:
    """Zero-Point 역양자화"""
    return (quantized.float() - zero_point) * scale

# 비대칭 분포 테스트 (ReLU 출력처럼 양수 편향)
weight = torch.randn(4096, 4096).abs()  # 양수만
q_weight, scale, zp = zeropoint_quantize(weight)
restored = zeropoint_dequantize(q_weight, scale, zp)
error = (weight - restored).abs().mean()
print(f"Zero-Point 양자화 평균 오차: {error:.6f}")

Group-wise 양자화

텐서 전체가 아니라 작은 그룹(보통 128개 원소) 단위로 양자화하면 정확도가 크게 향상된다. GPTQ, AWQ 모두 group_size=128을 기본 설정으로 사용한다.

GPTQ: Generative Pre-trained Transformer Quantization

알고리즘 상세

GPTQ는 2022년 Frantar 등이 제안한 Post-Training Quantization(PTQ) 방법으로, OBQ(Optimal Brain Quantization) 알고리즘을 기반으로 한다. 핵심 아이디어는 다음과 같다.

  1. Layer-wise 양자화: 모델 전체를 한꺼번에 양자화하지 않고, 레이어 단위로 순차 처리한다
  2. Hessian 기반 보정: 양자화로 인한 출력 오차를 Hessian 역행렬을 활용해 나머지 가중치로 보상한다
  3. Column 순서 최적화: 양자화 순서를 최적화하여 오차 전파를 최소화한다

수학적으로, 각 가중치 w_q를 양자화할 때 나머지 가중치를 아래와 같이 업데이트한다:

delta_w = -(w - quant(w)) / H_inv[q,q] * H_inv[:, q]

여기서 H_inv는 Hessian 역행렬이다. 이를 통해 양자화 오차가 다른 가중치로 재분배된다.

AutoGPTQ로 양자화 수행

from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer
import torch

# 1. 양자화 설정
model_name = "meta-llama/Llama-3.1-8B-Instruct"
quantize_config = BaseQuantizeConfig(
    bits=4,                  # 4비트 양자화
    group_size=128,          # 128개 원소 단위 그룹
    desc_act=True,           # Activation order 사용
    damp_percent=0.01,       # Hessian damping 비율
    sym=True,                # 대칭 양자화
)

# 2. 모델 및 토크나이저 로드
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoGPTQForCausalLM.from_pretrained(
    model_name,
    quantize_config=quantize_config,
    torch_dtype=torch.float16,
)

# 3. 캘리브레이션 데이터 준비
calibration_data = [
    tokenizer("The meaning of life is", return_tensors="pt"),
    tokenizer("Artificial intelligence has", return_tensors="pt"),
    tokenizer("In the context of machine learning", return_tensors="pt"),
    # 실제로는 128~256개의 다양한 샘플 사용 권장
]

# 4. 양자화 실행 (Llama-3.1-8B 기준 약 15~30분)
model.quantize(calibration_data)

# 5. 양자화된 모델 저장
output_dir = "./llama-3.1-8b-gptq-4bit"
model.save_quantized(output_dir)
tokenizer.save_pretrained(output_dir)
print(f"양자화 완료. 저장 위치: {output_dir}")

GPTQ의 장단점

장점:

  • 4비트까지 양자화해도 원본 대비 높은 정확도 유지
  • Marlin 커널 사용 시 추론 속도 대폭 향상 (2.6배 속도 증가)
  • Hugging Face Transformers 네이티브 지원

단점:

  • 캘리브레이션 데이터 필요 (보통 128~256 샘플)
  • 양자화 시간이 상대적으로 긺 (8B 모델 기준 15~30분)
  • GPU 전용 (CPU 추론 미지원)

AWQ: Activation-aware Weight Quantization

원리와 차별점

AWQ는 2024년 MIT의 Lin 등이 제안한 방법으로, 핵심 관찰은 모든 가중치가 동일하게 중요하지 않다는 점이다.

  1. Salient Weight 식별: 활성화(Activation) 크기를 기준으로 중요한 가중치 채널(약 1%)을 식별한다
  2. 선택적 보호: 중요 가중치는 스케일링 팩터를 적용하여 양자화 오차를 줄인다
  3. 나머지 양자화: 나머지 99% 가중치는 일반 양자화를 적용한다

GPTQ와의 핵심 차이점은 Hessian 역행렬 계산이 불필요하다는 점이다. AWQ는 단순한 통계 기반 접근으로 더 빠르게 양자화하면서도 비슷하거나 더 나은 품질을 달성한다.

AutoAWQ로 양자화 수행

from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

# 1. 모델 로드
model_path = "meta-llama/Llama-3.1-8B-Instruct"
model = AutoAWQForCausalLM.from_pretrained(
    model_path,
    low_cpu_mem_usage=True,
    use_cache=False,
)
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)

# 2. 양자화 설정
quant_config = {
    "zero_point": True,       # Zero-Point 양자화 사용
    "q_group_size": 128,      # 그룹 크기
    "w_bit": 4,               # 4비트 양자화
    "version": "GEMM",        # GEMM 커널 (범용) vs GEMV (배치=1 최적화)
}

# 3. 양자화 실행 (GPTQ보다 빠름, 약 10분)
model.quantize(tokenizer, quant_config=quant_config)

# 4. 저장
output_dir = "./llama-3.1-8b-awq-4bit"
model.save_quantized(output_dir)
tokenizer.save_pretrained(output_dir)
print(f"AWQ 양자화 완료: {output_dir}")

AWQ의 장단점

장점:

  • GPTQ보다 빠른 양자화 속도
  • Activation-aware 접근으로 높은 품질 유지 (HumanEval Pass@1: 51.8%)
  • Marlin-AWQ 커널 적용 시 741 tok/s의 높은 처리량
  • vLLM 네이티브 지원

단점:

  • GPU 전용 (CPU 추론 미지원)
  • GGUF 대비 에코시스템이 작음
  • 일부 모델 아키텍처에서 호환성 문제

GGUF: GPU-poor를 위한 범용 포맷

GGUF 포맷과 llama.cpp 생태계

GGUF(GPT-Generated Unified Format)는 양자화 알고리즘이 아니라 파일 포맷이다. llama.cpp 프로젝트에서 만든 이 포맷은 CPU와 GPU 하이브리드 추론을 지원한다.

GGUF의 핵심 특징:

  • 단일 파일: 모델 가중치, 토크나이저, 메타데이터가 하나의 파일에 포함
  • CPU 추론 지원: GPU 없이도 추론 가능
  • 하이브리드 오프로딩: 일부 레이어는 GPU, 나머지는 CPU로 처리
  • 다양한 양자화 수준: Q2_K부터 Q8_0까지 세밀한 품질-크기 조절

GGUF 양자화 타입별 비교

양자화 타입비트 수Llama-3.1-8B 크기Perplexity 증가권장 용도
Q2_K2.6bit~2.8GB+2.5~3.0극한 메모리 제한
Q3_K_M3.3bit~3.5GB+1.0~1.5메모리 제한 환경
Q4_K_M4.5bit~4.9GB+0.3~0.5범용 권장
Q5_K_M5.3bit~5.7GB+0.1~0.2품질 중시
Q6_K6.6bit~6.6GB+0.05거의 무손실
Q8_08.0bit~8.5GB~0무손실에 가까움

llama.cpp로 GGUF 양자화

# 1. llama.cpp 빌드
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp && mkdir build && cd build
cmake .. -DGGML_CUDA=ON  # GPU 가속 사용 시
cmake --build . --config Release

# 2. HF 모델을 GGUF FP16으로 변환
python convert_hf_to_gguf.py \
    /path/to/Llama-3.1-8B-Instruct \
    --outtype f16 \
    --outfile llama-3.1-8b-f16.gguf

# 3. Q4_K_M으로 양자화
./build/bin/llama-quantize \
    llama-3.1-8b-f16.gguf \
    llama-3.1-8b-q4_k_m.gguf \
    Q4_K_M

# 4. 추론 테스트
./build/bin/llama-cli \
    -m llama-3.1-8b-q4_k_m.gguf \
    -p "Explain quantum computing in simple terms:" \
    -n 256 \
    --n-gpu-layers 35  # GPU 오프로딩 레이어 수

llama-cpp-python으로 Python 연동

from llama_cpp import Llama

# GGUF 모델 로드 (GPU 레이어 오프로딩 포함)
llm = Llama(
    model_path="./llama-3.1-8b-q4_k_m.gguf",
    n_gpu_layers=35,    # GPU에 올릴 레이어 수 (-1이면 전체)
    n_ctx=4096,          # 컨텍스트 길이
    n_threads=8,         # CPU 스레드 수
    verbose=False,
)

# 텍스트 생성
output = llm(
    "Explain the difference between TCP and UDP:",
    max_tokens=512,
    temperature=0.7,
    top_p=0.9,
    stop=["\\n\\n"],
)
print(output["choices"][0]["text"])

# ChatCompletion API 스타일
response = llm.create_chat_completion(
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "What is quantization in ML?"},
    ],
    max_tokens=512,
    temperature=0.7,
)
print(response["choices"][0]["message"]["content"])

BitsAndBytes와 QLoRA 통합

4비트 추론과 파인튜닝

BitsAndBytes는 Hugging Face Transformers와 직접 통합되어 별도 양자화 과정 없이 로딩 시점에 양자화를 적용한다. QLoRA와 결합하면 양자화된 모델 위에 LoRA 어댑터를 학습할 수 있다.

import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
)

# 4비트 양자화 설정
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",           # NormalFloat4 (QLoRA 논문)
    bnb_4bit_compute_dtype=torch.bfloat16,  # 연산은 BF16으로
    bnb_4bit_use_double_quant=True,       # 이중 양자화 (추가 메모리 절약)
)

# 모델 로드 (로딩 시 자동 양자화)
model_name = "meta-llama/Llama-3.1-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True,
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 메모리 사용량 확인
print(f"모델 메모리: {model.get_memory_footprint() / 1e9:.2f} GB")

# 추론
inputs = tokenizer("Explain gradient descent:", return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=200)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

BitsAndBytes의 특징

장점:

  • 사전 양자화 과정 불필요 (로딩 시 즉시 적용)
  • QLoRA 파인튜닝과 자연스러운 통합
  • Hugging Face 에코시스템 완벽 지원
  • NF4 양자화로 높은 품질 유지

단점:

  • 추론 속도가 GPTQ/AWQ 대비 느림 (커스텀 커널 부재)
  • 양자화된 모델을 파일로 저장/공유하기 어려움
  • CUDA 전용 (AMD/CPU 미지원, 다만 2025년부터 ROCm 부분 지원)

종합 비교: GPTQ vs AWQ vs GGUF vs BitsAndBytes

핵심 특성 비교표

항목GPTQAWQGGUFBitsAndBytes
양자화 방식PTQ (Hessian 기반)PTQ (Activation-aware)PTQ (다양한 방식)동적 양자화
기본 비트4bit4bit2~8bit 선택4bit (NF4)
캘리브레이션필요 (128~256샘플)필요 (소량)불필요불필요
양자화 시간15~30분 (8B)~10분 (8B)수분로딩 시 즉시
GPU 추론매우 빠름가장 빠름빠름 (오프로딩 시)보통
CPU 추론미지원미지원지원미지원
vLLM 지원지원지원부분 지원지원
파인튜닝제한적제한적미지원QLoRA 최적
모델 공유HF Hub 업로드HF Hub 업로드단일 파일 배포어려움
Perplexity (4bit)기준 +0.3~0.5기준 +0.2~0.4Q4_K_M: +0.3~0.5기준 +0.2~0.4

추론 성능 벤치마크 (Llama-3.1-8B, A100 80GB)

방법처리량 (tok/s)VRAM 사용HumanEval Pass@1Latency (TTFT)
FP16 (기준)~35016GB53.2%45ms
GPTQ 4bit~5205.5GB50.6%32ms
GPTQ + Marlin~7125.5GB50.6%25ms
AWQ 4bit~5505.2GB51.8%30ms
AWQ + Marlin~7415.2GB51.8%23ms
GGUF Q4_K_M~2804.9GB51.8%55ms
BitsAndBytes NF4~3005.8GB51.8%50ms

위 벤치마크에서 주목할 점은 커널 구현이 알고리즘보다 중요하다는 것이다. Marlin 커널을 사용하면 GPTQ는 2.6배, AWQ는 10.9배 속도 향상을 얻는다.

vLLM에서 양자화 모델 서빙

from vllm import LLM, SamplingParams

# AWQ 모델 서빙 (Marlin 커널 자동 적용)
llm = LLM(
    model="casperhansen/llama-3.1-8b-instruct-awq",
    quantization="awq_marlin",      # Marlin 커널 명시
    max_model_len=8192,
    gpu_memory_utilization=0.85,
    dtype="half",
)

sampling_params = SamplingParams(
    temperature=0.7,
    top_p=0.9,
    max_tokens=1024,
)

prompts = [
    "Explain the CAP theorem in distributed systems.",
    "Write a Python function to implement binary search.",
    "What are the SOLID principles in software engineering?",
]

outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    prompt = output.prompt
    generated = output.outputs[0].text
    print(f"Prompt: {prompt[:50]}...")
    print(f"Output: {generated[:200]}...")
    print("---")

모델별 양자화 품질 가이드

모델 크기별 권장 양자화

모델 크기소비자 GPU (24GB)서버 GPU (80GB)CPU 전용
7~8BGPTQ/AWQ 4bitFP16 권장GGUF Q4_K_M
13BGPTQ/AWQ 4bitFP16 또는 AWQ 4bitGGUF Q4_K_M
34BGGUF Q4_K_MAWQ 4bit + MarlinGGUF Q3_K_M
70BGGUF Q3_K_M (부분)AWQ 4bit + MarlinGGUF Q2_K

태스크별 권장 양자화 수준

  • 코드 생성: AWQ 4bit 권장 (HumanEval 기준 가장 높은 정확도)
  • 장문 생성/요약: Q5_K_M 이상 권장 (낮은 양자화에서 반복 증가)
  • 분류/NER: INT4도 충분 (정확도 영향 미미)
  • 수학적 추론: Q6_K 이상 또는 AWQ 4bit 권장 (수치 민감)

운영 시 주의사항과 트러블슈팅

자주 발생하는 문제

1. GPTQ 양자화 중 OOM 발생

캘리브레이션 시 메모리 부족이 발생하면 desc_act=False로 변경하고, damp_percent를 높여보자.

2. AWQ 모델에서 이상한 출력

양자화 버전(GEMM vs GEMV)과 추론 프레임워크가 맞지 않을 수 있다. vLLM에서는 awq_marlin을 명시하는 것이 안전하다.

3. GGUF 모델이 느릴 때

n_gpu_layers 값을 높여 더 많은 레이어를 GPU로 오프로딩하자. 전체 레이어 수보다 높게 설정하면 전부 GPU로 올라간다.

4. BitsAndBytes에서 torch 버전 충돌

bitsandbytes>=0.43.0torch>=2.1.0 조합을 권장한다. CUDA 버전도 12.1 이상이 안정적이다.

양자화 품질 검증 체크리스트

  1. Perplexity 측정: 원본 대비 perplexity 증가가 0.5 이내인지 확인
  2. 태스크별 벤치마크: 실제 사용 태스크(코드 생성, 요약 등)에서 품질 확인
  3. Edge Case 테스트: 긴 입력, 다국어, 수학 문제 등 경계 사례 검증
  4. 레이턴시 프로파일링: TTFT(Time to First Token)와 TPS(Tokens per Second) 측정
  5. 메모리 모니터링: 실제 서빙 환경에서의 VRAM 사용량 추적

마치며

LLM 양자화는 단순히 모델 크기를 줄이는 것이 아니라, 배포 가능성을 결정하는 핵심 기술이다. 2026년 현재 권장 사항을 정리하면 다음과 같다.

  • 프로덕션 GPU 서빙: AWQ + Marlin 커널 (최고 처리량 + 높은 품질)
  • 개발/실험: BitsAndBytes NF4 (양자화 과정 불필요, QLoRA 통합)
  • 엣지/CPU 배포: GGUF Q4_K_M (범용성, 단일 파일 배포)
  • 레거시 호환: GPTQ (가장 넓은 에코시스템 지원)

양자화 기법은 계속 발전하고 있다. 2025년 이후 등장한 Marlin 커널처럼, 알고리즘 자체보다 커널 구현의 최적화가 실제 성능에 더 큰 영향을 미치는 추세다. 새로운 양자화 방법을 평가할 때는 이론적 장점뿐 아니라 실제 서빙 환경에서의 벤치마크를 반드시 확인하자.

참고자료

  1. Frantar et al., "GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers" (2022)
  2. Lin et al., "AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration" (2024)
  3. Hugging Face Transformers Quantization Guide
  4. vLLM Quantization Documentation
  5. llama.cpp GitHub Repository - GGUF Format
  6. The Complete Guide to LLM Quantization with vLLM: Benchmarks
  7. AutoGPTQ GitHub Repository
  8. AutoAWQ - Applying AWQ Quantization
  9. BitsAndBytes Foundation GitHub
  10. Dettmers et al., "QLoRA: Efficient Finetuning of Quantized LLMs" (2023)