본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 18. 19:17

6 GB 노트북 GPU에 맞추기 위한 3가지 모델 양자화(Quantization) 시도와 실패한 모델 하나

요약

6GB VRAM을 가진 노트북 환경에서 LLM을 구동하기 위해 Qwen, Microsoft, Meta의 모델들을 4-bit GPTQ 방식으로 양자화하고 vLLM에서 벤치마킹한 실험 기록입니다. llmcompressor를 활용한 양자화 과정에서의 메모리 관리와 라이브러리 버전 충돌 문제를 다룹니다.

핵심 포인트

  • 6GB VRAM 제약 조건에서 3B/7B 모델 구동을 위한 4-bit 양자화 시도
  • llmcompressor를 이용한 W4A16 GPTQ 양자화 파이프라인 구축
  • 양자화 과정 중 CPU RAM 부족 및 VRAM 피크 메모리 관리의 중요성
  • vLLM과 compressed-tensors 간의 라이브러리 버전 의존성 문제 해결 방법

제 GPU는 RTX 3050 Laptop 카드입니다. 6 GB의 VRAM을 가지고 있으며, 데스크톱이 점유하는 부분을 제외하면 실제로 사용할 수 있는 용량은 약 5.67 GB입니다. 이는 그리 많은 양이 아닙니다. bfloat16 형식의 3B 모델은 가중치(weights)만 약 6 GB에 달하므로 들어가지 않으며, 7B 모델은 근처에도 가지 못합니다.

그래서 저는 당연한 선택을 했습니다. 4-bit로 양자화(quantize)하여 무엇이 들어가는지 확인하는 것이었습니다. 하지만 단순히 모델 하나를 양자화하고 끝내는 대신, 세 가지 인기 있는 제품군(Qwen, Microsoft, Meta)에 대해 동일한 파이프라인을 실행하고, vLLM에서 세 모델 모두를 벤치마킹(benchmark)했으며, 어디서 작동하고 어디서 실패했는지에 대해 정직하게 기록을 남겼습니다. 제가 시작했던 네 가지 모델 중 하나는 끝내 성공하지 못했고, 그 실패가 이번 연습에서 가장 유용한 부분이 되었습니다.

모든 코드, 양자화된 모델, 그리고 원본 수치는 하단에 링크되어 있습니다.

계획

llmcompressor를 사용하여 vLLM이 네이티브로 로드할 수 있는 compressed-tensors 체크포인트를 생성하며, GPTQ를 사용한 W4A16, 그룹 크기(group size) 128 방식을 사용합니다. W4A16은 4-bit 가중치(weights)와 16-bit 활성화(activations)를 의미합니다. 이는 GPU 친화적이며, vLLM은 Marlin 커널을 통해 이를 서빙하고, 대부분의 로컬 추론(local-inference) 사용자들이 이미 실행하고 있는 형식입니다.

전체 파이프라인은 하나의 스크립트로 구성됩니다. 모델과 출력 경로를 입력하면 됩니다:

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import GPTQModifier

...

모델은 CPU로 로드되며, llmcompressor는 교정 통계(calibration statistics)를 수집하기 위해 한 번에 한 레이어(layer)씩 GPU로 스트리밍합니다. 이를 통해 전체 모델이 RAM에 있더라도 피크 VRAM(peak VRAM) 사용량을 작게 유지할 수 있습니다. 제 컴퓨터에서 Phi-3.5-mini는 양자화 과정 동안 VRAM 피크가 약 1.6 GB였습니다. 문제는 RAM 측면입니다. 전체 bf16 모델이 메모리에 상주하며, 15 GB 사양의 기기에서 3B 모델은 이미 가용 RAM을 몇 백 MB 수준으로 밀어냅니다. 이 작업들은 nohup을 사용하여 백그라운드에서 실행하세요. 지난번에 그렇게 하지 않았을 때는 교정 메모리 압박(calibration memory pressure)으로 인해 터미널이 죽어버렸습니다.

첫 번째 주의사항: 환경이 이미 깨져 있었다

이 모든 것을 실행하기 전에, 임포트(import)가 실패했습니다:

ModuleNotFoundError: No module named 'compressed_tensors.distributed'

