LLM 파인튜닝 VRAM을 80% 절감하는 LoRA와 QLoRA 저순위 어댑터 아키텍처

LLM 파인튜닝 VRAM을 80% 절감하는 LoRA와 QLoRA 저순위 어댑터 아키텍처
LLM 파인튜닝 VRAM을 80% 절감하는 LoRA와 QLoRA 저순위 어댑터 아키텍처

수십억에서 수백억 개 파라미터를 가진 대규모 언어 모델(LLM)을 사내 도메인 특화 데이터로 학습시키거나 지시 미세조정(Instruction Tuning)할 때 가장 큰 장벽은 전체 파라미터 파인튜닝(Full Fine-Tuning)에 요구되는 천문학적인 GPU VRAM 메모리입니다.

16비트 부동소수점(FP16/BF16) 기준 70B 모델은 모델 가중치만 140GB를 차지하지만, 옵티마이저 상태(AdamW 모멘텀 및 분산), 그래디언트, 활성화 텐서(Activations)를 포함하면 700GB 이상의 엔터프라이즈 멀티 GPU 클러스터가 필수적이었습니다. 본 글에서는 사전 학습된 가중치를 동결(Freeze)하고 저순위 분해 행렬만을 훈련시키는 LoRA(Low-Rank Adaptation)와 4비트 NormalFloat 양자화 및 이중 양자화를 결합한 QLoRA 아키텍처의 수학적 메커니즘을 분석합니다.

1. LoRA의 본질적 순위(Intrinsic Rank) 가설과 행렬 분해

기존 전이 학습 연구(Aghajanyan et al.)에 따르면, 거대 모델의 가중치 행렬이 매우 높은 차원을 가지더라도 특정 다운스트림 태스크에 적응하는 동안 발생하는 가중치 변화량($\Delta W$)은 사실 매우 낮은 차원의 저순위 부분공간(Low Intrinsic Dimension)에 존재합니다.

  • 사전 학습 가중치 동결: $W_0 \in \mathbb{R}^{d \times k}$는 역전파(Backpropagation) 과정에서 가중치와 옵티마이저 상태를 전혀 업데이트하지 않고 고정합니다.
  • 저순위 분해($B \cdot A$): 가중치 변화량 $\Delta W$를 직접 학습하는 대신, 작은 랭크 $r$ ($r \ll \min(d, k)$, 통상 $r=8 \sim 64$)을 갖는 두 행렬 $A \in \mathbb{R}^{r \times k}$와 $B \in \mathbb{R}^{d \times r}$의 곱으로 분해합니다.
  • 스케일링 및 초기화: $A$는 가우시안 정규분포로, $B$는 0으로 초기화하여 학습 시작 시점에는 $\Delta W = 0$이 되도록 보장하며, 최종 순전파 시 $h = W_0 x + \frac{\alpha}{r} B A x$ 형태로 계산합니다.

3. 결론 및 기대 성과

QLoRA(Dettmers et al., NeurIPS 2023)는 LoRA의 개념을 한 단계 더 확장하여 기본 모델을 4비트로 양자화하면서도 16비트 전체 파인튜닝 수준의 성능을 100% 보존하는 3대 핵심 기술을 도입했습니다.

# QLoRA 어댑터 순전파 및 역양자화 개념 구현체
import torch
import torch.nn as nn

class QLoRALinear(nn.Module):
    def __init__(self, in_features: int, out_features: int, rank: int = 16, lora_alpha: float = 32.0):
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.rank = rank
        self.scaling = lora_alpha / rank

        # 1. 동결된 기본 가중치: 4비트 NormalFloat(NF4)로 압축 보관 (VRAM 대폭 절감)
        self.register_buffer("weight_nf4", torch.zeros(out_features, in_features // 2, dtype=torch.uint8))
        self.register_buffer("absmax_scale", torch.zeros(out_features, dtype=torch.float16))

        # 2. 학습 가능한 LoRA 저순위 어댑터 행렬 (FP16/BF16 고정밀 학습)
        self.lora_A = nn.Parameter(torch.randn(rank, in_features) * (1.0 / rank))
        self.lora_B = nn.Parameter(torch.zeros(out_features, rank))

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # 순전파 시점에만 NF4 가중치를 임시 역양자화(Dequantize)하여 행렬곱 수행
        dequantized_weight = dequantize_nf4(self.weight_nf4, self.absmax_scale)
        base_out = torch.matmul(x, dequantized_weight.t())

        # 저순위 어댑터 패스: (x * A^T) * B^T * scaling
        lora_out = (torch.matmul(x, self.lora_A.t()) @ self.lora_B.t()) * self.scaling
        return base_out + lora_out

위 아키텍처에서 NF4(4-bit NormalFloat)는 정규분포를 따르는 가중치 값의 정보 이론적 엔트로피를 보존하는 최적의 비균등 양자화 포맷이며, 이중 양자화(Double Quantization)는 64개 블록마다 존재하는 양자화 상수(Scale) 자체를 추가 양자화하여 파라미터당 메모리 오버헤드를 0.37비트 수준으로 억제합니다.

💡 프로덕션 QLoRA 튜닝 3대 실무 체크리스트:
1. 타깃 모듈(Target Modules) 선정: 초기 LoRA는 Attention의 Q, V 투영 레이어에만 적용했으나, 최신 연구에 따르면 MLP의 Gate, Up, Down 레이어를 포함한 모든 선형 레이어(All Linear)에 적용할 때 랭크(r)가 작더라도 전역 표현력이 극대화됩니다.
2. 어댑터 병합(Weight Merging) 무손실 배포: 추론 단계에서는 $W = W_0 + \frac{\alpha}{r}BA$를 사전에 단일 텐서로 병합(Merge)하여 추가 지연 시간(Latency) 없이 기존 서빙 엔진(vLLM, TGI)에 즉시 배포해야 합니다.
3. 페이징된 옵티마이저(Paged Optimizers) 활용: 긴 시퀀스 배치로 인해 순간적인 VRAM 스파이크 발생 시, CUDA 통합 메모리(Unified Memory)를 통해 페이지를 CPU RAM으로 스왑하여 OOM 크래시를 원천 방어합니다.

3. 결론 및 기대 성과

LoRA와 QLoRA는 수억 원대 하드웨어 클러스터가 필요했던 대규모 언어 모델 파인튜닝을 단일 상용 워크스테이션(A100 80GB 또는 RTX 4090 24GB) 영역으로 끌어내린 패러다임 전환입니다. 어댑터 기반 경량화와 정보 보존 양자화 설계를 통해 기업은 최소한의 인프라 비용으로 안전하게 사내 독자 지능 모델을 내재화할 수 있습니다.


출처 및 1차 공식 기술 문서

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다