파운데이션 모델 엔지니어링

6.1 대규모 데이터 엔지니어링

파운데이션 모델 학습은 추상적인 “데이터셋”이 아니라 버전이 고정된 토큰 시퀀스를 소비합니다. 두 실행이 같은 출처 URL을 사용하더라도 크롤 스냅샷, 필터, 토크나이저 리비전, 패킹 규칙, 샘플링 가중치가 다르면 같은 데이터로 학습한 것이 아닙니다. 따라서 대규모 데이터 엔지니어링은 최적화 알고리즘이자 복구 시스템의 일부입니다.

프로덕션 목표는 최대 스토리지 대역폭보다 넓습니다. 가속기가 굶지 않도록 의도한 토큰 혼합을 공급하고, 의심스러운 배치를 재구성하며, 조정된 체크포인트에서 재개하고, 평가 자료가 제외됐음을 증명해야 합니다.

1. 원천 데이터에서 불변 학습 스냅샷까지

실무 파이프라인은 네 가지 상태를 분리합니다.

  1. 원시 증거: 변경 불가능한 원본 객체와 수집 시점, 출처, 라이선스 또는 동의 근거, 삭제 식별자
  2. 정제 문서: 품질, 언어, 안전, PII/비밀정보, 정확·근사 중복 판정이 포함된 정규화 레코드
  3. 토큰화 샤드: 토크나이저에 결합된 레코드와 문서 경계, 샘플 ID, 토큰 수, 체크섬
  4. 실행 뷰: 샤드, 혼합 가중치, 에포크/스트림 정책, 결정적 시드, 패킹 의미를 고정한 매니페스트

필터가 바뀌었을 때 기존 스냅샷을 덮어쓰지 않습니다. 새 버전을 발행하고 변환 계보를 보존하며, 삭제가 파생 아티팩트까지 전파되게 합니다. 그래야 능력 회귀를 설명하고, 삭제 요청을 이행하고, 손실 급등에 연결된 배치를 재현할 수 있습니다.

Data Pipeline Evolution

Evolution of data infrastructure for foundation model training.

Level 1: Optimized Pipelines

Manual ETL and batch processing. The pipeline breaks on schema changes, requiring human intervention.

  • Manual ETL
  • Static Schema
  • High Human Dependency

Level 2: Self-Aware Pipelines

Continuously monitors data distribution and state. Detects anomalies and generates alerts, but cannot self-heal.

  • Data Profiling
  • Anomaly Detection
  • Automated Alerts

Level 3: Self-Adapting Pipelines

AI agents detect data changes, dynamically generate and test transformation code, and self-heal the pipeline.

  • Agent Orchestration
  • Auto-remediation
  • Dynamic Code Generation

2. 데이터셋 매니페스트와 평가 격리

매니페스트는 데이터 제어 평면입니다. 최소한 실행마다 다음을 기록합니다.

계약 필드필요한 증거
출처와 권리출처 ID/URI, 수집 시점, 라이선스 또는 동의, 정책 태그, 삭제 계보
변환 계보정규화, 언어/품질/PII 필터와 버전, 거부 사유
식별자안정적인 샘플 ID, 정확·근사 중복 클러스터 ID, 샤드 체크섬
토큰화토크나이저 해시, 정규화 정책, BOS/EOS와 문서 경계 정책
혼합설정한 샘플링 가중치와 출처·언어·도메인별 실제 소비 토큰
분할클러스터 단위 학습/개발/테스트 배정과 분할 시드
패키징시퀀스 길이, 패킹 알고리즘/버전, 문서 간 어텐션 정책

학습/개발/테스트 분할 전에 정확·근사 중복 제거와 의미 클러스터링을 수행하여 문서 변형, 대화, 엔터티, 합성 형제 샘플이 하나의 분할에 남도록 합니다. 공개 벤치마크, 비공개 릴리스 프롬프트, 루브릭, 의미적 이웃, 교사 모델이 만든 변형을 담은 평가 격리 영역 을 만듭니다. 실행 뷰를 발행하기 전에 모든 학습 후보를 이 격리 영역과 대조합니다. 정확 문자열 검색만으로는 의역이나 합성 파생본을 찾을 수 없습니다.

