0. 데이터 전처리
이 튜토리얼을 가장 먼저 실행하세요.
data/디렉토리의 원본 데이터를 EulerForge 표준 raw JSONL로 변환합니다. 이후 모든 튜토리얼은 변환된 파일을data.format=raw로 사용합니다.
1. EulerForge 표준 raw 포맷
EulerForge는 3종의 표준 raw JSONL을 지원합니다:
| task | 표준 raw 스키마 | 용도 |
|---|---|---|
sft |
{"prompt": "...", "response": "..."} 또는 {"text": "..."} |
SFT, PPO |
preference |
{"chosen": "...", "rejected": "..."} |
Preference (프롬프트 없음) |
prompted_preference |
{"prompt": "...", "chosen": "...", "rejected": "..."} |
DPO, ORPO, RM |
data.format=raw를 사용하면 훈련 시작 시 자동으로 토큰화(processed 변환)됩니다.
2. 제공 데이터 파일
data/ 디렉토리에는 원본 파일과 변환 완료된 _raw 파일이 있습니다.
파일 목록
| 파일 | 설명 | 크기 |
|---|---|---|
sft_10k.jsonl |
SFT 원본 (messages 중첩 구조) | 10k |
sft_10k_raw.jsonl |
표준 SFT raw (변환 완료) | 10k |
sft_1k.jsonl |
SFT 원본 (TDD/테스트용) | 1k |
sft_1k_bench_raw.jsonl |
SFT bench용 (변환 완료) | 1k |
dpo_10k.jsonl |
DPO 원본 (중첩 구조) | 10k |
dpo_10k_raw.jsonl |
표준 prompted_preference raw (변환 완료) | 10k |
dpo_1k.jsonl |
DPO 원본 (TDD/테스트용) | 1k |
dpo_1k_bench_raw.jsonl |
DPO bench용 (변환 완료) | 1k |
dpo_10k.jsonl |
DPO 선호 데이터 (이미 표준에 가까움) | 10k |
dpo_10k_raw.jsonl |
표준 prompted_preference raw (변환 완료) | 10k |
dpo_1k.jsonl |
DPO 선호 데이터 (TDD/테스트용) | 1k |
dpo_1k_bench_raw.jsonl |
SHP bench용 (변환 완료) | 1k |
명명 규칙
- 원본 (
*.jsonl): 변환 전 데이터. 중첩 구조가 있을 수 있음 _raw(*_raw.jsonl): 표준 컬럼(prompt,response,chosen,rejected)으로 매핑 변환 완료_bench_raw(*_bench_raw.jsonl):eulerforge bench테스트용 (변환 완료)1k: TDD/개발용 (1k 행). 훈련 실행 시 데이터 부족으로 에러 발생 가능10k: 튜토리얼/실사용 기준 (10k 행)
튜토리얼은 모두 10k 데이터를 전제로 합니다.
_raw파일이 이미 변환 완료되어 있으므로 대부분의 튜토리얼에서 convert 단계는 건너뛸 수 있습니다.
3. 데이터 변환 (eulerforge convert)
eulerforge convert의 기본 철학은 --map 기반 매핑입니다.
내장 레시피(--recipe)는 자주 쓰이는 구조의 편의 기능입니다.
필드 탐색 (--print-sample-flat)
변환 전 입력 파일의 필드 구조를 확인합니다:
eulerforge convert --task sft --input data/sft_10k.jsonl --print-sample-flat
# 출력 예시:
# --- row 0 flat keys ---
# 'json_record.messages' (list) [{'role': 'system', ...}]
(A) sft_10k.jsonl → 표준 SFT raw
sft_10k.jsonl은 messages 배열 구조입니다. 두 가지 방법 중 선택:
방법 1: Recipe 모드 (messages 배열)
eulerforge convert \
--task sft \
--input data/sft_10k.jsonl \
--output data/sft_10k_raw.jsonl \
--recipe sft_messages \
--messages-expr json_record.messages
방법 2: --map 모드 (커스텀 flat 필드)
커스텀 데이터에 instruction, output 같은 flat 필드가 있다면:
eulerforge convert \
--task sft \
--input data/custom.jsonl \
--output data/out_raw.jsonl \
--map prompt=instruction \
--map response=output
검증:
head -1 data/sft_10k_raw.jsonl | python3 -c "import sys,json; row=json.loads(sys.stdin.readline()); print(list(row.keys()))"
# 출력: ['prompt', 'response']
(B) dpo_10k.jsonl → 표준 prompted_preference raw
dpo_10k.jsonl은 중첩 {instruction.value, chosen.value, rejected.value} 구조입니다:
방법 1: --map 모드 (dot-path 자동 flatten)
eulerforge convert \
--task prompted_preference \
--input data/dpo_10k.jsonl \
--output data/dpo_10k_raw.jsonl \
--map prompt=instruction.value \
--map chosen=chosen.value \
--map rejected=rejected.value
방법 2: Recipe 모드
eulerforge convert \
--task prompted_preference \
--input data/dpo_10k.jsonl \
--output data/dpo_10k_raw.jsonl \
--recipe dpo_nested_v1
검증:
head -1 data/dpo_10k_raw.jsonl | python3 -c "import sys,json; row=json.loads(sys.stdin.readline()); print(list(row.keys()))"
# 출력: ['prompt', 'chosen', 'rejected']
(C) dpo_10k.jsonl → 표준 prompted_preference raw
dpo_10k.jsonl은 이미 표준에 가까운 {prompt, chosen, rejected} 구조입니다. 변환된 dpo_10k_raw.jsonl이 이미 제공되어 있으며, 스키마 검증:
eulerforge convert --task prompted_preference --input data/dpo_10k_raw.jsonl --validate
# 출력: [Validate] 10000/10000 valid rows (0 invalid)
멀티 CPU 변환
대용량 데이터의 경우 --num-proc으로 병렬 처리:
eulerforge convert \
--task sft \
--input data/sft_10k.jsonl \
--output data/sft_10k_raw.jsonl \
--recipe sft_messages \
--messages-expr json_record.messages \
--num-proc 4 \
--overwrite
4. 변환 결과 요약
| 변환 후 파일 | task | 사용 튜토리얼 |
|---|---|---|
data/sft_10k_raw.jsonl |
sft |
01~04 (SFT 전략별), 08 (PPO) |
data/dpo_10k_raw.jsonl |
prompted_preference |
05 (DPO) |
data/dpo_10k_raw.jsonl |
prompted_preference |
06 (ORPO), 07 (RM) |
5. 훈련에서 raw 데이터 사용
CLI --set 오버라이드
# SFT 훈련 (raw)
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_sft.yml \
--set data.format=raw \
--set data.task=sft \
--set data.path=data/sft_10k_raw.jsonl \
--set data.max_length=512
# DPO 훈련 (raw)
eulerforge train --preset configs/presets/qwen3.5_0.8b_moe_expert_lora_dpo.yml \
--set data.format=raw \
--set data.task=prompted_preference \
--set data.path=data/dpo_10k_raw.jsonl \
--set data.max_length=512
# ORPO/RM 훈련 (raw)
eulerforge train --preset configs/presets/qwen3.5_0.8b_dense_lora_orpo.yml \
--set data.format=raw \
--set data.task=prompted_preference \
--set data.path=data/dpo_10k_raw.jsonl \
--set data.max_length=256
YAML 설정 파일
data:
format: raw
task: sft
path: data/sft_10k_raw.jsonl
max_length: 512
# cache_dir: 기본값 outputs/.cache (모든 run이 공유, 명시하면 오버라이드)
# num_proc: 기본값 CPU 코어 50% (명시하면 오버라이드)
- 공유 캐시: 기본 캐시 위치는
outputs/.cache/입니다. run 디렉토리(outputs/run_YYYYMMDD_HHMMSS/)가 달라도 동일 데이터·설정이면 토큰화를 재활용합니다. - 캐시 파일명 규칙:
{입력파일명}_{task}_{모델명}_len{max_length}.jsonl형식으로 생성됩니다. 예:sft_10k_raw_sft_qwen3.5-0.8b-base_len512.jsonl. 파일명만으로 어떤 데이터·모델·설정의 전처리 결과인지 바로 확인할 수 있습니다. - 멀티코어:
data.num_proc을 생략하면 CPU 코어 수의 50%를 자동 사용합니다.num_proc: 1로 단일 코어 강제 지정 가능합니다. schema로 컬럼명 매핑 가능 (기본값:prompt,response,chosen,rejected)
6. 오프라인 전처리 (eulerforge preprocess)
자동 전처리 대신 미리 토큰화하려면:
# SFT (--num-proc 생략 시 CPU 코어 50% 자동 사용)
eulerforge preprocess \
--task sft \
--input data/sft_10k_raw.jsonl \
--output data/sft_10k_processed.jsonl \
--model-name Qwen/Qwen3.5-0.8B-Base
# Prompted-Preference
eulerforge preprocess \
--task prompted_preference \
--input data/dpo_10k_raw.jsonl \
--output data/dpo_10k_processed.jsonl \
--model-name Qwen/Qwen3.5-0.8B-Base \
--max-length 256
# 워커 수 명시 지정
eulerforge preprocess \
--task sft --input data/sft_10k_raw.jsonl --output data/sft_processed.jsonl \
--model-name Qwen/Qwen3.5-0.8B-Base --num-proc 8
--num-proc > 1 사용 시 EulerForge가 자동으로 TOKENIZERS_PARALLELISM=false를 설정합니다. 기본값은 CPU 코어 수의 50%이며, 단일 코어를 강제하려면 --num-proc 1을 지정하세요.
7. Labels 마스킹 정책
| 모드 | labels 구성 |
|---|---|
| SFT text-only | labels = input_ids (전체 causal LM) |
| SFT prompt/response | prompt 토큰 -100, response만 loss 반영 |
| Preference | chosen/rejected 각각 전체 시퀀스 labels |
| Prompted-Preference | prompt 토큰 -100, completion만 loss/logp 반영 |
8. 흔한 에러와 해결
Data: processed dataset row 0 missing 'input_ids'
Data: processed dataset row 0 missing 'input_ids'
Fix: Run `eulerforge preprocess ...` or set data.format=raw with schema mapping
See: docs/tutorials/00_data_preprocessing.md
원인: raw JSONL을 processed로 지정했거나, 전처리가 안 된 파일을 사용
해결: data.format=raw로 변경하거나 eulerforge preprocess로 전처리
Data Config: data.task is required for raw format
원인: data.format=raw인데 data.task 미지정
해결: data.task를 sft, preference, prompted_preference 중 하나로 설정
Data Config: data.task='sft' is incompatible with training.type='dpo'
원인: data.task와 training.type이 호환되지 않음. 예: DPO 프리셋(training.type=dpo)에 SFT 데이터(data.task=sft)를 지정
해결: --set training.type=sft로 훈련 타입도 함께 오버라이드하거나, data.task를 훈련 타입에 맞게 변경
| data.task | 호환 training.type |
|---|---|
sft |
sft, ppo |
preference |
dpo, orpo, rm |
prompted_preference |
dpo, orpo, rm |
Data: raw sft data row 0 missing column 'text'
원인: 데이터에 text 컬럼이 없음 (prompt/response 형식인 경우)
해결: 컬럼이 자동 감지됨. prompt+response 컬럼이 있으면 정상 동작