llmcompressor 0.12 버전은 compressed-tensors 0.17.x 버전을 요구하지만, vLLM 0.22 버전은 0.15.0.1 버전을 고정(pin)하고 있으며, 어떤 이유로 인해 버전이 다시 다운그레이드되어 있었습니다. 해결 방법은 더 최신 버전을 설치하고 pip의 경고를 무시하는 것입니다:

pip install "compressed-tensors==0.17.1"
# pip 경고: vllm 0.22.0은 compressed-tensors==0.15.0.1을 요구합니다

vLLM은 경고를 출력한 뒤에도 0.17.1 버전으로 생성된 모델을 어쨌든 로드합니다. 두 경우 모두 임포트(import)는 잘 작동합니다. 양자화(quantizing)와 서빙(serving) 사이에 하나의 가상 환경(venv)을 공유하는 경우(저도 그렇게 합니다), 이 사실을 알아두는 것이 좋습니다.

성공적인 실행: Phi-3.5-mini 및 Llama-3.2-3B

양자화는 명령어 하나로 수행됩니다:

HF_HUB_OFFLINE=1 python quantize.py \
  --model microsoft/Phi-3.5-mini-instruct \
  --out output/Phi-3.5-mini-instruct-W4A16-G128

HF_HUB_OFFLINE=1 설정이 중요합니다. 한 번은 일시적인 DNS 오류 때문에 이미 캐시된 모델임에도 불구하고 Hugging Face 재검증(revalidation) 재시도를 하느라 6분을 허비한 적이 있습니다.

이 명령은 레이어(layer)를 순회하며 결과를 기록합니다:

(33/33): Propagating: 100%|██████████| 512/512
Compressing model: 100%|██████████| 128/128
Writing model shards: 100%
...

Phi 모델은 약 34분 만에 7.6 GB에서 2.2 GB로 줄어들었습니다. Meta 모델의 경우, 라이선스 승인 절차를 거칠 필요가 없는 비폐쇄형(non-gated) 미러인 unsloth/Llama-3.2-3B-Instruct를 사용했습니다. 이 모델 역시 깔끔하게 양자화되었으며, VRAM 사용량은 피크 시 약 2 GB 정도로 한계치에 전혀 근접하지 않았습니다.

두 개를 해냈습니다. 그다음에는 7B 모델을 시도했습니다.

벽에 부딪히다: 6 GB 환경에서 양자화되지 않는 Qwen2.5-7B

동일한 명령어로 Qwen/Qwen2.5-7B-Instruct를 실행했습니다. 두 번째 레이어에서 실패했습니다:

torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 1.34 GiB.
GPU 0 has a total capacity of 5.67 GiB of which 484.69 MiB is free.
...
...

그 1.34 GiB 할당이 바로 실마리입니다. GPTQ는 각 선형 레이어 (linear layer)에 대해 레이어의 입력 차원의 제곱 크기를 가진 헤시안 (Hessian)을 구축합니다. Qwen2.5-7B의 down_proj는 18944 차원의 입력을 받으므로, 그 헤시안은 18944의 제곱에 4바이트를 곱한 값인 약 1.4 GB가 되며, GPTQ는 이를 GPU에서 역행렬 계산 (invert) 해야 합니다. 해당 할당이 이루어지기 전 이미 카드는 약 3.9 GB를 점유하고 있었기에, 임계치를 넘겨버린 것입니다.

저는 다음과 같은 순서로 명백한 해결책들을 시도해 보았습니다:

  1. 더 작은 보정 데이터 (Smaller calibration). 2048 토큰의 샘플 512개에서 512 토큰의 256개로, 다시 512 토큰의 64개로 줄였습니다. 변화는 없었습니다. GPU는 매번 동일하게 3.9 GB에 고정되어 있었고, 이는 상주 메모리 (resident memory, 임베딩 및 활성 레이어)가 고정되어 있으며 보정 데이터에 의존하지 않음을 의미했습니다.
  2. offload_hessians=True. 이는 헤시안을 CPU에 유지합니다. GPU 사용량은 낮아졌고 작업이 몇 초 만에 종료되지 않고 한 레이어에서 9분 동안 버텼지만, 여전히 OOM (Out of Memory)이 발생했습니다. 역행렬 계산을 위해 헤시안이 다시 GPU로 돌아와야 하기 때문입니다.
  3. expandable_segments:True. 파편화된 예약 메모리를 회수합니다. 약간의 도움이 되었으나 충분하지 않았습니다.
  4. GPTQ 대신 AWQ 사용. AWQ는 레이어당 거대한 헤시안을 피합니다. 기대를 걸었으나, 내부적으로 동일한 순차적 파이프라인 (sequential pipeline)을 사용하기 때문에 정확히 같은 지점에서 OOM이 발생했습니다.
  5. CUDA_VISIBLE_DEVICES=""를 사용한 CPU 전용 모드. 이 방식은 작동합니다. RAM과 스왑 (swap)을 사용하므로 6 GB의 한계가 없습니다. 하지만 매우 느립니다:
   (2/29): Calibrating: 57%|█████▋ | 146/256 [08:42<06:46, 3.70s/it]