혼합 분류기의 레이블을 믿기 전에 검증합니다. 사람이 감사한 세트에서 언어·도메인 정밀도와 재현율, “알 수 없음” 슬라이스를 보고합니다. 코드 30%, 한국어 10% 같은 샘플링 설정은 하이퍼파라미터입니다. 문서 수와 토큰 수는 다르므로 토큰화 후 실제 비율을 기록해야 합니다.

3. 스토리지, 캐시, 처리량

Apache Iceberg와 Delta Lake 같은 개방형 테이블 포맷은 객체 스토리지 위에서 스냅샷 메타데이터와 트랜잭션 발행을 제공할 수 있습니다 [1] [2]. 유용한 선택지이지만 모든 학습 말뭉치에 필수인 포맷은 아닙니다. 객체 스토리지는 내구성과 수평 확장성이 좋지만, 실제 처리량은 요청 제한, 네트워크 토폴로지, 스로틀링, 객체 크기, 압축 해제, 꼬리 지연의 영향을 받습니다.

학습 데이터를 위한 객체 스토리지와 캐시 아키텍처 출처: AI 생성 이미지. 객체 스토리지·네트워크·캐시 경로의 대역폭을 가정하지 말고 실제로 측정해야 합니다.

일반적인 경로는 객체 스토리지 → 노드 로컬 NVMe 캐시 → 비동기 프리페치/디코딩/토큰 읽기 → 고정 호스트 버퍼 → 가속기입니다. 토큰화와 디코딩 위치는 측정 결과로 정합니다. 사전 토큰화는 반복 CPU 작업을 줄이지만 샤드를 특정 토크나이저·패킹 정책에 결합합니다. 실행 중 처리는 유연하지만 CPU를 쓰고 지터를 늘릴 수 있습니다. .safetensors, Parquet, WebDataset 계열 아카이브, 사용자 정의 인덱스 바이너리는 서로 다른 문제를 해결하며, 어느 것도 보편적인 데이터셋 포맷은 아닙니다.

용량 계획은 실측 페이로드와 스텝 시간에서 시작합니다.

Brequired=global bytes consumed per stepTstep×η,B_{\text{required}} = \frac{\text{global bytes consumed per step}}{T_{\text{step}} \times \eta},

여기서 TstepT_{\text{step}}은 목표 스텝 시간이고 η<1\eta < 1은 꼬리 지연, 재시도, 다른 트래픽을 위한 여유입니다. 다중 노드 내구 시험에서 광고 수치가 아니라 달성한 대역폭과 수요를 비교합니다.

4. 결정적 샤딩과 재개 계약

IterableDataset 객체는 각 DataLoader 워커 프로세스로 복사됩니다. 워커 복사본의 필드를 변경해도 조정된 전역 커서가 되지 않습니다. 워커 로컬 JSON 파일은 다른 워커가 덮어쓸 수 있고, 랭크 상태를 빠뜨리거나, yield 뒤 코드가 실행되지 않아 저장 위치가 뒤처질 수 있습니다. 이런 로더는 정확히 재개할 수 없습니다.

대신 불변 실행 상태의 순수 함수로 할당을 만듭니다. 다음은 파티션 불변식만 보여주는 의도적으로 작은 예제입니다. 프로덕션에서는 인증된 스토리지, 재시도, 프리페치, 디코딩, 분산 체크포인트 조정자를 추가해야 합니다.

from dataclasses import dataclass, asdict
import hashlib
import json

@dataclass(frozen=True)
class DataCursor:
    dataset_version: str
    epoch: int
    permutation_seed: int
    world_size: int
    workers_per_rank: int
    shard_position: int
    sample_offset: int

def partition_shards(shard_ids, *, epoch, seed, rank, world_size,
                     worker_id, workers_per_rank):
    """한 랭크/워커에 샤드를 결정적이고 나머지 없이 배정합니다."""
    keyed = sorted(
        shard_ids,
        key=lambda shard: hashlib.sha256(
            f"{seed}:{epoch}:{shard}".encode()
        ).digest(),
    )
    partition_id = rank * workers_per_rank + worker_id
    partitions = world_size * workers_per_rank
    return keyed[partition_id::partitions]

