# FRANKENSTALLM

![Phase 3](https://img.shields.io/badge/Phase_3_ORPO-본_학습_중-blue)
![Model](https://img.shields.io/badge/Model-3B_Korean_LLM-green)
![GPU](https://img.shields.io/badge/GPU-8×_NVIDIA_B200-76b900)
![FP8](https://img.shields.io/badge/Precision-MXFP8-orange)
![SFT](https://img.shields.io/badge/SFT-완료_val__loss_1.8851-success)
![Status](https://img.shields.io/badge/Status-ORPO_Full_Training-ff9900)

> **한국어 3B LLM을 8× NVIDIA B200 위에서 처음부터 직접 만든다.**
> Frankenstein처럼 조각을 이어 붙이고, 철강처럼 단단하게 단련한다.

GitHub: [`pathcosmos/FRANKENSTALLM`](https://github.com/pathcosmos/FRANKENSTALLM)

---

## 목차

1. [왜 이 프로젝트인가](#1-왜-이-프로젝트인가)
2. [현재 상태 — 한눈에 보기](#2-현재-상태--한눈에-보기)
3. [하드웨어 환경](#3-하드웨어-환경)
4. [프로젝트 구조](#4-프로젝트-구조)
5. [프로젝트 여정 타임라인](#5-프로젝트-여정-타임라인)
6. [모델 아키텍처](#6-모델-아키텍처)
7. [학습 데이터](#7-학습-데이터)
8. [학습 설정 및 최적화](#8-학습-설정-및-최적화)
9. [실험 결과 — 1B 베이스라인](#9-실험-결과--1b-베이스라인)
10. [실험 결과 — 3B Base 종합 평가 (v2)](#10-실험-결과--3b-base-종합-평가-v2)
    - [10.1 학습 커브](#101-학습-커브)
    - [10.2 PPL (Perplexity) — 19개 데이터셋](#102-ppl-perplexity--19개-데이터셋)
    - [10.3 한국어 벤치마크](#103-한국어-벤치마크)
    - [10.4 영어 벤치마크](#104-영어-벤치마크)
    - [10.5 Calibration](#105-calibration)
    - [10.6 0-shot vs 5-shot 비교](#106-0-shot-vs-5-shot-비교)
    - [10.7 참고 모델 비교](#107-참고-모델-비교)
    - [10.8 생성 품질 및 파라미터 그리드 서치](#108-생성-품질-및-파라미터-그리드-서치)
    - [10.9 평가 파이프라인](#109-평가-파이프라인)
11. [실험 결과 — 3B SFT 종합 평가](#11-실험-결과--3b-sft-종합-평가)
    - [11.1 SFT 학습 결과](#111-sft-학습-결과)
    - [11.2 6차원 평가 요약](#112-6차원-평가-요약)
    - [11.3 Base vs SFT 비교](#113-base-vs-sft-비교)
    - [11.4 코드 개선 사항](#114-코드-개선-사항)
    - [11.5 ORPO 진행 판정](#115-orpo-진행-판정)
12. [Phase 3 — ORPO (선호도 정렬)](#12-phase-3--orpo-선호도-정렬)
    - [12.1 ORPO 선택 배경](#121-orpo-선택-배경)
    - [12.2 데이터](#122-데이터)
    - [12.3 HP Sweep 설계](#123-hp-sweep-설계-6-config)
    - [12.4 시도 이력](#124-시도-이력--5번의-실패)
    - [12.5 스윕 결과](#125-스윕-결과-진행-중)
    - [12.7 ORPO 본 학습](#127-orpo-본-학습-진행-중-2026-03-09)
    - [12.8 ORPO 종합 평가 파이프라인](#128-orpo-종합-평가-파이프라인)
13. [실행 방법](#13-실행-방법)
14. [로드맵](#14-로드맵)
15. [참고 문서](#15-참고-문서)
16. [기술 스택 요약](#16-기술-스택-요약)
17. [관련 프로젝트](#관련-프로젝트)
18. [다음 최적화 계획](#18-다음-최적화-계획--mfu-335--47-목표)
19. [GPU 하드웨어 & 비용 분석](#19-gpu-하드웨어--비용-분석--3b--60b-프리트레인)

---

## 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 1~2 (Feb 25~26) — 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 어벤저스"). 각 에이전트가 다른 입장을 맡아 논증했다.

토론의 핵심 발견:

1. **18% 반복은 1B 파라미터의 구조적 한계**다. 1B 모델은 장거리 의존성(long-range dependency)을 충분히 포착하지 못한다. ORPO 같은 선호도 정렬은 반복을 줄이는 데 일부 도움이 되지만, 근본 원인(파라미터 부족)을 해결하지는 못한다.

2. **스케일링 법칙 분석**: Chinchilla 법칙과 실험 데이터를 기반으로 3B 모델은 동일 데이터에서 반복률을 5~8%까지 낮출 수 있다는 추정이 나왔다.

3. **비용-편익 분석**: 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 (+8~12%), NUMA affinity (+4~9%), 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 split
- `filter_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`)

```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를 쓰면 내부에서 직접 처리한다.

```python
# 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 최적화

```python
# 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

```bash
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중 보호를 구축했다:

```bash
# 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 (효과 없음)**이었다. 두 가지 이유:

1. TransformerEngine의 kernel이 opaque하여 graph break가 발생한다. `torch.compile`은 Python 연산 그래프를 최적화하는데, TE kernel은 그 그래프 밖에 있다.
2. `/tmp` 디렉토리에 `noexec` 마운트 플래그가 있어 컴파일된 kernel을 캐시하지 못한다.

**교훈**: "일단 써보자"보다 "왜 효과가 있는지 먼저 이해하자"가 중요하다.

### 모니터링 시스템

```
텔레그램 알림 시스템
├── B200Bot (token 설정됨)
├── training_watchdog.sh → 10분 간격 cron
│   └── loss 이상, 프로세스 종료 감지 → 즉시 알림
└── hourly_status.sh → 1시간 간격 cron
    └── step, loss, 속도, VRAM, eta → 정기 리포트
```

```python
# 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 실험용)

```python
# 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 92~100% 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. 실행 방법

### 사전 요구사항

```bash
# PyTorch는 재설치 금지 (NVIDIA 커스텀 빌드)
# 아래 패키지만 추가 설치
pip install transformers accelerate peft trl deepspeed \
            bitsandbytes sentencepiece wandb
```

### 3B 프리트레인

```bash
# 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
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 (선호도 정렬)

```bash
# ORPO 학습
bash scripts/launch_3b_orpo.sh

# 학습 완료 후 자동 평가 (watchdog)
nohup bash scripts/orpo_eval_watchdog.sh \
  > checkpoints/korean_3b_orpo_v1/watchdog.log 2>&1 &
```

### 평가

```bash
# 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
```

### 배포

```bash
# 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 "한국의 철강 산업에 대해 설명해줘."
```

### 학습 모니터링

```bash
# 실시간 모니터 (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 테스트 (개발/디버그)

```bash
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](https://github.com/pathcosmos/EVAFRILL-Mo)

**하이브리드 Mamba-2 + Transformer 언어 모델** — FRANKENSTALLM의 자매 프로젝트.

NVIDIA [Nemotron-H](https://arxiv.org/abs/2504.03624) 아키텍처에서 영감을 받아 밑바닥부터 직접 구현한 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** (프랑켄슈타인의 신부) + **FRI**DAY (아이언맨 AI 비서) + **LL**M + Nemotron의 **Mo***

---

## 18. 다음 최적화 계획 — MFU 33.5% → 47% 목표

> 상세 문서: [`docs/NEXT_OPTIMIZATION_PLAN.md`](docs/NEXT_OPTIMIZATION_PLAN.md)

### 현재 성능 진단

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% | ~45~47% |
| 처리속도 | 292K tok/s | ~390~410K tok/s |
| 50B 토큰 학습 | ~47시간 | ~34~36시간 |

### 즉시 적용 가능한 코드

**NUMA affinity (launch script):**

```bash
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 환경변수:**

```bash
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`](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차원 종합 평가 자동 실행 대기*
