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% ๋น์ ์
์ฆ๊ฐ ๋์
- ํ์ต ์ฆ์ ์ค๋จ (Ctrl+C ๋๋
kill -SIGINT <PID>) - ๊ฐ์ฅ ์ต๊ทผ ์ ์ ์ฒดํฌํฌ์ธํธ ํ์ธ:
ls -lt checkpoints/korean_1b_sft/checkpoint-* | head -5
์์ธ๋ณ ์ง๋จ ๋ฐ ๋์
1-A. Labels Shift ๋ฒ๊ทธ ์ฌ๋ฐ
ํ์ธ ๋ฐฉ๋ฒ:
# ๋ฐ์ดํฐ์์ ์ํ ํ๋ ๋ก๋ ํ 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. ๋ฐ์ดํฐ ์ค์ผ
ํ์ธ ๋ฐฉ๋ฒ:
# ๋๋ค ๋ฐฐ์น์์ ์ค์ ํ์ต ํ ํฐ ๊ฒ์ฌ
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 ๋ฒ๊ทธ์ผ ๊ฐ๋ฅ์ฑ์ด ํจ์ฌ ๋์. ๊ทธ๋๋ ํ์ธ:
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ํ spike: ๋ฌด์ (๋จ๋ฐ์ฑ bad batch). ๋ค์ log์์ ๋ณต๊ตฌ ํ์ธ
- ์ฐ์ 3ํ spike: ํ์ต ์ค๋จ
- ๋ณต๊ตฌ ๋ฐฉ๋ฒ:
# ๋ง์ง๋ง ์ ์ ์ฒดํฌํฌ์ธํธ์์ ์ฌ์์, 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๊น์ง ํ์ต ๊ณ์
๋์
์ฆ์ ๊ฐ๋ฅํ ์กฐ์น
- ์๋ early stop: ๋ชจ๋ํฐ๋ง ์คํฌ๋ฆฝํธ๊ฐ ๊ฒฝ๊ณ โ ์๋ ์ค๋จ
- Best checkpoint ์ฌ์ฉ:
checkpoint-best๋๋ ํ ๋ฆฌ์ ์๋ ์ ์ฅ๋จ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 ์์ )
# 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 ๊ฐ๋ฅ์ฑ ๊ทนํ ๋ฎ์
๋ง์ฝ ๋ฐ์ํ๋ค๋ฉด
- ์ฆ์:
torch.cuda.OutOfMemoryErrorโ trainer.py์์ ์ด๋ฏธ catchํ์ฌ ์์ธ ๋ฉ์์ง ์ถ๋ ฅ - ์ฆ์ ๋์:
# batch_size ์ค์ด๊ธฐ (4โ2), grad_accum ๋๋ฆฌ๊ธฐ (2โ4) โ effective batch ๋์ผ bash scripts/launch_sft.sh --batch_size 2 --grad_accum 4 --resume <last_ckpt> - Gradient checkpointing:
- โ
์ด๋ฏธ ํ์ฑํ๋จ (sft.py์์
model.gradient_checkpointing_enable())
- โ
์ด๋ฏธ ํ์ฑํ๋จ (sft.py์์
- ์ถ๊ฐ ์กฐ์น:
# ๋ฉ๋ชจ๋ฆฌ fragmentation ๋ฐฉ์ง export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ํฐ๋ง
watch -n 5 nvidia-smi # ์ค์๊ฐ ํ์ธ
# ๋๋ monitor_training.sh ์ฌ์ฉ (์๋ ์ฐธ์กฐ)
์๋๋ฆฌ์ค 5: GPU Hang / NCCL ํต์ ์ฅ์
๊ฐ์ง ๋ฐฉ๋ฒ
- ์ฆ์: ํ์ต ๋ก๊ทธ๊ฐ ๋ฉ์ถค (์ step์ด N๋ถ ์ด์ ์ ๋์ด)
- NCCL timeout: ๊ธฐ๋ณธ 30๋ถ ํ ์๋ฌ ๋ฐ์
nvidia-smi์์ ํน์ GPU utilization 0%
์ง๋จ
# 1. GPU ์ํ ํ์ธ
nvidia-smi
# 2. NCCL ๋๋ฒ๊ทธ ํ์ฑํํ์ฌ ์ฌ์์
export NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=ALL
# 3. ํ๋ก์ธ์ค ์ํ ํ์ธ
ps aux | grep torchrun
๋ณต๊ตฌ ๋ฐฉ๋ฒ
# 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}"
์ต๊ทผ ์ฒดํฌํฌ์ธํธ ์๋ ๊ฐ์ง ์คํฌ๋ฆฝํธ
#!/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% | ๐ด ์คํจ | ์ฌํ์ต ํ์ |
ํ๋ผ๋ฏธํฐ ์กฐ์ ์ผ๋ก ํด๊ฒฐ ์๋ (์ฌํ์ต ์์ด)
# ์ถ๋ก ์ ์ ์ฉ
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%
- ์์ธ ๋ถ์:
- ๋ฐ์ดํฐ ๋ด ๋ฐ๋ณต ํจํด:
data_quality_audit.py๋ก ์ฌํ์ธ - Epoch ๊ณผ๋ค: 5+ epoch์ ๋ฐ๋ณต ํจํด ์๊ธฐ ์ ๋ฐ โ 3-4 epoch์ด ์ ์
- 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: ๋ชจ๋ธ ์ถ๋ ฅ ์ง์ ํ์ธ
# 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์์ ๋ฌ์ฑํ๊ธฐ ์ด๋ ค์
๋ฐ์ดํฐ ์ถ๊ฐ ์์ง ๋ฐฉํฅ
- Korean instruction-following ๋ฐ์ดํฐ: KoAlpaca, KULLM ๋ฑ์์ format-constrained ์ํ
- Multi-turn ํ๊ตญ์ด ๋ํ: ์ง์ ๋ฐ๋ฅด๊ธฐ ๋ฅ๋ ฅ ๊ฐํ
- 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
์ค๋๋ ์ฒดํฌํฌ์ธํธ ์๋ ์ญ์
#!/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
๋์คํฌ ๋ชจ๋ํฐ๋ง
# ํ์ต ์ค ์ฃผ๊ธฐ์ ํ์ธ
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๋ฐํ โ ์ด์ด์ ํ์ต
์ฌ์์ ๋ช ๋ น์ด
# ๋ฐฉ๋ฒ 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