def encode_cursor(cursor: DataCursor) -> bytes:
    return json.dumps(asdict(cursor), sort_keys=True).encode()

# 소유권 불변식을 확인하는 스모크 테스트입니다.
shards = [f"shard-{i:03d}" for i in range(17)]
owners = [
    partition_shards(shards, epoch=2, seed=7, rank=rank, world_size=2,
                     worker_id=worker, workers_per_rank=3)
    for rank in range(2) for worker in range(3)
]
flat = [shard for owned in owners for shard in owned]
assert sorted(flat) == sorted(shards)
assert len(flat) == len(set(flat))

체크포인트는 데이터 커서를 샤딩된 모델·옵티마이저 상태, 스케줄러, 정밀도 스케일러/FP8 상태, 전역 스텝·토큰, RNG 상태, 샘플러 상태, 데이터셋/토크나이저/코드/설정 해시와 원자적으로 묶어야 합니다. 각 랭크가 고유한 임시 샤드를 쓰고, 조정자가 체크섬과 예상 작성자를 검증한 뒤 완료 마커를 발행합니다. 읽는 쪽은 미완료 세대를 무시합니다.

전달 의미를 정직하게 명시합니다. 옵티마이저 업데이트 뒤 짝을 이루는 커서가 커밋되기 전에 장애가 날 수 있으면 마지막 완료 체크포인트 이후 데이터를 재생할 수 있습니다. “정확히 한 번”은 수락된 모델 업데이트와 커서를 묶는 트랜잭션 경계가 있어야 합니다. 많은 학습 시스템은 대신 마지막 체크포인트부터 결정적으로 최소 한 번 재생 을 제공합니다. 이 재생의 영향이 허용 가능한지 측정해야 합니다.

월드 크기가 바뀌면 소유권도 바뀝니다. 체크포인트 포맷과 샘플러가 전역 커서에서 샘플 누락·중복 없이 재샤딩할 수 있을 때만 탄력적 재개를 주장합니다. 복원 훈련을 하고 다음 샘플 ID, 토큰 카운터, 학습률, 여러 차례의 업데이트를 중단 없는 대조군과 비교합니다.

5. 데이터 로더 수용 시험

전체 클러스터를 예약하기 전에 실제 로더를 실행합니다. 실제 스토리지 엔드포인트와 보안 정책을 사용해 단일 워커, 단일 노드, 목표 다중 노드 토폴로지를 시험합니다.

  • 무결성: 매니페스트와 샤드 체크섬을 검증하고, 손상 객체를 다른 데이터로 조용히 대체하지 않고 격리합니다.
  • 포괄성: 선언한 에포크/스트림 정책마다 모든 배정 샘플이 한 번 나타나고 파티션이 겹치지 않음을 증명합니다.
  • 경계: EOS/문서 구분자, 패딩 마스크, 패킹 경계, 샘플 간 어텐션 동작을 검사합니다.
  • 성능: GPU당 토큰/s, 데이터 대기 비율, 캐시 적중률, p50/p95/p99 샤드 지연, 압축 해제 시간, 재시도, 지연 랭크를 기록합니다.
  • 감사 가능성: 의심 배치를 재구성할 수 있도록 학습 스텝에 샘플·팩 ID를 저장합니다.
  • 복구: 내구 시험 중 랭크와 워커를 종료하고, 완료된 체크포인트만 복원해 재개 동등성을 확인합니다.
  • 백프레셔: 객체 스토리지가 느려질 때 큐와 디스크 사용량이 제한되는지 확인합니다. 프리페치가 호스트 RAM이나 로컬 NVMe를 소진해서는 안 됩니다.

수용 임계값은 일반적인 GB/s 수치가 아니라 목표 토큰 속도에서 허용할 데이터 대기 비율처럼 모델 수요에 상대적으로 정의합니다.

6. 승인 경계가 있는 에이전트 자동화

