Q-LoRA
Contents
- Quantization이란?
- Q-LoRA의 필요성
- Q-LoRA 개요
- Q-LoRA Details
- 코드 예시
1. Quantization (양자화)란?
모델이 사용하는 수치 표현을 줄여서 연산을 가볍게 하고, 메모리 사용을 줄이는 기법
Example
- FP32 (32-bit 부동소수점)
3.141592653589793
(매우 정밀한 숫자 표현)- 메모리 사용량: 4바이트(32비트)
- FP16 (16-bit 부동소수점)
3.1416
(소수점 이하 일부 손실)- 메모리 사용량: 2바이트(16비트)
- INT8 (8-bit 정수형)
3
또는3.14
(더 적은 정밀도)- 메모리 사용량: 1바이트(8비트)
2. Q-LoRA의 필요성
LLM은 많은 GPU 메모리를 필요로 함
- ex) LLaMA-7B: FP16 사용 시 약 28GB GPU 메모리 필요
- FP32로 학습하면 최소 100GB 이상 필요 → 일반 GPU로 불가능
- 비록 LoRA가 일부 작은 어댑터만 학습한다고 하더라도, 원본 모델은 여전히 큰 메모리를 차지
3. Q-LoRA 개요
Q-LoRA (Quantized Low-Rank Adaptation)
- 기존 LoRA에 Quantization을 추가
- 더 적은 메모리로 학습을 가능하게 만든 방법!
LoRA vs. Q-LoRA
LoRA | Q-LoRA | |
---|---|---|
모델 크기 | 원본 모델 크기 유지 | Quantization으로 모델 크기 감소 |
파인튜닝 방식 | LoRA 어댑터만 학습 | LoRA 어댑터만 학습 (동일) |
메모리 사용 | 기존보다 적지만 여전히 큼 | 더 적은 메모리 사용 가능 |
4. Q-LoRA Details
Q-LoRA (Quantized Low-Rank Adaptation)
- 메모리 효율적인 방법으로 대형 언어 모델(LLM)을 파인튜닝하는 기술
How?
- 기존 LoRA(Low-Rank Adaptation)에서 Quantization(양자화)를 추가
- 훨씬 적은 GPU 메모리로 파인튜닝 가능
- 모델 전체를 수정하지 않고, 일부 작은 어댑터(LoRA)만 학습
- 4-bit 양자화를 활용해 기존보다 훨씬 적은 메모리로 저장 및 연산 가능
핵심 아이디어
아래의 2가지를 결합
- 4-bit Quantization
- 모델의 파라미터를 FP16 (16-bit) → INT4 (4-bit)로 변환
- 메모리를 최대 4배 절약 가능
- 원본 모델을 변경하지 않고 (즉, weight를 직접 업데이트하지 않고) 양자화된 상태에서 작동
- LoRA (Low-Rank Adaptation)
- 원본 모델의 가중치를 직접 수정하지 않고, 작은 어댑터 행렬을 추가로 학습
- 메모리 효율이 뛰어나고, 적은 데이터로도 파인튜닝 가능
Full Fine-Tuning vs. LoRA vs. Q-LoRA
방법 | 모델 양자화 | 메모리 사용량 | 파인튜닝 방식 |
---|---|---|---|
Full Fine-Tuning | X (FP16, FP32) | 매우 큼 | 모든 가중치 업데이트 |
LoRA | X (FP16, FP32) | 중간 | 작은 LoRA 어댑터 추가 |
Q-LoRA | 4-bit Quantization | 최소 | 작은 LoRA 어댑터 추가 |
5. 코드 예시
아래 코드는 bitsandbytes
라이브러리를 사용해 LLaMA 모델을 4-bit로 양자화하고, Q-LoRA를 적용하는 예제이다.
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import bitsandbytes as bnb
# (1) LLaMA 모델을 4-bit로 Quantization
model_name = "meta-llama/Llama-2-7b-hf"
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # 4-bit 양자화 적용
quantization_config=bnb.nn.Linear4bitQuantizationConfig(
compute_dtype=torch.float16, # 연산은 FP16으로 유지
use_double_quant=True, # 추가적인 양자화 최적화
)
)
# (2) Tokenizer 불러오기
tokenizer = AutoTokenizer.from_pretrained(model_name)
# (3) LoRA 적용을 위한 PEFT (Parameter-Efficient Fine-Tuning) 설정
from peft import get_peft_model, LoraConfig, TaskType
config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # 언어 모델용 LoRA
r=16, # Low-Rank 차원 (낮을수록 적은 메모리 사용)
lora_alpha=32,
lora_dropout=0.1
)
# (4) 모델에 LoRA 적용
model = get_peft_model(model, config)
# (5) 모델 파라미터 확인 (양자화 + LoRA 적용됨)
print(model)
# (6) 텍스트 생성 테스트
text = "Q-LoRA is a technique that"
inputs = tokenizer(text, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_length=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
- 참고) Q-LoRA는 기존 모델을 양자화(quantize)하지만, LoRA 어댑터는 양자화하지 않는다!
왜 LoRA 어댑터는 quantize하지 않을까?
-
학습 가능한 가중치는 높은 정밀도가 필요함
-
INT4는 연산이 빠르고 메모리를 절약하지만, 정보 손실이 큼.
-
LoRA 어댑터는 학습을 해야 하므로 FP16을 유지해서 미세한 변화도 반영할 수 있도록 함.
-
-
모델 본체는 양자화 가능하지만, 업데이트해야 할 부분은 정밀도가 필요함
-
원래 모델의 가중치는 업데이트하지 않고, 4-bit (INT4)로 변환하여 메모리를 절약.
-
대신, 학습해야 하는 LoRA 어댑터는 FP16으로 유지해서 학습 가능하게 만듦.
-
요약
- Pretrained 모델 → INT4 (NF4로 양자화됨)
- LoRA 어댑터 → FP16 (학습을 위해 높은 정밀도 유지)