13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[AI] CUDA 기반 LLM 양자화 벤치마크: BitsAndBytes vs AWQ 성능 비교

[AI] CUDA 기반 LLM 양자화 기법 비교: BitsAndBytes vs AWQ 성능 벤치마크

LLM 양자화는 이제 GPU 메모리가 빠듯한 환경에서 거의 필수처럼 다뤄집니다. 특히 CUDA 성능을 끝까지 끌어내고 싶다면 BitsAndBytes와 AWQ 중 뭘 먼저 볼지 한 번쯤 고민해보셨을 거예요. 저도 홈랩에서 모델 올릴 때 처음엔 “일단 돌아가면 됐지” 하고 시작했는데, 막상 추론 속도와 VRAM 사용량, 로딩 편의성까지 같이 보니까 선택 기준이 꽤 달라지더라고요.

이번 글은 CUDA 기반 LLM 양자화 기법 비교라는 관점에서, BitsAndBytes와 AWQ를 실제 운영/실험 관점으로 풀어보는 글입니다. 특정 수치를 과장해서 보여드리기보다는, 어떤 조건에서 무엇이 유리한지, 벤치마크는 어떻게 잡아야 덜 헷갈리는지, 그리고 실전에서 어떤 삽질이 나오는지를 중심으로 정리해보겠습니다. 혹시 “왜 같은 4bit인데 속도가 다르지?” 같은 경험 있으신가요? 여기서 중요한 포인트가 바로 커널(kernel, GPU 연산 루틴)과 로딩 방식입니다.

1. 왜 LLM 양자화 벤치마크가 중요한가

쉽게 말해 양자화(Quantization, 저정밀도 변환)는 모델의 가중치(weight)를 더 작은 비트 폭으로 저장하고 계산하는 방식입니다. 보통은 메모리를 줄이기 위해 시작하지만, 실제로 써보면 목적이 하나가 아니거든요.

  • VRAM 절약: 같은 GPU에서 더 큰 모델을 띄우거나 배치(batch) 크기를 늘릴 수 있습니다.
  • CUDA 성능 최적화: 경우에 따라 추론 처리량(throughput)이 좋아질 수 있습니다.
  • 운영 단순화: 서버 한 대로도 테스트 범위를 넓힐 수 있습니다.
  • 비용 절감: 클라우드 GPU 사용 시 메모리 요구량이 줄어드는 효과가 있습니다.

근데 여기서 함정이 있습니다. 메모리를 줄였다고 항상 더 빠른 건 아닙니다. 저도 처음엔 4bit면 무조건 이득인 줄 알았는데, 실제로는 디코딩 단계에서 토큰 생성 속도(token/s)가 기대보다 안 나오는 경우가 있었고, 반대로 로딩은 번거로워도 실제 서비스 응답은 더 안정적으로 나오는 조합도 있었습니다. 그래서 LLM 최적화에서는 “몇 bit냐”보다 “어떤 방식으로 quantize 했고, 어떤 CUDA 경로를 타느냐”가 더 중요합니다.

LLM 양자화와 CUDA 성능 비교를 위한 BitsAndBytes와 AWQ 전체 아키텍처 개요 이미지

BitsAndBytes와 AWQ가 CUDA GPU 위에서 각각 어떤 로딩 경로와 추론 경로를 사용하는지 보여주는 개요 이미지입니다.

2. BitsAndBytes와 AWQ 개념을 쉽게 정리해보면

2-1. BitsAndBytes란?

BitsAndBytes는 Hugging Face 생태계에서 많이 쓰는 양자화 라이브러리입니다. 특히 Transformers(트랜스포머, 대규모 언어모델 로딩 프레임워크)와의 궁합이 좋아서, 모델을 비교적 쉽게 8bit 또는 4bit로 로딩할 수 있다는 장점이 있습니다. 실무에서 빠르게 PoC를 할 때 진입장벽이 낮은 편이죠.

제가 직접 써본 결과 BitsAndBytes는 “일단 빨리 올려서 확인”하기에 정말 편했습니다. 코드 몇 줄로 들어가고, 모델 허브(Hub) 기반 워크플로우에도 잘 붙거든요. 대신 성능은 모델 구조, CUDA 환경, 커널 지원 상황에 따라 편차가 좀 있더라고요.

2-2. AWQ란?

AWQ는 Activation-aware Weight Quantization의 약자입니다. 이름 그대로 활성값(activation)을 고려해서 가중치 양자화를 설계하는 접근입니다. 실전에서는 사전 양자화된(pre-quantized) 모델 아티팩트를 로드해 추론하는 흐름으로 많이 접하게 됩니다.

