1. 출발점: Fine-tuning을 더 싸게 할 수 없을까?
LLM을 fine-tuning하면 모든 파라미터를 업데이트해야 한다. 10B 모델이라면 수십 GB의 가중치를 전부 학습하고 저장해야 하는데, 이건 너무 비싸다.
여기서 나온 아이디어:
"pre-trained 가중치 W₀에서
fine-tuned 가중치 W_θ' 로의 변화량 ΔW 만
따로 표현할 수 있지 않을까?"
W_θ' = W₀ + ΔW
이건 수학적으로 항등식이라 틀릴 수가 없다. 어떤 두 행렬이든 차이는 항상 존재하니까.
2. 진짜 질문: ΔW가 low-rank일까?
단순히 ΔW를 저장하는 건 W_θ'를 통째로 저장하는 것과 다를 바 없다. 의미가 있으려면:
ΔW가 low-rank여야 한다
즉 ΔW를 분해했을 때 실제로 중요한 성분이 소수에 집중되어야 한다는 것. 이걸 이용하면 ΔW를 두 개의 작은 행렬 곱으로 근사할 수 있다:
ΔW ≈ BA (B ∈ ℝ^{d×r}, A ∈ ℝ^{r×k}, r ≪ min(d,k))
low-rank가 왜 말이 되는지는 SVD와 Eckart-Young 정리로 설명할 수 있는데,
이건 아래 포스팅에서 다룬다.
여기서는 "ΔW의 중요한 성분이 소수에 집중된다"는 관찰 자체에 집중한다.
SVD와 Eckart-Young 정리: low-rank란?
1. 행렬 = 공간 변환행렬 M에 벡터 x를 곱한다는 건 공간을 변환하는 것이다:y = MxSVD는 이 변환이 사실 세 단계로 분해된다는 것을 보여준다.2. SVD 직관: 회전-늘리기-회전M = U Σ Vᵀx → Vᵀx → ΣVᵀ
utto.tistory.com
3. 이게 수학적으로 증명된 건가?
결론부터 말하면 아니다.
| W_θ' = W₀ + ΔW | ✅ 항등식 |
| ΔW가 실제로 low-rank다 | ❌ 경험적 관찰 |
| low-rank ΔW로도 충분하다 | ❌ 실험적 확인 |
LoRA 논문이 실제로 한 것은:
- "ΔW가 low-rank 아닐까?" 라는 가설을 세우고
- BA 형태로만 업데이트되도록 제한해서 학습시켜봤더니 성능이 잘 나왔다
- 학습 후 ΔW의 singular value를 봤더니 실제로 집중되어 있었다
가설 → 실험 → 사후 관찰 순서다. 이론에서 출발한 게 아니다. Dropout, BatchNorm, Residual Connection도 모두 "해봤더니 됐다"가 먼저였고 이론이 나중에 따라왔다.
4. LoRA가 실제로 하는 것: SVD 분해가 아니다. Low-rank라고 믿고 시작한다.
많은 사람들이 LoRA를 SVD 분해라고 오해한다. 만약 그랬다면:
1. W₀로 full fine-tuning 진행
2. ΔW = W_θ' - W₀ 계산
3. ΔW를 SVD로 분해해서 상위 r개만 저장
이 방식은 결국 full fine-tuning을 다 해야 하기 때문에 학습 비용 절감이 없다.
LoRA는 다르다. "ΔW는 어차피 low-rank일 것이다"라는 가정을 처음부터 믿고 시작한다:
처음부터 ΔW = BA 라고 가정
B, A만 학습, W₀는 frozen
| 순서 | 학습 후 → 분해 | 처음부터 BA로 제한 |
| full fine-tuning | 필요 | 불필요 |
| 비용 절감 | 저장만 절감 | 학습 비용 자체를 절감 |
5. 왜 ΔW를 B, A로 나누는가?
ΔW를 그냥 하나의 행렬로 학습하면 파라미터 수가 W₀랑 똑같아서 절약이 없다.
BA로 나누면 두 가지가 동시에 달성된다:
첫째, 파라미터 절약
ΔW 통째로: d × k
BA로 나누면: d×r + r×k = r(d+k)
둘째, low-rank를 수학적으로 강제
BA의 rank ≤ r (항상 수학적으로 보장)
ΔW를 통째로 학습하면 학습 중 rank가 커질 수 있다. BA 형태로 제한하면 아무리 학습해도 rank가 r을 넘을 수 없다.
6. 구체적인 사이즈: 얼마나 작아지나?
LLaMA 계열 7B 모델의 attention 레이어 기준 (d=4096, k=4096, r=16):
W₀: 4096 × 4096 = 16,777,216 (약 16M 파라미터)
A: 16 × 4096 = 65,536
B: 4096 × 16 = 65,536
BA 합계: 131,072 (약 131K 파라미터)
→ 128배 절약
Forward pass에서 실제로 일어나는 것:
x (4096차원)
↓ A
(16차원) ← 병목 (bottleneck)
↓ B
(4096차원)
↓
W₀x + BAx
x가 4096차원 → A가 16차원으로 압축 → B가 다시 4096으로 복원하는 병목 구조다. 이 병목의 크기가 바로 r이고, r이 곧 "얼마나 low-rank로 제한할 것인가"를 결정한다.
7. ΔW = BA, 이게 곧 adapter다
ΔW = BA가 실제로 저장하고 배포하는 adapter 파일이다.
베이스 모델 (W₀) ← 모두가 공유, 한 번만 로드 (예: 20GB)
adapter (B, A) ← 태스크마다 다름, 수십 MB 수준
베이스 모델 하나에 adapter만 갈아끼우는 방식으로 여러 태스크를 저렴하게 운용할 수 있다.
8. r은 얼마로 해야 하나?
r은 완전히 하이퍼파라미터다. 이론적으로 최적값을 구하는 방법은 없다.
| 간단한 chat / 말투 변경 | 8~16 |
| instruction following | 16~32 |
| 코드, 수학, 추론 | 32~64 |
| 도메인 특화 (의료, 법률) | 64 |
실무에서는 r=8로 시작해서 성능이 부족하면 올리는 방식이 일반적이다. r을 두 배 올려도 성능이 별로 안 오른다면 bottleneck이 r이 아닌 다른 곳(데이터 품질, learning rate)에 있다는 신호다. 10B 모델이라면 r=16~32가 합리적인 시작점이다.
요약
1. 파인튜닝할때 ΔW는 low-rank다 (경험적 관찰)
2. low-rank니까 BA로 압축할 수 있다 (rank ≤ r 강제)
3. B, A는 작으니까 학습 리소스를 아낄 수 있다
4. 추론할 때는 W₀ + BA로 합쳐버리면 된다 (오버헤드 없음)
'Study > 머신러닝' 카테고리의 다른 글
| SVD와 Eckart-Young 정리: low-rank란? (0) | 2026.04.16 |
|---|---|
| Keras에서 Learning Rate 로그 남기기 (0) | 2020.06.16 |
| Tensorflow CuDNN RNN vs 그냥 RNN 비교 (0) | 2020.05.12 |
| Bazel을 이용해 Tensorflow Lite 빌드해보기 (1) | 2020.03.31 |
| Tensorflow2.0 / CUDA / Nvidia driver 호환 (0) | 2020.02.14 |
댓글