> EulerStack > 튜토리얼 > 5. 학습 데이터 준비

5. 학습 데이터 준비

⚠️ 범위 경고 — 이것은 EulerStack 의 주 목적이 아닙니다

EulerStack 은 아키텍처 설계 언어 (ADL) 입니다 — 훈련 프레임워크도, 데이터 파이프라인도 아닙니다. 이 장에서 소개하는 eulerstack.data.prepare"아키텍처가 스스로 무너지지 않았는지" 를 빠르게 확인하기 위한 개발용 Sanity 헬퍼입니다.

구체적으로 이 모듈이 하는 것하지 않는 것 을 분명히 해둡니다.

EulerStack 이 하는 일 EulerStack 이 하지 않는
YAML 스펙 validate / compile 실전 데이터 정제 (dedup, quality filter, PII 제거 등)
HF PreTrainedModel 로 export 멀티-소스 혼합·샘플링 비율 조정
아키텍처 회귀 검증용 작은 샘플 을 토큰화 스트리밍 / 샤딩 / 분산 I/O
tokenize_jsonl 결과를 캐시 저장 토크나이저 훈련 / 어휘 확장

실전 LLM 프로젝트에서 데이터 파이프라인은 반드시 전용 도구를 쓰세요. 이 헬퍼는 "새 mixer 를 추가하고 20 step 짜리 sanity 를 돌리는" 용도에만 맞습니다.

실전 훈련 데이터 준비에 권장하는 도구

EulerStack YAML 로 export 한 HF 모델은 아래 어떤 도구와도 그대로 연동됩니다.

사용 사례 권장 도구
대규모 사전학습 데이터 큐레이션 dolma (AI2), datatrove (HF) 중복 제거, 품질 필터, PII 제거, dedup 가 production-grade
일반 HF Datasets 스트리밍 / 샤딩 datasets + streaming=True 수 TB 스케일 스트리밍, caching, mapping 표준
Fine-tune / SFT 데이터 Axolotl / LLaMA-Factory / torchtune 채팅 템플릿, 패킹, 멀티-태스크 샘플링 포함
RLHF / 선호도 데이터 TRL, OpenRLHF PPO / DPO / GRPO 레시피에 맞춘 포맷팅
HPC 분산 사전학습 MosaicML Composer, Megatron-LM, TorchTitan 수십억 토큰/GPU 처리량, stream-from-S3

이 중 어떤 파이프라인의 출력도 AutoModelForCausalLM.from_pretrained( "./my_eulerstack_export", trust_remote_code=True) 로 로드한 모델에 그대로 투입됩니다. EulerStack 은 "모델 정의 레이어" 로서 위 도구들과 직교적으로 결합됩니다. 자세한 포지셔닝은 튜토리얼 0: EulerStack 의 자리 참조.

이 장이 설명하는 것 — 내부 Sanity 헬퍼

이하의 내용은 이 저장소의 테스트와 sanity 루프에서만 쓰이는 최소 헬퍼를 정의합니다. 본 훈련용이 아닙니다. 그래도 필요한 이유는 다음 한 가지:

새 mixer / 새 primitive 를 추가했을 때, "loss 가 감소하는 기본 구조" 인지 몇 초 안에 확인하는 CPU-friendly 회귀 테스트 를 EulerStack 자체 CI 에서 돌려야 하기 때문.

따라서 이하에서 설명하는 tokenize_jsonl / TokenizedDataset 은:

이 제약을 이해한 상태에서 아래 섹션을 읽으세요.

데이터 소스의 두 가지 종류

EulerStack 은 두 종류의 소스를 지원합니다.

로컬 JSONL 파일

가장 단순하고 재현성이 높은 방식입니다. 각 줄이 하나의 JSON 객체이고, text 필드에 해당 문서의 원시 텍스트가 들어있는 형식입니다.

{"text": "예시 문서 내용입니다.", "source": "example"}
{"text": "또 다른 문서 내용입니다.", "source": "example"}

저장소에는 이미 data/dolma_10k.jsonl 파일이 포함되어 있습니다. 이는 Dolma 코퍼스 (Soldaini et al., 2024) 에서 추출한 영어 일반 텍스트 10,000 개 문서 로, 다운로드 없이 바로 사용할 수 있는 안정적인 테스트 데이터입니다. 이 튜토리얼과 대부분의 sanity/integration 테스트가 이 파일을 기본 데이터로 사용합니다.

HuggingFace Datasets

표준 벤치마크(wikitext, c4 등)를 쓰고 싶다면 HF Datasets 에서 받아올 수 있습니다.

from eulerstack.data.prepare import download_and_tokenize

dataset = download_and_tokenize(
    tokenizer_name="gpt2",
    max_seq_len=512,
    dataset_name="wikitext",
    dataset_config="wikitext-2-raw-v1",
    num_rows=10000,
)

인터넷 접근이 필요하며, 다운로드 결과는 HF cache (기본 ~/.cache/huggingface/) 에 저장됩니다.

토큰화 파이프라인의 사용법

로컬 JSONL 을 토큰화하는 가장 일반적인 호출 형태는 다음과 같습니다.

from eulerstack.data.prepare import tokenize_jsonl