처음엔 이게 뭔가 싶었는데, 실제로 써보니까 AWQ는 “양자화 자체”보다도 서빙 시 추론 경로가 잘 맞느냐가 더 중요한 포인트였습니다. 특히 CUDA에서 최적화된 구현을 잘 타면, 체감 응답성이 좋아지는 경우가 꽤 있었습니다.

2-3. 한눈에 보는 차이

항목 BitsAndBytes AWQ
접근 방식 런타임 로딩 중심의 저정밀도 적용 사전 양자화된 가중치 활용이 일반적
도입 난이도 상대적으로 쉬움 모델/런타임 조합 확인 필요
Hugging Face 연동 매우 편한 편 도구 체인에 따라 차이 있음
벤치마크 포인트 로딩 편의성, 메모리 절감, 범용성 추론 성능, 서빙 효율, 커널 적합성
적합한 상황 빠른 테스트, 개발 환경 서빙 튜닝, 성능 중심 검토

3. 벤치마크를 어떻게 잡아야 덜 속을까

여기서 많이들 실수하는 게, 단순히 “모델 뜨는지”만 보고 끝내는 겁니다. 근데 benchmark는 관측 기준이 명확해야 해요. 제가 보통 보는 항목은 아래 정도입니다.

  1. 로딩 시간: 모델 초기화와 첫 추론 준비까지 걸리는 시간
  2. VRAM 사용량: 모델 로딩 직후와 추론 중 피크 메모리
  3. 첫 토큰 지연: TTFT(Time To First Token, 첫 토큰 응답 시간)
  4. 지속 생성 속도: 초당 토큰 수(token/s)
  5. 출력 품질 안정성: 과도한 품질 저하나 이상 출력 여부

중요한 건 같은 프롬프트, 같은 max_new_tokens, 같은 GPU, 같은 드라이버 조건으로 맞춰야 한다는 점입니다. 이거 안 맞추면 결과가 거의 의미가 없어요. 저도 예전에 캐시 상태 다른 줄 모르고 두 방식 비교했다가 완전히 엉뚱한 결론 낸 적이 있거든요. 정말 한 번 삽질했습니다. (웃음)

4. CUDA 환경에서 BitsAndBytes 실전 구현

먼저 BitsAndBytes 쪽입니다. 빠르게 비교 환경을 만들 때 가장 무난한 흐름입니다.

4-1. 기본 설치

python -m venv .venv
source .venv/bin/activate
pip install torch transformers accelerate bitsandbytes

환경에 따라 PyTorch는 CUDA 빌드가 이미 맞춰져 있어야 합니다. 이 부분이 안 맞으면 양자화 이전에 GPU 자체를 못 잡는 경우가 생기거든요.

4-2. 4bit 로딩 예시

import time
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

model_id = "meta-llama/Llama-2-7b-chat-hf"

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.float16,
)

start = time.time()
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    quantization_config=bnb_config,
    device_map="auto"
)
load_time = time.time() - start

prompt = "Explain the difference between AWQ and BitsAndBytes in simple terms."
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

gen_start = time.time()
output = model.generate(**inputs, max_new_tokens=128)
gen_time = time.time() - gen_start

print("load_time_sec=", round(load_time, 2))
print("gen_time_sec=", round(gen_time, 2))
print(tokenizer.decode(output[0], skip_special_tokens=True))

여기서 포인트는 NF4 설정과 compute dtype이에요. 실제로 써본 결과 이 조합은 비교 기준을 잡을 때 많이 사용하게 되더라고요. 물론 모델마다 최선의 조합은 다를 수 있습니다.

BitsAndBytes 기반 LLM 양자화의 CUDA 메모리 로딩 흐름을 보여주는 이미지

Python 코드에서 BitsAndBytes 4bit 로딩이 수행되고 CUDA 메모리에 모델이 배치되는 흐름을 보여주는 구성 이미지입니다.

5. CUDA 환경에서 AWQ 실전 구현

AWQ는 조금 더 “서빙용 경로를 염두에 둔 구성”으로 보는 편이 좋습니다. 구현 방식은 사용하는 런타임에 따라 다르지만, 아래처럼 사전 양자화 모델을 로드하는 식으로 접근할 수 있어요.

5-1. 기본 설치 예시

python -m venv .venv
source .venv/bin/activate
pip install torch transformers accelerate autoawq

패키지 조합은 시점과 환경에 따라 달라질 수 있어서, 실제 적용할 때는 프로젝트에서 쓰는 Transformers 계열과의 호환성을 먼저 확인하시는 게 좋습니다.