에이전트는 로그를 요약하고 SQL·필터 패치를 제안하며 품질 보고서를 준비할 수 있습니다. 그러나 프로덕션 데이터 스냅샷을 조용히 바꾸게 해서는 안 됩니다. 안전한 워크플로는 에이전트 입력, 제안 diff, 도구 출력, 검토자, 드라이런 지표, 롤백 아티팩트를 기록합니다. 스키마 변경, 필터 임계값 완화, 삭제 정책 변경, 실행 혼합 발행에는 명시적 승인이 필요합니다.

자동화 후보안전한 기본값금지할 기본값
실패 분석증거를 링크하고 진단 초안 작성원인을 모르는 손상 샤드 무한 재시도
코드/SQL 생성검토할 패치를 열고 샘플에서 실행발행된 스냅샷에 직접 쓰기
품질 모니터링드리프트와 영향 샘플 ID 보고경보를 없애려고 게이트 완화
혼합 탐색후보 토큰 예산 계산승인 없이 예약 학습 실행 변경

따라서 데이터 파이프라인의 실무 산출물은 변경 불가능하고 감사 가능한 실행 뷰와, 식별성·처리량·복구 특성을 측정한 로더입니다. 그래야 옵티마이저 곡선을 알 수 없는 데이터 스트림이 아니라 모델에 대한 증거로 해석할 수 있습니다.

Quizzes

Quiz 1: 두 실행이 같은 원본 문서를 사용하지만 토크나이저 해시와 패킹 정책이 다릅니다. 같은 데이터셋을 사용한 것인가요? 아닙니다. 옵티마이저는 토큰 시퀀스, 경계, 마스크를 소비합니다. 실행 뷰는 원본 스냅샷을 토크나이저와 패킹 식별자에 묶어야 하며, 그렇지 않으면 토큰 예산과 예제를 재현할 수 없습니다.

Quiz 2: 근사 중복 클러스터링을 학습/개발/테스트 분할 전에 해야 하는 이유는 무엇인가요? 먼저 분할하면 같은 문서의 변형이 학습과 평가에 나뉠 수 있습니다. 클러스터링을 먼저 하면 관련된 전체 그룹에 하나의 분할을 배정하여 오염과 과대평가를 줄일 수 있습니다.

Quiz 3: 결정적 파티션 예제가 바닥 나눗셈 기반의 연속 슬라이스 대신 스트라이드 소유권을 사용하는 이유는 무엇인가요? 스트라이드는 나머지 샤드도 자연스럽게 배정합니다. 바닥 나눗셈은 일부 샤드를 남길 수 있고, 랭크와 워커를 결합한 파티션 ID는 서로 다른 데이터 병렬 랭크가 같은 샤드를 읽는 것도 막습니다.

Quiz 4: 30분마다 체크포인트를 저장하는 작업이 옵티마이저 스텝 뒤 다음 저장 전에 중단됐습니다. 데이터가 정확히 한 번 전달됐다고 주장할 수 있나요? 업데이트와 커서를 묶는 트랜잭션이 없다면 주장할 수 없습니다. 일반적으로 마지막 완료 체크포인트부터 재생하므로 결정적인 최소 한 번 동작입니다. 이 계약과 영향을 정직하게 측정해야 합니다.

Quiz 5: 평균 로더 대역폭은 충분하지만 한 랭크의 p99 샤드 지연이 높습니다. 로더가 준비된 것인가요? 반드시 그렇지는 않습니다. 동기식 학습은 지연 랭크를 기다리므로 평균 처리량이 높아도 꼬리 지연이 스텝 시간을 지배할 수 있습니다. 다중 노드 내구 시험에서 데이터 대기와 꼬리 지연 게이트를 통과해야 합니다.

References

  1. Apache Software Foundation. Apache Iceberg: A Table Format for Huge Analytic Datasets. 공식 문서.
  2. Armbrust, M., et al. (2020). Delta Lake: High-Performance ACID Table Storage over Cloud Object Stores. arXiv:2008.06750.
  3. PyTorch. Data Loading Order and IterableDataset behavior. PyTorch 문서.
  4. PyTorch. Distributed Checkpoint. PyTorch 문서.