15장. 모델 경량화: 양자화, 프루닝, 지식증류
양자화를 하는 때와 도구
옷을 압축할 때 다 입은 옷을 압축 팩에 넣을 수도 있고, 처음부터 얇은 옷을 살 수도 있습니다. 양자화도 다 학습한 모델을 나중에 줄이는 방법(학습 후 양자화)과, 학습할 때부터 줄어들 것을 감안해 학습하는 방법(양자화 인식 학습)이 있습니다. 대부분은 학습 후 양자화를 씁니다.
네 카드는 학습 후 양자화의 도구입니다. bitsandbytes 는 모델을 불러올 때 바로 8비트·4비트로 바꿔 GPU 에 올립니다. GGUF 는 llama.cpp 로 미리 양자화해 파일로 만든 것으로, Ollama 가 씁니다. GPTQ 와 AWQ 는 보정 데이터로 오차를 줄이며 4비트로 바꿔 파일로 저장하고, vLLM 같은 서빙 도구가 씁니다. 여기서 압축 팩에 넣는 일이 학습 후 양자화입니다.
도구 견주기
| 도구 | 언제 줄이나 | 보정 데이터 | 이 PC(Windows, GTX 1650) | 주로 쓰는 곳 |
|---|---|---|---|---|
| bitsandbytes | 불러올 때마다 | 없음 | 됨(실습 2) | transformers 로 실험, QLoRA 학습 |
| GGUF(llama.cpp) | 미리 파일로 | 없음(일부 단계는 중요도 데이터) | 됨. Ollama 로(실습 3) | CPU, 노트북, Ollama |
| GPTQ | 미리 파일로 | 있음(수백 문장) | 돌리지 않음 | vLLM, TGI 의 GPU 서빙 |
| AWQ | 미리 파일로 | 있음 | 돌리지 않음 | vLLM 의 GPU 서빙 |
GGUF 단계 이름 읽기
# Ollama 의 모델 이름 끝에 붙은 양자화 단계와, 숫자 하나에 드는 대략의 비트
levels = {"Q4_K_M": 4.8, "Q5_K_M": 5.7, "Q8_0": 8.5, "F16": 16}
for name, bits in levels.items():
print(f"{name}: 숫자 하나에 약 {bits}비트 → 0.5B 모델이면 약 {0.494e9 * bits / 8 / 1e6:.0f}MB")코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
levels = {"Q4_K_M": 4.8, ...} | GGUF 단계 이름마다 숫자 하나에 드는 대략의 비트를 적은 사전이다 |
0.494e9 | 0.494 × 10억, 곧 4억 9,400만이다. Qwen2.5-0.5B 의 파라미터 수다 |
* bits / 8 / 1e6 | 비트를 바이트로, 바이트를 MB(메가바이트)로 바꾼다. 여기서는 100만 바이트를 1MB 로 셌다 |
f"{...:.0f}MB" | 소수점 없이 정수로 반올림해 찍는다 |
이 코드가 뜻하는 것
같은 옷 더미를 압축 팩의 종류에 따라 얼마나 줄일 수 있는지 어림하는 것과 같습니다. 팩마다 줄이는 정도가 적혀 있고, 옷의 양은 같습니다. 여기서 압축 팩의 종류가 GGUF 단계, 옷의 양이 파라미터 수입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
for name, bits in levels.items(): | 단계 넷을 하나씩 본다 | 압축 팩 네 종류 |
0.494e9 * bits / 8 / 1e6 | 파라미터 수 × 비트를 MB 로 바꾼다 | 이 팩에 넣으면 부피가 얼마인가 |
print(...) | 단계마다 대략의 크기를 찍는다 | 팩별 부피 표 |
결과는 Q4_K_M 이 약 296MB, Q8_0 이 약 525MB, F16 이 약 988MB 입니다. 비트 수가 대략의 값이라 실제 파일 크기와는 조금 다릅니다.
주의할 점
- 같은 "4비트"라도 도구마다 방법과 품질이 다릅니다. 이름이 아니라 우리 데이터로 잰 결과로 고릅니다.
- GGUF 의 K 단계(Q4_K_M 등)는 층마다 비트를 조금씩 달리 써서, 숫자 하나에 4비트보다 조금 더 듭니다. 위의 비트 수는 대략의 값입니다.