448 lines
15 KiB
Markdown
448 lines
15 KiB
Markdown
|
|
# SFT 학습 예외 상황 플레이북
|
|||
|
|
|
|||
|
|
**프로젝트:** Korean 1B SFT 재학습
|
|||
|
|
**서버:** 8× B200 183GB, Driver 580.95.05, CUDA 13.1, PyTorch 2.10
|
|||
|
|
**작성일:** 2026-02-26
|
|||
|
|
**설정:** bs=4 × 8GPU × grad_accum=2 = effective batch 64, max_steps=10000, lr=2e-5, FP8
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 1: Loss가 0으로 떨어지는 경우
|
|||
|
|
|
|||
|
|
### 감지 기준
|
|||
|
|
- **즉각 경고:** loss < 0.01이 3 step 연속 발생
|
|||
|
|
- **주의:** loss < 0.1이 10 step 이상 지속
|
|||
|
|
- **정상 범위:** 1B SFT에서 수렴 시 loss ≈ 1.5~2.0. 0에 가까우면 100% 비정상
|
|||
|
|
|
|||
|
|
### 즉각 대응
|
|||
|
|
1. 학습 즉시 중단 (Ctrl+C 또는 `kill -SIGINT <PID>`)
|
|||
|
|
2. 가장 최근 정상 체크포인트 확인:
|
|||
|
|
```bash
|
|||
|
|
ls -lt checkpoints/korean_1b_sft/checkpoint-* | head -5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 원인별 진단 및 대응
|
|||
|
|
|
|||
|
|
#### 1-A. Labels Shift 버그 재발
|
|||
|
|
**확인 방법:**
|
|||
|
|
```python
|
|||
|
|
# 데이터에서 샘플 하나 로드 후 labels 검증
|
|||
|
|
from data.sft_dataset import SFTDataset
|
|||
|
|
from tokenizers import Tokenizer
|
|||
|
|
tok = Tokenizer.from_file("tokenizer/korean_sp/tokenizer.json")
|
|||
|
|
ds = SFTDataset("data/sft/train.jsonl", tok, max_seq_len=4096)
|
|||
|
|
ids, labels = ds[0]
|
|||
|
|
# labels에서 -1이 아닌 부분이 input_ids의 다음 토큰과 일치하는지 확인
|
|||
|
|
mask = labels != -1
|
|||
|
|
print(f"유효 labels 수: {mask.sum()}")
|
|||
|
|
print(f"첫 유효 label 위치: {mask.nonzero()[0].item() if mask.any() else 'NONE'}")
|
|||
|
|
# labels[i]는 input_ids[i+1]과 같아야 함 (autoregressive)
|
|||
|
|
# 만약 labels == input_ids 이면 shift 안 됨 → 버그
|
|||
|
|
```
|
|||
|
|
**수정:** `sft_dataset.py`에서 `labels = input_ids[1:]`, `input_ids = input_ids[:-1]` shift 확인
|
|||
|
|
|
|||
|
|
#### 1-B. 데이터 오염
|
|||
|
|
**확인 방법:**
|
|||
|
|
```python
|
|||
|
|
# 랜덤 배치에서 실제 학습 토큰 검사
|
|||
|
|
for batch in train_loader:
|
|||
|
|
ids, labels, mask = batch
|
|||
|
|
valid = (labels != -1)
|
|||
|
|
print(f"유효 토큰 비율: {valid.float().mean():.4f}")
|
|||
|
|
# 유효 토큰이 0이면 모든 labels가 -1 → loss=0
|
|||
|
|
if valid.sum() == 0:
|
|||
|
|
print("🔴 모든 labels가 ignore_index! 데이터 문제")
|
|||
|
|
break
|
|||
|
|
```
|
|||
|
|
**대응:** 데이터 재생성, `prepare_sft_data.py` 재실행
|
|||
|
|
|
|||
|
|
#### 1-C. Learning Rate 문제
|
|||
|
|
**확인:** loss가 갑자기 0이면 lr 문제보다는 labels 버그일 가능성이 훨씬 높음. 그래도 확인:
|
|||
|
|
```bash
|
|||
|
|
grep "lr " checkpoints/korean_1b_sft/train.log | tail -20
|
|||
|
|
# lr이 비정상적으로 높으면 (>1e-3) 수정
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 2: Loss Spike (급등)
|
|||
|
|
|
|||
|
|
### 감지 기준
|
|||
|
|
- **Spike 정의:** 이전 log_interval 평균 대비 **3배 이상** 급등
|
|||
|
|
- **예:** 평균 loss 1.9에서 갑자기 5.7 이상
|
|||
|
|
- **GNorm 기준:** grad_norm > 10.0이면 주의, > 50.0이면 심각
|
|||
|
|
|
|||
|
|
### 원인별 대응
|
|||
|
|
|
|||
|
|
| 원인 | 진단 | 대응 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| Bad batch (이상 데이터) | 해당 step의 배치 내용 확인 | 1~2회 spike 후 자연 복구되면 무시 |
|
|||
|
|
| LR 문제 | warmup 직후 spike → lr 너무 높음 | lr을 1e-5로 낮추고 재시작 |
|
|||
|
|
| GNorm 폭발 | gnorm > 50 | max_grad_norm을 0.5로 강화 |
|
|||
|
|
| FP8 수치 불안정 | FP8 관련 warning 확인 | `--use_fp8` 제거하고 BF16으로 전환 |
|
|||
|
|
|
|||
|
|
### 대응 절차
|
|||
|
|
1. **1회 spike:** 무시 (단발성 bad batch). 다음 log에서 복구 확인
|
|||
|
|
2. **연속 3회 spike:** 학습 중단
|
|||
|
|
3. **복구 방법:**
|
|||
|
|
```bash
|
|||
|
|
# 마지막 정상 체크포인트에서 재시작, lr 낮추기
|
|||
|
|
bash scripts/launch_sft.sh --resume checkpoints/korean_1b_sft/checkpoint-XXXX --lr 1e-5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 현재 코드의 보호 장치
|
|||
|
|
- ✅ `max_grad_norm=1.0` (gradient clipping 활성화)
|
|||
|
|
- ✅ Non-finite loss 감지 → RuntimeError 발생 (trainer.py `_step()`)
|
|||
|
|
- ❌ Loss spike 자동 감지/skip은 미구현 → `monitor_training.sh`로 보완
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 3: 과적합 (val_loss > train_loss 지속)
|
|||
|
|
|
|||
|
|
### 감지 기준
|
|||
|
|
- **주의:** val_loss - train_loss > 0.15 (상대갭 8% 이상)
|
|||
|
|
- **심각:** val_loss가 3회 연속 eval에서 상승 (train_loss는 하강 중)
|
|||
|
|
- **eval_interval:** 현재 250 steps → 매 250 step마다 val_loss 기록됨
|
|||
|
|
|
|||
|
|
### 현재 코드 상태
|
|||
|
|
- ✅ `val_loader` 지원 (sft.py에서 `--val_data` 인자 있음)
|
|||
|
|
- ✅ `_run_validation()` 구현됨 (trainer.py)
|
|||
|
|
- ✅ Best checkpoint 자동 저장 (`val_loss < self._best_val_loss`)
|
|||
|
|
- ❌ **Early stopping 미구현** — val_loss가 올라도 max_steps까지 학습 계속
|
|||
|
|
|
|||
|
|
### 대응
|
|||
|
|
|
|||
|
|
#### 즉시 가능한 조치
|
|||
|
|
1. **수동 early stop:** 모니터링 스크립트가 경고 → 수동 중단
|
|||
|
|
2. **Best checkpoint 사용:** `checkpoint-best` 디렉토리에 자동 저장됨
|
|||
|
|
```bash
|
|||
|
|
ls checkpoints/korean_1b_sft/checkpoint-best/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 과적합 해소 방법 (재학습 시)
|
|||
|
|
| 방법 | 설정 변경 |
|
|||
|
|
|------|-----------|
|
|||
|
|
| LR 낮추기 | `--lr 1e-5` |
|
|||
|
|
| Weight decay 높이기 | `--weight_decay 0.05` |
|
|||
|
|
| 데이터 augmentation | NEFTune 이미 활성화 (noise_alpha=10.0) ✅ |
|
|||
|
|
| Steps 줄이기 | `--max_steps 7000` (과적합 시작 전 step에서 멈춤) |
|
|||
|
|
| Dropout | 모델 구조 수정 필요 (현재 코드에서 쉽지 않음) |
|
|||
|
|
|
|||
|
|
#### Early Stopping 추가 방법 (trainer.py 수정)
|
|||
|
|
```python
|
|||
|
|
# trainer.py의 train() 메서드에서 validation 후:
|
|||
|
|
if val_loss > self._best_val_loss:
|
|||
|
|
self._patience_counter += 1
|
|||
|
|
if self._patience_counter >= 5: # 5회 연속 개선 없으면 중단
|
|||
|
|
self._log("Early stopping triggered")
|
|||
|
|
break
|
|||
|
|
else:
|
|||
|
|
self._patience_counter = 0
|
|||
|
|
self._best_val_loss = val_loss
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 4: OOM (Out of Memory)
|
|||
|
|
|
|||
|
|
### 현재 메모리 추정
|
|||
|
|
|
|||
|
|
| 항목 | 추정 |
|
|||
|
|
|------|------|
|
|||
|
|
| 모델 파라미터 (1.19B, BF16) | ~2.4 GB |
|
|||
|
|
| 옵티마이저 상태 (AdamW, fp32) | ~9.5 GB |
|
|||
|
|
| Gradient (BF16) | ~2.4 GB |
|
|||
|
|
| Activation (bs=4, seq=4096, gradient checkpointing ON) | ~8-15 GB |
|
|||
|
|
| **Peak 총합 (per GPU)** | **~25-35 GB** |
|
|||
|
|
| **B200 여유** | **183 - 35 = ~148 GB 여유** |
|
|||
|
|
|
|||
|
|
→ 1B 모델에서 OOM 가능성 **극히 낮음**
|
|||
|
|
|
|||
|
|
### 만약 발생한다면
|
|||
|
|
1. **증상:** `torch.cuda.OutOfMemoryError` → trainer.py에서 이미 catch하여 상세 메시지 출력
|
|||
|
|
2. **즉시 대응:**
|
|||
|
|
```bash
|
|||
|
|
# batch_size 줄이기 (4→2), grad_accum 늘리기 (2→4) → effective batch 동일
|
|||
|
|
bash scripts/launch_sft.sh --batch_size 2 --grad_accum 4 --resume <last_ckpt>
|
|||
|
|
```
|
|||
|
|
3. **Gradient checkpointing:**
|
|||
|
|
- ✅ **이미 활성화됨** (sft.py에서 `model.gradient_checkpointing_enable()`)
|
|||
|
|
4. **추가 조치:**
|
|||
|
|
```bash
|
|||
|
|
# 메모리 fragmentation 방지
|
|||
|
|
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 메모리 모니터링
|
|||
|
|
```bash
|
|||
|
|
watch -n 5 nvidia-smi # 실시간 확인
|
|||
|
|
# 또는 monitor_training.sh 사용 (아래 참조)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 5: GPU Hang / NCCL 통신 장애
|
|||
|
|
|
|||
|
|
### 감지 방법
|
|||
|
|
- **증상:** 학습 로그가 멈춤 (새 step이 N분 이상 안 나옴)
|
|||
|
|
- **NCCL timeout:** 기본 30분 후 에러 발생
|
|||
|
|
- `nvidia-smi`에서 특정 GPU utilization 0%
|
|||
|
|
|
|||
|
|
### 진단
|
|||
|
|
```bash
|
|||
|
|
# 1. GPU 상태 확인
|
|||
|
|
nvidia-smi
|
|||
|
|
|
|||
|
|
# 2. NCCL 디버그 활성화하여 재시작
|
|||
|
|
export NCCL_DEBUG=INFO
|
|||
|
|
export NCCL_DEBUG_SUBSYS=ALL
|
|||
|
|
|
|||
|
|
# 3. 프로세스 상태 확인
|
|||
|
|
ps aux | grep torchrun
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 복구 방법
|
|||
|
|
```bash
|
|||
|
|
# 1. 기존 프로세스 정리
|
|||
|
|
pkill -f torchrun
|
|||
|
|
sleep 5
|
|||
|
|
|
|||
|
|
# 2. 가장 최근 체크포인트 자동 감지
|
|||
|
|
LATEST_CKPT=$(ls -d checkpoints/korean_1b_sft/checkpoint-* 2>/dev/null \
|
|||
|
|
| grep -v best | sort -t- -k2 -n | tail -1)
|
|||
|
|
echo "Latest checkpoint: ${LATEST_CKPT}"
|
|||
|
|
|
|||
|
|
# 3. 재시작
|
|||
|
|
bash scripts/launch_sft.sh --resume "${LATEST_CKPT}"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 최근 체크포인트 자동 감지 스크립트
|
|||
|
|
```bash
|
|||
|
|
#!/bin/bash
|
|||
|
|
# find_latest_checkpoint.sh
|
|||
|
|
CKPT_DIR="${1:-checkpoints/korean_1b_sft}"
|
|||
|
|
LATEST=$(ls -d "${CKPT_DIR}"/checkpoint-[0-9]* 2>/dev/null \
|
|||
|
|
| sort -t- -k2 -n | tail -1)
|
|||
|
|
if [[ -z "$LATEST" ]]; then
|
|||
|
|
echo "No checkpoint found in ${CKPT_DIR}" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
echo "$LATEST"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 예방
|
|||
|
|
- `save_interval=500` (현재 설정) → 최대 500 step 손실
|
|||
|
|
- NCCL timeout 조정: `export NCCL_TIMEOUT=1800` (30분 → 필요 시 줄이기)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 6: 학습 완료 후 반복률 >15%
|
|||
|
|
|
|||
|
|
### 판단 기준
|
|||
|
|
|
|||
|
|
| 반복률 | 판단 | 대응 |
|
|||
|
|
|--------|------|------|
|
|||
|
|
| <5% (rep_penalty 없이) | ✅ 성공 | 배포 가능 |
|
|||
|
|
| 5-10% | 🟡 OK | rep_penalty=1.1로 배포 |
|
|||
|
|
| 10-20% | 🟠 경계 | 아래 파라미터 조정 시도 |
|
|||
|
|
| >20% | 🔴 실패 | 재학습 필요 |
|
|||
|
|
|
|||
|
|
### 파라미터 조정으로 해결 시도 (재학습 없이)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 추론 시 적용
|
|||
|
|
generate_kwargs = {
|
|||
|
|
"repetition_penalty": 1.1, # 1.05~1.2 범위 탐색
|
|||
|
|
"no_repeat_ngram_size": 3, # 3-gram 반복 차단
|
|||
|
|
"temperature": 0.7, # 약간 낮추면 반복 감소
|
|||
|
|
"top_p": 0.9,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 재학습이 필요한 경우
|
|||
|
|
- rep_penalty=1.2 + no_repeat_3gram에서도 >10%
|
|||
|
|
- 원인 분석:
|
|||
|
|
1. **데이터 내 반복 패턴:** `data_quality_audit.py`로 재확인
|
|||
|
|
2. **Epoch 과다:** 5+ epoch은 반복 패턴 암기 유발 → 3-4 epoch이 적정
|
|||
|
|
3. **EOS 학습 부족:** truncation 시 EOS 손실 여부 확인
|
|||
|
|
|
|||
|
|
### 고급 대응 (추가 학습 방법)
|
|||
|
|
| 방법 | 설명 | 소요 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| ORPO | Preference optimization, 반복 패턴 직접 penalize | +3-6시간 |
|
|||
|
|
| DPO | Chosen(비반복) vs Rejected(반복) 쌍 필요 | +4-8시간 |
|
|||
|
|
| rep_penalty fine-tuning | 추론 시 penalty 결과를 reward로 RL | 복잡 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 7: ko_ifeval 기대치 미달 (<15%)
|
|||
|
|
|
|||
|
|
### 원인 분석 방법
|
|||
|
|
|
|||
|
|
#### Step 1: 모델 출력 직접 확인
|
|||
|
|
```bash
|
|||
|
|
# ko_ifeval 실패 샘플 분석
|
|||
|
|
python -c "
|
|||
|
|
# lm_eval 결과에서 실패 케이스 추출
|
|||
|
|
# 지시문 이해 부족 vs 포맷 오류 vs 한국어 능력 부족 구분
|
|||
|
|
"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Step 2: 카테고리별 분석
|
|||
|
|
| 실패 유형 | 의미 | 대응 |
|
|||
|
|
|-----------|------|------|
|
|||
|
|
| 지시 무시 (wrong format) | instruction following 약함 | SFT 데이터에 format-constrained 샘플 추가 |
|
|||
|
|
| 한국어 이해 실패 | 한국어 능력 부족 | 한국어 비율 높이기 (현재 ~70%) |
|
|||
|
|
| 추론 오류 | 1B 모델 한계 | 모델 크기 한계 → 3B 전환 |
|
|||
|
|
|
|||
|
|
#### Step 3: 모델 한계 vs 데이터 문제 구분
|
|||
|
|
```
|
|||
|
|
1B 모델 ko_ifeval 현실적 범위: 15-30%
|
|||
|
|
- <15%: 데이터/학습 문제 가능성 높음
|
|||
|
|
- 15-25%: 정상 범위, 데이터로 개선 여지 있음
|
|||
|
|
- 25-30%: 1B 한계에 근접, 3B 전환 필요
|
|||
|
|
- >30%: 1B에서 달성하기 어려움
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 데이터 추가 수집 방향
|
|||
|
|
1. **Korean instruction-following 데이터:** KoAlpaca, KULLM 등에서 format-constrained 샘플
|
|||
|
|
2. **Multi-turn 한국어 대화:** 지시 따르기 능력 강화
|
|||
|
|
3. **ko_ifeval과 유사한 포맷 데이터:** "~형식으로 답하시오" 유형
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 시나리오 8: 디스크 공간 부족
|
|||
|
|
|
|||
|
|
### 현재 상태
|
|||
|
|
```
|
|||
|
|
/PROJECT: 3.5TB 총, 1.4TB 사용, 2.2TB 가용 (39% 사용)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 체크포인트 크기 추정
|
|||
|
|
| 항목 | 크기 |
|
|||
|
|
|------|------|
|
|||
|
|
| model.pt (1.19B BF16) | ~2.4 GB |
|
|||
|
|
| optimizer.pt (AdamW states) | ~9.5 GB |
|
|||
|
|
| scheduler + meta | ~1 MB |
|
|||
|
|
| **체크포인트 1개** | **~12 GB** |
|
|||
|
|
| 10,000 steps / 500 save = 20개 | **~240 GB** |
|
|||
|
|
| + best checkpoint | +12 GB |
|
|||
|
|
| + tensorboard logs | ~100 MB |
|
|||
|
|
| **총 예상** | **~252 GB** |
|
|||
|
|
|
|||
|
|
→ 2.2TB 가용 대비 충분하지만, 여러 실험 시 누적 주의
|
|||
|
|
|
|||
|
|
### 체크포인트 관리 전략
|
|||
|
|
|
|||
|
|
#### 저장 주기 최적화
|
|||
|
|
- **현재:** 500 step마다 (추천 유지)
|
|||
|
|
- 디스크 부족 시: 1000 step으로 변경 → 120 GB로 절반 감소
|
|||
|
|
- `train_config.save_interval = 1000`
|
|||
|
|
|
|||
|
|
#### 오래된 체크포인트 자동 삭제
|
|||
|
|
```bash
|
|||
|
|
#!/bin/bash
|
|||
|
|
# cleanup_checkpoints.sh — 최신 N개만 유지, best는 항상 보존
|
|||
|
|
CKPT_DIR="${1:-checkpoints/korean_1b_sft}"
|
|||
|
|
KEEP="${2:-5}" # 최신 5개 유지
|
|||
|
|
|
|||
|
|
CKPTS=$(ls -d "${CKPT_DIR}"/checkpoint-[0-9]* 2>/dev/null | sort -t- -k2 -n)
|
|||
|
|
TOTAL=$(echo "$CKPTS" | wc -l)
|
|||
|
|
DELETE=$((TOTAL - KEEP))
|
|||
|
|
|
|||
|
|
if [[ $DELETE -gt 0 ]]; then
|
|||
|
|
echo "$CKPTS" | head -n "$DELETE" | while read ckpt; do
|
|||
|
|
echo "Removing: $ckpt"
|
|||
|
|
rm -rf "$ckpt"
|
|||
|
|
done
|
|||
|
|
echo "Kept latest $KEEP checkpoints + checkpoint-best"
|
|||
|
|
else
|
|||
|
|
echo "Only $TOTAL checkpoints, nothing to delete (keep=$KEEP)"
|
|||
|
|
fi
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 디스크 모니터링
|
|||
|
|
```bash
|
|||
|
|
# 학습 중 주기적 확인
|
|||
|
|
df -h /PROJECT | awk 'NR==2 {if ($5+0 > 80) print "🔴 DISK >80%: "$5}'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 학습 재시작 가이드
|
|||
|
|
|
|||
|
|
### 현재 코드의 Resume 지원
|
|||
|
|
|
|||
|
|
✅ **완전 지원됨:**
|
|||
|
|
- `sft.py`에 `--resume` 인자 있음
|
|||
|
|
- `load_checkpoint()`으로 model, optimizer, scheduler 상태 모두 복원
|
|||
|
|
- `start_step` 반환 → 이어서 학습
|
|||
|
|
|
|||
|
|
### 재시작 명령어
|
|||
|
|
```bash
|
|||
|
|
# 방법 1: 최신 체크포인트에서 자동 재시작
|
|||
|
|
LATEST=$(ls -d checkpoints/korean_1b_sft/checkpoint-[0-9]* 2>/dev/null \
|
|||
|
|
| sort -t- -k2 -n | tail -1)
|
|||
|
|
bash scripts/launch_sft.sh --resume "${LATEST}"
|
|||
|
|
|
|||
|
|
# 방법 2: 특정 체크포인트 지정
|
|||
|
|
bash scripts/launch_sft.sh --resume checkpoints/korean_1b_sft/checkpoint-0003000
|
|||
|
|
|
|||
|
|
# 방법 3: LR 변경하며 재시작 (과적합/spike 대응)
|
|||
|
|
bash scripts/launch_sft.sh --resume "${LATEST}" --lr 1e-5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 주의사항
|
|||
|
|
- **cosine schedule:** resume 시 scheduler가 중간 step에서 복원됨 → LR이 올바른 위치에서 재개
|
|||
|
|
- **max_steps 변경 시:** 원래 5000 step 기준 schedule인데 10000으로 변경하면 LR curve가 달라짐 → 처음부터 재학습 권장
|
|||
|
|
- **DDP seed:** resume 시 동일 seed 사용해야 데이터 순서 재현 (현재 코드에서 자동 처리)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 모니터링 자동화
|
|||
|
|
|
|||
|
|
별도 스크립트: `scripts/monitor_training.sh` 참조
|
|||
|
|
|
|||
|
|
### 감시 항목 요약
|
|||
|
|
|
|||
|
|
| 항목 | 임계값 | 의미 |
|
|||
|
|
|------|--------|------|
|
|||
|
|
| loss = 0.0000 (3 step 연속) | 🔴 Critical | Labels 버그 |
|
|||
|
|
| loss spike (3× 평균) | 🟠 Warning | Bad batch / LR |
|
|||
|
|
| gnorm > 10.0 | 🟠 Warning | 불안정 |
|
|||
|
|
| gnorm > 50.0 | 🔴 Critical | 발산 직전 |
|
|||
|
|
| GPU util < 50% | 🟡 Info | 병목 (data loading?) |
|
|||
|
|
| 로그 5분 이상 멈춤 | 🔴 Critical | Hang / NCCL 장애 |
|
|||
|
|
| 디스크 사용 > 80% | 🟠 Warning | 체크포인트 정리 필요 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 위험도 순위 (높음 → 낮음)
|
|||
|
|
|
|||
|
|
| 순위 | 시나리오 | 위험도 | 예방 |
|
|||
|
|
|------|----------|--------|------|
|
|||
|
|
| 1 | **Loss → 0 (Labels 버그)** | 🔴🔴🔴 | 학습 전 labels shift 검증 스크립트 실행 |
|
|||
|
|
| 2 | **GPU Hang (NCCL)** | 🔴🔴 | save_interval=500, NCCL 환경변수 설정 |
|
|||
|
|
| 3 | **과적합** | 🔴 | val_data 필수, 모니터링 |
|
|||
|
|
| 4 | **반복률 >15%** | 🟠🟠 | 깨끗한 데이터, 적정 epoch |
|
|||
|
|
| 5 | **Loss Spike** | 🟠 | grad_clip=1.0, 이미 설정됨 |
|
|||
|
|
| 6 | **ko_ifeval 미달** | 🟠 | 1B 한계 인지, 데이터 다양성 |
|
|||
|
|
| 7 | **디스크 부족** | 🟡 | 2.2TB 여유, 자동 정리 |
|
|||
|
|
| 8 | **OOM** | 🟢 | 183GB에 1B 모델, 거의 불가능 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 학습 전 체크리스트
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
□ 데이터 필터링 완료 (data_quality_audit.py)
|
|||
|
|
□ Val split 생성 (90/10)
|
|||
|
|
□ Labels shift 검증 (위 코드 스니펫 실행)
|
|||
|
|
□ sft_dataset.py 수정 확인 (dynamic padding, EOS 보존)
|
|||
|
|
□ launch_sft.sh 설정 확인 (max_steps, val_data, lr)
|
|||
|
|
□ 디스크 공간 확인 (df -h /PROJECT)
|
|||
|
|
□ GPU 상태 확인 (nvidia-smi)
|
|||
|
|
□ monitor_training.sh 백그라운드 실행
|
|||
|
|
□ tensorboard 실행: tensorboard --logdir checkpoints/korean_1b_sft/tensorboard
|
|||
|
|
```
|