본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 4장: LoRA의 원리와 실전 적용
2026년 1월 20일·AI / ML·

4장: LoRA의 원리와 실전 적용

LoRA(Low-Rank Adaptation)의 수학적 원리를 이해하고, 타겟 레이어 선택부터 하이퍼파라미터 튜닝까지 실전 적용법을 다룹니다.

15분617자7개 섹션
llmtrainingmlopsdata-engineering
공유
fine-tuning4 / 10
12345678910
이전3장: 데이터 품질 관리와 전처리 파이프라인다음5장: QLoRA로 소비자 GPU에서 파인튜닝하기

LoRA가 해결하는 문제

Full Fine-Tuning은 모델의 모든 파라미터를 업데이트합니다. 70억 파라미터 모델 기준으로 가중치(14GB), 그래디언트(14GB), 옵티마이저 상태(28GB)를 합치면 최소 56GB 이상의 GPU 메모리가 필요합니다. 이는 대부분의 연구자와 엔지니어에게 현실적인 장벽입니다.

LoRA(Low-Rank Adaptation of Large Language Models)는 2021년 Microsoft Research에서 발표한 논문에서 제안된 방법으로, 이 문제를 우아하게 해결합니다. 핵심 아이디어는 간단합니다. 파인튜닝 과정에서 실제로 변경되는 가중치의 변화량은 저랭크(Low-Rank) 행렬로 충분히 근사할 수 있다는 것입니다.

저랭크 분해의 수학적 원리

Transformer 모델의 각 레이어에는 큰 가중치 행렬 W가 존재합니다. 예를 들어, 어텐션 레이어의 Query 행렬 W_q의 크기는 d x d입니다 (d는 모델의 hidden dimension). Llama 3.1 8B 모델의 경우 d = 4096이므로, 하나의 행렬에만 약 1,600만 개의 파라미터가 있습니다.

Full Fine-Tuning에서는 이 행렬 전체를 업데이트합니다.

text
W' = W + Delta_W
 
여기서:
  W:       원본 가중치 행렬 (d x d)
  Delta_W: 학습된 변화량 (d x d)
  W':      업데이트된 가중치 (d x d)

LoRA의 핵심 통찰은 Delta_W가 실제로는 저랭크(Low-Rank)라는 것입니다. 즉, 전체 d x d 행렬을 직접 학습하는 대신, 두 개의 작은 행렬 A와 B의 곱으로 분해할 수 있습니다.

text
Delta_W = B x A
 
여기서:
  A: d x r 행렬 (다운 프로젝션)
  B: r x d 행렬 (업 프로젝션)
  r: 랭크 (rank), 보통 4 ~ 64
 
파라미터 수 비교 (d = 4096, r = 16):
  Full:  4096 x 4096 = 16,777,216
  LoRA:  4096 x 16 + 16 x 4096 = 131,072
  비율:  약 0.78%

이 분해를 통해 학습해야 할 파라미터 수가 원본의 1% 미만으로 줄어듭니다. 원본 가중치 W는 고정(Freeze)하고 A와 B만 학습하므로, 메모리 사용량이 극적으로 감소합니다.

초기화 전략

LoRA의 두 행렬은 특별한 방식으로 초기화됩니다. 행렬 A는 가우시안 분포로 초기화하고, 행렬 B는 영행렬(Zero Matrix)로 초기화합니다. 이렇게 하면 학습 시작 시 Delta_W = B x A = 0이 되어, 파인튜닝 이전의 원본 모델과 동일하게 동작합니다.

text
초기화:
  A ~ N(0, sigma^2)  (가우시안 랜덤)
  B = 0              (영행렬)
  
  Delta_W = B x A = 0 x A = 0
  W' = W + 0 = W     (원본 모델과 동일)

스케일링 팩터

LoRA에는 alpha라는 스케일링 하이퍼파라미터가 있습니다. 실제 가중치 업데이트는 다음과 같이 적용됩니다.

text
W' = W + (alpha / r) x B x A
 
alpha/r은 스케일링 팩터로, 학습률(Learning Rate)과 함께
LoRA 업데이트의 크기를 조절합니다.
 
일반적인 설정:
  alpha = 2 x r  (예: r=16이면 alpha=32)
  이 경우 스케일링 팩터 = 32/16 = 2

타겟 레이어 선택

LoRA를 모델의 어떤 레이어에 적용할지 결정하는 것은 성능에 직접적인 영향을 미칩니다. Transformer 모델의 주요 가중치 행렬은 다음과 같습니다.

text
Transformer 레이어의 가중치 행렬:
 
  Self-Attention:
    q_proj: Query 프로젝션
    k_proj: Key 프로젝션
    v_proj: Value 프로젝션
    o_proj: Output 프로젝션
 
  Feed-Forward Network (MLP):
    gate_proj: 게이트 프로젝션 (SwiGLU 활성화)
    up_proj:   업 프로젝션
    down_proj: 다운 프로젝션

어떤 레이어에 적용해야 하는가