보정 샘플당 3.7초가 소요되는데, 이는 레이어당 약 16분, 전체 모델에는 약 10시간이 걸린다는 뜻입니다. 벤치마크 용도로는 부적합합니다.

솔직한 결론을 내리자면, 이 카드와 이 툴체인 (toolchain) 환경에서 GPU 기반 GPTQ는 파라미터 수가 약 30억(3 billion) 개 정도가 한계입니다. 7B 모델의 down_proj 헤시안과 고정된 상주 메모리의 합이 맞지 않으며, 어떤 보정 기술로도 이를 바꿀 수 없습니다. 그래서 저는 7B 대신 down_proj 입력이 8192 차원인 Llama-3.2-3B로 교체했습니다. 이 모델의 헤시안은 약 268 MB였으며, 아무런 문제 없이 양자화(quantization)를 마쳤습니다.

결론은 이렇습니다. 만약 당신이 6 GB 그래픽 카드를 가지고 있고, 그 카드에서 7B 모델을 직접 양자화(quantization)할 수 있을지 궁금하다면, llmcompressor의 순차적 GPTQ 방식으로는 GPU에서 불가능하다는 것이 답입니다. 다른 곳에서 양자화를 진행한 뒤 결과물을 다운로드해야 합니다.

(참고로, CPU 시도를 하기 전에 24 GB의 스왑(swap) 공간을 추가했습니다. bf16 형식의 7B 모델은 15 GB이며 제 컴퓨터의 RAM은 15 GB이기 때문입니다. Btrfs 파일 시스템에서는 스왑 파일이 NOCOW(Copy-on-Write 비활성화) 설정이 되어 있어야 하며, 완전히 할당되어 있어야 swapon 명령이 거부되지 않습니다.)

벤치마크 (The benchmark)

세 가지 모델, 하나의 파이프라인을 사용했으며, 공정한 비교를 위해 세 모델 모두 동일한 설정인 vLLM 0.22, 최대 길이(max length) 4096, gpu_memory_utilization=0.88 환경에서 테스트했습니다.

BF16 vs W4A16 size, all three cross the 6 GB line only after quantization

모델 (Model)계열 (Family)BF16W4A16비율 (Ratio)KV 예산 (토큰)단일 스트림 (Single-stream)배치 x32 (Batched x32)
VibeThinker-3BQwen2.5-3B reasoning5.8 GB2.07 GB2.8x53,10443.9 tok/s186 tok/s
...

두 가지 사실이 눈에 띕니다.

KV 예산(KV budgets)이 비슷하지 않습니다. 세 모델 모두 디스크 용량은 약 2.2 GB로 비슷하지만, 동일한 카드에서 처리할 수 있는 컨텍스트(context) 범위는 4,608 토큰(Phi)에서 53,104 토큰(VibeThinker)까지로, 11배나 차이가 납니다. 이것은 파일 크기가 아니라 아키텍처(architecture)의 차이입니다. VibeThinker는 공격적인 그룹화된 쿼리 주의(grouped-query attention)를 사용하는 Qwen2.5-3B 모델이므로, 각 토큰의 키-값(key-value) 상태가 매우 작아 예산이 매우 큽니다. 반면 Phi-3.5-mini는 더 큰 모델(3.8B)이며 KV 점유율(footprint)이 더 높기 때문에, 가중치(weights)가 로드되고 나면 캐시(cache)를 위한 공간이 훨씬 적게 남습니다. 작은 그래픽 카드에서 실행할 모델을 선택할 때, 가중치 크기는 모델이 로드될 수 있는지를 알려줍니다. 하지만 KV 기하학(KV geometry)은 실제로 얼마나 많은 컨텍스트를 사용할 수 있는지를 알려주며, 나중에 당신을 곤란하게 만드는 숫자는 바로 이것입니다.