dataset = tokenize_jsonl(
    jsonl_path="data/dolma_10k.jsonl",
    tokenizer_name="gpt2",
    max_seq_len=512,
    num_rows=1000,      # 앞에서 1,000 개 문서만 사용
    cache_dir="data/.cache",
)
print(f"청크: {len(dataset)}, 시퀀스 길이: 512")

인자의 의미는 다음과 같습니다.

내부 동작 단계

tokenize_jsonl 은 내부적으로 다음 단계를 수행합니다.

  1. JSONL 읽기 — 각 줄을 파싱해 text 필드를 추출합니다.
  2. 토큰화 — 지정된 HF 토크나이저로 각 문서를 토큰 ID 리스트로 변환합니다. 기본적으로 BOS/EOS 특수 토큰을 각 문서 경계에 추가합니다.
  3. 이어붙이기(concatenation) — 모든 문서의 토큰을 하나의 긴 스트림으로 연결합니다.
  4. 청킹(chunking)max_seq_len 단위로 잘라 고정 길이 시퀀스 배열을 만듭니다. 이때 패딩도 truncation 도 쓰지 않습니다. 스트림 끝에서 길이가 모자라는 마지막 조각은 버립니다. 이런 방식을 "packed chunking" 이라고 부르며, GPU 활용률을 극대화하는 표준 기법입니다.
  5. 캐시 저장 — 결과를 .pt 파일과 .meta.json 파일로 저장합니다. 같은 인자로 다시 호출하면 토큰화를 건너뛰고 곧바로 로드합니다.

캐시 키의 구성

재호출 시 캐시를 재사용할지 결정하는 키는 다음 요소의 해시로 만들어집니다.

이 중 하나라도 달라지면 캐시는 무효화되고 새로 토큰화됩니다. 캐시 파일의 위치는 다음과 같습니다.

data/.cache/tokenized_<hash>.pt
data/.cache/tokenized_<hash>.meta.json

.meta.json 에는 토큰화에 쓴 인자들이 모두 기록되어 있어, 나중에 이 캐시가 어떤 조건으로 만들어졌는지 추적할 수 있습니다.

Vocab 크기 클램프

이 단계는 토크나이저와 모델의 vocab 크기가 다를 때 반드시 거쳐야 하는 과정입니다. 예를 들어 GPT-2 토크나이저는 50,257 개의 토큰 ID 를 가지지만, EulerStack 의 기본 프리셋은 vocab_size=32000 으로 설정되어 있습니다. 이 상태로 토큰화한 결과를 모델에 그대로 넣으면 index out of bounds 에러 가 발생합니다.

해결책은 TokenizedDataset 래퍼로 감싸 모델의 vocab 크기로 클램프하는 것입니다.

from eulerstack.data.prepare import TokenizedDataset

# 캐시에서 원시 토큰 로드
raw_dataset = tokenize_jsonl(...)

# 모델의 vocab_size 로 클램프
safe_dataset = TokenizedDataset(raw_dataset.input_ids, vocab_size=32000)

TokenizedDataset__getitem__ 시점에 input_idslabels 를 모두 [0, vocab_size) 범위로 클램프합니다. 이 방식은 데이터 자체를 수정하지 않으므로 캐시가 오염되지 않으며, 모델별로 다른 vocab_size 를 써도 같은 캐시를 재활용할 수 있습니다.

Sanity 루프와의 통합 (내부 테스트 전용)

토큰화한 샘플은 다음 장의 sanity 루프 — 역시 내부 테스트 전용 — 에 직접 투입 가능합니다. 다시 한 번, 이것은 프로덕션 훈련이 아닙니다.

from eulerstack.training.sanity import run_e2e_training, build_model_from_ir
from eulerstack.ir.normalizer import normalize_to_ir
import yaml, torch

# 프리셋 로드 및 IR 생성
with open("configs/presets/arch_beginner_llama.yml") as f:
    ir = normalize_to_ir(yaml.safe_load(f))

# 데이터 준비 (토큰화 + vocab 클램프) — 이 저장소 CI 용 샘플 규모
raw = tokenize_jsonl(
    jsonl_path="data/dolma_10k.jsonl",
    tokenizer_name="gpt2",
    max_seq_len=512,
    num_rows=1000,
    cache_dir="data/.cache",
)
dataset = TokenizedDataset(raw.input_ids, vocab_size=ir.model.vocab_size)

# 모델 빌드 및 20-step sanity 훈련 (structural smoke test 용)
model = build_model_from_ir(ir, device="cuda:0", dtype=torch.bfloat16)
result = run_e2e_training(
    model, dataset, batch_size=2, max_steps=20, device="cuda:0",
)
print(result.summary())

이 20-step 결과는 "코드가 안 깨졌다" 만 알려줍니다. 실제 훈련 품질 · 다운스트림 점수 · reward 학습 · multi-GPU scaling 등은 위 §"권장 도구" 표의 전용 프레임워크 가 측정합니다.

실전 데이터 준비 체크리스트 (외부 도구 사용 시)

전용 데이터 파이프라인 도구로 넘어갈 때 다음을 점검하세요.

이 단계들은 EulerStack 의 범위 밖입니다 — 모델 정의 레이어는 "데이터가 어떻게 오든 일관된 모델" 을 보장할 뿐입니다.

다음 단계

이 저장소 안에서 계속 학습

실전 훈련으로 넘어가는 경로