원본 LoRA 논문에서는 주로 Query와 Value 프로젝션에 LoRA를 적용했습니다. 그러나 후속 연구들은 더 많은 레이어에 적용할수록 성능이 향상됨을 보여주었습니다.

text
타겟 레이어 설정별 성능 비교 (경험적):
 
  설정                      상대 성능    학습 파라미터
  q_proj, v_proj만          기준선       가장 적음
  q_proj, k_proj, v_proj    +2~3%       약간 증가
  모든 Attention 레이어      +3~5%       중간
  Attention + MLP 전체      +5~8%       가장 많음
Tip

실무에서는 모든 선형 레이어(Attention + MLP)에 LoRA를 적용하는 것이 가장 좋은 결과를 보이는 경우가 많습니다. PEFT 라이브러리에서는 target_modules="all-linear"로 간편하게 설정할 수 있습니다.

실전 LoRA 구현

Hugging Face의 PEFT 라이브러리를 사용하여 LoRA를 적용하는 전체 과정을 살펴봅니다.

LoRA 설정

python
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer
 
# 베이스 모델 로드
model_name = "meta-llama/Llama-3.1-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
 
# LoRA 설정
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=16,                          # 랭크
    lora_alpha=32,                 # 스케일링 팩터
    lora_dropout=0.05,             # 드롭아웃
    target_modules=[               # 타겟 레이어
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj"
    ],
    bias="none",                   # 바이어스 학습 여부
)
 
# LoRA 적용
model = get_peft_model(model, lora_config)
 
# 학습 파라미터 수 확인
model.print_trainable_parameters()
# 출력 예시:
# trainable params: 83,886,080 || all params: 8,114,212,864
# || trainable%: 1.0335

하이퍼파라미터 상세 설명

각 하이퍼파라미터의 역할과 권장 값을 정리합니다.

text
LoRA 하이퍼파라미터 가이드:
 
  r (랭크):
    역할: LoRA 행렬의 차원, 모델 용량을 결정
    범위: 4 ~ 256
    권장: 16 ~ 64 (작업 복잡도에 비례)
    팁: 작은 r에서 시작하여 성능이 부족하면 늘림
 
  lora_alpha (스케일링):
    역할: LoRA 업데이트의 크기를 조절
    범위: r ~ 4*r
    권장: 2 * r (r=16이면 alpha=32)
    팁: alpha/r 비율이 중요, 보통 1~2 사이
 
  lora_dropout:
    역할: 과적합 방지를 위한 드롭아웃
    범위: 0.0 ~ 0.2
    권장: 0.05 ~ 0.1
    팁: 데이터가 적을수록 높게 설정
 
  target_modules:
    역할: LoRA를 적용할 레이어 지정
    권장: "all-linear" 또는 주요 프로젝션 레이어 목록
    팁: 더 많은 레이어 = 더 높은 성능, 더 많은 메모리
 
  bias:
    역할: 바이어스 파라미터 학습 여부
    옵션: "none", "all", "lora_only"
    권장: "none" (대부분의 경우)

학습 실행

python
from transformers import TrainingArguments
from trl import SFTTrainer
from datasets import load_dataset
 
# 데이터셋 로드
dataset = load_dataset("json", data_files="training_data.jsonl")
 
# 학습 설정
training_args = TrainingArguments(
    output_dir="./lora-output",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    lr_scheduler_type="cosine",
    warmup_ratio=0.1,
    logging_steps=10,
    save_strategy="epoch",
    evaluation_strategy="epoch",
    fp16=True,
    report_to="wandb",
)
 
# SFTTrainer로 학습
trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset["train"],
    eval_dataset=dataset.get("validation"),
    tokenizer=tokenizer,
    max_seq_length=2048,
)
 
# 학습 시작
trainer.train()
 
# LoRA 어댑터 저장
trainer.save_model("./lora-adapter")

LoRA 어댑터 관리

LoRA의 큰 장점 중 하나는 어댑터가 원본 모델과 분리되어 있다는 것입니다. 이를 통해 여러 용도의 어댑터를 효율적으로 관리할 수 있습니다.

어댑터 로드와 추론

python
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
 
# 베이스 모델 로드
base_model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    torch_dtype="auto",
    device_map="auto"
)
 
# LoRA 어댑터 적용
model = PeftModel.from_pretrained(base_model, "./lora-adapter")
 
# 추론
tokenizer = AutoTokenizer.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct"
)
 
messages = [
    {"role": "user", "content": "Python의 GIL에 대해 설명해 주세요."}
]
inputs = tokenizer.apply_chat_template(
    messages, return_tensors="pt", add_generation_prompt=True
)
outputs = model.generate(
    inputs.to(model.device),
    max_new_tokens=512,
    temperature=0.7
)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

어댑터 병합 (Merge)

배포 시에는 LoRA 어댑터를 베이스 모델에 병합하여 추론 오버헤드를 제거할 수 있습니다.

python
# 어댑터를 베이스 모델에 병합
merged_model = model.merge_and_unload()
 
