[MLOps 실전] Day 1: 데이터와 피처 - 재현 가능한 머신러닝의 토대
[MLOps 실전] Day 1: 데이터와 피처 - 재현 가능한 머신러닝의 토대
이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.
서론: 모델보다 데이터가 먼저다
지난 시리즈에서 LLM을 만들고 활용하는 법을 다뤘다. 이번 시리즈는 그 모델을 프로덕션에서 안정적으로 운영하는 공학, MLOps다. 흔한 오해는 “MLOps = 모델 배포”라는 것이다. 실제로 ML 시스템 장애의 대부분은 코드가 아니라 데이터에서 온다. 그래서 첫날은 모델이 아니라 데이터와 피처에서 시작한다. 재현 불가능한 ML은 운영할 수 없다.
1. ML 시스템은 일반 소프트웨어와 다르다
1
2
3
4
5
6
일반 소프트웨어: 코드만 버전 관리하면 동작이 재현됨
ML 시스템: 동작 = 코드 + 데이터 + 모델 + 하이퍼파라미터
→ 넷 중 하나만 바뀌어도 결과가 달라짐
"내 노트북에선 정확도 95%였는데..." 의 진짜 원인:
코드가 아니라 그때 그 데이터·전처리·시드를 재현 못 함
핵심 원칙: 재현성(reproducibility). 같은 입력으로 같은 모델을 다시 만들 수 있어야 한다.
2. 데이터 버전 관리
코드를 Git으로 관리하듯, 데이터셋도 버전을 고정해야 한다. 하지만 GB~TB 데이터를 Git에 직접 넣을 순 없다.
1
2
3
4
DVC (Data Version Control) 방식:
실제 데이터는 객체 스토리지(S3 등)에 저장
Git에는 데이터의 "포인터"(해시·경로)만 커밋
→ 코드 커밋과 데이터 버전이 함께 묶임
1
2
3
4
5
6
7
8
9
10
11
12
# 데이터를 DVC로 추적 (Git에는 .dvc 메타파일만)
dvc add data/train.csv
git add data/train.csv.dvc .gitignore
git commit -m "데이터셋 v1"
# 실제 데이터는 원격 스토리지로
dvc remote add -d storage s3://my-bucket/dvc
dvc push
# 다른 사람/시점에서 정확히 같은 데이터 복원
git checkout <commit>
dvc pull # 그 커밋 시점의 데이터를 가져옴
이제 “이 모델은 어떤 데이터로 학습됐나”가 Git 히스토리로 추적된다(분산 시스템·대규모 시스템 시리즈의 “단일 출처” 사고와 같다).
3. 데이터 검증: 쓰레기를 막기
학습·추론에 들어오는 데이터가 기대한 형태인지 자동 검증한다. ML 장애의 최대 원인이 여기다.
1
2
3
4
5
6
7
8
9
10
# 스키마·통계 기반 데이터 검증 (Great Expectations / Pandera 등)
import pandera as pa
schema = pa.DataFrameSchema({
"age": pa.Column(int, pa.Check.in_range(0, 120)), # 범위 검증
"income": pa.Column(float, pa.Check.greater_than(0)),
"category": pa.Column(str, pa.Check.isin(["A", "B", "C"])), # 허용값
})
schema.validate(df) # 위반 시 예외 → 파이프라인 중단 (나쁜 데이터 차단)
1
2
3
4
5
6
7
검증 항목:
- 스키마: 컬럼·타입·필수값
- 범위·분포: 음수 나이, 비정상 이상치
- 결측·중복: null 비율, 중복 행
- 참조 무결성: 카테고리 값이 허용 집합 안인가
원칙: 나쁜 데이터는 입구에서 막는다. 모델까지 흘러가면 조용히 망가진다.
4. 피처 엔지니어링과 그 함정
원시 데이터를 모델이 쓸 피처로 변환한다. 여기에 ML 특유의 위험한 버그가 숨는다.
1
2
3
4
5
6
7
8
9
데이터 누수(Data Leakage):
미래 정보나 정답이 피처에 새어 들어감
→ 학습 시 정확도 99%, 프로덕션에서 폭망
예: "환불 여부" 예측에 "환불 처리일" 피처 사용 (정답이 새어듦)
학습-서빙 불일치(Training-Serving Skew):
학습 때의 피처 계산과 서빙 때의 계산이 다름
→ 같은 입력인데 다른 피처 → 성능 저하
원인: 학습은 SQL로, 서빙은 Python으로 따로 구현 → 미묘한 차이
5. 피처 스토어
학습-서빙 불일치를 구조적으로 막는 것이 피처 스토어다. 피처 정의를 한 곳에서 관리한다.
1
2
3
4
5
6
7
피처 스토어의 역할:
- 피처 정의를 한 번 작성 → 학습과 서빙이 같은 로직 사용 (불일치 제거)
- 오프라인 스토어: 대량 과거 데이터로 학습용 (배치)
- 온라인 스토어: 저지연 조회로 실시간 서빙용 (대규모 Day 1의 캐시와 유사)
- 피처 재사용: 팀 간 공유, 중복 계산 제거
도구: Feast, Tecton 등
1
2
3
4
5
6
7
8
# 피처를 한 번 정의하면 학습/서빙 양쪽이 동일하게 사용
@feature_view(entities=["user_id"], ttl="7d")
def user_features(df):
return df.assign(
avg_purchase=df.groupby("user_id")["amount"].transform("mean"),
)
# 학습: 오프라인 스토어에서 시점 정확(point-in-time) 조회
# 서빙: 온라인 스토어에서 최신 값 저지연 조회 — 같은 정의
6. 시점 정확성: 미래를 보지 않기
학습 데이터를 만들 때, 각 예측 시점에 실제로 알 수 있었던 값만 써야 한다.
1
2
3
4
5
6
시점 정확 조인(point-in-time join):
2026-01-15에 일어난 사건을 예측하려면,
2026-01-15 "이전"의 피처만 사용 (그 이후 값은 미래 누수)
피처 스토어가 이를 자동 처리 → 누수 방지
수동 SQL로 하면 실수하기 쉬운 부분
7. Day 1 체크리스트
- ML 동작이 코드+데이터+모델+설정의 조합이며 재현성이 핵심임을 이해했다.
- DVC로 데이터를 버전 관리해 코드 커밋과 묶었다.
- 스키마·분포 검증으로 나쁜 데이터를 입구에서 차단했다.
- 데이터 누수와 학습-서빙 불일치라는 ML 특유의 버그를 파악했다.
- 피처 스토어와 시점 정확 조인으로 불일치·누수를 구조적으로 막았다.
다음 편 예고
데이터와 피처가 재현 가능해졌다. 이제 그 위에서 모델을 학습할 차례다. Day 2에서는 학습 파이프라인과 실험 관리 — 실험을 추적하고, 모델을 버전 관리하고, 학습을 자동화하는 법을 다룬다.
This post is licensed under CC BY 4.0 by the author.