본문 바로가기
Bigdata

LLM 28B 모델 GPU 메모리 부족시(24GB이하) LLM 메모리 최적화 완벽 가이드

by 올엠 2025. 12. 19.
반응형

요즘  OpenModel들이 잘 나오고 있는데, 가정용으로 많이 사용되는 보통 30B 근접한 모델들을 사용하는 것이 효율적으로 좋은 것으로 판단된다. 이때 어떻게 메모리를 효율적으로 사용할 수 있는지를 확인해보고자 한다.

여기에서 사용해볼만한 그래픽 카드는 랩탑 그래픽 카드 기준으로 다음과 같다.

- Geforce RTX 4090 (24GB)
- Geforce RTX 5090 (24GB)
- AMD AI 300시리즈 (온보드 메모리 64GB 이상인 모델부터 32GB 이상 iGPU 사용이 가능)
   - 온보드 메모리 64GB(32GB)
   - 온보드 메모리 128GB(92GB)

 

가장 먼저 28B 모델이라고 가정하고 GPU 메모리 요구량을 개산해 보았다.

28B 모델 GPU 메모리 요구사항 계산

추론 모드 (Inference)

정밀도모델 메모리 KV Cache활성화에 필요한 총 메모리 (오버헤드 포함)와 권장 GPU이다. 최소한 14GB에서 19GB가 필요한 것을 알 수 있다.

FP16 56 GB ~2 GB ~4 GB ~75 GB A100 80GB, H100
INT8 28 GB ~1 GB ~2 GB ~37 GB A100 40GB, A6000
INT4 14 GB ~0.5 GB ~1 GB ~19 GB RTX 4090, A5000

학습 모드 (Training)

설정 총 메모리와 필요량 권장 GPU 구성으로 학습에는 많은 GPU가 필요하고, INT4로 양자화를 하더라도 24GB는 필요하므로 INT2, INT1등도 고려해야겠지만, 성능과 효율을 고려해야 할 것이다.

FP16 + Adam ~224 GB 4x A100 80GB (Multi-GPU)
FP16 + LoRA ~85 GB 2x A100 80GB

28B 모델 실행을 위한 최소 요구사항:

  • FP16 추론: 80GB GPU (A100 80GB 또는 H100)
  • INT8 추론: 40GB GPU (A100 40GB, A6000)
  • INT4 추론: 24GB GPU (RTX 4090, A5000) ✅ 가장 현실적
  • 학습: 멀티 GPU 필수 (4x A100 80GB 권장)

1. 양자화 (Quantization) 

가장 효과적인 메모리 절감 방법으로 일반적으로 가장 많이 사용된다.

INT8 양자화 (bitsandbytes)

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# INT8 로딩 - 메모리 50% 절감
model = AutoModelForCausalLM.from_pretrained(
    "model-name-28b",
    load_in_8bit=True,
    device_map="auto",  # 자동 GPU 분산
    torch_dtype=torch.float16
)

# 메모리 절감: 56GB → 28GB
# 성능 손실: < 2%

INT4/NF4 양자화 (QLoRA)

from transformers import BitsAndBytesConfig

# 4비트 양자화 설정
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",  # NormalFloat 4-bit
    bnb_4bit_use_double_quant=True,  # 추가 압축
    bnb_4bit_compute_dtype=torch.bfloat16
)

model = AutoModelForCausalLM.from_pretrained(
    "model-name-28b",
    quantization_config=bnb_config,
    device_map="auto"
)

# 메모리 절감: 56GB → 14GB (75% 절감!)
# 성능 손실: < 5%

GPTQ/AWQ 양자화

# GPTQ - 사전 양자화된 모델 로드
from auto_gptq import AutoGPTQForCausalLM

model = AutoGPTQForCausalLM.from_quantized(
    "model-name-28b-gptq",
    use_safetensors=True,
    device_map="auto"
)

# AWQ - 더 빠른 추론 속도
from awq import AutoAWQForCausalLM

model = AutoAWQForCausalLM.from_quantized(
    "model-name-28b-awq",
    fuse_layers=True
)

2. FlashAttention 2.0 

Attention 메커니즘의 메모리 효율을 개선할 수 있는데, 바로 한번에 처리하지 않고 나누어서 GPU메모리에 올려서 처리하는 방식으로 변경하여 개선할 수 있다.

# FlashAttention 활성화
model = AutoModelForCausalLM.from_pretrained(
    "model-name-28b",
    torch_dtype=torch.float16,
    attn_implementation="flash_attention_2",  # 핵심!
    device_map="auto"
)

