LoRA 이해하기

LoRA 이해하기

모델을 파인튜닝할 때 가장 부담되는 것 중 하나가 '전체 파라미터를 다 갱신한다'는 점이에요. LoRA(Low-Rank Adaptation)는 이 부담을 크게 줄여서, 대형 모델을 더 적은 메모리로 빠르게 파인튜닝하게 해주는 기법입니다. 이 개념 가이드에서는 LoRA가 대략 어떻게 동작하는지 살펴봐요.

출처: LoRA - Hugging Face PEFT 공식문서

LoRA의 핵심 아이디어

LoRA는 가중치 갱신(weight update)을 저랭크 분해(low-rank decomposition)로 표현해요. 쉽게 말해, 큰 가중치 갱신 행렬을 두 개의 더 작은 행렬([update matrices])로 대신 표현하는 거죠.

  • 이 작은 행렬들만 새 데이터에 맞게 학습됩니다.
  • 원래의 가중치 행렬(original weight matrix)은 **얼어 있는 상태(frozen)**로 유지돼요.
  • 최종 결과를 만들 때는 원본 가중치와 적응(adapted)된 가중치를 합쳐서 사용합니다.

이 접근 방식은 몇 가지 장점이 있습니다.

  • 학습 가능한 파라미터 수를 크게 줄여서 파인튜닝이 훨씬 효율적이에요.
  • 사전학습 가중치가 그대로 남아 있으니, 그 위에 다양한 다운스트림 작업용의 가볍고 휴대 가능한 LoRA 모델을 여러 개 만들 수 있어요.
  • LoRA는 다른 파라미터 효율 기법과 직교(orthogonal)해서 여러 기법과 조합할 수 있어요.
  • LoRA로 파인튜닝한 모델의 성능은 전체 파라미터를 파인튜닝한 모델과 비슷합니다.
  • 어댑터 가중치가 기본 모델과 병합(merge)될 수 있어서 추론 지연(latency)이 추가되지 않아요.

원칙적으로 LoRA는 신경망의 어떤 가중치 행렬 부분집합에도 적용할 수 있습니다. 다만 트랜스포머 모델에서는 간단함과 추가 파라미터 효율을 위해 보통 어텐션 블록에만 적용해요. 이때 학습 가능한 파라미터 수는 저랭크 행렬의 크기에 따라 결정되는데, 그 크기는 주로 랭크 r과 원래 가중치 행렬의 형태가 좌우합니다.

LoRA 가중치를 기본 모델에 병합하기

LoRA는 훈련이 훨씬 가볍고 빠르지만, 추론할 때 기본 모델과 LoRA 모델을 따로 로딩하면 지연이 생길 수 있어요. 이 지연을 없애려면 merge_and_unload() 함수로 어댑터 가중치를 기본 모델과 병합하면 됩니다. 그러면 병합된 모델을 단독 모델처럼 쓸 수 있어요.

훈련 중에는 작은 가중치 행렬(A, B)이 분리되어 있지만, 훈련이 끝나면 이 가중치들이 하나의 동일한 새 가중치 행렬로 병합될 수 있기 때문이에요.

LoRA 유틸함수

  • merge_adapter(): LoRA 레이어를 기본 모델에 병합하되 PeftModel은 유지해요. 나중에 병합 해제(unmerge)·삭제·다른 어댑터 로딩 등을 하는 데 도움이 됩니다.
  • unmerge_adapter(): LoRA 레이어를 기본 모델에서 병합 해제하되 PeftModel은 유지해요.
  • unload(): 활성 LoRA 모듈을 병합하지 않고 기본 모델을 되돌려줘요. 모델을 원래 상태로 리셋하고 싶을 때 씁니다.
  • delete_adapter(): 기존 어댑터를 삭제해요.
  • add_weighted_adapter(): 여러 LoRA를 사용자가 지정한 가중치 방식에 따라 새 어댑터로 결합해요.

PEFT에서 흔히 쓰는 LoRA 파라미터

PEFT가 지원하는 다른 기법과 마찬가지로, LoRA로 모델을 파인튜닝하려면 다음 순서를 따릅니다.

  1. 기본 모델을 인스턴스화한다.
  2. LoRA 전용 파라미터를 정의하는 설정(LoraConfig)을 만든다.
  3. get_peft_model()로 기본 모델을 감싸 학습 가능한 PeftModel을 얻는다.
  4. 기본 모델을 훈련하듯 PeftModel을 훈련한다.

