제6부 생성형 AI 플랫폼
6.4 NeMo III — SFT·PEFT(LoRA)
6.4.1 이 장에서 배우는 것 / 선수 지식
6.3에서 거대 모델 사전학습의 병렬화를 봤다. 그러나 6.1의 적응 스펙트럼이 말하듯, 대부분의 팀은 사전학습이 아니라 커스터마이즈를 한다 — 기성 모델을 자기 도메인·형식·행동에 맞추는 일. 이 장은 그 핵심 두 방법을 판다: SFT(지도 미세조정) — instruction-response로 모델 행동을 바꾸는 것 — 과 PEFT/LoRA — 전체 가중치 대신 소수 어댑터만 학습해 비용이 낮고 빠르게 적응하는 것.
핵심 통찰: LoRA는 "파인튜닝 중 가중치 변화가 저랭크(low-rank)"라는 관찰을 이용해, 거대 가중치를 동결하고 작은 두 행렬만 학습한다. 그 결과 옵티마이저 상태가 어댑터 크기로 줄어(6.3의 메모리 폭발 회피) 단일 GPU에서도 큰 모델을 적응시키고, 수 MB 크기의 어댑터를 여럿 만들어 교체하거나 동시에 서빙한다. 이것이 실무 커스터마이즈의 가성비 지점이다.
배우는 것:
- SFT — instruction-response 데이터 형식, 챗 템플릿(system/user/assistant), 손실 마스킹(응답 토큰에만 손실), 패킹(6.2).
- LoRA — 저랭크 분해
W + BA원리, 왜 통하나, 하이퍼파라미터(rank·alpha·target modules). - QLoRA·변형 — 양자화 베이스 + LoRA로 메모리 더 절감.
- 다중 어댑터 운영 — 작은 어댑터 여럿 저장·교체·동시 서빙(multi-LoRA), TRT-LLM/NIM 연결(5.9·6.7).
- SFT vs LoRA 트레이드오프 — 용량·메모리·데이터·여러 작업.
- 데이터 형식·LoRA config·학습·병합/서빙의 흐름(실습).
선수 지식: 6.1(적응 스펙트럼 — LoRA=스타일·SFT=능력), 6.2(데이터·패킹·토크나이저), 6.3(메모리·옵티마이저 상태), 5.9(다중 LoRA 서빙·양자화), 1.14(정밀도·QLoRA). 정렬(RLHF/DPO)은 6.5. 환경은 6.2.
정직: LoRA·SFT의 수학·원리는 안정적이지만, NeMo의 PEFT API·config 키·지원 변형(LoRA·DoRA·IA3 등)은 버전 의존이다. 이 장은 개념·형식·의사결정을 중심에 두고 코드는 한 형태를 예시 — 정확한 인자는 그 버전 문서로 확인. 수치는 환경 의존 개략값.
6.4.2 SFT — instruction으로 행동을 바꾼다
베이스(사전학습) 모델은 "다음 토큰 예측"만 안다. SFT는 instruction-response 쌍으로 학습해 모델이 지시를 따르고·특정 형식·어조로 답하게 만든다.
SFT 데이터(한 예시):
{
"system": "너는 정중한 고객 지원 봇이다.",
"user": "환불 절차를 알려줘.",
"assistant": "환불은 다음 절차로 진행됩니다: 1) ... 2) ..."
}
수천~수십만 개의 이런 예시로 학습 → 모델이 그 행동·형식을 습득한다.
챗 템플릿. 멀티턴·역할(system/user/assistant)을 모델이 구분하도록 특수 토큰·형식으로 감싼다:
<|system|>너는 정중한...<|end|>
<|user|>환불 절차를...<|end|>
<|assistant|>환불은 다음...<|end|>
템플릿은 모델·토크나이저에 맞춰야 한다(6.2 토크나이저 일관성). 학습·추론 템플릿 동일.
손실 마스킹(핵심). SFT는 응답(assistant) 토큰에만 손실을 계산하고 프롬프트(system/user) 토큰은 마스킹한다:
| 토큰 구간 | 손실 처리 |
|---|---|
| <|user|>환불 절차를... | 손실 계산 안 함 (입력, 마스킹) |
| <|assistant|>환불은... | 손실 계산 (모델이 생성할 부분) |
왜: 모델이 '사용자 입력을 생성하도록' 학습하면 안 됨. '응답을 생성'하도록만. 마스킹 누락 시: 모델이 프롬프트도 외워 비정상적인 행동·과적합.
패킹(6.2)은 SFT에서 특히 중요하다 — 짧은 예시가 많아 패딩 낭비가 크다. 패킹 + 경계 마스킹으로 효율을 높인다.
핵심: SFT의 세 불변식 — 챗 템플릿 일관성(학습=추론), 손실 마스킹(응답만), 토크나이저 일관성(6.2). 셋 중 하나라도 어기면 모델이 이상하게 행동한다. SFT는 전체 가중치를 학습(full fine-tune)하거나, LoRA로(6.4.3) 할 수 있다.
6.4.3 LoRA — 저랭크 어댑터
전체 SFT는 모든 가중치를 학습해 메모리·데이터가 크다(6.3). LoRA(Low-Rank Adaptation) 는 베이스 가중치를 동결하고, 각 가중치 행렬에 저랭크 보정만 더해 그것만 학습한다.
| 방식 | 학습 대상·구조 | 메모리 효과 |
|---|---|---|
| 원래(full) | W (d×k) 전체를 학습 | 옵티마이저 상태도 d×k (큼) |
| LoRA | W는 동결. 보정 ΔW = B·A 로 근사(저랭크). B (d×r), A (r×k), r ≪ d,k (rank r = 8·16·64 등). 순전파: h = W·x + (B·A)·x · (alpha/r). 학습: A, B만 (파라미터 수 = r·(d+k) ≪ d·k) | 학습 파라미터가 1~0.1%로 급감 → 옵티마이저 상태도 그만큼 → 메모리·시간↓↓ |
왜 통하나. 파인튜닝 중 일어나는 가중치 변화(ΔW)는 본질적으로 저랭크라는 관찰 — 적은 방향으로 충분히 적응된다. 그래서 r=8~64 같은 작은 랭크로도 full fine-tune에 근접한다(작업·데이터 의존).
메모리 이점(6.3과 연결):
| 방식 | 메모리 |
|---|---|
| full SFT | 가중치 + grad + 옵티마이저 상태(~16B/param 전체) → 큰 GPU·다수 |
| LoRA | 베이스는 동결(grad·옵티마이저 상태 없음, forward만). 학습 상태는 '어댑터'에만(전체의 ~0.1~1%) |
옵티마이저 상태 폭발(6.3)을 회피 → 단일 GPU에서도 큰 모델 적응 가능.
하이퍼파라미터:
| 파라미터 | 의미 | 튜닝 |
|---|---|---|
| rank r | 어댑터 용량(저랭크 차원) | 작으면 경량·용량↓, 크면 반대. 8~64 흔함 |
| alpha | 보정 스케일(alpha/r) | 보통 r에 비례. 학습률과 상호작용 |
| target modules | 어느 가중치에 LoRA(attention q·k·v·o, MLP) | 더 많이 = 용량↑·크기↑. 보통 attention+MLP |
| dropout | 어댑터 정규화 | 과적합 방지 |
6.4.4 QLoRA·변형 — 메모리 더 줄이기
QLoRA: 베이스 모델을 양자화(예: 4비트, 5.4~5.5) 해 동결하고, 그 위에 LoRA 어댑터(고정밀)를 학습한다.
다른 변형(가용성 버전 의존): DoRA(크기·방향 분해로 LoRA 개선), IA3(스케일 벡터만) 등. 핵심은 같다 — 소수 파라미터만 학습.
견해: 메모리가 부족하면 QLoRA(4비트 베이스 + LoRA)로 단일 GPU에서도 큰 모델을 적응한다. 품질이 최우선이고 메모리 여유가 있으면 일반 LoRA(BF16 베이스). 둘 다 full SFT보다 1~2자릿수 비용이 낮다.
6.4.5 다중 어댑터 — 여럿 만들고 동시에 서빙
LoRA 어댑터는 수 MB(전체 모델은 수십 GB)라, 작업마다 어댑터를 따로 만들어 운영하는 것이 강력하다.
작업에 맞는 어댑터를 '교체'(베이스는 공유, 재배포 불필요). 더 나아가: 한 서버가 '여러 어댑터를 동시에' 서빙한다(multi-LoRA, 5.9·6.7) — 요청마다 다른 어댑터를 적용해 한 베이스로 여러 맞춤 모델 제공.
운영 이점:
- 저장·배포: 어댑터가 작아 수십 개를 두고 교체. 베이스는 한 벌.
- multi-LoRA 서빙: TRT-LLM/NIM이 여러 어댑터를 동시에 적재해 요청마다 다른 어댑터 적용(5.9) → 한 GPU로 여러 맞춤 모델. 베이스 가중치·KV는 공유.
- 병합(merge) 옵션: 어댑터를 베이스에 병합(W ← W + (alpha/r)·BA — 순전파와 같은 스케일을 반드시 포함)해 단일 모델로 만들 수도 있다(교체 불가·오버헤드 0). 다중 어댑터를 쓸 거면 병합하지 않고 분리 유지.
견해: 여러 작업·테넌트를 한 베이스로 서비스하면 LoRA 다중 어댑터가 매우 효율적이다(메모리·운영). 단일 고정 작업이면 병합해 오버헤드를 없애도 된다. multi-LoRA 서빙은 SaaS·멀티테넌트 LLM의 핵심 패턴.
6.4.6 워크스루 — SFT/LoRA 흐름
데이터 형식 → LoRA config → 학습 → 서빙(개념, 인자 버전 의존).
# sft_data.jsonl — 한 줄에 한 예시(형식은 레시피 의존)
{"system":"...","conversations":[{"role":"user","content":"..."},{"role":"assistant","content":"..."}]}
# LoRA config(개념) — full SFT 대비 peft 섹션만 추가
model:
restore_from_path: base_model.nemo # 기성 베이스
peft:
peft_scheme: lora # lora / dora / none(=full SFT)
lora_tuning:
adapter_dim: 16 # rank r
alpha: 32 # 스케일
target_modules: [attention_qkv, attention_dense, mlp] # 어느 가중치에
adapter_dropout: 0.05
data:
train_ds: { file_names: [sft_data.jsonl], ... }
# 손실 마스킹·챗 템플릿은 데이터 처리에서(응답에만 손실)
optim: { lr: 1e-4 } # full SFT보다 보통 높은 LR 가능(어댑터만)
# 학습(스크립트·런처 버전 의존). 단일/소수 GPU로도 가능(LoRA 메모리 이점)
python megatron_gpt_finetuning.py --config-path ... --config-name lora_config
# 산출: 작은 어댑터 체크포인트(수 MB)
# 서빙: 어댑터를 TRT-LLM/NIM에 적용(multi-LoRA, 5.9·6.7) 또는 베이스에 병합
평가(잊지 말 것). 적응 후 반드시 평가: 목표 작업 지표 + 회귀(원래 능력이 망가지지 않았나, catastrophic forgetting). LoRA는 베이스를 동결해 망각이 덜하지만, SFT는 과적합·망각 위험이 더 크다 — 검증셋·일반 벤치로 확인.
6.4.7 중간 점검 (체크포인트)
- [ ] SFT의 데이터 형식(instruction-response·챗 템플릿)과 손실 마스킹(응답에만)·패킹의 필요를 안다.
- [ ] LoRA의
W + BA저랭크 원리, 왜 통하나(ΔW 저랭크), 메모리 이점(옵티마이저 상태 회피, 6.3)을 설명한다. - [ ] LoRA 하이퍼파라미터(rank·alpha·target modules)의 역할을 안다.
- [ ] QLoRA가 양자화 베이스로 메모리를 더 줄임을 안다.
- [ ] 다중 어댑터(교체·multi-LoRA 동시 서빙·병합)의 운영 이점을 설명한다.
- [ ] SFT vs LoRA를 용량·메모리·데이터·여러 작업으로 선택하고, 평가·망각 점검을 한다.
증상→의심: SFT 후 모델이 프롬프트도 생성·이상 → 손실 마스킹 누락(응답만 학습해야) → 마스킹 확인. 추론 출력 형식 깨짐 → 챗 템플릿 학습≠추론 불일치 → 동일 템플릿. LoRA 용량 부족(적응 약함) → rank·target modules↑. LoRA 과적합 → rank↓·dropout·데이터↑. 메모리 OOM(LoRA여도) → 베이스가 큼 → QLoRA(4비트 베이스). 적응 후 원래 능력 저하 → catastrophic forgetting → LoRA(동결)·데이터 혼합·평가. 여러 작업 배포 번거로움 → 다중 어댑터·multi-LoRA(5.9).
6.4.8 트레이드오프·대안
커스터마이즈 방법 선택(적응 스펙트럼 6.1의 중간).
| 방법 | 누구에게 | 비고 |
|---|---|---|
| 프롬프트/RAG | 지식·간단 적응 | 학습 불필요(6.1·6.9) |
| LoRA | 스타일·형식·도메인 어조·여러 작업 | 비용이 낮고 빠름, 작은 어댑터, multi-LoRA |
| QLoRA | 메모리 부족·단일 GPU | 4비트 베이스, 약간 품질 영향 |
| full SFT | 깊은 행동·능력·LoRA로 부족 | 비용이 크고 데이터·메모리 큼, 모델 1벌 |
| 정렬(RLHF/DPO) | 선호·안전(6.5) | SFT 위에 |
견해: LoRA가 커스터마이즈의 기본 출발점이다 — 비용이 낮고, 빠르고, 여러 어댑터를 운영하며, 망각이 덜하다. 스타일·형식·도메인 어조 적응은 대부분 LoRA로 충분. full SFT는 LoRA로 부족한 깊은 행동·능력 변화에만(데이터·메모리 감당될 때). 그리고 지식은 LoRA가 아니라 RAG로(6.1 반복). 메모리가 부족하면 QLoRA. 어느 쪽이든 평가·망각 점검 필수.
비엔비디아 정직: LoRA·QLoRA·SFT는 공개 연구·표준 기법으로 HF PEFT·기타 프레임워크도 제공한다(6.1 도구 중립). NeMo의 강점은 대규모 병렬(6.3)·multi-LoRA 서빙(TRT-LLM/NIM)·엔비디아 스택 통합이다. 중소 규모 LoRA는 HF PEFT가 매우 간편 — 규모·기존 스택으로 선택.
6.4.9 흔한 함정
- 손실 마스킹 누락. SFT에서 프롬프트에도 손실을 주면 모델이 사용자 입력을 생성하도록 학습 → 이상 행동·과적합. 응답 토큰에만 손실.
- 챗 템플릿 불일치. 학습 템플릿과 추론 템플릿(특수 토큰·형식)이 다르면 출력이 깨짐. 동일 템플릿·토크나이저(6.2).
- 지식을 LoRA/SFT로. 최신·사실 지식은 RAG로(6.1). 파인튜닝은 지식을 굳히고 환각을 안 준다.
- LoRA rank를 맹목적으로 키움. rank↑가 항상 좋은 것이 아니다 — 용량·크기↑, 과적합 위험. 작업·데이터로 8~64 탐색.
- target modules 부족. attention만 LoRA하면 용량 부족할 수 있다 — MLP 포함 등 조정. 너무 많으면 어댑터 커짐.
- 평가·망각 점검 생략. 적응 후 목표 지표 + 원래 능력(일반 벤치)을 봐야 catastrophic forgetting을 검출한다.
- 다중 어댑터인데 병합. 여러 작업을 쓸 거면 병합하면 교체 불가 — 분리 유지(multi-LoRA). 단일 고정이면 병합 OK.
- QLoRA 품질 미검증. 4비트 베이스는 약간의 품질 영향 가능 — 태스크 지표로 확인.
6.4.10 연습문제
연습 1 (손실 마스킹). SFT에서 손실 마스킹이 무엇이고 왜 필요한지, 마스킹을 빼면 어떤 일이 생기는지 설명하라.
정답
손실 마스킹: SFT 예시에서 응답(assistant) 토큰에만 손실을 계산하고 프롬프트(system/user) 토큰은 손실에서 제외(마스킹)하는 것. 필요 이유: 모델이 학습해야 할 것은 "주어진 입력에 응답을 생성하는 것"이지 "사용자 입력을 생성하는 것"이 아니다. 마스킹을 빼면: 모델이 프롬프트(사용자 발화)도 생성하도록 학습 → 추론 시 사용자 입력을 흉내 내거나, 프롬프트를 외워 과적합·이상 행동. 또 짧은 응답 대비 긴 프롬프트가 손실을 지배해 학습 신호가 왜곡. 따라서 응답에만 손실을 주어 "입력→응답 매핑"만 학습시킨다.
연습 2 (LoRA 원리). LoRA가 W + BA로 소수 파라미터만 학습하는 구조를 설명하고, (a) 왜 메모리(특히 옵티마이저 상태)가 급감하나, (b) 왜 저랭크로 충분한가?
정답
구조: 베이스 가중치 W(d×k)를 동결하고 보정 ΔW를 저랭크 곱 B(d×r)·A(r×k)로 근사(r≪d,k). 순전파는 Wx + (alpha/r)·BAx, 학습은 A·B만(파라미터 r·(d+k) ≪ d·k). (a) 메모리: 동결된 W는 그래디언트·옵티마이저 상태가 없다(forward만). 학습 상태(grad·Adam m·v·master)는 작은 어댑터에만 → 6.3의 ~16B/param 폭발이 전체가 아니라 ~1% 파라미터에만 적용 → 옵티마이저 상태 급감 → 단일 GPU 가능. (b) 저랭크 충분: 파인튜닝 중 가중치 변화 ΔW는 본질적으로 저랭크(적은 방향으로 적응)라는 관찰 — r=8~64로도 full fine-tune에 근접(작업 의존). 새 능력을 처음부터 만드는 것이 아니라 기존 표현을 조정하므로 저차원으로 족하다.
연습 3 (방법 선택). 다음에 LoRA·QLoRA·full SFT 중 무엇을 권하나: (a) 8-GPU로 13B 모델에 회사 어조 적응, (b) 단일 24GB 소비자 GPU로 큰 모델 파인튜닝, (c) LoRA로 시도했으나 깊은 도메인 추론이 안 늘어남.
정답
(a) LoRA — 어조·형식은 스타일 적응이라 저랭크로 충분, 8-GPU면 메모리 여유로 BF16 베이스 LoRA. 비용이 낮고 빠르고 여러 어댑터 운영 가능. full SFT는 과함. (b) QLoRA — 단일 24GB로 큰 모델은 베이스를 4비트로 동결해야 메모리에 들어감 + LoRA 어댑터 학습. 일반 LoRA는 BF16 베이스가 24GB 초과 가능. 약간 품질 영향은 검증. (c) full SFT(또는 rank·target 대폭↑ 먼저) — 깊은 도메인 추론은 저랭크 어댑터 용량을 넘는 능력 변화일 수 있어, LoRA rank·target modules를 키워보고도 부족하면 전체 가중치 SFT. 데이터·메모리 감당 확인. 단 '지식' 부족이면 SFT가 아니라 RAG(6.1).
연습 4 (다중 어댑터). 한 베이스 모델로 고객지원·코드·요약 세 작업을 서비스한다. (a) LoRA 다중 어댑터의 운영 이점, (b) multi-LoRA 서빙이 무엇인지, (c) 언제 병합하나?
정답
(a) 어댑터가 각 수 MB(베이스는 수십 GB)라 작업마다 어댑터를 따로 만들어 두고, 베이스는 한 벌만 공유 → 저장·배포 효율, 새 작업 추가가 작은 어댑터 추가로 끝남(베이스 재배포 불필요). (b) multi-LoRA 서빙: 한 서버가 여러 어댑터를 동시에 적재하고 요청마다 다른 어댑터를 적용(TRT-LLM/NIM, 5.9) → 한 GPU·한 베이스로 여러 맞춤 모델 제공, 베이스 가중치·KV 공유로 메모리 효율. 멀티테넌트 SaaS의 핵심. (c) 병합(W ← W + (alpha/r)·BA) 은 단일 고정 작업이라 어댑터 교체가 필요 없고 오버헤드를 0으로 하고 싶을 때. 여러 작업을 동시에 쓸 거면 병합하면 교체 불가하므로 분리 유지.
연습 5 (평가·망각). 코드 LoRA를 학습했더니 코딩은 늘었는데 일반 대화가 어색해졌다. (a) 무슨 현상이고, (b) LoRA가 full SFT보다 이것이 덜한 이유, (c) 진단·완화책은?
정답
(a) catastrophic forgetting(파국적 망각) — 새 작업(코드)에 적응하며 원래 능력(일반 대화)이 저하. (b) LoRA는 베이스 가중치를 동결하고 작은 어댑터만 학습하므로, 원래 표현을 크게 덮어쓰지 않아 망각이 full SFT보다 덜하다(full SFT는 전체 가중치를 바꿔 망각 위험↑). 단 LoRA도 rank·데이터·학습 강도에 따라 발생 가능. (c) 진단: 적응 후 일반 벤치마크·회귀셋으로 원래 능력 측정(목표 지표만 보지 말 것). 완화: ① 학습 데이터에 일반 대화 예시를 일부 혼합(코드만으로 학습 안 함), ② rank·학습률·에폭을 낮춰 과한 적응 방지, ③ 다중 어댑터로 분리(코드는 코드 어댑터, 일반은 베이스/다른 어댑터), ④ early stopping. 핵심: 적응은 목표+회귀 둘 다 평가.
6.4.11 요약 · 다음 장
- SFT는 instruction-response로 모델 행동을 바꾼다 — 챗 템플릿 일관성·손실 마스킹(응답에만)·토크나이저 일관성의 세 불변식과 패킹(6.2)이 핵심.
- LoRA는 베이스를 동결하고 저랭크 보정
W + BA만 학습한다(ΔW가 저랭크라 통함) → 옵티마이저 상태 폭발 회피(6.3)로 비용이 낮고 빠르며 단일 GPU도 가능. 하이퍼파라미터는 rank·alpha·target modules. - QLoRA(4비트 베이스 + LoRA)는 메모리를 더 줄인다. 다중 어댑터는 작업마다 수 MB 어댑터를 두고 교체·동시 서빙(multi-LoRA)·병합한다.
- LoRA가 커스터마이즈의 기본, full SFT는 깊은 능력 변화에만, 지식은 RAG로(6.1). 어느 쪽이든 목표+회귀(망각) 평가 필수.
- 정직: LoRA·QLoRA는 공개 표준 기법(HF PEFT도), config·변형은 버전 의존, 모든 적응은 평가와 함께.
다음 장: 6.5 NeMo IV — 정렬(RLHF·DPO). SFT로 instruction을 따르게 했으니, 이제 사람 선호·정책에 맞게 정렬한다. 보상 모델·RLHF의 구조, 더 단순한 DPO(선호 쌍 직접 최적화), 그리고 정렬이 유용성·안전성·형식을 어떻게 다듬는지를 실습으로 본다. 이로써 NeMo 학습 묶음(6.2~6.5)을 마무리한다.