9장. 모델 평가와 활용, QLoRA
QLoRA 메모리와 학습 결과 비교
이삿짐을 싸는 두 방법을 견줘 봅니다. 모든 짐을 진공 압축 팩에 넣으면 부피가 크게 줄지만, 압축이 안 되는 가구(임베딩)는 그대로 실어야 합니다. 그래서 압축률이 생각보다 작게 나옵니다. 짐을 풀고 정리하는 시간(역양자화)도 조금 더 듭니다. 짐이 아주 많은 집(7B, 70B 모델)일수록 압축 팩의 효과가 커집니다.
그림은 실습 4의 실제 측정값입니다. FP32 에서 BF16 으로는 정확히 절반이 됩니다. 하지만 4비트는 FP32 의 1/8(약 236MB)이 아니라 430MB 입니다. bitsandbytes 는 선형층만 4비트로 줄이고, 임베딩은 BF16 으로 남기기 때문입니다.
둘째 그림은 같은 데이터, 같은 LoRA 설정으로 학습한 두 결과입니다. QLoRA 의 마지막 검증 손실(3.298)이 LoRA(3.209)보다 조금 높고, 학습 시간도 4분 46초로 LoRA 의 3분 11초보다 깁니다. 0.5B 모델에서는 QLoRA 의 이득이 작다는 원본 노트북의 설명과 맞습니다.
4비트 메모리 손으로 계산하기
5장에서 본 것처럼 Qwen2.5-0.5B 의 임베딩은 151,936 × 896 = 136,134,656개, 전체의 약 27.6% 입니다.
- 임베딩(BF16 유지): 136,134,656 × 2바이트 = 272,269,312바이트
- 나머지(4비트): (494,032,768 - 136,134,656) × 0.5바이트 = 357,898,112 × 0.5 = 178,949,056바이트
- 합: 451,218,368바이트 ÷ 1024² ≈ 430 MB
측정값 430MB 와 거의 같습니다. 양자화 상수 같은 작은 몫은 손계산에서 뺐습니다.
세 가지 파인튜닝 견주기
| 구분 | Full Fine-tuning | LoRA | QLoRA |
|---|---|---|---|
| 기반 모델 | 학습(FP32/BF16) | 동결(BF16) | 동결(4bit) |
| 학습 파라미터 | 전체 | 1% 내외(이 과정 1.75%) | 1% 내외 |
| GPU 메모리 | 매우 큼 | 중간 | 작음 |
| 학습 속도 | 느림 | 빠름(이 과정 3분 11초) | LoRA 보다 약간 느릴 수 있음(4분 46초) |
| 결과물 | 전체 모델(GB 단위) | 어댑터(MB 단위) | 어댑터(MB 단위) |
| 알맞은 상황 | 자원이 넉넉하고 큰 변화가 필요 | 대부분의 업무 적응 | GPU 메모리가 모자랄 때, 큰 모델 |
짧은 예시
import pandas as pd
params = 494_032_768 # Qwen2.5-0.5B 전체 파라미터 수(6차시 실행 출력)
embed = 151_936 * 896 # 임베딩 표: 낱말 151,936개 × 896칸
MB = 1024**2
# 정밀도별 메모리 손계산: 파라미터 수 × 바이트 수
estimate = {
"FP32": params * 4 / MB,
"BF16": params * 2 / MB,
"4bit 전부": params * 0.5 / MB,
"4bit (임베딩은 BF16)": (embed * 2 + (params - embed) * 0.5) / MB,
}
print(pd.Series(estimate).round(0))
# 결과: FP32 1885 / BF16 942 / 4bit 전부 236 / 4bit (임베딩은 BF16) 430
# 실습 5와 8차시의 실제 결과로 두 학습 견주기
compare = pd.DataFrame({
"마지막 검증 손실": [3.209, 3.298],
"학습 시간(초)": [3 * 60 + 11, 4 * 60 + 46],
}, index=["LoRA", "QLoRA"])
print(compare)
print(round(286 / 191, 2)) # 결과: 1.5 → QLoRA 가 약 1.5배 오래 걸렸다이 코드가 뜻하는 것
이삿짐 부피를 상자마다 재서 더해 본 것과 같습니다. "모든 짐을 압축"(236MB)과 "가구는 그대로"(430MB)의 차이가 바로 임베딩 몫입니다. 여기서 압축 팩이 0.5바이트, 압축 안 되는 가구가 embed, 이삿짐 견적서가 estimate 입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
params * 4 / MB | FP32 메모리 | 짐을 그대로 싣기 |
params * 0.5 / MB | 모두 4비트라고 칠 때 | 전부 압축 팩 |
embed * 2 + (params - embed) * 0.5 | 임베딩만 BF16 | 가구는 빼고 압축 |
compare | 손실과 시간 표 | 두 이사 방법 견주기 |
7B 모델은 파라미터 대부분이 선형층에 있어 임베딩 몫이 작습니다. 그래서 큰 모델일수록 4비트 메모리가 1/4(BF16 대비)에 가까워집니다.
주의할 점
get_memory_footprint()는 가중치만 잽니다. 학습할 때는 activation, 그래디언트, 옵티마이저 상태가 더 들어nvidia-smi로 보는 사용량이 훨씬 큽니다.- 학습 시간과 손실은 이 PC(RTX 3080)에서 한 번 돌린 값입니다. 학습 결과는 실행할 때마다, GPU 마다 조금씩 달라집니다.
- 검증 손실이 2 epoch 에서 가장 낮고 3 epoch 에서 다시 오르는 것은 LoRA 와 QLoRA 모두 같습니다. 8장에서 본 과적합 신호입니다.