LoraConfig는 다음 파라미터로 LoRA 적용 방식을 제어합니다.

  • r: 갱신 행렬의 랭크(int). 랭크가 낮을수록 갱신 행렬이 작아져 학습 가능한 파라미터가 줄어요.
  • target_modules: LoRA 갱신 행렬을 적용할 모듈(예: 어텐션 블록).
  • lora_alpha: LoRA 스케일링 팩터.
  • bias: bias 파라미터를 훈련할지 여부. 'none', 'all', 'lora_only' 중 하나.
  • use_rslora: True로 설정 시 어댑터 스케일링 팩터를 lora_alpha/math.sqrt(r)로 쓰는 Rank-Stabilized LoRA를 사용(더 좋은 성능으로 검증됨). 아니면 원래 기본값 lora_alpha/r를 사용.
  • modules_to_save: LoRA 레이어 외에 학습 가능하게 설정하고 최종 체크포인트에 저장할 모듈 목록. 보통 파인튜닝 작업을 위해 랜덤 초기화된 모델의 커스텀 헤드가 여기 포함돼요.
  • layers_to_transform: LoRA로 변환할 레이어 목록. 지정하지 않으면 target_modules의 모든 레이어가 변환돼요.
  • layers_pattern: layers_to_transform이 지정됐을 때 target_modules의 레이어 이름을 매칭하는 패턴. 기본적으로 PeftModel은 흔한 레이어 패턴(layers, h, blocks 등)을 보고, 특이하거나 커스텀 모델에는 이 패턴을 씁니다.
  • rank_pattern: 랭크가 기본값 r과 다른 레이어 이름/정규식 → 랭크 매핑.
  • alpha_pattern: 알파가 기본값 lora_alpha와 다른 레이어 이름/정규식 → 알파 매핑.

LoRA 예시

LoRA 기법을 다양한 다운스트림 작업에 적용하는 예시는 다음 가이드를 참고하세요.

원래 논문은 언어 모델을 대상으로 하지만, 이 기법은 딥러닝 모델의 모든 dense 레이어에 적용 가능해요. 그래서 diffusion 모델에도 쓸 수 있으며, 예시는 Dreambooth fine-tuning with LoRA 태스크 가이드를 참고하세요.

초기화 옵션

LoRA 가중치 초기화는 LoraConfiginit_lora_weights 파라미터로 제어합니다. 기본적으로 PEFT는 참고 구현과 같은 방식으로, 즉 가중치 A를 Kaiming-uniform으로, 가중치 B를 0으로 초기화해서 항등 변환(identity transform)으로 만듭니다.

init_lora_weights="gaussian"도 전달할 수 있어요. 이름대로 가중치 A를 가우시안 분포로 초기화하고(가중치 B는 여전히 0), diffusers가 LoRA 가중치를 초기화하는 방식과 동일합니다.

QLoRA 훈련처럼 기본 모델을 양자화할 때는 LoftQ 초기화를 고려하세요. 양자화 성능을 개선하는 것으로 알려져 있어요. 핵심은 LoRA 가중치를 양자화 오차가 최소화되도록 초기화하는 것인데, 이 옵션을 쓰려면 기본 모델을 양자화하면 안 됩니다. 대신 이렇게 진행해요.

from peft import LoftQConfig, LoraConfig, get_peft_model

base_model = AutoModelForCausalLM.from_pretrained(...)  # don't quantize here
loftq_config = LoftQConfig(loftq_bits=4, ...)           # set 4bit quantization
lora_config = LoraConfig(..., init_lora_weights="loftq", loftq_config=loftq_config)
peft_model = get_peft_model(base_model, lora_config)

initialize_lora_weights=False로 설정하는 옵션도 있어요. 이 경우 LoRA 가중치가 항등 변환을 만들지 않도록 초기화되는데, 디버깅·테스트 용도로만 쓰고 그 외에는 쓰지 않는 게 좋습니다.

마지막으로, LoRA 구조는 매 forward pass마다 각 어댑터를 초기화 시점에 고정된 스칼라로 스케일링하며, 그 값은 랭크 r에 의존해요. 원래 LoRA는 lora_alpha/r 스칼라 함수를 쓰지만, Rank-Stabilized LoRA 연구는 lora_alpha/math.sqrt(r)를 쓰는 것이 어댑터를 안정화하고 높은 랭크에서 성능 개선 여지가 더 크다고 증명합니다. use_rslora=True로 설정하면 안정화된 랭크 스케일링 lora_alpha/math.sqrt(r)를 사용합니다.

더 알아보기 (Learn more)

  • PEFT 설치는 설치 가이드에서 확인하세요.
  • 트랜스포머 어텐션 구조 위에서 LoRA가 어떻게 동작하는지는 PEFT 태스크 가이드를 참고하세요.