1. Dense LoRA
개요
dense_lora는 EulerForge에서 가장 기본적인 파인튜닝 전략입니다. 모델의 FFN(Feed-Forward Network) 및 어텐션 레이어에 있는 nn.Linear를 LoRALinear로 래핑하여, 원본 가중치는 동결(freeze)하고 소규모 저랭크(low-rank) 파라미터만 학습합니다.
- 적합한 용도: 단순 파인튜닝, 도메인 적응, 빠른 실험
- 호환 모델: Qwen, LLaMA, Gemma 3, Mixtral (모든 백본)
- 참조 프리셋:
configs/presets/qwen3.5_0.8b_dense_lora_sft.yml,configs/presets/gemma3_1b_dense_lora_sft.yml
사전 요구 사항
1. Where: 어디에 주입되는가?
BackboneAdapter가 모델 구조를 탐색하여 주입 대상을 찾습니다.
탐색 과정
BackboneAdapter.find_transformer_layers(model)— 모든 트랜스포머 블록을 찾습니다.- 각 블록 내에서
target_keywords에 매칭되는nn.Linear모듈을 탐색합니다. - (선택)
attn_lora.enabled: true이면 어텐션 프로젝션도 탐색합니다.
타겟 모듈
| 영역 | 타겟 키워드 | 대상 모듈 |
|---|---|---|
| FFN | gate_proj, up_proj, down_proj |
FFN 내부 Linear 레이어 |
| Attention | q_proj, v_proj |
어텐션 프로젝션 Linear |
관련 설정
backbone: qwen3 # 백본 어댑터 선택 (qwen3/qwen3.5/llama3/gemma3/mixtral)
injection:
target_keywords: [gate_proj, up_proj, down_proj] # FFN 타겟
start_layer: 0 # 적용 시작 레이어 (0 = 처음부터)
num_layers: 0 # 적용 레이어 수 (0 = 전체)
attn_lora:
enabled: true # 어텐션 LoRA 활성화
keywords: [q_proj, v_proj] # 어텐션 타겟
start_layer와 num_layers로 적용 범위를 제어할 수 있습니다. 예를 들어 28개 레이어 모델에서 start_layer: 14, num_layers: 14로 설정하면 후반 14개 레이어에만 LoRA를 적용합니다.
2. What: 무엇이 주입되는가?
DenseLoRAInjection 클래스가 inject_dense_lora_inplace()를 호출하여 모델을 in-place로 수정합니다.
변환 과정
각 타겟 nn.Linear가 LoRALinear로 래핑됩니다:
변환 전: nn.Linear(in_features, out_features)
변환 후: LoRALinear
├── base_layer: nn.Linear (frozen, 원본 가중치)
├── lora_A: Parameter(r, in_features) ← 학습 대상
├── lora_B: Parameter(out_features, r) ← 학습 대상
└── scaling: alpha / r
Forward 동작
[입력 x]
├── base_layer(x) → base_out (원본 출력, 동결)
│
└── dropout(x)
→ x @ lora_A.T → (batch, r) 저랭크 투영
→ result @ lora_B.T → (batch, out) 복원
→ * scaling → lora_out 스케일링
최종 출력 = base_out + lora_out
scaling = alpha / r(예: 96/48 = 2.0)으로 LoRA 출력의 크기를 조절합니다.dropout은 LoRA 브랜치 입력에만 적용됩니다.r=0이면 LoRA가 Identity로 동작합니다 (출력 변화 없음).
관련 설정
injection:
strategy: dense_lora # 전략 선택
lora_r: 48 # LoRA 랭크 (학습 파라미터 크기)
lora_alpha: 96 # 스케일링 팩터 (scaling = alpha/r)
lora_dropout: 0.05 # LoRA 브랜치 드롭아웃
파라미터 가이드:
- lora_r: 클수록 표현력 증가, 메모리/연산 비용도 증가. 일반적으로 16~64.
- lora_alpha: 보통 lora_r의 2배로 설정 (scaling = 2.0).
- lora_dropout: 과적합 방지. 0.0~0.1 범위 권장.
3. When: 언제 어떤 파라미터를 훈련하는가?
dense_lora는 가장 단순한 단일 페이즈 스케줄을 사용합니다.
페이즈 구성
training:
phases:
- step: 0
trainable: ["lora", "attn_lora"]
스텝 0부터 훈련 끝까지 lora(FFN LoRA)와 attn_lora(어텐션 LoRA) 그룹만 학습합니다.
타임라인
Step 0 ──────────────────────────────────────> Step 5000
│
└── [lora + attn_lora 훈련]
base_layer: frozen (동결)
lora_A, lora_B: trainable (학습)
- 단일 페이즈이므로 페이즈 전환이 없습니다.
- 옵티마이저 재구축도 발생하지 않습니다.
router그룹은 사용하지 않습니다 (dense_lora에는 라우터가 없음).
4. 전체 설정 파일 해설
configs/presets/qwen3.5_0.8b_dense_lora_sft.yml 전문:
# ── 모델 정보 ──
device: cuda:0 # GPU 디바이스
backbone: qwen3 # [Where] 백본 어댑터: Qwen3Adapter
model_name: Qwen/Qwen3.5-0.8B-Base # HuggingFace 모델 ID
# ── 인젝션 설정 ──
injection:
strategy: dense_lora # [What] Dense LoRA 전략
lora_r: 48 # [What] LoRA 랭크
lora_alpha: 96 # [What] 스케일링 팩터 (96/48 = 2.0)
lora_dropout: 0.05 # [What] LoRA 드롭아웃
target_keywords: [gate_proj, up_proj, down_proj] # [Where] FFN 타겟 키워드
start_layer: 0 # [Where] 시작 레이어
num_layers: 0 # [Where] 0 = 전체 레이어
attn_lora: # [Where] 어텐션 LoRA
enabled: true
keywords: [q_proj, v_proj]
# ── 훈련 설정 ──
training:
type: sft # SFT (Supervised Fine-Tuning)
phases: # [When] 페이즈 스케줄
- step: 0
trainable: ["lora", "attn_lora"] # 스텝 0부터 LoRA만 훈련
lr: 1.0e-5 # 학습률
weight_decay: 0.01 # 가중치 감쇠
warmup_steps: 200 # 학습률 워밍업 스텝
max_train_steps: 5000 # 최대 훈련 스텝
batch_size: 4 # 배치 크기
grad_accum_steps: 4 # 그래디언트 축적 스텝 (유효 배치 = 4*4 = 16)
max_grad_norm: 1.0 # 그래디언트 클리핑
log_steps: 50 # 로그 출력 간격
save_steps: 1000 # 체크포인트 저장 간격
val_steps: 500 # 검증 간격
5. 실행하기
기본 실행
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_sft.yml \
--set data.format=raw \
--set data.task=sft \
--set data.path=data/sft_10k_raw.jsonl \
--set data.max_length=512
설정 오버라이드
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_sft.yml \
--set data.format=raw \
--set data.task=sft \
--set data.path=data/sft_10k_raw.jsonl \
--set data.max_length=512 \
--set training.lr=2e-5 \
--set injection.lora_r=32 \
--set training.max_train_steps=10000
설정 검증만
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_sft.yml \
--validate-only
프리플라이트 검사
모델을 로드하고 인젝션을 적용한 뒤, 페이즈 그룹별 파라미터 수를 확인합니다. 훈련은 수행하지 않습니다.
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_sft.yml \
--preflight
디버그 모드
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_sft.yml \
--debug \
--debug-trainable-names \
--debug-every 10 \
--set data.format=raw \
--set data.task=sft \
--set data.path=data/sft_10k_raw.jsonl \
--set data.max_length=512
6. 체크포인트 저장 구조
훈련 완료 시 체크포인트에는 base weight + LoRA 파라미터가 저장됩니다.
체크포인트 구조:
├── layer.N.mlp.gate_proj.base_layer.weight ← 원본 weight (freeze 상태)
├── layer.N.mlp.gate_proj.lora_A ← LoRA 학습 파라미터
├── layer.N.mlp.gate_proj.lora_B ← LoRA 학습 파라미터
├── layer.N.mlp.up_proj.base_layer.weight
├── layer.N.mlp.up_proj.lora_A
├── layer.N.mlp.up_proj.lora_B
├── ...
└── (attn_lora 활성화 시 q_proj, v_proj도 동일 패턴)
Bench 로딩
eulerforge bench에서 dense_lora 체크포인트를 로드하면:
- LoRA를 base에 병합:
merged = base_w + (lora_B @ lora_A) * (alpha / r) - 결과는 dense 모델 (LoRA 없는 표준 모델)
- 별도의 MoE 구조 없이 일반 HF 모델로 추론
7. 디버깅 및 트러블슈팅
| 증상 | 원인 | 해결 |
|---|---|---|
| "No trainable parameters" | target_keywords가 모델의 실제 레이어 이름과 불일치 |
--debug-trainable-names로 파라미터 이름 확인 |
| "LoRA layers will act as Identity" | lora_r: 0으로 설정됨 |
lora_r을 1 이상으로 설정 |
| OOM (메모리 부족) | 모델 크기 대비 VRAM 부족 | model.load_precision.mode: int4 추가 (4bit QLoRA), batch_size 감소, lora_r 감소 |
| "dense_lora typically has no router params" 경고 | 페이즈에 router 그룹 포함 |
trainable에서 router 제거 |
다음 단계
- 멀티태스크 적응형 LoRA가 필요하다면 → Mixture LoRA 튜토리얼
- Dense 모델을 MoE로 변환하려면 → MoE Expert LoRA 튜토리얼
- DPO 훈련을 원한다면 → DPO 훈련 가이드