Files
frankenstallm/reports/2026-03-08_ORPO_SWEEP_DEBUG_REPORT.md
ModelHub XC d4abdb70fa 初始化项目,由ModelHub XC社区提供模型
Model: pathcosmos/frankenstallm
Source: Original Platform
2026-07-14 04:21:16 +08:00

186 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ORPO HP Sweep 디버깅 및 체크포인트 수정 보고서
**날짜**: 2026-03-08
**작성**: Claude Opus 4.6 + ghong
---
## 1. 목표
SFT v2 best 체크포인트 기반 ORPO hyperparameter sweep (6개 조합, 각 200 steps)을 8×B200 GPU에서 실행하여 최적 HP 조합을 찾는다.
## 2. 발견된 문제 및 해결
### 2.1 [CRITICAL] 체크포인트 QKV Weight 누락
**증상**: 모델 로딩 시 28개 레이어의 `self_attn.q_proj`, `k_proj`, `v_proj` weight가 `MISSING`으로 표시. attention layer가 랜덤 초기화 상태로 학습됨.
**원인**: `scripts/convert_to_hf.py``remap_weights()` 함수가 fused QKV 프로젝션(`attn.qkv_proj.weight`)을 처리하지 못함. 원본 체크포인트(TransformerEngine FP8)는 Q+K+V를 하나의 `qkv_proj`로 저장하지만, 변환 스크립트는 `q_proj`, `k_proj`, `v_proj`를 개별 키로 찾았음.
**체크포인트 구조**:
```
원본 (TE FP8): layers.0.attn.qkv_proj.weight → shape [5120, 3072]
Q(3072) + K(1024) + V(1024) = 5120
변환 전 (HF): self_attn.o_proj.weight만 존재 → 171 keys (QKV 84개 누락)
변환 후 (HF): q_proj [3072,3072] + k_proj [1024,3072] + v_proj [1024,3072] → 255 keys
```
**GQA 구조**: heads=24, kv_heads=8, head_dim=128
- Q: 24 × 128 = 3072
- K: 8 × 128 = 1024
- V: 8 × 128 = 1024
**수정**: `scripts/convert_to_hf.py` — fused `qkv_proj`를 감지하면 GQA 구조에 따라 Q/K/V로 분리:
```python
qkv = src_state_dict[qkv_key].float()
dst["q_proj.weight"] = qkv[:q_dim] # [3072, 3072]
dst["k_proj.weight"] = qkv[q_dim:q_dim+k_dim] # [1024, 3072]
dst["v_proj.weight"] = qkv[q_dim+k_dim:] # [1024, 3072]
```
**검증**: `q_proj norm=73.54` (학습된 weight), 변환 후 255 keys 정상.
### 2.2 NCCL Timeout (1800s)
**증상**: `torch.distributed.DistBackendError: wait timeout after 1800000ms`
**원인**: ORPOTrainer 초기화 시 Rank 0이 649K 샘플 토크나이징에 ~30분 소요. 다른 rank들이 NCCL communicator setup을 기다리다 기본 timeout(1800s) 초과.
**수정**:
- `train/orpo.py`: `ddp_timeout=7200` (2시간)
- `configs/korean_3b_orpo.yaml`: `dataset_num_proc: 64` (토크나이징 ~10분으로 단축)
- `scripts/orpo_hp_sweep.sh`: `--dataset_num_proc 64` 추가
### 2.3 TensorBoard Import 크래시
**증상**: `AttributeError: module 'tensorflow' has no attribute 'io'`
**수정**: `report_to="none"` (orpo.py, YAML config, sweep script 3곳)
### 2.4 TRL 0.29.0 API 변경
**증상**: `from trl import ORPOTrainer` → ImportError
**수정**: `from trl.experimental.orpo import ORPOConfig, ORPOTrainer`
### 2.5 `load_best_model_at_end` + `save_steps` 충돌
**증상**: `ValueError: save_steps(9999) is not a round multiple of eval_steps(100)`
**수정**:
- `orpo.py`: `--no_load_best` CLI 인자 추가
- sweep script: `--no_load_best --save_steps 200` 사용
### 2.6 포트 충돌 (EADDRINUSE)
**증상**: Run 1이 이전 프로세스의 port 29510 점유로 실패
**원인**: 이전 실행의 좀비 프로세스가 소켓을 점유
**교훈**: 실행 전 항상 `pkill -f torchrun` + `sleep 2` 로 정리 필요
### 2.7 TRL 0.29.0 ORPOTrainer 토크나이저 버그 (이전 세션)
**증상**: Korean tokenizer의 merge ops로 인한 prompt token 길이 불일치 → `zip(strict=True)` ValueError
**수정**: TRL 소스 패치 8건 (`.029bak` 백업):
1. `build_tokenized_answer` length mismatch → graceful fallback
2. chosen/rejected prompt tokens truncation
3. `zip(..., strict=True)``zip(...)` 변경
4. prompt diff ValueError → `pass` (warn-and-continue)
5. `add_bos_token_if_needed` args 통일
6. evaluation_loop zip strict 제거
## 3. 수정된 파일 목록
| 파일 | 변경 내용 |
|------|-----------|
| `scripts/convert_to_hf.py` | fused QKV → separate Q/K/V 분리 로직 추가 |
| `train/orpo.py` | TRL 0.29.0 import, ddp_timeout, dataset_num_proc, 예외처리/로깅 강화, --no_load_best |
| `configs/korean_3b_orpo.yaml` | dataset_num_proc: 64, report_to: none |
| `scripts/orpo_hp_sweep.sh` | --dataset_num_proc 64, --no_load_best, set +e, FAILED_RUNS 추적 |
| `eval/outputs/hf_3b_sft_best/` | 재변환 완료 (171→255 keys) |
## 4. Sweep 설정
| Run | Name | Beta | LR | Max Length | Effective BS |
|-----|------|------|----|-----------|-------------|
| 1 | baseline_b015_lr8e6 | 0.15 | 8e-6 | 1536 | 128 |
| 2 | baseline_b025_lr8e6 | 0.25 | 8e-6 | 1536 | 128 |
| 3 | strong_b035_lr8e6 | 0.35 | 8e-6 | 1536 | 128 |
| 4 | fast_b025_lr12e6 | 0.25 | 1.2e-5 | 1536 | 128 |
| 5 | conserv_b025_lr5e6 | 0.25 | 5e-6 | 1536 | 128 |
| 6 | short_b025_lr8e6 | 0.25 | 8e-6 | 1024 | 128 |
각 run: 200 steps, eval_steps=100, 8×B200 DDP
## 5. 하드웨어 최적화 설정
```bash
# NCCL (NVSwitch mesh — auto-detect)
NCCL_IB_DISABLE=1
NCCL_BUFFSIZE=134217728
NCCL_P2P_LEVEL=NVL
# CPU
OMP_NUM_THREADS=9 # 72 cores / 8 GPUs
MKL_NUM_THREADS=9
dataset_num_proc=64 # 토크나이징 병렬화
# GPU
bf16=true
flash_attention_2
gradient_checkpointing=true
dataloader_pin_memory=true
ddp_find_unused_parameters=false
ddp_timeout=7200
```
## 6. 현재 상태
- 체크포인트 재변환 완료 (QKV 정상)
## 7. Sweep 실행 이력
### 시도 1 — NCCL Timeout (이전 세션)
- **시각**: ~03:00
- **결과**: Rank 0이 토크나이징(649K, num_proc=8)에 30분 소요 → 나머지 rank NCCL 1800s timeout
- **교훈**: `ddp_timeout=7200` + `dataset_num_proc=64` 필요
### 시도 2 — save_steps/eval_steps 충돌
- **시각**: 03:28
- **결과**: `ValueError: save_steps(9999) not a multiple of eval_steps(100)`
- 6개 run 전부 즉시 실패 (ORPOConfig 생성 단계)
- **수정**: `--save_steps 200`, `--no_load_best` 추가
### 시도 3 — 포트 충돌 + 깨진 체크포인트
- **시각**: 03:45
- **결과**:
- Run 1: port 29510 EADDRINUSE (이전 좀비 프로세스) → 2초 만에 실패
- Run 2~6: 모델 로딩 성공하나 `q_proj/k_proj/v_proj MISSING` (랜덤 초기화)
- GPU 0만 100% utilization, 나머지 7개 0% → DDP 비정상
- **근본 원인 발견**: `convert_to_hf.py`가 fused `qkv_proj` [5120, 3072]를 분리 안 함
- **수정**: QKV split 로직 추가, 체크포인트 재변환 (171→255 keys)
### 시도 4 — 정상 실행 중 (현재)
- **시각**: 04:20
- **사전 정리**: `pkill -9` + 포트 해제 + sweep 디렉토리 초기화
- **상태**: Run 1/6 `baseline_b015_lr8e6` 토크나이징 진행 중 (Map 69%, num_proc=64)
- **확인 사항**:
- 모델 255 keys 정상 로딩 (MISSING 경고 없음)
- 8 GPU 모두 모델 로드 (726MB/GPU)
- utilization 0% = 정상 (CPU 토크나이징 단계)
- **대기 중**: 토크나이징 완료 → NCCL init → 8 GPU DDP 학습 시작 확인 필요
### GPU 1개만 사용 문제 설명
- **현상**: 시도 3에서 GPU 0만 100%, 나머지 0%
- **원인**: 깨진 체크포인트(attention 랜덤 초기화)로 인해 DDP 동기화 실패. Rank 0만 학습 진입, 나머지 rank는 NCCL communicator 대기 상태
- **현재**: 아직 토크나이징 단계라 GPU utilization 0%는 정상. 토크나이징 완료 후 8 GPU 학습 진입 확인 필요
## 8. 다음 단계
1. 토크나이징 완료 후 8 GPU DDP 학습 정상 진입 확인
2. 6개 HP 조합 결과 비교 (eval_loss, margin 기준)
3. 최적 HP로 본 학습 (full epochs)
4. 학습 후 6차원 평가 재실행