82 KiB
FRANKENSTALLM
한국어 3B LLM을 8× NVIDIA B200 위에서 처음부터 직접 만든다. Frankenstein처럼 조각을 이어 붙이고, 철강처럼 단단하게 단련한다.
GitHub: pathcosmos/FRANKENSTALLM
목차
- 왜 이 프로젝트인가
- 현재 상태 — 한눈에 보기
- 하드웨어 환경
- 프로젝트 구조
- 프로젝트 여정 타임라인
- 모델 아키텍처
- 학습 데이터
- 학습 설정 및 최적화
- 실험 결과 — 1B 베이스라인
- 실험 결과 — 3B Base 종합 평가 (v2)
- 실험 결과 — 3B SFT 종합 평가
- Phase 3 — ORPO (선호도 정렬)
- 실행 방법
- 로드맵
- 참고 문서
- 기술 스택 요약
- 관련 프로젝트
- 다음 최적화 계획
- GPU 하드웨어 & 비용 분석
1. 왜 이 프로젝트인가
한국어 LLM 생태계는 빠르게 성장하고 있다. 그러나 대부분의 공개 모델은 영어 기반 사전학습 위에 한국어 파인튜닝을 얹은 형태거나, 학습 과정이 공개되지 않아 재현이 불가능하다.
이 프로젝트는 다르다.
- 처음부터(from scratch): 토크나이저 학습부터 프리트레인, SFT, 선호도 정렬까지 모든 단계를 직접 구현한다.
- 완전 공개 빌더 로그: 성공만 기록하지 않는다. 버그, 실패, 판단 착오, 그리고 그 원인 분석까지 모두 기록한다.
- 실용적인 규모: 학술 논문용 장난감 모델(125M)도 아니고, 연구소가 아니면 재현 불가능한 70B도 아닌, 3B 규모의 실용적 한국어 모델이 목표다.
- B200 최적화: NVIDIA B200의 FP8 Tensor Core, NVLink 5.0, FlashAttention-2를 최대한 활용한다. 최신 하드웨어를 최대로 쥐어짜는 과정 자체가 학습이다.
이 README는 완성된 결과물의 발표가 아니라, 현재 진행 중인 빌더의 로그다.
2. 현재 상태 — 한눈에 보기
2026-03-09 기준
| 단계 | 상태 | 세부 내용 |
|---|---|---|
| Phase 0: 기반 구축 | ✅ 완료 | OOM 수정, GQA FA 최적화, NCCL NVLS, 파이프라인 준비 |
| Phase 1: 3B Pretrain | ✅ 완료 | 57,000 steps, loss 1.466, ~63시간 |
| Phase 2: SFT | ✅ 완료 | 25,500 steps (early stop), val_loss 1.8851, ~15.5시간 |
| Phase 2.5: SFT 평가 | ✅ 완료 | 6차원 평가 4/6 PASS, ORPO 진행 결정 |
| Phase 3: ORPO Sweep | ✅ 완료 | 6-config sweep 완료, best: lr=1.2e-5, beta=0.25 |
| Phase 3: ORPO 본 학습 | 🔄 진행 중 | 630K pairs, 2 epochs, ~9,840 steps, ~4.8시간 |
| Phase 4: 배포 | 📋 대기 | GGUF 변환 → Ollama 서빙 |
Phase 2 (SFT) 최종 결과
| 항목 | 값 |
|---|---|
| 최종 step | 25,500 / 33,000 (77.3%, early stopping) |
| Val loss (best) | 1.8851 (step 23,000) |
| 학습 시간 | ~15시간 41분 (2026-03-05 22:15 ~ 2026-03-06 13:56) |
| VRAM 사용 | 24.2GB / 183GB per GPU (13.2%) |
| Base 모델 | checkpoint-0057000 (pretrain loss 1.466) |
| SFT 데이터 | 2,439,397 samples (24개 소스, 7.48 GB) |
| 사고 | 0건 (OOM, NCCL, NaN 없음) |
SFT Val Loss 전체 추이:
Step 500: 2.073
Step 2,000: 1.956 (-0.117)
Step 5,000: 1.911 (-0.045)
Step 10,000: 1.892 (-0.019)
Step 15,000: 1.886 (-0.006)
Step 20,000: 1.885 (-0.001)
Step 23,000: 1.8851 ← BEST
Step 25,500: 1.8851 → Early Stop (patience 5/5)
SFT 6차원 평가 요약
| 차원 | 결과 | 핵심 수치 |
|---|---|---|
| Perplexity (지식 보존) | PASS | forgetting 0.9% |
| 생성 품질 | FAIL | Greedy 반복률 72.97% |
| 한국어 벤치마크 | FAIL | KoBEST 평균 43.26% |
| 영어 벤치마크 | PASS | 전 태스크 하한 초과 |
| Calibration | PASS | Top-1 68.59% |
| SFT Chat 능력 | PASS | EOS 종료율 60% (Base 0%) |
판정: ORPO 진행 — 지식 보존 우수(0.9%), 반복률은 선호도 정렬로 해결. 상세:
reports/2026-03-06_3B_SFT_COMPLETION_AND_EVAL_SUMMARY.md
3. 하드웨어 환경
GPU
| 항목 | 사양 |
|---|---|
| 모델 | 8× NVIDIA B200 |
| VRAM | 183GB HBM3e per GPU (~1.47TB 합계) |
| FP8 Tensor Core | 2,250 TFLOPS/GPU (총 18,000 TFLOPS) |
| BF16 | 1,125 TFLOPS/GPU |
| HBM3e 대역폭 | ~7.67 TB/s per GPU |
| 인터커넥트 | NVLink 5.0 (900 GB/s bidirectional per GPU) |
| 토폴로지 | NVSwitch — 모든 GPU↔GPU 단일 홉 All-to-All Mesh |
| 전력 | 940W 실측 / 1000W cap |
B200은 FP8 네이티브 지원 모델이다. torch.float8_e4m3fn 을 TransformerEngine의 MXFP8 레시피와 결합해 학습한다. BF16 대비 연산량이 이론상 2배이며, 메모리 효율도 향상된다.
CPU 및 시스템 메모리
| 항목 | 사양 |
|---|---|
| CPU | 2× AMD EPYC 9365 (Turin / Zen 5) |
| 물리 코어 | 72개 (36코어 × 2소켓) |
| NUMA 구성 | 2노드: node0 (core 0-35) / node1 (core 36-71) |
| GPU↔NUMA 매핑 | GPU 0-3 → NUMA node 0, GPU 4-7 → NUMA node 1 |
| RAM | 2.21TB DDR5 (~2.03TB 여유) |
| L3 캐시 | 384MB (12 CCX × 32MB) |
NUMA 주의: 초기 DDP 런칭 시 5/8 rank가 잘못된 NUMA 노드에서 실행되는 문제 발생. 69%의 DataLoader worker가 크로스-NUMA였다. NUMA affinity 최적화는 미적용 상태(로드맵 항목).
스토리지
| 경로 | 용도 | 여유 공간 |
|---|---|---|
/PROJECT/0325120031_A/ghong/taketimes/llm-bang/ |
메인 작업 (체크포인트, 데이터) | 2.2TB |
/home/ghong/ |
소규모 코드 | 5GB (제한) |
주의: 체크포인트(수십 GB), 학습 데이터(82GB+), 중간 산출물은 모두
/PROJECT/...경로에 저장한다. 홈 디렉토리 용량 초과 위험.
소프트웨어 환경
| 패키지 | 버전 |
|---|---|
| PyTorch | 2.10.0a0+b4e4ee81d3.nv25.12 (NVIDIA 커스텀) |
| FlashAttention | 2.7.4.post1+25.12 |
| TransformerEngine | 2.10.0 |
| NCCL | 2.28.9 |
| Triton | 3.5.1 |
| CUDA | 13.1 |
| Driver | 580.95.05 |
경고: PyTorch는 NVIDIA B200 최적화 커스텀 빌드다.
pip install torch로 재설치하면 B200 최적화가 깨진다. 절대 재설치 금지.
4. 프로젝트 구조
llm-bang/
├── CLAUDE.md # Claude Code 가이드
├── README.md # 이 파일
├── PROGRESS.md # 진행 기록 (날짜별 로그)
├── Modelfile.3b # Ollama 모델 파일
│
├── configs/
│ ├── korean_3b_fp8.yaml # 3B FP8 학습 설정 (현재 사용 중)
│ ├── 3b_pretrain.yaml # 3B 프리트레인 설정 (대체)
│ ├── korean_1b_fp8.yaml # 1B FP8 설정 (아카이브)
│ ├── korean_3b_sft.yaml # 3B SFT v1 설정 (완료)
│ ├── korean_3b_sft_v2.yaml # 3B SFT v2 설정 (lr=5e-5, data mixing)
│ ├── korean_3b_orpo.yaml # 3B ORPO 설정 (lr=5e-6, beta=0.1)
│ ├── hybrid_3b.yaml # Hybrid 3B (Mamba-2 + Attention)
│ ├── small_fp8.yaml # 125M FP8 검증용
│ ├── medium.yaml # 중형 모델 설정
│ └── small.yaml # 소형 모델 설정
│
├── data/
│ ├── 3b_train.bin # 프리트레인 학습 데이터 (82GB, 41.12B tokens)
│ ├── 3b_val.bin # 검증 데이터 (151MB)
│ ├── cc100_ko_train.bin # CC100 한국어 (4.5GB)
│ ├── cosmo_auto_math_text_train.bin # 수학 텍스트 (2.6GB)
│ └── build scripts, __init__.py
│
├── model/
│ ├── attention.py # GQA FlashAttention (Phase 0 최적화 적용)
│ ├── transformer.py # 트랜스포머 메인 아키텍처
│ ├── config.py # 모델 설정 dataclass
│ └── layers.py # 커스텀 레이어 (RMSNorm, SwiGLU 등)
│
├── train/
│ ├── pretrain.py # 프리트레인 스크립트 (DDP 최적화)
│ ├── sft.py # SFT 학습
│ ├── orpo.py # ORPO 학습
│ ├── trainer.py # 통합 트레이너 (loss sync 최적화)
│ └── utils.py # 유틸리티 (NCCL 7200s timeout 등)
│
├── scripts/
│ ├── launch_3b_pretrain.sh # 3B 프리트레인 런처 (NCCL 환경변수 포함)
│ ├── launch_3b_sft.sh # 3B SFT v1 런처
│ ├── launch_3b_sft_v2.sh # 3B SFT v2 런처 (data mixing)
│ ├── launch_3b_orpo.sh # 3B ORPO 런처
│ ├── monitor_3b.sh # 실시간 학습 모니터
│ ├── training_watchdog.sh # 워치독 (10분 간격, 크론)
│ ├── convert_3b_gguf.sh # GGUF 변환 스크립트
│ ├── deploy_3b_ollama.sh # Ollama 배포
│ ├── quality_gate.sh # 배포 전 품질 게이트
│ ├── telegram_notify.py # 텔레그램 알림 (urllib 사용, curl 차단)
│ └── hourly_status.sh # 1시간 간격 상태 리포트
│
├── eval/
│ ├── debate/
│ │ └── justice_league_3b_case.md # 3B 전환 논증 (저스티스리그 멀티에이전트)
│ ├── decision/
│ │ └── FINAL_DECISION_REPORT.md # SFT 재시작 판결문
│ ├── plan/
│ │ └── 3B_MASTER_PLAN.md # 3B 마스터 플랜
│ ├── tasks/ # 모듈화된 평가 태스크
│ │ ├── task_runner.py # 8-GPU 병렬 태스크 실행기
│ │ ├── ppl_task.py # Perplexity 평가 태스크
│ │ ├── lm_eval_task.py # lm-evaluation-harness 래퍼
│ │ ├── calibration_task.py # Calibration 분석
│ │ ├── generation_task.py # 생성 품질 + 파라미터 그리드 서치
│ │ └── token_nll_task.py # Token NLL 분포 분석
│ ├── outputs/ # 평가 결과 (자동 생성, .gitignore)
│ ├── full_eval_pipeline.py # v2 종합 평가 파이프라인 (8-GPU 병렬)
│ ├── sft_eval_pipeline.py # SFT 6차원 평가 파이프라인
│ ├── reeval_pipeline.py # 재평가 파이프라인 (0+5-shot 연속)
│ ├── report_generator.py # 마크다운 리포트 자동 생성
│ ├── comprehensive_eval.py # v1 종합 평가 (레거시)
│ └── test_generation_params.py # 생성 파라미터 탐색
│
├── tokenizer/
│ ├── korean_sp/ # SentencePiece 64K 모델 파일
│ ├── tokenizer.json # HuggingFace 포맷 (2.4MB)
│ ├── train_sp_tokenizer.py # 토크나이저 학습 스크립트
│ └── convert_sp_to_hf.py # SentencePiece → HF 변환
│
├── checkpoints/ # 모델 체크포인트 (대용량, .gitignore)
│
├── docs/
│ ├── PROJECT_HISTORY.md # 프로젝트 전체 여정 상세 기록
│ └── 3B_WORKPLAN.md # 3B 작업 계획
│
└── reports/
├── 2026-03-02_0200_FRANKENSTALLM_phase0_optimization_report.md
├── 2026-03-05_3B_BASE_EVALUATION_REPORT.md
├── 2026-03-05_3B_SFT_PROGRESS_REPORT.md # SFT 학습 보고서 (Phase 2)
├── 2026-03-05_3B_NEXT_STEPS_REFERENCE.md
├── 2026-03-05_NEMOTRON_NANO_FEASIBILITY_STUDY.md
├── 2026-03-05_PPL_EVALUATION.md
├── 2026-03-05_BENCHMARK_RESULTS.md
├── 2026-03-05_GENERATION_QUALITY.md
├── 2026-03-06_3B_SFT_EVAL_PLAN.md # SFT 6차원 평가 계획서
├── 2026-03-06_3B_SFT_EVALUATION_REPORT.md # SFT 6차원 평가 결과
└── 2026-03-06_3B_SFT_COMPLETION_AND_EVAL_SUMMARY.md # SFT 완료 + 코드 개선 종합
5. 프로젝트 여정 타임라인
이 섹션이 이 README의 핵심이다. 결과만이 아니라 왜 그런 결정을 내렸는지, 어디서 실패했는지를 솔직하게 기록한다.
Day 1 (Feb 25) — 첫 불씨: 125M FP8 검증
프로젝트의 시작은 작은 의문에서 출발했다. B200에서 FP8이 실제로 안정적으로 학습되는가?
TransformerEngine의 MXFP8 레시피를 125M 소형 모델에 적용해 검증했다. 결론은 안정적으로 동작한다. loss 수렴도 정상이었고, VRAM 효율도 BF16 대비 확연한 개선이 있었다. 이 검증이 전체 파이프라인의 첫 번째 녹색 신호였다.
같은 날, 인프라 세팅도 완료했다. DDP 8-GPU 환경, NCCL 환경변수, 체크포인트 저장 경로, 텔레그램 알림 시스템의 초안이 이날 갖춰졌다.
Day 12 (Feb 2526) — 1B 프리트레인: 34K 스텝, PPL 5.67
125M 검증 직후 1B 모델 프리트레인에 돌입했다.
- 아키텍처: d_model=2048, 24 layers, GQA 4:1, SwiGLU, RoPE
- 데이터: C4 Korean 기반
- 학습: 34,000 스텝, FP8, 8× B200 DDP
최종 결과:
- Loss: 1.904
- PPL (C4 Korean): 5.67
수치만 보면 그럭저럭 괜찮다. 그러나 실제 텍스트 생성을 시켜보면 문제가 보였다. 반복 패턴, 어색한 문장 구조, 맥락 이탈. 프리트레인 모델이니 당연하다. 이제 SFT 차례였다.
Day 2 (Feb 26) — SFT v1: 0.0이라는 재앙
SFT를 돌렸다. 학습이 시작되자마자 loss가 빠르게 떨어지기 시작했다. 처음엔 좋은 신호라고 생각했다.
그런데 loss가 0.0이 됐다.
val loss도 0.0. 생성 결과는 완전한 쓰레기였다.
원인을 찾았다: label off-by-one 버그. 입력 토큰과 레이블 토큰이 한 칸씩 밀려 있었다. 모델이 실제로 다음 토큰을 예측하는 것이 아니라, 이미 알고 있는 정답을 맞추는 구조가 돼 있었다. loss가 0이 된 건 "완벽한 학습"이 아니라 데이터 누수(label leakage) 였다.
하루를 날렸다.
Day 3 (Feb 27) — 5가지 버그, 루트 코즈 분석
실패를 분석하기 위해 5-에이전트 루트 코즈 분석을 수행했다. 결론은 버그 하나가 아니었다. SFT 파이프라인 전체에 문제가 있었다.
발견된 5가지 핵심 버그:
| 버그 | 증상 | 영향 |
|---|---|---|
| Static padding (no packing) | 짧은 샘플도 max_len으로 패딩 | GPU 낭비, 학습 비효율 |
| EOS 토큰 절단 | 응답 끝에 EOS가 없음 | 모델이 "문장 끝"을 못 배움 |
| 단일 에폭 | 데이터를 한 번만 봄 | 언더피팅 |
| 검증 분리 없음 | val_loss 측정 불가 | 오버피팅 감지 불가 |
| 데이터 품질 | 노이즈, 중복, 불균형 | 반복 생성 패턴 유도 |
특히 EOS 절단 버그는 subtle하다. 모델이 응답을 마치는 시점을 배우지 못하면, 생성 시 끊임없이 같은 패턴을 반복하거나 의미 없는 토큰을 이어붙인다. 18% 반복률의 원인 중 하나였다.
Day 3 (Feb 27) — SFT v2: 성공이지만 18% 반복
5가지 버그를 모두 수정하고 SFT v2를 돌렸다.
- val_loss: 2.2062 — 합리적 수준
- 반복률: 18% (rep_penalty=1.1 적용 후)
생성 품질은 v1에 비해 확연히 개선됐다. 하지만 18% 반복률은 여전히 높다. rep_penalty를 높이면 반복은 줄지만 생성 다양성도 줄고 어색해진다. 디코딩 파라미터로 해결하기엔 구조적 한계가 있다.
kobest_copa 기준 0.646. 괜찮은 수치이지만 목표에는 미치지 못한다.
Day 3 (Feb 27) — "저스티스리그 vs 어벤저스": 3B 전환 결정
반복률 18%를 놓고 팀 내부 토론이 벌어졌다. 핵심 질문은 하나였다:
ORPO로 반복을 잡을 수 있는가, 아니면 3B로 가야 하는가?
이 질문에 답하기 위해 멀티에이전트 토론을 수행했다 (코드명: "저스티스리그 vs 어벤저스"). 각 에이전트가 다른 입장을 맡아 논증했다.
토론의 핵심 발견:
-
18% 반복은 1B 파라미터의 구조적 한계다. 1B 모델은 장거리 의존성(long-range dependency)을 충분히 포착하지 못한다. ORPO 같은 선호도 정렬은 반복을 줄이는 데 일부 도움이 되지만, 근본 원인(파라미터 부족)을 해결하지는 못한다.
-
스케일링 법칙 분석: Chinchilla 법칙과 실험 데이터를 기반으로 3B 모델은 동일 데이터에서 반복률을 5~8%까지 낮출 수 있다는 추정이 나왔다.
-
비용-편익 분석: ORPO를 1B에 투자하는 것보다 3B 프리트레인에 투자하는 것이 최종 모델 품질 측면에서 우월하다.
결론: 3B 전환. 1B는 아카이브하고 3B 프리트레인을 시작한다.
이 결정은 eval/debate/justice_league_3b_case.md에 전체 논증과 함께 기록돼 있다.
Day 3 (Feb 27) — 640GB+ 데이터 조립
3B 전환이 결정되자마자 데이터 파이프라인을 가동했다. 1B에 비해 훨씬 많은 데이터가 필요하다 (Chinchilla 최적 비율: 3B 모델 × 20 = 60B tokens).
최종적으로 조립한 데이터:
- 총 토큰: 41.12B tokens (최종 이진 파일)
- 원시 데이터: 640GB+ 다국어 텍스트
- 소스: C4 Korean, 나무위키, Wikipedia Korean, korean_extra 데이터셋
데이터 전처리(토크나이즈, 셔플, 이진 변환)가 완료된 data/3b_train.bin은 82GB다. 검증셋 data/3b_val.bin은 151MB.
Mar 2 — Phase 0: OOM 격퇴 및 최적화
3B 학습을 처음 시작하자 OOM(Out of Memory)이 발생했다. 183GB VRAM인데 3B 모델이 OOM이 난다는 게 이상하지만, 원인은 있었다.
GQA FlashAttention 구현 문제였다. GQA(Grouped-Query Attention)에서 KV 캐시를 expand하는 방식이 메모리를 불필요하게 복사하고 있었다. FlashAttention의 native GQA support를 제대로 활용하지 않은 것이다.
Phase 0에서 수행한 최적화 목록:
| 최적화 | 방법 | 효과 |
|---|---|---|
| GQA FA Native | flash_attn_varlen_func native GQA 경로 사용 |
VRAM 60.4GB → 48.3GB (-20%) |
| DDP 최적화 | gradient_as_bucket_view=True |
GPU-CPU 동기화 오버헤드 -87.5% |
| NCCL NVLS | Ring+Tree 토폴로지, NVLS 활성화 | AllReduce 효율 개선 |
| 배치 크기 분석 | GPU 2,4,6의 NCCL relay node 역할 파악 | bs=5 최적, bs=6 위험 판정 |
| SIGHUP 방어 | nohup+setsid + Python signal handler + emergency ckpt | 3중 보호 |
| 모니터링 | Telegram Bot (B200Bot) + cron | 10분 워치독, 1시간 상태 리포트 |
torch.compile 테스트: 효과 없음(1.00x). 원인은 TransformerEngine의 opaque kernel이 graph break를 유발하고, /tmp 디렉토리에 noexec 플래그가 걸려 있어 컴파일된 kernel 캐시가 쓰이지 않았다. 시간 낭비를 한 셈이지만, "효과 없다"는 것을 실측으로 확인한 것도 성과다.
bs=5의 이유: NCCL ring topology에서 GPU 2, 4, 6이 relay node 역할을 맡는다. 이 GPU들은 다른 GPU보다 약 11GB를 더 사용한다. bs=5에서는 여유가 있지만, bs=6으로 올리면 이 relay GPU들이 183GB 경계에 너무 가까워진다. 안전 마진을 위해 bs=5를 유지한다.
Mar 2~Mar 5 — Phase 1: 3B 프리트레인 완료
Phase 0 최적화가 완료된 후 Phase 1이 시작됐다.
초기 지표 (step 3150):
- Loss: 2.38
- 처리 속도: 36K tok/s per rank
- 시스템 전체: ~292K tok/s (8 GPU)
- MFU: ~33.5%
MFU 33.5%는 처음에는 낮아 보일 수 있다. 하지만 TE MXFP8가 이미 최적화된 상태에서 나온 수치다. 이론적 피크(18,000 TFLOPS) 대비 실효율이다. 추가 최적화 여지로 QKV fusion (+812%), NUMA affinity (+49%), FA2 native RoPE (+3~5%)가 남아있다.
Phase 1 완료 (2026-03-05):
- 57,000 steps 완료, 최종 loss 1.466
- 41.12B 토큰 처리, 총 학습 시간 약 63시간
- 무사고 완료 (SIGHUP, OOM, NCCL 이상 없음)
종합 평가 결과 요약 (v2 재평가 반영):
| 항목 | 결과 |
|---|---|
| PPL (통합 검증셋) | 5.2263 (초기 v1 평가: 5.709) |
| PPL (C4 Korean) | 5.717 |
| KoBEST 평균 (5태스크) | 43.69% |
| MMLU-KO 평균 (6카테고리) | 22.75% |
| HAE-RAE | 19.71% |
| winogrande / piqa | 50.59% / 52.50% |
| Calibration Top-1 | 68.75% |
| Greedy 3-gram 반복률 | 60.99% (SFT 후 개선 예정) |
| 최적 생성 파라미터 | temp=0.7, rep_penalty=1.3 → 반복률 0% |
SFT 진행 결정: loss 1.466은 건강한 학습 완료 시그널. PPL/반복률/벤치마크 모두 SFT가 해결할 영역. 모델 구조 문제 징후 없음. → Phase 2 SFT 진행.
Mar 5~ — Phase 2: 3B SFT 시작 — 2.44M 샘플, val_loss 1.956
Phase 1 완료 직후, 대규모 SFT 데이터를 준비하고 학습을 시작했다.
데이터 파이프라인:
- 24개 소스에서 6.59M raw samples 수집
prepare_sft_combined.sh: 포맷 통일(6가지 포맷 → messages), MD5 중복 제거, 98:2 splitfilter_sft_v2.py: 5단계 품질 필터 (EOS strip, QA marker 제거, 길이 필터, 4-gram 반복 필터)- 최종: 2,439,397 train + 49,801 val (7.48 GB)
데이터 구성은 추론/CoT(38%), 한국어 지시(22.5%), 영어 다목적(16%), 수학(12%), 대화/코드(11.5%)로 균형을 맞췄다. 1B SFT의 161K에서 15배 확대한 규모다.
SFT 설계 — 1B 실패에서 배운 교훈 반영:
| 1B 교훈 | 3B SFT 적용 |
|---|---|
| Label off-by-one → loss=0 | Loss masking 검증 (prompt=-1, response만 학습) |
| EOS 절단 → 종료 불가 | Chat template <|user|>...<|assistant|>...</s> EOS 포함 |
| Static padding → GPU 낭비 | Dynamic padding (64-token 정렬) |
| 검증 없음 → 오버피팅 미감지 | 49,801 val samples, 500 step 간격 eval |
| 데이터 노이즈 | 5단계 품질 필터 (1B에는 없었음) |
| 반복률 18% | NEFTune alpha=5.0 추가 (임베딩 노이즈 주입) |
학습 설정:
- LR: 1e-5 (pretrain의 1/15 — catastrophic forgetting 방지)
- Effective batch: 2 × 8 GPU × 4 accum = 64 sequences
- 33,000 steps (~3.3 epochs)
- MXFP8, gradient checkpointing, NCCL Ring+Tree
초기 결과 (step 2,000, 6%):
- Val loss: 2.073 → 2.004 → 1.975 → 1.956 (단조 감소)
- Train-Val 갭 ~0.1 (오버피팅 징후 없음)
- VRAM 24.2 GB (13.2%) — pretrain의 절반, 매우 안정
- Grad norm 1.0 일정 (학습률 적절)
상세 보고서: reports/2026-03-05_3B_SFT_PROGRESS_REPORT.md
Mar 6 — Phase 2 완료: SFT Early Stopping (val_loss 1.8851)
SFT는 33,000 steps 중 25,500 steps에서 early stopping으로 종료되었다. Val loss는 step 23,000에서 1.8851에 도달한 뒤, 5회 연속 개선 없이 학습이 자동 중단되었다.
총 학습 시간: ~15시간 41분 (2026-03-05 22:15 ~ 2026-03-06 13:56)
이 결과는 LR 1e-5의 cosine decay가 step 20K 이후 사실상 0에 수렴한 것과 일치한다. 모델은 주어진 LR schedule 하에서 학습 가능한 만큼 완전히 학습했다.
Mar 6 — SFT 6차원 종합 평가: 4/6 PASS → ORPO 결정
SFT 체크포인트(checkpoint-best, step 23000)에 대해 6차원 종합 평가를 수행했다. 49분 27초 소요.
핵심 결과:
- Perplexity: forgetting 0.9% (19개 데이터셋 전체 PASS) — 지식 보존 우수
- 반복률: greedy 72.97% (Base 60.99%보다 악화) — FAIL
- EOS 종료율: 0% → 60% — 개선됐지만 목표(90%) 미달
- KoBEST: 43.26% (Base 43.69%와 거의 동일) — FAIL
- MMLU-KO: 22.75% → 26.00% (+3.2pp) — 부분 개선
- Calibration: Top-1 68.59% — PASS
결정: greedy 반복률 72.97%는 SFT만으로 해결 불가. 그러나 rep_penalty=1.2 적용 시 반복률 0%가 달성되므로, ORPO(선호도 정렬)로 이 행동을 내재화하는 것이 올바른 경로다.
Mar 6 — 코드 개선 및 ORPO 준비
SFT 평가와 병행하여 다수의 코드 개선 및 Phase 3 준비를 완료했다:
| 변경 | 내용 | 영향 |
|---|---|---|
train/sft.py +238줄 |
MixingDataLoader (SFT+pretrain 인터리빙), DDP rank 0 토크나이징 | forgetting 방지, 메모리 8배 절감 |
train/trainer.py +17줄 |
DDP early stopping broadcast (hang 방지), patience 5→10 | DDP 안정성 |
train/orpo.py +30줄 |
YAML config 지원, 3B 기본값 | ORPO 실행 준비 |
eval/report_generator.py +831줄 |
Base vs SFT 비교 보고서 자동 생성 | 평가 자동화 |
eval/sft_eval_pipeline.py 신규 |
SFT 6차원 평가 파이프라인 | 종합 평가 |
eval/tasks/generation_task.py +75줄 |
Chat template, 다양성 메트릭 | SFT 평가 |
configs/korean_3b_sft_v2.yaml 신규 |
SFT v2 설정 (lr=5e-5, data mixing 70/30) | 백업 경로 |
configs/korean_3b_orpo.yaml 신규 |
ORPO 설정 (lr=5e-6, beta=0.1) | Phase 3 |
상세: reports/2026-03-06_3B_SFT_COMPLETION_AND_EVAL_SUMMARY.md
6. 모델 아키텍처
1B (아카이브)
| 항목 | 값 |
|---|---|
| vocab_size | 64,000 |
| d_model | 2,048 |
| n_layers | 24 |
| n_heads | 16 |
| n_kv_heads | 4 (GQA 4:1) |
| d_ffn | 5,461 (SwiGLU) |
| 파라미터 수 | ~1.19B |
| context | 2,048 |
| rope_theta | 500,000 |
3B (현재)
| 항목 | 값 |
|---|---|
| vocab_size | 64,000 |
| d_model | 3,072 |
| n_layers | 28 |
| n_heads | 24 |
| n_kv_heads | 8 (GQA 3:1) |
| d_ffn | 8,192 (SwiGLU) |
| 파라미터 수 | ~3.0B |
| context | 2,048 |
| rope_theta | 500,000 |
공통 설계 원칙
| 컴포넌트 | 선택 | 이유 |
|---|---|---|
| 정규화 | Pre-norm RMSNorm | Post-norm보다 학습 안정적 |
| 활성화 | SwiGLU FFN | Llama 계열에서 검증된 선택 |
| 위치 인코딩 | RoPE (θ=500K) | 긴 컨텍스트 확장 가능성 |
| 어텐션 | GQA (Grouped-Query Attention) | KV 캐시 메모리 절감 |
| 구현 | FlashAttention-2 | IO-aware, VRAM 효율 |
| 정밀도 | FP8 (MXFP8 via TransformerEngine) | B200 최적 활용 |
GQA 비율 선택 근거
1B는 GQA 4:1 (head 16개, kv_head 4개), 3B는 GQA 3:1 (head 24개, kv_head 8개)을 선택했다. 3B에서 비율을 다소 완화한 이유는, 파라미터 수가 늘어나면서 어텐션 품질을 다소 희생하는 것이 3B 규모에서는 손해라는 판단이었다. Mistral 7B (GQA 8:1)와 Llama 3 (GQA 8:1)를 참고했다.
rope_theta=500,000의 의미
표준 RoPE의 θ=10,000에서 500,000으로 늘린 것은 긴 컨텍스트에서 주파수 간섭을 줄이기 위해서다. Code Llama, Llama 3 등이 채택한 방식이다. 현재 max_seq_len=2048이므로 당장 효과를 보기는 어렵지만, 향후 컨텍스트 확장 파인튜닝을 위한 기반이다.
7. 학습 데이터
7.1 토크나이저
| 항목 | 값 |
|---|---|
| 종류 | SentencePiece Unigram |
| 어휘 크기 | 64,000 |
| 한국어 문자 커버리지 | 99.95% |
| 위치 | tokenizer/korean_sp/ |
| HF 포맷 | tokenizer/tokenizer.json (2.4MB) |
64K 어휘는 32K(너무 작음, 한국어 서브워드 단편화 심함)와 128K(너무 큼, 임베딩 레이어 오버헤드 증가) 사이의 균형이다. Llama 3(128K)와 GPT-4(100K)가 큰 어휘를 사용하는 추세지만, 3B 모델에서 128K 어휘는 임베딩 레이어만으로도 파라미터 비중이 지나치게 커진다.
7.2 프리트레인 데이터 — 전체 구성
최종 학습 파일: data/3b_train.bin (77GB, ~38.5B tokens) + data/3b_val.bin (145MB)
Chinchilla 법칙 기준: 3B × 20 = 60B 토큰이 최적이다. 현재 38.5B 토큰을 57,000 스텝(batch 5 × accum 8 × seq 2048 × 8 GPU)으로 반복 소비하며, 처음 3B 학습으로서 합리적인 범위다.
한국어 — 웹크롤 (Web Crawl)
| 데이터셋 | HuggingFace ID | 토큰화 파일 | 크기 | 추정 토큰 | 설명 |
|---|---|---|---|---|---|
| C4 Korean | allenai/c4 (ko subset) |
korean_c4_train.bin |
15GB | ~7.5B | Google C4 한국어 필터링, 대규모 클린 웹 텍스트 |
| CC-100 Korean | cc100 (ko subset) |
cc100_ko_train.bin |
4.3GB | ~2.15B | Common Crawl 기반 단일언어 코퍼스 |
| HPLT Korean | HPLT/hplt_monolingual_v2 (ko) |
hplt_ko_train.bin |
15GB | ~7.5B | High Performance Language Technologies 웹 데이터 |
한국어 — 백과사전 (Encyclopedia)
| 데이터셋 | HuggingFace ID | 토큰화 파일 | 크기 | 추정 토큰 | 설명 |
|---|---|---|---|---|---|
| 위키백과 한국어 | wikimedia/wikipedia (20231101.ko) |
wikipedia_ko_train.bin |
566MB | ~283M | 한국어 위키백과 전체, 구조화된 문어체 |
| 위키백과 한국어 (v2) | wikimedia/wikipedia (ko) |
korean_wiki_train.bin |
500MB | ~250M | 위키백과 별도 버전 |
| 나무위키 | heegyu/namuwiki-extracted |
korean_namuwiki_train.bin |
2.1GB | ~1.05B | 나무위키 추출본, 서브컬처·시사 풍부 |
| 나무위키 2023b | heegyu/namuwiki-extracted (2023b) |
namuwiki_2023b_train.bin |
2.5GB | ~1.25B | 2023년 업데이트 스냅샷 |
영어/다국어 — 교육 (Educational)
| 데이터셋 | HuggingFace ID | 토큰화 파일 | 크기 | 추정 토큰 | 설명 |
|---|---|---|---|---|---|
| Cosmopedia Stories | HuggingFaceTB/cosmopedia |
cosmo_stories_train.bin |
5.9GB | ~2.95B | 합성 교육용 스토리 |
| Cosmopedia Web v2 | HuggingFaceTB/cosmopedia |
cosmo_web_v2_train.bin |
2.7GB | ~1.35B | 웹 기반 교육 텍스트 |
| Cosmopedia Stanford | HuggingFaceTB/cosmopedia |
cosmo_stanford_train.bin |
2.1GB | ~1.05B | Stanford 강의 기반 |
| Cosmopedia WikiHow | HuggingFaceTB/cosmopedia |
cosmo_wikihow_train.bin |
382MB | ~191M | WikiHow 가이드 |
| Cosmopedia OpenStax | HuggingFaceTB/cosmopedia |
cosmo_openstax_train.bin |
224MB | ~112M | 오픈 교과서 |
| Cosmopedia Khan Academy | HuggingFaceTB/cosmopedia |
cosmo_khanacademy_train.bin |
46MB | ~23M | 칸 아카데미 |
영어/다국어 — 수학·과학 (Math & Science)
| 데이터셋 | HuggingFace ID | 토큰화 파일 | 크기 | 추정 토큰 | 설명 |
|---|---|---|---|---|---|
| Open Web Math | open-web-math/open-web-math |
open_web_math_train.bin |
4.8GB | ~2.4B | 웹에서 추출한 수학 텍스트 |
| MathPile | GAIR/MathPile |
mathpile_train.bin |
2.9GB | ~1.45B | 수학 교과서·논문·포럼 |
| Cosmopedia AutoMath | HuggingFaceTB/cosmopedia |
cosmo_auto_math_text_train.bin |
2.5GB | ~1.25B | 합성 수학 문제·풀이 |
한국어 — 혼합 (Legacy Merged)
| 데이터셋 | 토큰화 파일 | 크기 | 추정 토큰 | 설명 |
|---|---|---|---|---|
| 초기 혼합 (C4+나무+위키) | korean_train.bin |
17GB | ~8.5B | 1B 학습에 사용된 원본 혼합 데이터 |
| 125M 검증용 | train.bin |
1.2GB | ~600M | 최초 FP8 검증에 사용 |
미사용 수집 데이터 (korean_extra/ — 640GB+)
data/korean_extra/ 에 39개 서브디렉토리로 수집되었으나, 토큰화·병합은 일부만 완료된 대규모 원시 데이터:
| 분류 | 데이터셋 | 설명 | 비고 |
|---|---|---|---|
| 웹크롤 | CulturaX Korean | 대규모 다국어 웹 코퍼스 한국어 | ~50B+ tokens |
| 웹크롤 | FineWeb2 Educational Korean | 교육적 품질 필터링 웹 데이터 | 234GB raw |
| 웹크롤 | Korean Web Collection | KORMo 웹 컬렉션 | 175GB raw |
| 웹크롤 | OSCAR Korean | 다국어 웹 코퍼스 한국어 | |
| 교육 | Korean Textbooks | 한국어 교과서 텍스트 | 45개 서브카테고리 |
| 교육 | FinePDFs Educational Korean | PDF 기반 교육 자료 | |
| 법률 | Korean Law | 한국 법률 텍스트 | 15GB |
| 뉴스 | Korean News Archive | 한국어 뉴스 아카이브 | |
| 공개코퍼스 | Korean Public Corpus | KORMo 공개 코퍼스 | 26GB |
| 코드 | Code Pretrain | 프로그래밍 코드 | |
| 학술 | Academic Pretrain | 학술 논문·리포트 | |
| 범용 | SlimPajama | RedPajama 경량 버전 |
이 데이터는 Extended Pretrain (80-100B tokens) 단계에서 활용 예정이다.
프리트레인 데이터 분야별 비율
┌─────────────────────────────────────────────────────────┐
│ 3b_train.bin 토큰 구성 (~38.5B) │
├─────────────────────────────────────────────────────────┤
│ ████████████████████░░░░░░░░░░ 한국어 웹크롤 44.7% │
│ ██████████░░░░░░░░░░░░░░░░░░░░ 혼합 레거시 22.1% │
│ ██████░░░░░░░░░░░░░░░░░░░░░░░░ 교육 (EN) 14.7% │
│ █████░░░░░░░░░░░░░░░░░░░░░░░░░ 수학·과학 13.2% │
│ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 백과사전 (KO) 5.3% │
└─────────────────────────────────────────────────────────┘
7.3 SFT 데이터 — 2.44M 샘플 (현재 학습 중)
24개 소스에서 6.59M raw → 통합·중복 제거 → 품질 필터링 → 2,439,397 train + 49,801 val
주요 SFT 소스 (상위 12, 전체의 96%)
| # | 데이터셋 | 샘플 수 | 크기 | 도메인 |
|---|---|---|---|---|
| 1 | reasoning_r1_1.4m | 1,400,000 | 14.77 GB | 추론 (CoT) |
| 2 | openhermes_2.5 | 1,001,551 | 1.82 GB | 영어 다목적 |
| 3 | AI-MO_NuminaMath-CoT | 859,494 | 2.51 GB | 수학 CoT |
| 4 | korean_instruction_mix | 515,911 | 1.39 GB | 한국어 혼합 |
| 5 | lemon-mint_smol-koreantalk | 460,281 | 5.23 GB | 한국어 대화 |
| 6 | open_korean_instructions | 375,159 | 0.73 GB | 한국어 지시 |
| 7 | magpie_reasoning_v2 | 249,922 | 3.99 GB | 추론 (영어) |
| 8 | magpie_reasoning_ko | 224,929 | 3.19 GB | 추론 (한국어) |
| 9 | ultrachat_200k | 207,865 | 1.34 GB | 대화 |
| 10 | kuotient_orca-math-ko | 193,789 | 0.61 GB | 수학 (한국어) |
| 11 | data/sft/train.jsonl (원본) | 161,848 | 0.27 GB | 원본 SFT |
| 12 | kullm_v2 | 152,630 | 0.42 GB | 한국어 지시 |
기타 12개 소스: DeepMath-103K, Evol-Instruct-Code-80k-ko, ShareGPT-74k-ko, evol-instruct-korean, alpaca-gpt4-korean, ko_wikidata_QA, Ko.WizardLM, KOR-OpenOrca-Platypus-v3, korean-writing-style-instruct, ko_lima, koalpaca_v1_1a, OpenAssistant_oasst1_ko
데이터 처리 파이프라인
24개 소스 (6.59M raw)
↓ prepare_sft_combined.sh (포맷 통일, MD5 중복 제거, 98:2 split)
통합: 2,559,492 train + 52,234 val (7.95 GB)
↓ filter_sft_v2.py (5단계: EOS strip, QA marker 제거, 길이 50~20K, 4-gram 반복 >30% 제거)
최종: 2,439,397 train + 49,801 val (7.63 GB) ← 제거율 4.69%
도메인 비율
추론/CoT 38.0% ████████████████████████
한국어 지시 22.5% ██████████████
영어 다목적 16.0% ██████████
수학 12.0% ████████
대화/코드/기타 11.5% ███████
7.4 선호도 데이터 (ORPO용) — 795K 쌍
총 795,468 preference pairs (7.9GB, data/preference/combined_preference.jsonl)
| HuggingFace ID | 크기 | 분야 | 포맷 |
|---|---|---|---|
nayohan/preference-collection-ko-full |
4.9GB | 범용 선호도 평가 | instruction + response_A/B + preference |
heegyu/orca-math-korean-preference-cleaned |
1.6GB | 수학 추론 | prompt + chosen + rejected |
kuotient/orca-math-korean-dpo-pairs |
750MB | 수학 DPO | prompt + chosen + rejected |
maywell/ko_Ultrafeedback_binarized |
394MB | 피드백 기반 정렬 | prompt + winning/losing response |
tellang/yeji-preference-ko-v1 |
171MB | 범용 선호도 | prompt + chosen + rejected |
jojo0217/korean_rlhf_dataset |
137MB | RLHF 쌍 | prompt + chosen + rejected |
lemon-mint/korean-realqa-reasoning-v01-preference |
58MB | QA 추론 | prompt + chosen + rejected |
필터링 기준: 최소 길이 20자, EOS 제거, 포맷 정규화 후 통합
ORPO는 Phase 3에서 반복률이 5% 초과할 경우에만 실행한다. 3B 모델이 1B의 구조적 반복 문제를 스스로 해결한다면 ORPO 없이 배포할 수 있다.
7.5 데이터 파이프라인 요약
[HuggingFace / 웹 수집]
│
▼
┌─── 원시 수집 ───────────────────────────────────────────┐
│ korean_extra/ (39개 디렉토리, 640GB+) │
│ sft_extra/ (27개 디렉토리, 1.08M 샘플) │
│ preference/ (7개 JSONL, 795K 쌍) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─── 토큰화 (SentencePiece 64K) ──────────────────────────┐
│ tokenize_extra.py — 자동 포맷 감지 (Arrow/Parquet/JSONL) │
│ 8 workers 병렬 처리, uint16 memmap (.bin) 출력 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─── 최종 병합 ───────────────────────────────────────────┐
│ Pretrain: 3b_train.bin (77GB, ~38.5B tokens) │
│ SFT: sft_combined/train_filtered.jsonl (7.48GB, 2.44M 샘플) │
│ ORPO: preference/combined_preference.jsonl (7.9GB) │
└─────────────────────────────────────────────────────────┘
8. 학습 설정 및 최적화
현재 학습 설정 (configs/korean_3b_fp8.yaml)
model:
vocab_size: 64000
d_model: 3072
n_layers: 28
n_heads: 24
n_kv_heads: 8
d_ffn: 8192
max_seq_len: 2048
rope_theta: 500000.0
training:
batch_size: 5
gradient_accumulation_steps: 8
learning_rate: 1.5e-4
min_lr: 1.5e-5
warmup_steps: 2000
max_steps: 57000
weight_decay: 0.1
grad_clip: 1.0
optimizer: adamw
scheduler: cosine
fp8:
enabled: true
recipe: "mxfp8"
use_transformer_engine: true
distributed:
strategy: ddp
gradient_as_bucket_view: true
find_unused_parameters: false
nccl:
timeout_seconds: 7200
nvls_enabled: true
유효 배치 크기 = batch_size(5) × grad_accum(8) × num_gpus(8) = 320
LR 스케줄: warmup 2000 스텝 → cosine decay → min_lr=1.5e-5 (max_lr의 10%)
Phase 0에서 배운 최적화 교훈
GQA FlashAttention Native
가장 큰 VRAM 절감을 가져온 최적화. 핵심은 FlashAttention이 GQA를 native로 지원한다는 점이다. KV head를 expand하여 MHA처럼 처리하면 메모리 복사가 발생하지만, native path를 쓰면 내부에서 직접 처리한다.
# Before (비효율적): KV expand → MHA처럼 처리
k = k.repeat_interleave(n_heads // n_kv_heads, dim=1)
v = v.repeat_interleave(n_heads // n_kv_heads, dim=1)
out = flash_attn_func(q, k, v)
# After (native GQA): flash_attn이 내부에서 GQA 처리
out = flash_attn_func(q, k, v) # q: [B, S, H, D], k/v: [B, S, Hkv, D]
# VRAM 60.4GB → 48.3GB (-20%)
DDP 최적화
# gradient_as_bucket_view=True: gradient tensor를 bucket 메모리의 view로 직접 매핑
# → 불필요한 메모리 복사 제거, GPU-CPU 동기화 오버헤드 -87.5%
model = torch.nn.parallel.DistributedDataParallel(
model,
device_ids=[local_rank],
gradient_as_bucket_view=True,
find_unused_parameters=False, # 모든 파라미터가 사용됨
)
주의: static_graph=True는 사용하지 않는다. TransformerEngine의 te.Linear가 일부 케이스에서 dynamic graph를 요구하는데, static_graph를 켜면 런타임 에러가 발생한다.
NCCL NVLS
export NCCL_ALGO=NVLSTree # NVLink SHARP (NVLS) 활성화
export NCCL_PROTO=Simple
export NCCL_P2P_DISABLE=0
export NCCL_TIMEOUT=7200 # 긴 backward에 대비한 타임아웃 여유
NVSwitch가 All-to-All single hop을 지원하므로 Ring topology보다 NVLSTree가 효율적이다.
SIGHUP 3중 방어
장시간 학습에서 세션 연결 끊김(SIGHUP)은 치명적이다. 3중 보호를 구축했다:
# 1중: nohup + setsid (새 세션 그룹)
nohup setsid torchrun --nproc_per_node=8 train/pretrain.py ... &
# 2중: Python signal handler (Python 레벨 SIGHUP 무시)
import signal
signal.signal(signal.SIGHUP, signal.SIG_IGN)
# 3중: emergency checkpoint (SIGTERM에도 체크포인트 저장)
def emergency_save(signum, frame):
save_checkpoint(model, optimizer, step, "emergency")
sys.exit(0)
signal.signal(signal.SIGTERM, emergency_save)
torch.compile — 테스트 결과: 효과 없음
torch.compile을 적용해 speedup을 기대했지만 실측 결과 **1.00x (효과 없음)**이었다. 두 가지 이유:
- TransformerEngine의 kernel이 opaque하여 graph break가 발생한다.
torch.compile은 Python 연산 그래프를 최적화하는데, TE kernel은 그 그래프 밖에 있다. /tmp디렉토리에noexec마운트 플래그가 있어 컴파일된 kernel을 캐시하지 못한다.
교훈: "일단 써보자"보다 "왜 효과가 있는지 먼저 이해하자"가 중요하다.
모니터링 시스템
텔레그램 알림 시스템
├── B200Bot (token 설정됨)
├── training_watchdog.sh → 10분 간격 cron
│ └── loss 이상, 프로세스 종료 감지 → 즉시 알림
└── hourly_status.sh → 1시간 간격 cron
└── step, loss, 속도, VRAM, eta → 정기 리포트
# curl이 차단돼 있어 urllib 사용
import urllib.request, json
def send_telegram(message):
url = f"https://api.telegram.org/bot{TOKEN}/sendMessage"
data = json.dumps({"chat_id": CHAT_ID, "text": message}).encode()
req = urllib.request.Request(url, data=data,
headers={"Content-Type": "application/json"})
urllib.request.urlopen(req)
9. 실험 결과 — 1B 베이스라인
1B 모델의 실험 결과를 정직하게 기록한다. 성공과 실패 모두.
프리트레인 결과
| 지표 | 값 |
|---|---|
| 최종 Loss | 1.904 |
| PPL (C4 Korean) | 5.67 |
| 학습 스텝 | 34,000 |
| 학습 시간 | ~2일 |
SFT v1 결과 — 실패
| 지표 | 값 |
|---|---|
| val_loss | 0.0 (비정상) |
| 원인 | label off-by-one 버그 (데이터 누수) |
| 결론 | 전면 폐기 |
SFT v2 결과 — 부분 성공
| 지표 | 값 |
|---|---|
| val_loss | 2.2062 |
| 반복률 | 18% (rep_penalty=1.1 적용) |
| kobest_copa | 0.646 |
| 결론 | 기능하지만 구조적 한계 존재 |
3B 기대 목표치 (스케일링 법칙 기반 예측)
| 벤치마크 | 1B 현재 | 3B 목표 |
|---|---|---|
| kobest_copa | 0.646 | >0.72 |
| kobest_hellaswag | ~0.42 | >0.52 |
| 반복률 | 18% | <5% |
| PPL (C4 Korean) | 5.67 | <4.5 |
1B에서 3B로의 스케일업은 단순히 파라미터를 늘리는 것이 아니다. 모델이 더 긴 맥락을 기억하고, 더 다양한 패턴을 학습할 수 있어야 반복률이 구조적으로 낮아진다. 3B 목표치는 Chinchilla 스케일링 곡선과 유사 규모 모델들의 벤치마크를 참고한 예측값이다.
10. 실험 결과 — 3B Base 종합 평가 (v2)
3B 사전학습 완료 후 checkpoint-0057000 기준으로 수행한 종합 평가. v2 재평가는 8-GPU 병렬 파이프라인으로 13+ 벤치마크, 0/5-shot 비교, calibration, 참고모델 비교를 포함한다. 총 소요 시간 256.6초.
v1 → v2 변경점: v1(초기 평가)에서는 PPL 3개 데이터셋 + belebele/MMLU 2개 벤치마크만 측정했다. v2는 PPL 19개 데이터셋, KoBEST 5개, HAE-RAE 전체, MMLU-KO 6카테고리, MMLU-EN 61과목, 영어 5대 벤치마크, Calibration, 0/5-shot 비교, 12조합 파라미터 그리드 서치를 포함한다.
10.1 학습 커브
| Step | Loss | LR | 비고 |
|---|---|---|---|
| 10 | 11.657 | 1.50e-06 | 초기 (warmup 시작) |
| 500 | 5.047 | 7.50e-05 | warmup 진행 |
| 2,000 | 2.851 | 3.00e-04 | warmup 완료, peak LR |
| 10,000 | 2.057 | 2.86e-04 | 안정 하강 |
| 30,000 | 1.789 | 1.61e-04 | 중반, epoch 1 진입 |
| 57,000 | 1.466 | 3.00e-05 | 최종 (cosine min) |
처리 속도는 전 구간 36~38K tok/s로 안정. 총 학습 시간 약 63시간.
Base Model 백업
| 항목 | 값 |
|---|---|
| 원본 체크포인트 | checkpoints/korean_3b_fp8_run1/checkpoint-0057000/ (34GB) |
| 백업 | checkpoints/korean_3b_fp8_run1/checkpoint-0057000_BASE_BACKUP/ |
| MD5 검증 | 4f493d7bcc843727d32453bb3a4e6b7d (일치 확인) |
| HF 변환 | eval/outputs/hf_3b_base/ (11GB safetensors) |
10.2 PPL (Perplexity) — 19개 데이터셋
주요 PPL (3b_val 통합): 5.2263 (초기 v1 평가: 5.709)
| 데이터셋 | PPL | Bits/Token | 평가 토큰 | 소요 시간 |
|---|---|---|---|---|
| korean_namuwiki | 25.88 | 4.694 | 6.5M | 63.7s |
| cc100_ko | 21.78 | 4.445 | 13.6M | 133.2s |
| namuwiki_2023b | 18.92 | 4.242 | 7.7M | 75.1s |
| val | 18.30 | 4.194 | 9.1M | 89.4s |
| korean_wiki | 11.84 | 3.565 | 1.6M | 15.5s |
| wikipedia_ko | 10.71 | 3.420 | 1.8M | 17.4s |
| korean | 7.02 | 2.811 | 53.5M | 521.6s |
| open_web_math | 6.93 | 2.792 | 15.7M | 153.5s |
| korean_c4 | 5.72 | 2.515 | 45.4M | 443.1s |
| 3b (통합) | 5.23 | 2.386 | 226.9M | 2227.3s |
| cosmo_web_v2 | 4.17 | 2.059 | 8.6M | 84.6s |
| cosmo_stories | 3.96 | 1.984 | 18.9M | 185.2s |
| cosmo_openstax | 3.87 | 1.951 | 0.7M | 7.2s |
| cosmo_stanford | 3.36 | 1.750 | 6.6M | 65.3s |
| cosmo_wikihow | 3.31 | 1.727 | 1.2M | 11.8s |
| cosmo_auto_math_text | 3.15 | 1.655 | 7.9M | 77.3s |
| cosmo_khanacademy | 2.93 | 1.552 | 0.1M | 1.5s |
| mathpile | 2.72 | 1.446 | 7.1M | 69.9s |
| hplt_ko | 2.40 | 1.265 | 48.5M | 475.9s |
해석: in-distribution(학습에 포함된) 데이터(hplt_ko: 2.40, mathpile: 2.72)가 낮고, OOD(학습 비중 낮은) 데이터(cc100_ko: 21.78, namuwiki: 25.88)가 높은 것은 예상된 패턴. korean_c4 5.72는 v1의 5.717과 일치하여 평가 재현성을 확인.
10.3 한국어 벤치마크
KoBEST (0-shot) — 평균 43.69%
| 태스크 | Accuracy | F1 |
|---|---|---|
| kobest_boolq | 50.28% | 0.3457 |
| kobest_copa | 49.30% | 0.4921 |
| kobest_hellaswag | 21.60% | 0.2153 |
| kobest_sentineg | 48.61% | 0.4737 |
| kobest_wic | 48.65% | 0.3286 |
| 평균 | 43.69% |
HAE-RAE (0-shot) — 전체 19.71%
| 서브태스크 | Accuracy |
|---|---|
| haerae_general_knowledge | 21.59% |
| haerae_history | 23.40% |
| haerae_loan_word | 21.30% |
| haerae_rare_word | 18.77% |
| haerae_standard_nomenclature | 13.73% |
| 전체 | 19.71% |
MMLU-KO (0-shot) — 6카테고리 평균 22.75%
| 카테고리 | Accuracy |
|---|---|
| medical | 30.56% |
| humanities | 24.51% |
| business | 24.14% |
| social_sciences | 20.59% |
| other | 19.64% |
| stem | 19.57% |
| 평균 | 22.75% |
Base model은 instruction-following 없이 4지선다 형식 벤치마크를 풀도록 최적화되지 않음. KoBEST boolq/copa/sentineg/wic는 ~50% 수준으로 2지/4지선다 랜덤 기준 부근이며, SFT 후 향상 기대.
10.4 영어 벤치마크
주요 벤치마크 (0-shot)
| 태스크 | Accuracy | Acc (norm) |
|---|---|---|
| hellaswag | 26.00% | 26.15% |
| arc_easy | 25.63% | 26.64% |
| arc_challenge | 21.67% | 27.90% |
| winogrande | 50.59% | — |
| piqa | 52.50% | 48.31% |
winogrande(50.59%)와 piqa(52.50%)는 2지선다로 랜덤 기준 50%에 근접. hellaswag/arc는 4지선다로 랜덤 기준 25%.
MMLU-EN (0-shot) — 61과목 평균 25.81%
상위 10개 과목:
| 과목 | Accuracy |
|---|---|
| college_physics | 37.25% |
| college_computer_science | 34.00% |
| high_school_statistics | 33.80% |
| us_foreign_policy | 32.00% |
| security_studies | 31.43% |
| world_religions | 30.99% |
| professional_medicine | 30.88% |
| high_school_government_and_politics | 30.57% |
| jurisprudence | 30.56% |
| human_sexuality | 30.53% |
하위 5개 과목:
| 과목 | Accuracy |
|---|---|
| human_aging | 19.73% |
| college_biology | 19.44% |
| anatomy | 17.04% |
| global_facts | 17.00% |
| abstract_algebra | 15.00% |
10.5 Calibration
| 메트릭 | 값 |
|---|---|
| Top-1 Accuracy | 68.75% |
| Top-5 Accuracy | 81.64% |
| Top-10 Accuracy | 85.93% |
| Mean Correct Prob | 0.6152 |
| Mean Entropy | 1.5682 |
Token NLL 분포:
| 통계 | 값 |
|---|---|
| 평균 NLL | 1.5561 |
| 표준편차 | 2.4926 |
| 중앙값 | 0.1221 |
| p95 | 7.0312 |
| p99 | 10.3125 |
| NLL > 5 비율 | 10.86% |
| NLL > 10 비율 | 1.18% |
Top-1 68.75%는 모델이 가장 확신하는 예측이 ~69% 확률로 정확하다는 의미. 중앙값 NLL 0.12 (≈ e^0.12 = 1.13 PPL)로 대부분의 토큰을 매우 높은 확신도로 예측하고, 소수의 고난이도 토큰이 평균 NLL을 끌어올리는 전형적인 분포.
10.6 0-shot vs 5-shot 비교
18개 한국어 태스크에서 0-shot과 5-shot 성능을 비교했다.
| 태스크 | 0-shot | 5-shot | 변화 |
|---|---|---|---|
| global_mmlu_ko | 22.75% | 26.75% | +4.00pp |
| global_mmlu_ko_business | 24.14% | 31.03% | +6.90pp |
| global_mmlu_ko_humanities | 24.51% | 28.43% | +3.92pp |
| global_mmlu_ko_medical | 30.56% | 36.11% | +5.56pp |
| global_mmlu_ko_other | 19.64% | 23.21% | +3.57pp |
| global_mmlu_ko_social_sciences | 20.59% | 23.53% | +2.94pp |
| global_mmlu_ko_stem | 19.57% | 21.74% | +2.17pp |
| haerae | 19.71% | 20.26% | +0.55pp |
| haerae_general_knowledge | 21.59% | 22.73% | +1.14pp |
| haerae_history | 23.40% | 14.89% | -8.51pp |
| haerae_loan_word | 21.30% | 24.26% | +2.96pp |
| haerae_rare_word | 18.77% | 18.02% | -0.74pp |
| haerae_standard_nomenclature | 13.73% | 25.49% | +11.76pp |
| kobest_boolq | 50.28% | 50.21% | -0.07pp |
| kobest_copa | 49.30% | 46.80% | -2.50pp |
| kobest_hellaswag | 21.60% | 20.80% | -0.80pp |
| kobest_sentineg | 48.61% | 47.86% | -0.76pp |
| kobest_wic | 48.65% | 48.97% | +0.32pp |
평균 변화: +1.80pp | 개선: 12 | 하락: 6
MMLU-KO는 5-shot에서 일관되게 개선(+2~7pp)되어 in-context learning 능력이 작동함을 확인. KoBEST는 거의 변동 없거나 소폭 하락—이미 0-shot에서 패턴 매칭을 잘하고 있어 few-shot 예시가 오히려 방해가 되는 패턴. haerae_standard_nomenclature의 +11.76pp는 이 태스크의 특수한 포맷을 few-shot에서 학습한 결과.
10.7 참고 모델 비교
| 모델 | 파라미터 | MMLU-KO | MMLU-EN | KoBEST 평균 | PPL |
|---|---|---|---|---|---|
| FRANKENSTALLM 3B | 3B | 22.75% | 25.81% | 43.69% | 5.2263 |
| Llama-3.2-3B | 3B | ~42% | ~58% | ~55% | — |
| Qwen2.5-3B | 3B | ~48% | ~65% | ~60% | — |
| EXAONE-3.5-2.4B | 2.4B | ~35% | ~50% | ~50% | — |
참고 모델들은 수조 토큰 규모의 학습 데이터와 수천 GPU-hour를 투입한 결과. FRANKENSTALLM 3B는 41.12B 토큰(Chinchilla 최적의 ~68%), 63시간, 8 GPU로 학습한 점을 감안해야 한다. SFT + 확장 프리트레인(80-100B 토큰) 이후 격차 축소 예상.
10.8 생성 품질 및 파라미터 그리드 서치
반복률 요약
| 설정 | 3-gram 반복률 | 4-gram 반복률 |
|---|---|---|
| greedy (temp=0.0) | 60.99% | 57.02% |
| temp=0.5 | 60.12% | 58.68% |
| temp=0.7 | 47.69% | 43.40% |
| temp=1.0 | 3.58% | 2.81% |
초기 v1 평가의 greedy 71.1% 반복률은
no_repeat_ngram_size=3적용 기준이었다. v2에서는 미적용 기준(raw)으로 통일하여 60.99%를 기록.
12조합 파라미터 그리드 서치 결과
| 설정 | Temp | Rep Pen | 3-gram | 4-gram | 비고 |
|---|---|---|---|---|---|
| t0.7_rep1.3 | 0.70 | 1.30 | 0.00% | 0.00% | 최적 |
| t0.9_rep1.2 | 0.90 | 1.20 | 0.00% | 0.00% | 차선 |
| t0.7_rep1.2 | 0.70 | 1.20 | 0.88% | 0.00% | |
| t0.9_rep1.1 | 0.90 | 1.10 | 0.94% | 0.13% | |
| t1.0_rep1.1 | 1.00 | 1.10 | 1.21% | 0.48% | |
| t0.5_rep1.1 | 0.50 | 1.10 | 1.92% | 1.19% | |
| t1.0 | 1.00 | 1.00 | 3.58% | 2.81% | |
| t0.9 | 0.90 | 1.00 | 8.39% | 4.64% | |
| t0.7_rep1.1 | 0.70 | 1.10 | 8.51% | 5.51% | |
| t0.7 | 0.70 | 1.00 | 47.69% | 43.40% | |
| t0.5 | 0.50 | 1.00 | 60.12% | 58.68% | |
| greedy | 0.00 | 1.00 | 60.99% | 57.02% |
권장 추론 파라미터 (base 실험용)
# v2 그리드 서치 최적값
temp=0.7, repetition_penalty=1.3
# 또는 (더 다양한 생성)
temp=0.9, repetition_penalty=1.2
초기 v1 권장값(
temp=0.9, top_p=0.9, no_repeat_ngram=3, repetition_penalty=1.1)에서repetition_penalty=1.3으로 상향 조정.no_repeat_ngram_size는 그리드 서치에서repetition_penalty만으로 충분히 반복 제거가 가능함을 확인하여 불필요.
10.9 평가 파이프라인
v2 재평가는 모듈화된 8-GPU 병렬 파이프라인(eval/reeval_pipeline.py)으로 수행되었다.
아키텍처
reeval_pipeline.py
├── 모델 1회 로드 (GPU 0에 HF 모델)
├── Phase 1: PPL 평가 (19개 데이터셋, 순차)
├── Phase 2: Calibration + Token NLL
├── Phase 3: 생성 품질 + 파라미터 그리드 서치 (12조합)
├── Phase 4: lm-evaluation-harness (0-shot, 8-GPU 병렬)
├── Phase 5: lm-evaluation-harness (5-shot, 8-GPU 병렬)
└── Phase 6: 리포트 자동 생성 (5개 개별 + 1개 종합)
Pipeline Mode
모델을 1회 로드하여 0-shot과 5-shot을 연속 실행한다. 기존 방식(별도 프로세스 2회)에 비해 모델 로딩 시간을 절반으로 줄인다.
GPU별 태스크 분배
| GPU | 0-shot 태스크 | 5-shot 태스크 |
|---|---|---|
| 0 | kobest_boolq, kobest_copa, kobest_hellaswag | 동일 |
| 1 | kobest_sentineg, kobest_wic | 동일 |
| 2 | haerae (전체 + 5개 서브) | 동일 |
| 3 | global_mmlu_ko (6카테고리) | 동일 |
| 4 | hellaswag, arc_easy | 동일 |
| 5 | arc_challenge, winogrande | 동일 |
| 6 | piqa, global_mmlu_en (61과목) | 동일 |
| 7 | (예비 — PPL/calibration 전담) | — |
NUMA affinity 적용: GPU 0-3은 NUMA node 0 (cores 0-35), GPU 4-7은 NUMA node 1 (cores 36-71).
총 소요 시간: 256.6초 (모델 로드 포함)
SFT 진행 판단
결론: SFT 진행 — loss 1.466 건강한 완료 시그널, 구조 문제 없음. → Phase 2 SFT 시작 (2026-03-05)
상세 보고서:
- v2 종합:
eval/outputs/3b_reeval_20260305_1451/reports/(5개 개별 리포트 + 종합) - v1 레거시:
reports/2026-03-05_3B_BASE_EVALUATION_REPORT.md
11. 실험 결과 — 3B SFT 종합 평가
Phase 2 SFT가 early stopping으로 완료된 후 수행한 6차원 종합 평가.
11.1 SFT 학습 결과
| 항목 | 값 |
|---|---|
| 최종 Step | 25,500 / 33,000 (77.3%, early stopping) |
| Best val_loss | 1.8851 (step 23,000) |
| 학습 시간 | ~15시간 41분 |
| 데이터 | 24개 소스 → 2,439,397 samples (7.48 GB) |
| 설정 | LR=1e-5, eff_batch=64, NEFTune alpha=5.0 |
Val Loss 추이:
Step 500: 2.0732 (warmup 완료)
Step 2,000: 1.9558 (급속 하강)
Step 5,000: 1.9107 (안정 수렴)
Step 10,000: 1.8917 (미세 감소)
Step 15,000: 1.8864 (plateau 진입)
Step 20,000: 1.8853 (변동 < 0.001)
Step 23,000: 1.8851 ← BEST (early stopping 기준점)
Step 25,500: Early Stop (patience 5/5 소진)
11.2 6차원 평가 요약
| # | 차원 | 결과 | 핵심 수치 |
|---|---|---|---|
| 1 | Perplexity (지식 보존) | PASS | 최대 forgetting 0.9%, 19개 데이터셋 전체 PASS |
| 2 | 생성 품질 | FAIL | Greedy 반복률 72.97% (목표 <5%), EOS 60% (목표 >90%) |
| 3 | 한국어 벤치마크 | FAIL | KoBEST 평균 43.26% (목표 >55%) |
| 4 | 영어 벤치마크 | PASS | hellaswag 26.1%, winogrande 50.8%, piqa 52.6% (전 항목 하한 초과) |
| 5 | Calibration | PASS | Top-1 68.59%, Top-5 81.55%, Entropy 1.54 |
| 6 | SFT Chat 능력 | PASS | EOS 종료율 0%→60%, Chat template 응답 |
11.3 Base vs SFT 비교
| 지표 | Base | SFT | 변화 | 판정 |
|---|---|---|---|---|
| PPL (통합) | 5.2263 | 5.2529 | +0.5% forgetting | PASS |
| Greedy 3-gram 반복률 | 60.99% | 72.97% | +12pp (악화) | FAIL |
| EOS 종료율 | 0% | 60% | +60pp (대폭 개선) | 부분 PASS |
| KoBEST 평균 | 43.69% | 43.26% | -0.4pp | FAIL |
| MMLU-KO | 22.75% | 26.00% | +3.2pp | 부분 개선 |
| 영어 벤치마크 | — | — | ±0.3pp 이내 | PASS (유지) |
| Calibration Top-1 | 68.75% | 68.59% | -0.2pp | PASS (유지) |
Repetition 파라미터 검색 (희망적):
| 설정 | 반복률 | EOS Rate |
|---|---|---|
| t0.7_rep1.2 | 0.00% | 100% |
| t1.0_rep1.1 | 0.00% | 100% |
| greedy (raw) | 72.97% | 60% |
rep_penalty 1.1~1.3 적용 시 반복률 0% 달성 → 모델이 반복하지 않는 능력 자체는 보유. ORPO로 내재화 가능.
11.4 코드 개선 사항
이번 Phase에서 수행한 주요 코드 변경:
| 파일 | 변경 | 줄 수 | 목적 |
|---|---|---|---|
train/sft.py |
MixingDataLoader, DDP rank 0 토크나이징 | +238 | SFT+pretrain 인터리빙, 메모리 8배 절감 |
train/trainer.py |
DDP early stop broadcast | +17 | DDP hang 방지, patience 5→10 |
train/orpo.py |
YAML config, 3B 기본값 | +30 | ORPO 실행 준비 |
eval/report_generator.py |
SFT 비교 보고서 자동 생성 | +831 | 평가 자동화 |
eval/sft_eval_pipeline.py |
6차원 평가 파이프라인 | 신규 | SFT 종합 평가 |
eval/tasks/generation_task.py |
Chat template, diversity metrics | +75 | SFT 평가 지원 |
11.5 ORPO 진행 판정
판정: Phase 3 ORPO 진행
| 근거 | 상세 |
|---|---|
| 지식 보존 양호 | forgetting 0.9% — SFT가 base 지식을 파괴하지 않음 |
| 반복 미해결 | greedy 72.97% — 선호도 정렬이 직접적 해결 경로 |
| 희망적 신호 | rep_penalty 적용 시 0% → ORPO가 내재화 가능 |
| 데이터 준비 완료 | 795,468 preference pairs (7.9 GB) |
| 코드/설정 완비 | train/orpo.py + configs/korean_3b_orpo.yaml |
ORPO 후 판정 기준:
- 반복률 < 5% AND KoBEST > 50% → GGUF + Ollama 배포
- 반복률 5~15% → 하이퍼파라미터 조정 후 재시도
- 반복률 > 15% → SFT v2 (lr=5e-5, data mixing) 후 재도전
상세: reports/2026-03-06_3B_SFT_COMPLETION_AND_EVAL_SUMMARY.md
12. Phase 3 — ORPO (선호도 정렬)
12.1 ORPO 선택 배경
SFT 6차원 평가에서 greedy 반복률 72.97%, EOS 종료율 0%라는 치명적 문제가 발견됐다. SFT는 "좋은 응답만 모방"하는 학습이므로, "나쁜 응답을 억제"하는 신호가 없다. 반복 문제 해결에는 preference optimization이 필수적이다.
ORPO vs DPO:
| 항목 | ORPO | DPO |
|---|---|---|
| Reference model | 불필요 | 필요 (VRAM 2배) |
| 구현 복잡도 | 낮음 | 중간 |
| 메모리 효율 | 높음 (3B 1개만 로드) | 낮음 (3B 2개 로드) |
| 학습 안정성 | 중간 | 높음 |
ORPO를 1차 선택, DPO를 Plan B로 설정했다.
12.2 데이터
- 원본: 683,181 preference pairs (7개 소스 통합)
- 필터 후: ~630,000 pairs (NaN 방지 필터 적용)
- Eval split: 5% (~31,500 pairs, seed=42)
- Effective batch: 4 × 8 GPU × 4 accum = 128
12.3 HP Sweep 설계 (6-Config)
3개 축(beta, LR, max_length)을 중심축 고정 방식으로 6개 조합 선정:
| Run | Name | Beta | LR | Max Length | 목적 |
|---|---|---|---|---|---|
| 1 | baseline_b015 | 0.15 | 8e-6 | 1536 | 약한 beta 베이스라인 |
| 2 | baseline_b025 | 0.25 | 8e-6 | 1536 | 중간 beta 베이스라인 |
| 3 | strong_b035 | 0.35 | 8e-6 | 1536 | 강한 beta — 적극적 반복 억제 |
| 4 | fast_lr12e6 | 0.25 | 1.2e-5 | 1536 | 높은 LR — 빠른 수렴 |
| 5 | conserv_lr5e6 | 0.25 | 5e-6 | 1536 | 보수적 LR — 안정성 |
| 6 | short_1024 | 0.25 | 8e-6 | 1024 | 짧은 max_length — VRAM 절약 |
각 200 steps, eval_steps=100, 8×B200 DDP.
12.4 시도 이력 — 5번의 실패
| # | 문제 | 원인 | 수정 |
|---|---|---|---|
| 1 | NCCL Timeout | 토크나이징 30분 > timeout 1800s | ddp_timeout=7200, num_proc=64 |
| 2 | Config 충돌 | save_steps ≠ eval_steps 배수 | --no_load_best --save_steps 200 |
| 3 | 포트 충돌 + QKV 누락 | 좀비 프로세스 + fused QKV 미분리 | pkill + QKV split 로직 |
| 4 | TRL NaN 버그 | tokenize_row 양쪽 response 동시 잘림 | 3중 패치 (clamp, truncation) |
| 5 | Tokenizer 호환 | zip(strict=True) + 한국어 merge ops | TRL 소스 8건 패치 |
가장 심각했던 것은 TRL NaN 버그로, 0 response tokens → log(0) = -inf → NaN 전파 체인을 일으켰다. 상세: reports/2026-03-08_ORPO_TRAINING_JOURNEY.md
12.5 스윕 최종 결과
| Run | Name | Beta | LR | MaxLen | Train Loss | Eval Loss | Margin | Status |
|---|---|---|---|---|---|---|---|---|
| 1 | baseline_b015 | 0.15 | 8e-6 | 1536 | 1.811 | 1.827 | 0.004 | ✅ |
| 2 | baseline_b025 | 0.25 | 8e-6 | 1536 | 1.890 | 1.906 | 0.009 | ✅ |
| 3 | strong_b035 | 0.35 | 8e-6 | 1536 | 2.055 | 1.985 | 0.007 | ✅ |
| 4 | fast_lr12e6 | 0.25 | 1.2e-5 | 1536 | 1.917 | 1.862 | 0.009 | 🏆 Best |
| 5 | conserv_lr5e6 | 0.25 | 5e-6 | 1536 | 1.833 | 1.910 | 0.004 | ✅ |
| 6 | short_1024 | 0.25 | 8e-6 | 1024 | 1.664 | 1.695 | 0.007 | ✅ |
Best config: Run 4 (eval_loss 1.862 최저, margin 0.009 최고, 빠른 수렴).
12.6 Throughput 벤치마크 → 본 학습 설정
본 학습 전 batch/grad_accum 조합의 throughput을 측정하여 최적 설정을 결정:
| batch_size | grad_accum | eff_batch | Throughput | 비고 |
|---|---|---|---|---|
| 4 | 4 | 128 | 80.63 samples/s | 선정 |
| 2 | 8 | 128 | 73.14 samples/s | 기존 설정 |
| 8 | 2 | 128 | OOM |
12.7 ORPO 본 학습 (진행 중, 2026-03-09)
| 파라미터 | 값 |
|---|---|
| Beta / LR | 0.25 / 1.2e-5 (Sweep Run 4) |
| Batch / Accum / Eff | 4 / 4 / 128 (벤치마크 최적) |
| Max length | 1536 |
| Epochs | 2 (~9,840 steps) |
| GPU VRAM | ~52GB / 183GB (28%) |
| 속도 | ~1.75 s/step |
| 예상 시간 | ~4.8시간 |
학습 지표 추이 (step ~1,660 기준):
| Step | Eval Loss | Pref Accuracy | Reward Margin | NLL Loss |
|---|---|---|---|---|
| ~1,000 | 1.791 | 66.8% | 0.107 | 1.647 |
| ~2,000 | 1.713 | 70.1% | 0.293 | 1.591 |
| ~3,000 | 1.681 | 71.9% | 0.372 | 1.567 |
- Train loss: 2.34 → 1.68 (-0.66)
- rewards/accuracies: 0.43 → 0.74 (chosen/rejected 구분 능력 급상승)
- rewards/margins: -0.005 → 0.387 (preference signal 학습 확인)
- 속도
1.76 s/step, GPU 92100% utilization, 안정적 진행 중
학습 완료 후 자동 평가: scripts/orpo_eval_watchdog.sh 가 학습 프로세스를 감시하며, 완료 시 자동으로 10차원 종합 평가 파이프라인 실행
12.8 ORPO 종합 평가 파이프라인
SFT v2 평가의 6차원에 ORPO 고유 4차원을 추가한 10차원 종합 평가.
학습 완료 시 eval/orpo_eval_pipeline.py가 자동 실행되어 Base vs SFT vs ORPO 3-way 비교 보고서를 생성한다.
평가 구조:
| Phase | 내용 | GPU | 예상 시간 |
|---|---|---|---|
| Pre-phase | train.log에서 학습 곡선 추출 | - | ~1초 |
| Phase 1 | 내부 평가 (PPL 19셋, Calibration, Generation, Repetition Grid) | 8 GPU 병렬 | ~30분 |
| Phase 2 | 벤치마크 (KoBEST, HAE-RAE, MMLU-KO/EN, hellaswag, arc, piqa) | 8 GPU 병렬 | ~1시간 |
| Phase 3 | 3-way 비교 보고서 자동 생성 | - | ~10초 |
10차원 평가 항목:
| # | 차원 | 기준 | SFT v2 결과 | ORPO 목표 |
|---|---|---|---|---|
| 1 | 지식 보존 (PPL) | forgetting < 15% | 0.9% | < 5% |
| 2 | 생성 품질 | greedy 반복률 < 5%, EOS > 90% | 72.97% / 60% | < 5% / > 90% |
| 3 | 한국어 벤치마크 | KoBEST 평균 > 55% | 43.26% | ≥ 43% |
| 4 | 영어 벤치마크 | 하한 초과 | PASS | 유지 |
| 5 | Calibration | Top-1 ≥ 65% | 68.59% | ≥ 65% |
| 6 | Chat 능력 | EOS 종료율 | 60% | > 90% |
| 7 | Preference Accuracy | > 65% | — | > 65% |
| 8 | Reward Margins | > 0.1 | — | > 0.1 |
| 9 | 반복 파라미터 민감도 | rep_penalty=1.0에서도 < 5% | — | PASS |
| 10 | SFT→ORPO 개선 | 반복률↓ + EOS↑ | — | PASS |
핵심 파일:
eval/orpo_eval_pipeline.py— ORPO 평가 오케스트레이터eval/report_generator.py— 3-way 비교 보고서 생성기 (generate_three_way_report())scripts/orpo_eval_watchdog.sh— 학습 완료 감지 + 자동 평가 실행
배포 기준: greedy 반복률 < 5% AND EOS > 90% AND forgetting < 5% AND KoBEST ≥ 43% → DEPLOY
13. 실행 방법
사전 요구사항
# PyTorch는 재설치 금지 (NVIDIA 커스텀 빌드)
# 아래 패키지만 추가 설치
pip install transformers accelerate peft trl deepspeed \
bitsandbytes sentencepiece wandb
3B 프리트레인
# NCCL 환경변수와 함께 8-GPU 학습 실행
bash scripts/launch_3b_pretrain.sh
# 수동 실행 (직접 제어)
torchrun --nproc_per_node=8 \
--master_port=29500 \
train/pretrain.py \
--config configs/korean_3b_fp8.yaml
SFT
bash scripts/launch_3b_sft.sh
# 또는 직접 실행
torchrun --nproc_per_node=8 \
train/sft.py \
--config configs/korean_3b_sft.yaml \
--pretrain_ckpt checkpoints/3b_pretrain_best.pt
ORPO (선호도 정렬)
# ORPO 학습
bash scripts/launch_3b_orpo.sh
# 학습 완료 후 자동 평가 (watchdog)
nohup bash scripts/orpo_eval_watchdog.sh \
> checkpoints/korean_3b_orpo_v1/watchdog.log 2>&1 &
평가
# Base 모델 전체 평가 (8 GPU 병렬)
python eval/full_eval_pipeline.py
# SFT 모델 평가 (Base vs SFT 2-way 비교)
python eval/sft_eval_pipeline.py --skip-phase0 \
--hf-model-path eval/outputs/hf_3b_sft_best
# ORPO 모델 평가 (Base vs SFT vs ORPO 3-way 비교)
python eval/orpo_eval_pipeline.py # 자동으로 최신 checkpoint 감지
python eval/orpo_eval_pipeline.py --dry-run # 실행 계획만 확인
# 빠른 평가 (kobest_copa + PPL)
bash scripts/run_eval_quick.sh
# 생성 파라미터 탐색
python eval/test_generation_params.py \
--checkpoint checkpoints/3b_best.pt
배포
# Step 1: GGUF 변환 (llama.cpp 포맷)
bash scripts/convert_3b_gguf.sh
# Step 2: Ollama 모델 등록 및 서빙
bash scripts/deploy_3b_ollama.sh
# Ollama로 테스트
ollama run frankenstallm-3b "한국의 철강 산업에 대해 설명해줘."
학습 모니터링
# 실시간 모니터 (tail -f 방식)
bash scripts/monitor_3b.sh
# 프로세스 상태 확인
ps aux | grep pretrain
# GPU 상태
nvidia-smi --query-gpu=index,name,memory.used,memory.total,utilization.gpu \
--format=csv -l 5
단일 GPU 테스트 (개발/디버그)
python train/pretrain.py \
--config configs/korean_3b_fp8.yaml \
--device cuda:0 \
--max_steps 100 \
--debug
14. 로드맵
단기 (2026년 3월)
| 항목 | 상태 | 비고 |
|---|---|---|
| Phase 1 (3B Pretrain) 완료 | ✅ 완료 | 57K steps, loss 1.466, 2026-03-05 |
| Phase 2 (SFT) 완료 | ✅ 완료 | 25.5K steps, val_loss 1.8851, 2026-03-06 |
| SFT 6차원 평가 | ✅ 완료 | 4/6 PASS, ORPO 판정 |
| Phase 3 (ORPO Sweep) | ✅ 완료 | 6-config sweep 완료, best config 선정 |
| Phase 3 (ORPO 본 학습) | 🔄 진행 중 | lr=1.2e-5, beta=0.25, 2 epochs, ~9,840 steps |
| Phase 3.5 (ORPO 종합 평가) | 📋 대기 | 10차원 평가 (6 기본 + 4 ORPO 고유), 3-way 비교 보고서 |
| GGUF 변환 + Ollama 배포 | 📋 대기 | Phase 4 (ORPO 평가 PASS 시) |
중기 (2026년 2분기)
| 항목 | 비고 |
|---|---|
| 확장 프리트레인 (80~100B 토큰) | Chinchilla 최적점 달성 |
| QKV Fusion | +8~12% MFU 기대 |
| NUMA Affinity 설정 | +4~9% 예상 |
| FA2 native RoPE | +3~5% 예상 |
| Context length 확장 (4096) | RoPE θ=500K 기반 |
장기 (2026년 하반기)
| 항목 | 비고 |
|---|---|
| 7B 실험 | FSDP 전략 필요 |
| vLLM serving | PagedAttention 기반 추론 서버 |
| 도메인 특화 파인튜닝 | 철강/제조업 도메인 |
| 공개 배포 | HuggingFace Hub 업로드 |
알려진 미적용 최적화
Phase 0 분석에서 발견했지만 아직 적용하지 않은 최적화들:
| 최적화 | 예상 효과 | 구현 복잡도 |
|---|---|---|
| QKV Fusion | +8~12% MFU | 중간 |
| NUMA Affinity | +4~9% | 낮음 |
| FA2 Native RoPE | +3~5% | 낮음 |
| HugePages | +1~3% (TLB 최적화) | 낮음 (sysctl) |
이 최적화들을 모두 적용하면 현재 33.5% MFU에서 45~50%까지 도달할 가능성이 있다.
15. 참고 문서
| 문서 | 위치 | 내용 |
|---|---|---|
| 프로젝트 전체 여정 | docs/PROJECT_HISTORY.md |
일별 상세 진행 기록 |
| 3B 작업 계획 | docs/3B_WORKPLAN.md |
3B 단계별 작업 계획 상세 |
| 저스티스리그 논증 | eval/debate/justice_league_3b_case.md |
1B→3B 전환 멀티에이전트 토론 전문 |
| SFT 재시작 판결 | eval/decision/FINAL_DECISION_REPORT.md |
SFT v1 실패 → v2 설계 판결문 |
| 3B 마스터 플랜 | eval/plan/3B_MASTER_PLAN.md |
전체 학습 파이프라인 마스터 플랜 |
| Phase 0 최적화 보고서 | reports/2026-03-02_0200_FRANKENSTALLM_phase0_optimization_report.md |
VRAM/MFU 최적화 전체 보고 |
| 3B Base 평가 보고서 (v1) | reports/2026-03-05_3B_BASE_EVALUATION_REPORT.md |
초기 PPL/벤치마크/반복률 평가 |
| PPL 평가 보고서 (v1) | reports/2026-03-05_PPL_EVALUATION.md |
4개 검증셋 PPL 상세 |
| 벤치마크 결과 (v1) | reports/2026-03-05_BENCHMARK_RESULTS.md |
belebele, MMLU 상세 |
| 생성 품질 분석 (v1) | reports/2026-03-05_GENERATION_QUALITY.md |
반복률, 디코딩 파라미터 |
| SFT 학습 보고서 | reports/2026-03-05_3B_SFT_PROGRESS_REPORT.md |
Phase 2 SFT 학습 과정 기록 |
| SFT 완료 종합 보고서 | reports/2026-03-06_3B_SFT_COMPLETION_AND_EVAL_SUMMARY.md |
SFT 완료 + 평가 + 코드 개선 + ORPO 결정 (최신) |
| SFT 평가 계획서 | reports/2026-03-06_3B_SFT_EVAL_PLAN.md |
6차원 평가 설계 |
| SFT 평가 결과 | reports/2026-03-06_3B_SFT_EVALUATION_REPORT.md |
6차원 평가 상세 결과 |
| 3B 후속 단계 참조 | reports/2026-03-05_3B_NEXT_STEPS_REFERENCE.md |
SFT 후 방향성 |
| Nemotron Nano 타당성 | reports/2026-03-05_NEMOTRON_NANO_FEASIBILITY_STUDY.md |
Hybrid 아키텍처 검토 |
| v2 종합 평가 리포트 | eval/outputs/3b_reeval_20260305_1451/full_eval_report.md |
13+ 벤치마크 종합 |
| v2 PPL 리포트 | eval/outputs/3b_reeval_20260305_1451/reports/01_perplexity_report.md |
19개 데이터셋 PPL 상세 |
| v2 Calibration 리포트 | eval/outputs/3b_reeval_20260305_1451/reports/02_calibration_report.md |
Top-K 정확도, NLL 분포 |
| v2 생성 품질 리포트 | eval/outputs/3b_reeval_20260305_1451/reports/03_generation_quality.md |
12조합 파라미터 그리드 서치 |
| v2 벤치마크 리포트 | eval/outputs/3b_reeval_20260305_1451/reports/04_benchmark_report.md |
KoBEST, HAE-RAE, MMLU, 0/5-shot |
| 진행 기록 | PROGRESS.md |
날짜별 체크포인트, 지표, 결정 로그 |
| ORPO 분석 및 계획 | reports/2026-03-07_ORPO_ANALYSIS_AND_PLAN.md |
ORPO 진행 근거, HP 설계, 실행 절차 |
| ORPO Sweep 디버그 | reports/2026-03-08_ORPO_SWEEP_DEBUG_REPORT.md |
QKV 버그, NCCL timeout, TRL 패치 상세 |
| ORPO 학습 여정 | reports/2026-03-08_ORPO_TRAINING_JOURNEY.md |
ORPO 전체 과정: 5번의 실패와 HP sweep (최신) |
16. 기술 스택 요약
| 영역 | 기술 | 버전 |
|---|---|---|
| 딥러닝 프레임워크 | PyTorch (NVIDIA 커스텀 빌드) | nv25.12 |
| 어텐션 | FlashAttention-2 | 2.7.4.post1+25.12 |
| FP8 / 혼합 정밀도 | TransformerEngine (MXFP8) | 2.10.0 |
| 분산 학습 | DDP + NCCL (NVLS) | NCCL 2.28.9 |
| 커널 컴파일 | Triton | 3.5.1 |
| 토크나이저 | SentencePiece Unigram 64K | - |
| 모니터링 | Telegram Bot (B200Bot) + cron watchdog | - |
| 추론 서빙 | GGUF + Ollama | - |
| GPU | 8× NVIDIA B200 (NVLink 5.0, NVSwitch) | CUDA 13.1 |
| CPU | 2× AMD EPYC 9365 (Zen 5) | - |
관련 프로젝트
EVAFRILL-Mo
하이브리드 Mamba-2 + Transformer 언어 모델 — FRANKENSTALLM의 자매 프로젝트.
NVIDIA Nemotron-H 아키텍처에서 영감을 받아 밑바닥부터 직접 구현한 3B 하이브리드 모델이다. FRANKENSTALLM이 순수 Transformer 기반이라면, EVAFRILL-Mo는 Mamba-2 SSM + 희소 Transformer 어텐션 하이브리드 구조를 채택했다.
| 항목 | FRANKENSTALLM | EVAFRILL-Mo |
|---|---|---|
| 아키텍처 | 순수 Transformer (28L) | Mamba-2 24L + Attention 2L |
| 파라미터 | 3.17B | 2.94B |
| 핵심 기술 | GQA, FP8, FlashAttention-2 | Selective Scan, SwiGLU FFN in Mamba, GQA |
| 설계 원칙 | 검증된 Transformer 아키텍처 | Nemotron-H 단편화 도입 |
| GPU | 8× B200 | 7× B200 |
| 학습 전략 | Chinchilla-optimal | Chinchilla 93% 달성 목표 |
두 프로젝트는 동일한 토크나이저(64K SentencePiece), 학습 데이터 파이프라인, DDP/FP8 인프라를 공유한다. "같은 재료, 다른 레시피"로 아키텍처 차이가 성능에 미치는 영향을 비교 실험할 수 있다.
이름의 유래: Bride Eva (프랑켄슈타인의 신부) + FRIDAY (아이언맨 AI 비서) + LLM + Nemotron의 Mo
18. 다음 최적화 계획 — MFU 33.5% → 47% 목표
현재 성능 진단
Phase 1 프리트레인 실측:
- 57,000 steps, ~38.5B tokens, 약 63시간
- 처리 속도: 36~38K tok/s per rank → 전체 ~292K tok/s (8GPU)
- MFU: ~33.5%
핵심 병목: NUMA Misalignment
AMD EPYC 9365 × 2소켓:
GPU 0~3 → NUMA node 0 (core 0-35)
GPU 4~7 → NUMA node 1 (core 36-71)
초기 DDP 런칭 시 5/8 rank가 잘못된 NUMA 노드에서 실행.
69%의 DataLoader worker가 크로스-NUMA — ~2배 지연 발생.
최적화 항목별 예상 효과
| 최적화 | 예상 MFU 개선 | 난이도 |
|---|---|---|
| NUMA affinity 고정 | +4~9% | 낮음 (launch script 수정) |
| QKV fusion (TransformerEngine) | +8~12% | 중간 (모델 코드 수정) |
| FA2 native RoPE | +3~5% | 중간 (FA2 버전 의존) |
| NCCL 환경변수 튜닝 | +1~2% | 낮음 (한 줄 추가) |
최적화 전후 예상 비교
| 항목 | 현재 | 최적화 후 |
|---|---|---|
| MFU | 33.5% | |
| 처리속도 | 292K tok/s | |
| 50B 토큰 학습 | ~47시간 |
즉시 적용 가능한 코드
NUMA affinity (launch script):
numactl --cpunodebind=0 --membind=0 torchrun \
--nproc_per_node=4 --node_rank=0 train/pretrain.py ... &
numactl --cpunodebind=1 --membind=1 torchrun \
--nproc_per_node=4 --node_rank=1 train/pretrain.py ... &
NCCL 환경변수:
export NCCL_MIN_NCHANNELS=4
export NCCL_SOCKET_NTHREADS=4
export CUDA_DEVICE_MAX_CONNECTIONS=1
Phase 3 ORPO 완료 후, 다음 프리트레인 런 전에 NUMA affinity를 먼저 적용하면 학습 시간을 ~30% 단축할 수 있다.
19. GPU 하드웨어 & 비용 분석 — 3B × 60B 프리트레인
상세 문서:
docs/GPU_COST_ANALYSIS.md
실측 기준 베이스라인
FRANKENSTALLM Phase 1 실측:
B200 × 8, MFU 33.5%, 292K tok/s
38.5B 토큰 → 63시간
60B 토큰 환산 → 약 98시간
클라우드 가성비 Top 3 (60B 토큰, 최적화 후)
| 순위 | 구성 | 소요시간 | 총 비용 |
|---|---|---|---|
| 1 | H100×8 Cudo | 44.8hr | $645 (~93만원) |
| 2 | H100×8 Vast.ai | 44.8hr | $670 (~97만원) |
| 3 | H100×8 RunPod | 44.8hr | $713 (~103만원) |
B200 Blackwell이 빠르지만, 클라우드 단가가 H100의 3배 → H100이 총비용 4.3배 저렴
개인 GPU 구성 추천
| 구성 | VRAM | NVLink | 가격 | 추천도 |
|---|---|---|---|---|
| A6000 Ada × 2 중고 | 96GB (통합) | ✅ | ~1,000만원 | ⭐⭐⭐⭐⭐ |
| L40S × 2 | 96GB (통합) | ✅ | ~1,400만원 | ⭐⭐⭐⭐ |
| RTX Pro 6000 Blackwell | 96GB (단일) | ❌ | ~1,200만원 | ⭐⭐⭐ |
소비자용 GPU(RTX 5090/4090)는 NVLink 미지원. 80GB+ 통합 메모리 필요 시 전문가용 필수.
추천 전략: 로컬 + 클라우드 하이브리드
[로컬] RTX 4090 × 4 (880만원) — 데이터 전처리, 실험, SFT/ORPO
[클라우드] H100×8 (런당 ~103만원) — 본 프리트레인만
마치며
이 프로젝트의 모토는 하나다:
"망하는 것도 기록한다."
SFT v1의 loss=0.0 실패, torch.compile이 효과 없었던 것, 18% 반복률의 좌절 — 이 모든 것이 기록에 남아 있다. 그리고 이제 Phase 3 ORPO에서도 그 전통은 이어진다. 5번의 실패 — NCCL timeout, config 충돌, QKV 변환 버그, 포트 충돌, TRL NaN 버그 — 를 거쳐 마침내 6-config HP sweep이 돌아가고 있다.
Frankenstein이 조각들을 이어 붙여 생명을 만들었듯, 우리도 다양한 소스의 데이터와 기술을 이어 붙여 한국어를 이해하고 말하는 모델을 만들어가고 있다. 아직 완성되지 않았지만, 그 과정 자체가 이 프로젝트의 가치다.
Phase 1 프리트레인은 57,000 steps, loss 1.466으로 완료됐다. Phase 2 SFT는 25,500 steps에서 early stopping (val_loss 1.8851). 6차원 종합 평가에서 4/6을 통과했다.
좋은 소식: 지식 보존이 거의 완벽하다 (forgetting 0.9%). SFT가 base 모델의 지식을 파괴하지 않았다. EOS 종료율은 0%에서 60%로 올라갔다. MMLU-KO도 +3.2pp 개선되었다.
아쉬운 소식: greedy 반복률 72.97%. SFT만으로는 반복 문제가 해결되지 않았다. 오히려 악화되었다 (Base 60.99% → SFT 72.97%). 하지만 rep_penalty=1.2만 적용하면 반복률 0%가 달성된다. 모델은 반복하지 않는 능력을 가지고 있다. 다만 그것을 "기본 행동"으로 학습하지 못했을 뿐이다.
현재: Phase 3 ORPO 본 학습이 진행 중이다. 6-config HP sweep을 모두 완료하고, eval_loss 기준 최적 config (lr=1.2e-5, beta=0.25)를 선정했다. Throughput 벤치마크로 batch_size=4, grad_accum=4 조합이 80.63 samples/s로 최적임을 확인하고, 8×B200 전체 GPU로 본 학습을 시작했다. ~9,840 steps, 예상 ~4.8시간. 학습 완료 시 watchdog이 자동으로 10차원 종합 평가(Base vs SFT vs ORPO 3-way 비교)를 실행한다.
ORPO가 greedy 반복률을 5% 미만으로 끌어내릴 수 있는가?
그 답이 곧 나온다. 학습이 끝나면 6차원 재평가를 수행하고, 통과하면 GGUF로 변환되어 Ollama 위에서 돌아가게 된다. 한국어를 이해하고 말하는 3B 모델, 처음부터 만든 것.
최종 업데이트: 2026-03-09 현재 상태: Phase 3 ORPO 본 학습 진행 중 (lr=1.2e-5, beta=0.25, step ~1,660/9,840, 17%) — 학습 완료 시 10차원 종합 평가 자동 실행 대기