# 병합된 모델 저장
merged_model.save_pretrained("./merged-model")
tokenizer.save_pretrained("./merged-model")
text
어댑터 병합의 장단점:
 
  장점:
    - 추론 시 추가 연산 없음 (LoRA 연산 제거)
    - 배포 간소화 (단일 모델 파일)
    - 추론 속도 미세하게 향상
 
  단점:
    - 병합 후에는 어댑터 교체 불가
    - 원본 모델 + 어댑터 조합의 유연성 상실
    - 모델 전체 크기의 저장 공간 필요

여러 어댑터 전환

하나의 베이스 모델에 여러 LoRA 어댑터를 동적으로 전환할 수 있습니다. 이는 멀티 태넌트 환경에서 유용합니다.

python
from peft import PeftModel
 
# 베이스 모델에 첫 번째 어댑터 로드
model = PeftModel.from_pretrained(
    base_model,
    "./adapters/code-review",
    adapter_name="code_review"
)
 
# 두 번째 어댑터 추가
model.load_adapter("./adapters/tech-writing", adapter_name="tech_writing")
 
# 어댑터 전환
model.set_adapter("code_review")     # 코드 리뷰 모드
# model.set_adapter("tech_writing")  # 기술 문서 모드
 
# 어댑터 비활성화 (베이스 모델로 복귀)
model.disable_adapter_layers()

LoRA 변형과 확장

LoRA의 성공 이후 다양한 변형 기법이 등장했습니다.

DoRA (Weight-Decomposed Low-Rank Adaptation)

DoRA는 가중치를 크기(Magnitude)와 방향(Direction)으로 분해한 후, 방향 성분에만 LoRA를 적용합니다. 이를 통해 Full Fine-Tuning에 더 가까운 학습 역학(Learning Dynamics)을 달성합니다.

python
from peft import LoraConfig
 
# DoRA 설정 (PEFT 라이브러리 지원)
dora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules="all-linear",
    use_dora=True,  # DoRA 활성화
)

rsLoRA (Rank-Stabilized LoRA)

rsLoRA는 스케일링 팩터를 alpha/r 대신 alpha/sqrt(r)로 변경하여, 높은 랭크에서도 안정적인 학습을 가능하게 합니다.

python
# rsLoRA 설정
rslora_config = LoraConfig(
    r=64,
    lora_alpha=128,
    target_modules="all-linear",
    use_rslora=True,  # rsLoRA 활성화
)

정리

이번 장에서는 LoRA의 수학적 원리와 실전 적용 방법을 깊이 있게 다루었습니다.

  • LoRA는 가중치 변화량을 저랭크 행렬로 분해하여 학습 파라미터를 원본의 1% 미만으로 줄입니다.
  • 랭크(r), 스케일링(alpha), 타겟 레이어 등 주요 하이퍼파라미터의 역할과 권장 값을 정리했습니다.
  • 어댑터 로드, 병합, 전환 등 실전 운영 방법을 다루었습니다.
  • DoRA, rsLoRA 등 LoRA의 최신 변형 기법을 소개했습니다.

다음 장에서는 QLoRA를 사용하여 16GB 이하의 소비자용 GPU에서 대규모 모델을 파인튜닝하는 방법을 실습합니다. 4비트 양자화의 원리와 실전 구현을 상세하게 다룹니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#llm#training#mlops#data-engineering

관련 글

AI / ML

5장: QLoRA로 소비자 GPU에서 파인튜닝하기

4비트 양자화와 LoRA를 결합한 QLoRA의 원리를 이해하고, 단일 소비자 GPU에서 대규모 모델을 파인튜닝하는 실전 방법을 다룹니다.

2026년 1월 22일·14분
AI / ML

3장: 데이터 품질 관리와 전처리 파이프라인

파인튜닝 학습 데이터의 정제, 중복 제거, 토큰화, 패딩 전략 등 실전 전처리 파이프라인을 구축하는 방법을 다룹니다.

2026년 1월 18일·18분
AI / ML

6장: 학습 파이프라인 구축과 하이퍼파라미터 최적화

파인튜닝 학습 파이프라인의 전체 구조를 설계하고, 학습률, 배치 크기, 스케줄링 등 핵심 하이퍼파라미터를 최적화하는 전략을 다룹니다.

2026년 1월 24일·18분
이전 글3장: 데이터 품질 관리와 전처리 파이프라인
다음 글5장: QLoRA로 소비자 GPU에서 파인튜닝하기

댓글

목차

약 15분 남음
  • LoRA가 해결하는 문제
  • 저랭크 분해의 수학적 원리
    • 초기화 전략
    • 스케일링 팩터
  • 타겟 레이어 선택
    • 어떤 레이어에 적용해야 하는가
  • 실전 LoRA 구현
    • LoRA 설정
    • 하이퍼파라미터 상세 설명
    • 학습 실행
  • LoRA 어댑터 관리
    • 어댑터 로드와 추론
    • 어댑터 병합 (Merge)
    • 여러 어댑터 전환
  • LoRA 변형과 확장
    • DoRA (Weight-Decomposed Low-Rank Adaptation)
    • rsLoRA (Rank-Stabilized LoRA)
  • 정리