# 효과:
# - 메모리: 30-50% 절감
# - 속도: 2-4배 향상
# - 긴 시퀀스(4k, 8k+)에서 특히 효과적

설치:

pip install flash-attn --no-build-isolation

3. Gradient Checkpointing (학습 시)

Gradient Checkpointing은 순전파 때 모든 중간 activation을 다 저장하지 않고, 일부 지점만 “체크포인트”로 저장해 두었다가, 역전파 때 필요한 구간의 순전파를 다시 계산(recompute)함으로써 메모리를 줄이는 기법이다.

# 학습 시 메모리 절감
model.gradient_checkpointing_enable()

# Trainer에서 사용
from transformers import TrainingArguments

training_args = TrainingArguments(
    gradient_checkpointing=True,
    gradient_checkpointing_kwargs={"use_reentrant": False},  # 더 안정적
    # ...
)

# 효과:
# - Activation 메모리: 70-80% 절감
# - 속도: 20-30% 느려짐
# - 트레이드오프 가치 있음

4. KV Cache 최적화

KV Cache는 디코딩 동안 각 레이어의 Key/Value 벡터를 계속 쌓아 두고, 새 토큰의 Query가 이전 K/V와만 어텐션 하도록 해서 이전 토큰의 연산을 다시 하지 않게 하는 구조다.

# 1. Past Key Values 재사용
outputs = model.generate(
    input_ids,
    max_new_tokens=100,
    use_cache=True,  # KV cache 사용
    return_dict_in_generate=True
)

# 2. PagedAttention (vLLM)
from vllm import LLM, SamplingParams

llm = LLM(
    model="model-name-28b",
    tensor_parallel_size=2,  # 2개 GPU
    gpu_memory_utilization=0.95,  # GPU 메모리 95% 사용
)

# PagedAttention으로 KV cache를 블록 단위로 관리
# - 메모리 파편화 방지
# - 배치 처리 효율 증가

7. LoRA/QLoRA (Fine-tuning 시)

LoRA/QLoRA는 “적은 파라미터만 학습해서, 훨씬 적은 메모리로 거대 LLM을 미세조정(finetune)하는 기법”이다.

from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

# 4비트 모델 로드
model = AutoModelForCausalLM.from_pretrained(
    "model-name-28b",
    load_in_4bit=True,
    quantization_config=bnb_config
)

# LoRA 준비
model = prepare_model_for_kbit_training(model)

# LoRA 설정
lora_config = LoraConfig(
    r=16,  # LoRA rank (낮을수록 메모리 적음)
    lora_alpha=32,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(model, lora_config)

# 효과:
# - 학습 가능 파라미터: 0.1-1% (28B → 28M~280M)
# - 메모리: Full fine-tuning 대비 70-80% 절감
# - 성능: Full fine-tuning과 비슷

메모리 최적화 조합 추천

# 🏆 최적의 조합 (28B 추론)
"""
1. INT4 양자화 (14GB)
2. FlashAttention (추가 30% 절감 → 10GB)
3. KV Cache 최적화 (2GB 절감 → 8GB)
→ RTX 4090 24GB에서 배치 2 가능!
"""

# 🏆 최적의 조합 (28B 학습)
"""
1. INT4 + QLoRA (15GB)
2. Gradient Checkpointing (추가 50% 절감)
3. DeepSpeed ZeRO-3 + CPU Offload
4. Gradient Accumulation (배치 크기 유지)
→ A100 40GB 1장으로 학습 가능!
"""

실전 예제: 28B 모델을 24GB GPU에서 실행한다면

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch

# 설정
model_name = "your-28b-model"

# 4비트 양자화 + FlashAttention
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.bfloat16
)

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    attn_implementation="flash_attention_2",
    torch_dtype=torch.bfloat16,
    low_cpu_mem_usage=True,
)

tokenizer = AutoTokenizer.from_pretrained(model_name)

# 추론
input_text = "Your prompt here"
inputs = tokenizer(input_text, return_tensors="pt").to("cuda")

with torch.no_grad():
    outputs = model.generate(
        **inputs,
        max_new_tokens=512,
        use_cache=True,
        do_sample=True,
        temperature=0.7
    )

print(tokenizer.decode(outputs[0], skip_special_tokens=True))

# 메모리 확인
print(f"\nGPU Memory: {torch.cuda.memory_allocated(0) / 1024**3:.2f}GB")

이 최적화 기법들을 조합하면 28B 모델을 소비자용 GPU(RTX 4090 24GB)에서도 실행할 수 있다.

 

 

 

반응형