319 lines
15 KiB
Markdown
319 lines
15 KiB
Markdown
# "처음부터 다시 시작" 시나리오 완전 분석
|
||
|
||
**작성일**: 2026-02-26
|
||
**역할**: "처음부터 제대로 다시" 옹호자
|
||
**결론**: ✅ **1B SFT 재학습 강력 권장 (40분), 3B 전환은 병렬 준비**
|
||
|
||
---
|
||
|
||
## 1. 현재 접근법의 근본적 한계
|
||
|
||
### 1.1 발견된 버그/문제가 가중치에 미친 영향
|
||
|
||
지금까지 발견된 문제들을 정리하면:
|
||
|
||
| # | 버그/문제 | 가중치 오염 정도 | 제거 가능? |
|
||
|---|-----------|-----------------|-----------|
|
||
| 1 | 프롬프트 포맷 불일치 (`### 질문:` vs `<\|user\|>`) | ❌ 가중치 무관 (추론 버그) | 추론 코드만 수정 |
|
||
| 2 | Dynamic padding 미작동 (4096 고정 패딩) | 🟡 간접 영향 — 학습 효율 저하로 실질 epoch 부족 | 재학습 필요 |
|
||
| 3 | 트렁케이션 시 EOS 손실 (0.04%) | 🟢 미미 (61/159K 샘플) | 코드 이미 수정됨 |
|
||
| 4 | `</s>` 리터럴 오염 데이터 113건 | 🟡 EOS 경계 혼란 유발 | 데이터 필터 필요 |
|
||
| 5 | Output 내 Q/A 마커 ~550건 | 🟡 자체 루프 패턴 학습 | 데이터 필터 필요 |
|
||
| 6 | OpenOrca 5배 업샘플링 → 과적합 | 🔴 가중치에 깊이 각인 | 재학습 필요 |
|
||
| 7 | Val split 없음 → 과적합 감지 불가 | — | 재학습 시 추가 |
|
||
| 8 | ~2 epoch만 학습 (업계 표준 3-5) | 🔴 underfitting | 재학습 필요 |
|
||
| 9 | 짧은 output 10.4% (50자 미만) | 🟡 EOS 타이밍 학습 불안정 | 데이터 필터 필요 |
|
||
|
||
### 1.2 "오염된 학습"의 가중치 잔류 여부
|
||
|
||
**결론: 부분적으로 남아있고, 완전 제거 불가능.**
|
||
|
||
SFT는 base model 위에 얇은 layer를 미세조정한 것이 아니라 **전체 가중치를 업데이트**한다. 5000 steps × lr=2e-5로 학습된 gradient update는 모든 layer에 분포되어 있으며:
|
||
|
||
- OpenOrca 5배 업샘플링으로 인해 해당 소스의 패턴이 **과도하게 각인**
|
||
- Q/A 마커 오염 데이터(550건)의 패턴도 가중치에 분산 저장
|
||
- `</s>` 리터럴이 포함된 113건이 EOS 토큰 예측 확률 분포를 왜곡
|
||
|
||
이들은 추가 학습(continual training)으로 "덮어쓸" 수는 있지만, **기존 오염을 정확히 역전시키는 것은 불가능**. 추가 학습은 새로운 gradient로 기존 가중치를 수정하지만, 이미 학습된 잘못된 패턴의 흔적(특히 low-rank subspace에서)은 완전히 사라지지 않는다.
|
||
|
||
### 1.3 반복 퇴화 17.7%: 파라미터 문제 vs 가중치 문제
|
||
|
||
수정 후 반복률 변화를 보면:
|
||
|
||
```
|
||
포맷 불일치 상태: 57% → 포맷 수정만으로 → 30.7% → +추론 파라미터 → 17.7%
|
||
```
|
||
|
||
**분석:**
|
||
- 57% → 30.7% (포맷 수정): **추론 버그** — 가중치 무관 ✅
|
||
- 30.7% → 17.7% (rep_penalty + no_repeat_ngram): **추론 파라미터** — 가중치 무관 ✅
|
||
- **잔여 17.7%**: 이것이 **가중치 수준의 문제**
|
||
|
||
17.7%의 구성:
|
||
- 코드 설명 시 알파벳 나열 반복 (샘플 #2: 30.5%)
|
||
- 리스트형 답변에서 유사 항목 반복 (샘플 #4: 21.3%, #7: 24.4%, #8: 23.8%)
|
||
- 단순 사실 답변은 정상 (샘플 #1: 0.0%, #9: 13.3%)
|
||
|
||
**결론: 17.7%는 가중치 수준 문제.** 원인:
|
||
1. 학습 데이터 자체의 반복 패턴 (57건 직접 반복 + 수백 건 간접)
|
||
2. 2 epoch의 underfitting으로 EOS 생성 신뢰도 부족
|
||
3. OpenOrca 과잉 대표로 인한 다양성 결핍
|
||
|
||
---
|
||
|
||
## 2. 처음부터 다시 한다면: 구체적 개선 사항
|
||
|
||
### 2.1 SFT 데이터 파이프라인
|
||
|
||
| 항목 | 현재 | 재시작 시 |
|
||
|------|------|----------|
|
||
| 포맷 | `<\|user\|>/<\|assistant\|>` ✅ | 동일 유지 |
|
||
| EOS 처리 | 트렁케이션 시 손실 가능 | **코드 이미 수정됨** (`response_ids[-1] = eos_token_id`) |
|
||
| Dynamic padding | 미작동 (고정 4096) | **코드 이미 수정됨** (가변 길이 반환) |
|
||
| 품질 필터 | 기본 (50자, 30% 한글) | **강화**: 80자, 40% 한글, EOS/Q&A 오염 제거, 5-gram 반복 필터 |
|
||
| Val split | 없음 | **5% val split** (prepare_sft_data.py에 이미 구현됨) |
|
||
| 가중치 샘플링 | OpenOrca 5.0× | **OpenOrca 2.0×** (이미 수정됨) |
|
||
| 예상 데이터 | 159K | **~120-130K** (필터링 후) |
|
||
|
||
**핵심 변경: `prepare_sft_data.py`를 다시 실행하면 된다.** 코드에 이미 enhanced filter와 수정된 가중치가 반영되어 있다.
|
||
|
||
### 2.2 학습 하이퍼파라미터
|
||
|
||
| 파라미터 | 현재 | 재시작 시 | 근거 |
|
||
|---------|------|----------|------|
|
||
| max_steps | 5,000 (~2 epoch) | **7,500-10,000** (3-4 epoch) | 업계 표준 3-5 epoch |
|
||
| lr | 2e-5 | **2e-5** 유지 | 업계 표준, loss curve 안정 |
|
||
| warmup | 150 (3%) | **225-300** (3%) | steps 증가에 비례 |
|
||
| NEFTune alpha | 10.0 | **10.0** 유지 | 159K 데이터에 적합 |
|
||
| val_data | 없음 | **val.jsonl** 전달 | 과적합 모니터링 |
|
||
| save_interval | 500 | **500** 유지 | best checkpoint 선택 가능 |
|
||
|
||
### 2.3 추가 고려사항
|
||
|
||
- **`<|user|>` / `<|assistant|>` 특수 토큰 등록**: 현재 서브워드 분할됨. 단일 토큰으로 등록하면 더 robust하나 base model 재학습 필요 → **SFT에서는 현행 유지, 3B에서 반영**
|
||
- **Repetition penalty loss (Unlikelihood Training)**: 중기 옵션. 재시작 1차에는 데이터 품질 개선만으로 충분할 것
|
||
|
||
---
|
||
|
||
## 3. 업계 최고 수준 SFT 파이프라인 비교
|
||
|
||
### 3.1 주요 프레임워크 비교
|
||
|
||
| 기능 | 현 프로젝트 (수정 후) | LLaMA-Factory | TRL SFTTrainer | Axolotl |
|
||
|------|---------------------|---------------|----------------|---------|
|
||
| Completion-only loss | ✅ (labels=-1) | ✅ | ✅ (DataCollator) | ✅ |
|
||
| Dynamic padding | ✅ (수정됨) | ✅ | ✅ | ✅ |
|
||
| Sample packing | ❌ | ✅ | ✅ (`packing=True`) | ✅ |
|
||
| EOS 보장 | ✅ (수정됨) | ✅ | ✅ | ✅ |
|
||
| Val monitoring | ✅ (구현됨) | ✅ | ✅ | ✅ |
|
||
| Flash Attention | ✅ (64-align) | ✅ | ✅ | ✅ |
|
||
| NEFTune | ✅ | ✅ | ✅ | ✅ |
|
||
|
||
### 3.2 `packing=True` + `completion_only_loss` 분석
|
||
|
||
**Sample Packing**: 여러 짧은 샘플을 하나의 시퀀스에 연결하여 패딩 완전 제거.
|
||
|
||
```
|
||
Before packing (dynamic padding):
|
||
[sample1 (200 tok)] [pad pad pad ... (312 pad)] = 512 total
|
||
[sample2 (480 tok)] [pad pad pad ... (32 pad)] = 512 total
|
||
|
||
After packing:
|
||
[sample1 (200 tok)][sample2 (480 tok)][pad ... (344)] = 1024 total
|
||
→ 2 samples in 1 sequence, less padding waste
|
||
```
|
||
|
||
**현 프로젝트 적용 가능성:**
|
||
- 평균 시퀀스 ~500 토큰이므로 packing 효과 **매우 큼** (4096 대비 88% 절약 → packing으로 추가 20-30% 절약)
|
||
- 그러나 구현 복잡도 높음: attention mask에 sample boundary 처리 필요
|
||
- **권장: 현재 dynamic padding만으로도 충분한 개선. Packing은 3B 또는 TRL 전환 시 도입.**
|
||
|
||
### 3.3 현 프로젝트에 바로 적용 가능한 것
|
||
|
||
1. ✅ **이미 적용됨**: Dynamic padding, EOS 보장, completion-only loss, NEFTune
|
||
2. 🟡 **미적용이나 중요도 낮음**: Sample packing (구현 복잡, 현재 효율 충분)
|
||
3. 🟡 **미적용이나 고려 가치**: TRL SFTTrainer 전환 (커스텀 LLM 클래스 호환성 확인 필요)
|
||
|
||
---
|
||
|
||
## 4. 3B 모델로의 전환 타이밍
|
||
|
||
### 4.1 1B 재학습 vs 바로 3B
|
||
|
||
| 기준 | 1B 재학습 | 바로 3B |
|
||
|------|----------|---------|
|
||
| 소요 시간 | ~40분 SFT | ~26시간 pretrain + ~2시간 SFT |
|
||
| 리스크 | 낮음 (검증된 파이프라인) | 중간 (새 아키텍처 설정 필요) |
|
||
| 기대 품질 | 반복률 17.7% → **5-8%** 예상 | 반복률 **2-5%** 예상 |
|
||
| ko_ifeval | 20-30% 예상 | 35-45% 예상 |
|
||
| 학습 검증 | 즉시 가능 | 26시간 후에야 확인 가능 |
|
||
|
||
### 4.2 Chinchilla Scaling Law 분석
|
||
|
||
```
|
||
Chinchilla 최적 학습 데이터 = 20 × 파라미터 수
|
||
|
||
1B 모델: 20 × 1B = 20B tokens (현재 ~8.91B → 부족하지만 SFT에는 충분)
|
||
3B 모델: 20 × 3B = 60B tokens (현재 데이터 ~150B → 충분)
|
||
70 × 3B = 210B tokens (최적 → 150B로 71% 수준)
|
||
```
|
||
|
||
**현재 150B tokens 데이터는 3B 학습에 충분하다** (Chinchilla 최소 기준의 2.5배).
|
||
|
||
### 4.3 3B가 반복 퇴화를 구조적으로 덜 겪는가?
|
||
|
||
**예, 스케일 효과가 있다.** 근거:
|
||
|
||
1. **Representation capacity**: 3B는 1B 대비 ~2.5배 파라미터 → EOS 예측, 반복 회피 등 복잡한 패턴을 더 정확하게 학습
|
||
2. **Attention head 수 증가**: 더 많은 head가 "이전에 말한 것" 추적에 전용 가능
|
||
3. **경험적 증거**: Open Ko-LLM 리더보드에서 3B 모델들은 1B 대비 일관되게 반복률 낮음
|
||
4. **같은 SFT 데이터라도 3B가 더 잘 일반화**: 더 큰 모델이 same data에서 더 많은 패턴 추출
|
||
|
||
### 4.4 권장: **1B 재학습 먼저, 3B 병렬 준비**
|
||
|
||
```
|
||
Day 0: 데이터 재준비 (30분) + 1B SFT 재학습 (40분) = 오늘 완료
|
||
Day 0: 결과 평가 (30분) → 1B 기준선 확보
|
||
Day 1-2: 3B 아키텍처 설정 + pretrain 시작 (26시간)
|
||
Day 2-3: 3B SFT (2시간) + 평가
|
||
```
|
||
|
||
**이유:**
|
||
- 1B 재학습은 비용이 너무 낮다 (40분). 안 할 이유가 없다.
|
||
- 1B 결과로 파이프라인 검증 → 3B에 동일한 (검증된) 파이프라인 적용
|
||
- 3B pretrain 동안 1B 모델을 배포/데모에 사용 가능
|
||
|
||
---
|
||
|
||
## 5. "다시 시작"의 타임라인
|
||
|
||
### 5.1 상세 타임라인
|
||
|
||
| 단계 | 작업 | 소요 시간 | 누적 |
|
||
|------|------|----------|------|
|
||
| **A. 데이터 재준비** | `prepare_sft_data.py` 재실행 (강화 필터 적용) | **20-30분** | 30분 |
|
||
| **B. 1B SFT 재학습** | 7500 steps, 8×B200, dynamic padding 적용 | **30-40분** | 1시간 |
|
||
| **C. 1B 평가** | 반복률 + 생성 품질 + (선택) ko_ifeval | **30분-2시간** | 1.5-3시간 |
|
||
| **D. 3B pretrain** | 150B tokens, 8×B200 | **~26시간** | 27-29시간 |
|
||
| **E. 3B SFT** | 동일 데이터, 10000 steps | **1.5-2시간** | 29-31시간 |
|
||
| **F. 3B 평가** | 전체 벤치마크 | **2-4시간** | 31-35시간 |
|
||
|
||
### 5.2 현재 고쳐서 가는 시간 vs 재시작
|
||
|
||
| 경로 | 소요 시간 | 예상 최종 품질 |
|
||
|------|----------|---------------|
|
||
| **경로 A: 현재 모델에서 추가 학습** | 추가 SFT 40분 + 평가 2시간 = ~3시간 | 반복률 12-15%, 잔여 오염 |
|
||
| **경로 B: 1B 클린 재학습** | 데이터 30분 + SFT 40분 + 평가 2시간 = **~3시간** | 반복률 **5-8%**, 오염 없음 |
|
||
| **경로 C: 3B 처음부터** | 데이터 30분 + pretrain 26시간 + SFT 2시간 + 평가 4시간 = **~33시간** | 반복률 **2-5%**, ko_ifeval 35-45% |
|
||
|
||
**경로 A와 B의 시간이 거의 같은데, B가 품질이 확실히 높다.** 이것이 재시작을 권장하는 핵심 이유다.
|
||
|
||
---
|
||
|
||
## 6. 재시작의 리스크와 예방
|
||
|
||
### 6.1 "다시 해도 또 새로운 문제가 나올 수 있다"
|
||
|
||
| 리스크 | 확률 | 예방 방법 |
|
||
|--------|------|----------|
|
||
| 데이터 파이프라인 새 버그 | 낮음 | 코드 이미 수정/검증됨, 단위 테스트 추가 |
|
||
| 과적합 감지 실패 | 낮음 | val split 이번엔 반드시 사용 |
|
||
| 새로운 유형의 반복 | 중간 | 다양한 프롬프트로 평가, rep_penalty 보험 |
|
||
| 학습 불안정 (loss spike) | 낮음 | 기존 학습에서 안정적이었음, 동일 lr 사용 |
|
||
| 데이터 필터 과도 → 데이터 부족 | 낮음 | 120K 여전히 충분 (3-4 epoch에 적합) |
|
||
|
||
### 6.2 지금까지의 교훈 반영 체크리스트
|
||
|
||
```
|
||
✅ 추론 시 올바른 프롬프트 포맷 (<|user|>/<|assistant|>) 사용
|
||
✅ Dynamic padding 실제 작동 확인 (배치별 가변 길이)
|
||
✅ 트렁케이션 시 EOS 강제 삽입
|
||
✅ EOS 리터럴 / Q&A 마커 오염 데이터 필터링
|
||
✅ 가중치 샘플링 정상화 (5.0 → 2.0)
|
||
✅ Val split으로 과적합 모니터링
|
||
✅ 3-4 epoch 충분히 학습
|
||
✅ 평가 시 rep_penalty=1.1 + no_repeat_ngram=3 기본 적용
|
||
✅ 다양한 프롬프트 유형으로 종합 평가
|
||
```
|
||
|
||
### 6.3 성공 확률 추정
|
||
|
||
- **위 체크리스트 100% 반영 시**: 반복률 5-8% 달성 확률 **85-90%**
|
||
- **기존 대비 개선**: 반복률 17.7% → 5-8% (55-70% 감소)
|
||
- **실패 시나리오**: 반복률이 10-15%에 머무는 경우 → 추가 대응 (ORPO/DPO)
|
||
|
||
---
|
||
|
||
## 7. 최종 결론 및 권장
|
||
|
||
### 7.1 "다시 시작"이 필요한 근본적 이유
|
||
|
||
**필요하다.** 이유:
|
||
|
||
1. **비용이 거의 없다** — 1B SFT 재학습은 40분. 기존 모델에서 추가 학습하는 시간과 동일.
|
||
2. **오염된 가중치 위에 쌓는 것은 비효율적** — OpenOrca 5배 업샘플링 + Q/A 마커 오염의 흔적이 남아있는 상태에서 추가 학습하면, 새 gradient가 오래된 오염을 완전히 덮지 못함.
|
||
3. **모든 수정 사항이 이미 코드에 반영됨** — sft_dataset.py (dynamic padding, EOS 보장), prepare_sft_data.py (강화 필터, 가중치 수정) 모두 수정 완료. 실행만 하면 됨.
|
||
4. **깨끗한 기준선이 필요** — 3B로 스케일업하기 전에, 깨끗한 1B 결과가 있어야 파이프라인이 올바른지 검증 가능.
|
||
|
||
### 7.2 다시 시작 시 예상 최종 품질
|
||
|
||
| 지표 | 현재 (수정 추론) | 1B 재학습 예상 | 3B 재학습 예상 |
|
||
|------|-----------------|---------------|---------------|
|
||
| 반복률 (3-gram) | 17.7% | **5-8%** | **2-5%** |
|
||
| 반복률 (rep_penalty 없이) | ~30% | **10-15%** | **5-10%** |
|
||
| EOS 정상 종료율 | ~60% | **85-90%** | **90-95%** |
|
||
| ko_ifeval (추정) | 15-25% | **20-30%** | **35-45%** |
|
||
| ko_winogrande (추정) | 50-55% | **53-58%** | **60-68%** |
|
||
| 한국어 답변 자연스러움 | 중간 | **중상** | **상** |
|
||
|
||
### 7.3 타임라인
|
||
|
||
```
|
||
[오늘 — 3시간]
|
||
├── 데이터 재준비: prepare_sft_data.py 재실행 (30분)
|
||
├── 1B SFT 재학습: 7500 steps (40분)
|
||
└── 평가: 반복률 + 생성 품질 (30분-2시간)
|
||
|
||
[내일-모레 — 30시간]
|
||
├── 3B pretrain (26시간, 백그라운드)
|
||
├── 3B SFT (2시간)
|
||
└── 3B 전체 평가 (2-4시간)
|
||
```
|
||
|
||
### 7.4 최종 권장
|
||
|
||
| 권장 | 근거 |
|
||
|------|------|
|
||
| ✅ **1B SFT 즉시 재학습** | 40분 투자, 반복률 17.7% → 5-8% 예상, 리스크 극히 낮음 |
|
||
| ✅ **3B pretrain 병렬 시작** | 1B 재학습 결과로 파이프라인 검증 후 동일 파이프라인 적용 |
|
||
| ❌ **현재 가중치에서 추가 학습** | 같은 시간으로 더 낮은 품질. 오염 잔류 위험. |
|
||
|
||
**한 줄 요약: 40분이면 깨끗한 모델을 얻을 수 있는데, 오염된 모델에 40분을 더 쓸 이유가 없다.**
|
||
|
||
---
|
||
|
||
## 부록: 재학습 실행 명령어
|
||
|
||
```bash
|
||
# Step 1: 데이터 재준비 (강화 필터 + 수정된 가중치 적용)
|
||
cd /PROJECT/0325120031_A/ghong/taketimes/llm-bang
|
||
python data/prepare_sft_data.py --output_dir data/sft_v2/ --val_split 0.05
|
||
|
||
# Step 2: 1B SFT 재학습
|
||
torchrun --nproc_per_node=8 train/sft.py \
|
||
--base_checkpoint checkpoints/korean_1b_fp8_run1/checkpoint-0034000 \
|
||
--sft_data data/sft_v2/train.jsonl \
|
||
--val_data data/sft_v2/val.jsonl \
|
||
--checkpoint_dir checkpoints/korean_1b_sft_v2 \
|
||
--max_steps 7500 \
|
||
--batch_size 4 \
|
||
--grad_accum 2 \
|
||
--lr 2e-5 \
|
||
--warmup_steps 225 \
|
||
--use_fp8
|
||
|
||
# Step 3: 평가
|
||
python eval/test_generation_params.py \
|
||
--checkpoint checkpoints/korean_1b_sft_v2/checkpoint-0007500
|
||
```
|