5-2. AWQ 모델 로딩 예시

import time
from transformers import AutoTokenizer
from awq import AutoAWQForCausalLM

model_id = "TheBloke/zephyr-7B-beta-AWQ"

start = time.time()
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoAWQForCausalLM.from_quantized(
    model_id,
    fuse_layers=True,
    trust_remote_code=True
)
load_time = time.time() - start

prompt = "Summarize when AWQ is useful for CUDA inference."
inputs = tokenizer(prompt, return_tensors="pt")

gen_start = time.time()
output = model.generate(**inputs, max_new_tokens=128)
gen_time = time.time() - gen_start

print("load_time_sec=", round(load_time, 2))
print("gen_time_sec=", round(gen_time, 2))
print(tokenizer.decode(output[0], skip_special_tokens=True))

AWQ는 구현에 따라 fused layer(연산 결합) 같은 옵션이 성능에 영향을 주기도 해요. 그래서 단순히 “4bit라서 비슷하겠지”라고 보면 안 되고, 어떤 런타임 경로를 탔는지를 꼭 같이 확인해야 합니다.

6. 제가 실제로 자주 보는 벤치마크 절차

이 부분은 꽤 중요합니다. LLM 양자화 benchmark를 제대로 하려면 절차를 고정해야 하거든요.

  1. 동일한 GPU와 동일한 CUDA 드라이버 환경을 준비합니다.
  2. 같은 계열 모델 크기끼리 비교합니다.
  3. 프롬프트 길이와 출력 길이를 고정합니다.
  4. 첫 실행은 워밍업(warm-up)으로 버립니다.
  5. 2회차 이상부터 평균 응답 시간을 기록합니다.
  6. <code>nvidia-smi로 VRAM 사용량 피크를 같이 봅니다.
  7. TTFT와 token/s를 분리해서 기록합니다.
watch -n 1 nvidia-smi
CUDA_VISIBLE_DEVICES=0 python benchmark_bnb.py
CUDA_VISIBLE_DEVICES=0 python benchmark_awq.py

실제로 써본 결과 AWQ는 지속 생성 속도에서 체감이 괜찮은 경우가 있었고, BitsAndBytes는 세팅 편의성에서 확실히 이점이 컸습니다. 다만 이건 어디까지나 일반적인 경향에 가깝고, 모델 아키텍처와 커널 지원에 따라 결과가 바뀔 수 있거든요. 여기서 중요한 포인트! 벤치마크 결과를 일반화하기 전에, 내 워크로드가 긴 입력 위주인지 짧은 질의응답 위주인지 먼저 구분해야 합니다.

CUDA 성능 기준으로 AWQ와 BitsAndBytes를 벤치마크하는 LLM 양자화 실행 이미지

동일한 CUDA 환경에서 두 양자화 방식을 번갈아 실행하고 GPU 메모리와 추론 시간을 관찰하는 장면을 표현한 이미지입니다.

7. ⚠️ 트러블슈팅: 여기서 많이 막힙니다

7-1. GPU는 잡히는데 성능이 생각보다 안 나오는 경우

이건 정말 흔해요. 원인은 여러 가지인데, 보통은 아래 범주로 묶입니다.

  • CUDA 커널 최적화 경로 차이: 같은 4bit라도 내부 구현 차이가 큽니다.
  • 모델별 적합성 차이: 어떤 모델은 특정 양자화 포맷과 더 잘 맞습니다.
  • 입력 길이 편향: 짧은 질의에서는 차이가 덜 보일 수 있습니다.
  • 비교 조건 불일치: 캐시 상태, 배치 크기, dtype이 다르면 결과가 흔들립니다.

저도 처음엔 AWQ가 무조건 빠를 줄 알았는데, 짧은 프롬프트 위주 테스트에서는 체감 차이가 거의 없었던 적이 있어요. 반대로 긴 생성 구간에서는 차이가 보이기도 했고요. 그래서 벤치마크는 꼭 실제 사용 패턴과 비슷하게 잡아야 합니다.

7-2. 설치는 되는데 import 에러가 나는 경우

대개는 패키지 호환성 문제예요. Transformers, PyTorch, 양자화 라이브러리 버전 축이 어긋나면 바로 티가 납니다. 이럴 땐 아래 순서로 점검하면 됩니다.

  1. torch.cuda.is_available() 확인
  2. python -c "import torch; print(torch.__version__)" 확인
  3. 양자화 라이브러리 import 단독 테스트
  4. 가상환경을 새로 만들어 최소 패키지로 재현

7-3. 메모리는 줄었는데 품질이 흔들리는 경우