Same disk size, an 11x spread in servable context

성능(Capability)은 처리량(throughput)을 희생합니다. VibeThinker는 36개의 레이어(layers)를 가진 추론 모델(reasoning model)입니다.
Llama의 1,404 tok/s와 비교했을 때, VibeThinker는 배치(batched) 기준 186 tok/s로 세 모델 중 압도적으로 느립니다.
모든 토큰마다 추가된 깊이(depth)에 대한 대가를 치르게 됩니다.

Single-stream and batched throughput across the three models

4비트(4-bit)가 추론 능력을 망가뜨렸을까?

이것이 실제로 중요한 질문입니다. 저는 세 모델 모두에게 동일한 프롬프트(prompt)를 주었습니다:
"페르마의 소정리(Fermat's little theorem)를 서술하고, 이를 사용하여 3^100 mod 7을 계산하시오." 정답은 4입니다. 세 모델 모두 정확한 풀이 과정과 함께 정답을 맞혔습니다. VibeThinker는 심지어 자신의 사고 과정(thinking)까지 보여주었습니다:

[VibeThinker] ... 3^100 = (3^6)^16 * 3^4 ≡ 1^16 * 3^4 ≡ 81 ≡ 4 (mod 7).
              최종 답변: \boxed{4}.
[Phi-3.5]     ... 3^4 = 81, 81 mod 7 = 4. 따라서 3^100 mod 7 = 4.
...

세 모델 모두 처음 10개의 소수(primes)도 정확하게 나열했습니다. 이 종류의 모델과 이러한 유형의 작업에 있어서, W4A16(4-bit weight, 16-bit activation) 양자화는 제가 측정할 수 있는 수준의 손실을 일으키지 않았습니다. 이는 VibeThinker를 양자화했을 때 4비트 버전이 여전히 수학 계산을 정확하게 수행했던 저의 이전 경험과 일치합니다.

한 가지 주의사항을 언급하자면, 이것은 MMLU와 같은 벤치마크가 아닌 일시적인 점검(spot check)이라는 점입니다. 일반적으로 4비트 양자화(4-bit quantization)는 정확도를 떨어뜨리며, 저 또한 이전에 문제가 되는 것을 본 적이 있습니다 (한 번은 fp8 KV 캐시(KV cache)가 bf16은 맞혔던 "처음 10개의 소수를 나열하시오"라는 프롬프트를 망쳐놓은 적이 있습니다). 저는 이 특정 모델들이 이 특정 프롬프트들에서 잘 버텨냈다는 사실만을 보고하는 것이며, 그 이상의 결론을 내리지는 않습니다.

이 작업을 시작하려는 사람에게 해주고 싶은 말

  • 하나의 스크립트가 여러 모델 제품군에 걸쳐 범용적으로 적용됩니다. Qwen, Phi, Llama 모두 동일한 GPTQModifier(targets="Linear", scheme="W4A16", ignore=["lm_head"]) 레시피로 양자화(Quantization)됩니다. 아키텍처의 차이는 양자화 시점이 아닌 서빙(Serving) 시점에 나타납니다.
  • 6 GB 그래픽 카드에서는 GPU 양자화를 위해 대략 3배의 크기 감소와 30억~40억(3 to 4 billion) 개의 파라미터 상한선을 계획하십시오.
  • 파일 크기뿐만 아니라 KV 버젯(KV budget)을 확인하십시오. 이는 가중치(Weights)보다 더 많이 변동합니다.
  • 양자화는 서빙이 아니라 메모리를 많이 소모하는 단계입니다. 별도로 실행하고, VRAM이 아닌 RAM을 모니터링하십시오.

코드는 아래에 링크된 quantize.pybench.py 하나씩으로 구성되어 있습니다. 세 가지 모델은 Hub에 있습니다. 만약 그래픽 카드가 작아서 모델이 들어가지 않는다면, 그 과정은 짧으며 이제 여러분은 그것이 어디서 끝나는지 정확히 알게 될 것입니다.

링크

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0