이건 양자화에서 완전히 피할 수 없는 주제입니다. 특히 공격적으로 메모리를 줄이면 일부 태스크에서 출력 품질이 흔들릴 수 있거든요. 그래서 성능만 보지 말고 간단한 품질 회귀 테스트(regression test, 성능 변경 후 품질 저하 확인)도 같이 돌려보셔야 합니다.

8. 검증 결과는 어떻게 읽어야 하나

수치 자체보다 방향성이 더 중요합니다. 제가 보통 내리는 결론은 아래처럼 정리됩니다.

판단 질문 BitsAndBytes가 유리한 경우 AWQ가 유리한 경우
빨리 실험해야 하나? 예 보통은 아님
모델 허브 기반 워크플로우인가? 대체로 예 환경에 따라 다름
CUDA 추론 성능 튜닝이 핵심인가? 상황에 따라 가능 검토 우선순위 높음
운영 직전 서빙 최적화 단계인가? 비교군으로 좋음 주력 후보가 되기 쉬움

정리하면 이렇습니다. BitsAndBytes는 시작이 빠르고, AWQ는 성능 튜닝의 잠재력이 크다는 쪽에 가깝습니다. 물론 절대적인 법칙은 아닙니다. 하지만 benchmark 관점에서는 이 프레임이 꽤 유용해요. 실제로 써보니까 개발 단계와 운영 단계에서 선택 기준이 달라지더라고요.

🎉 완료 기준도 명확해야 합니다. 단순히 “모델이 뜬다”가 아니라 아래 항목을 만족해야 진짜 의미 있는 검증이라고 볼 수 있습니다.

  • 동일 조건에서 반복 측정해도 편차가 크지 않을 것
  • VRAM 사용량과 응답 시간이 함께 기록될 것
  • 품질 저하 여부를 최소한 샘플 단위로라도 확인할 것
  • 실제 서비스 패턴과 비슷한 입력 길이를 사용할 것
LLM 양자화 벤치마크 결과에서 CUDA 성능과 VRAM 비교를 보여주는 대시보드 이미지

TTFT, token/s, VRAM 사용량을 함께 비교하는 대시보드 형태의 결과 시각화 이미지입니다.

9. 마무리: 어떤 기준으로 고르면 되나

이번 글의 핵심은 간단합니다. LLM 양자화는 단순히 메모리 줄이기 게임이 아니라, CUDA 성능과 운영 편의성, 그리고 품질 안정성을 같이 보는 작업이라는 점입니다. 저도 처음엔 BitsAndBytes 하나만 알면 되는 줄 알았는데, 실제로 서비스 비슷하게 붙여보니 AWQ 같은 접근을 같이 봐야 판단이 서더라고요.

제 기준으로는 이렇습니다. 빠르게 모델을 확인하고 개발 생산성을 높이고 싶다면 BitsAndBytes를 먼저 보시는 게 편합니다. 반대로 LLM 최적화와 추론 성능을 더 밀어붙이고 싶다면 AWQ를 함께 비교해보셔야 합니다. 둘 중 하나가 무조건 정답이라기보다, 어떤 단계에서 무엇을 우선하느냐가 더 중요해요.

💡 다음 글에서는 vLLM 같은 서빙 런타임과 양자화 조합을 어떻게 비교하면 좋은지 이어서 다뤄볼 예정입니다. 이전 글에서 다뤘던 GPU 메모리 모니터링 방법과 함께 보시면 더 이해가 잘 되실 겁니다.

BitsAndBytes와 AWQ 중 어떤 LLM 양자화 방식을 고를지 요약한 비교 이미지

개발 편의성, CUDA 성능, 운영 적합성을 기준으로 두 방식을 고르는 요약 인포그래픽 이미지입니다.

자주 묻는 질문 정리

Q1. BitsAndBytes와 AWQ 중 무엇부터 시작하면 좋을까요?

대부분은 BitsAndBytes부터 시작해도 괜찮습니다. 진입이 쉽고 비교군을 만들기 좋거든요. 그 다음 성능이 아쉬우면 AWQ를 붙여보는 흐름이 현실적입니다.

Q2. 4bit면 항상 8bit보다 빠른가요?

아닙니다. 메모리 절감 효과는 크지만, 실제 속도는 CUDA 커널과 런타임 구현에 따라 다릅니다. 그래서 벤치마크가 필요해요.

Q3. 운영 환경에서는 무엇을 특히 봐야 하나요?

TTFT, token/s, VRAM 피크, 품질 회귀 여부를 같이 보셔야 합니다. 하나만 좋고 나머지가 흔들리면 운영에서 결국 문제가 생기거든요.