데이터와 모델 버전관리
이 장에서 배울 것
이 장에서는 모델을 다시 만들 수 있는 상태로 관리하는 방법을 배웁니다. 핵심 질문은 하나입니다.
이 모델은 어떤 데이터, 어떤 코드, 어떤 설정, 어떤 환경에서 만들어졌는가?
이 질문에 답할 수 있어야 실험을 비교할 수 있고, 장애가 났을 때 원인을 찾을 수 있으며, 이전 버전으로 되돌릴 수 있습니다. 이번 장에서는 Git, DVC, lakeFS, Delta Lake의 역할 차이를 살펴보고, 데이터와 모델의 계보를 남기는 방법을 실습합니다.

왜 이 주제가 중요한가
일반 소프트웨어는 같은 코드로 빌드하면 대체로 같은 결과물이 나옵니다. 그러나 머신러닝은 다릅니다. 같은 학습 코드를 실행해도 데이터가 바뀌었거나, 학습·검증 분할 기준이 달라졌거나, 라이브러리 버전이 바뀌면 다른 모델이 만들어질 수 있습니다.
운영에서는 "정확도가 몇 퍼센트인가"보다 "그 정확도를 다시 만들 수 있는가"가 더 중요해집니다. MLOps에서 버전관리는 코드뿐 아니라 데이터셋과 모델 아티팩트를 함께 추적하는 활동입니다. MLOps 원칙에서도 코드, 데이터, 모델을 소프트웨어 자산처럼 버전 관리해야 한다고 설명합니다.1
모델 파일 하나만 저장해 두면 부족합니다. model.pkl이 있어도 그것이 어떤 학습 데이터에서 나왔는지 모르면 문제를 추적할 수 없습니다. 운영 중 성능이 떨어졌을 때도 데이터 문제인지, 전처리 코드 문제인지, 모델 구조 문제인지 구분하기 어렵습니다.
핵심 개념 설명
Git만으로는 부족하다
Git은 코드 변경 이력을 관리하는 데 매우 강합니다. 텍스트 파일의 차이를 비교하고, 브랜치를 만들고, 이전 커밋으로 돌아갈 수 있습니다. 하지만 대용량 데이터셋, 이미지·음성 파일, 학습된 모델 파일처럼 크고 자주 바뀌는 바이너리 파일을 직접 저장하기에는 적합하지 않습니다.
그래서 ML 프로젝트에서는 보통 다음 원칙을 씁니다.
코드는 Git에 저장합니다.
대용량 데이터와 모델 파일은 외부 스토리지에 저장합니다.
Git에는 그 파일을 가리키는 메타데이터만 저장합니다.
DVC는 이 방식의 대표적인 도구입니다. dvc add data/train.csv를 실행하면 실제 데이터 파일은 DVC 캐시나 원격 스토리지에 저장되고, Git에는 .dvc 메타파일이 올라갑니다. 이후 특정 Git 커밋으로 이동한 뒤 dvc checkout을 실행하면 그 커밋에 대응하는 데이터 파일을 다시 가져올 수 있습니다.2
DVC, lakeFS, Delta Lake의 차이
세 도구는 모두 데이터 버전관리에 쓰일 수 있지만 초점이 다릅니다.
| 방식 | 핵심 아이디어 | 적합한 상황 |
|---|---|---|
| DVC | Git에는 메타데이터를 두고 실제 파일은 외부 스토리지에 둔다 | ML 프로젝트 단위의 데이터셋·모델 파일 관리 |
| lakeFS | 객체 스토리지 기반 데이터 레이크에 Git처럼 커밋·브랜치 개념을 제공한다 | 여러 팀이 같은 데이터 레이크에서 안전하게 실험할 때 |
| Delta Lake | 테이블 단위 트랜잭션 로그와 time travel을 제공한다 | Spark·Lakehouse 환경에서 테이블 버전을 되돌려 조회할 때 |
DVC는 개발자가 Git을 쓰듯 데이터와 모델을 다루게 해 줍니다. lakeFS는 데이터 레이크 전체에 브랜치와 머지 흐름을 제공합니다. Delta Lake는 파일 묶음보다 테이블 단위의 안정성과 과거 시점 조회에 강합니다.3
중요한 것은 도구 이름이 아닙니다. "내가 배포한 모델이 참조하는 데이터 버전이 무엇인가"를 명확히 남기는 구조가 있어야 합니다.
재현 가능한 학습에 필요한 기록
재현 가능한 학습을 만들려면 다음 정보를 함께 기록해야 합니다.
학습, 검증, 테스트 데이터셋 버전
데이터 변경 이력과 생성 시각
데이터 분할 기준과 random seed
피처 생성 및 전처리 코드
모델 학습 코드의 Git 커밋
하이퍼파라미터
라이브러리와 패키지 버전
컨테이너 이미지 또는 실행 환경
실행 명령어
평가 지표와 평가 데이터 버전
여기서 random seed는 난수의 시작값입니다. 데이터를 섞거나 모델 가중치를 초기화할 때 같은 seed를 쓰면 결과 차이를 줄일 수 있습니다. 단, GPU 연산이나 일부 라이브러리는 완전히 같은 결과를 보장하지 않을 수 있으므로 실행 환경도 함께 남겨야 합니다.
모델은 파일이 아니라 계보다
모델은 단순한 파일이 아닙니다. 학습 프로세스의 출력 아티팩트입니다. 아티팩트란 어떤 작업의 입력이나 출력으로 만들어진 파일을 뜻합니다. 데이터셋은 학습의 입력 아티팩트이고, 학습된 모델은 출력 아티팩트입니다.4
따라서 모델 버전에는 다음 연결이 필요합니다.
model_version = {
model_file: "models/fraud_model.pkl",
dataset_version: "data@2026-05-18-v3",
code_commit: "a13f92c",
experiment_id: "exp-20260518-014",
metrics: { auc: 0.921, latency_ms: 38 },
eval_data: "eval@2026-05-v1",
created_at: "2026-05-18T10:20:00Z"
}이런 연결을 모델 계보, 또는 lineage라고 부릅니다. 계보가 있으면 실험 비교, 감사 대응, 장애 원인 분석이 가능해집니다.
운영에서는 모델 상태값도 필요합니다.
candidate: 후보 모델staging: 배포 전 검증 중인 모델production: 실제 서비스 중인 모델archived: 더 이상 사용하지 않는 모델
도구에 따라 이 상태는 stage, tag, alias, 별도 승인 필드로 표현됩니다. 예를 들어 MLflow는 과거에 Staging, Production 같은 고정 stage를 많이 사용했지만, 최신 흐름에서는 alias와 tag를 이용해 더 유연하게 모델 상태와 배포 대상을 표현하는 방식이 권장됩니다. 핵심은 특정 도구의 이름이 아니라, "어떤 모델이 현재 운영 트래픽을 받고 있으며, 어떤 모델이 다음 후보인가"를 코드와 문서로 명확히 남기는 것입니다.
또한 champion/challenger 관계를 정할 수 있습니다. champion은 현재 운영 중인 기준 모델이고, challenger는 이를 이기기 위해 비교 검증하는 후보 모델입니다. 새 모델은 정확도만 높다고 바로 배포하지 않습니다. 지연 시간, 비용, 안정성, 공정성 지표, 롤백 가능성까지 확인해야 합니다.
쉬운 비유 또는 사례
모델 버전관리는 요리 기록과 비슷합니다.
완성된 케이크 사진만 보관하면 다시 만들 수 없습니다. 어떤 밀가루를 썼는지, 오븐 온도는 몇 도였는지, 몇 분 구웠는지, 설탕을 얼마나 넣었는지 알아야 합니다. 같은 레시피라도 재료가 바뀌면 맛이 달라집니다.
ML도 같습니다. 모델 파일은 케이크 사진에 가깝습니다. 재현성을 원한다면 재료인 데이터, 레시피인 코드, 조리 조건인 실행 환경을 함께 남겨야 합니다.
직접 해보는 실습
이번 실습의 목표는 특정 Git 커밋에서 데이터와 모델을 다시 가져와 학습 결과를 비교하는 것입니다. 실제 ML 알고리즘을 깊게 다루기보다, "데이터 버전이 바뀌면 결과가 어떻게 연결되어 남는가"를 확인합니다.
1단계: 저장소 초기화
mkdir ml-versioning-demo
cd ml-versioning-demo
git init
dvc init
mkdir -p data models reports src처음 커밋을 만들어 둡니다.
git add .dvc .dvcignore
git commit -m "initialize git and dvc"2단계: 데이터 추가
cat > data/train.csv << 'EOF'
x1,x2,y
1,2,0
2,1,0
8,9,1
9,8,1
EOF
dvc add data/train.csv
git add data/train.csv.dvc data/.gitignore
git commit -m "add training data v1"원격 스토리지를 쓴다면 다음처럼 설정합니다. 실습에서는 S3, GCS, Azure Blob, 사내 MinIO 등을 사용할 수 있습니다. 개인 실습이라면 로컬 디렉터리를 원격처럼 써도 됩니다.
mkdir -p ../dvc-remote
dvc remote add -d storage ../dvc-remote
git add .dvc/config
git commit -m "configure dvc remote"
dvc push클라우드 스토리지를 쓸 때는 다음처럼 바꿀 수 있습니다.
# 예시: 실제 버킷과 권한이 준비되어 있을 때만 사용합니다.
# dvc remote add -d storage s3://my-bucket/ml-versioning-demo
# dvc push3단계: 학습 스크립트 작성
cat > src/train.py << 'EOF'
import json
from pathlib import Path
import pandas as pd
DATA_VERSION = "v1"
CODE_VERSION = "manual-demo"
df = pd.read_csv("data/train.csv")
score = round(df["y"].mean(), 4)
Path("models").mkdir(exist_ok=True)
Path("reports").mkdir(exist_ok=True)
Path("models/model.txt").write_text(
f"simple-model-score={score}n",
encoding="utf-8",
)
with open("reports/metrics.json", "w", encoding="utf-8") as f:
json.dump(
{
"metric_score": score,
"data_version": DATA_VERSION,
"code_version": CODE_VERSION,
"train_rows": len(df),
},
f,
indent=2,
)
EOF위 예시는 실제 ML 모델 대신 단순한 점수를 저장합니다. 목적은 알고리즘이 아니라 버전 흐름을 이해하는 것입니다. 코드가 실행되는지 확인합니다.
python src/train.py
cat reports/metrics.json실행 결과를 DVC와 Git에 기록합니다.
dvc add models/model.txt reports/metrics.json
git add src/train.py models/model.txt.dvc reports/metrics.json.dvc models/.gitignore reports/.gitignore
git commit -m "train model with data v1"
dvc push4단계: 데이터 변경 후 새 버전 만들기
cat >> data/train.csv << 'EOF'
10,9,1
3,2,0
EOF
dvc add data/train.csv
python src/train.py
dvc add models/model.txt reports/metrics.json
git add data/train.csv.dvc models/model.txt.dvc reports/metrics.json.dvc
git commit -m "train model with data v2"
dvc push두 번째 커밋에서는 데이터가 늘어났고, 그 데이터로 만든 모델 산출물과 지표도 함께 바뀌었습니다.
5단계: 이전 데이터와 모델 복원
먼저 커밋 목록을 확인합니다.
git log --onelinetrain model with data v1 커밋의 해시를 확인한 뒤 이동합니다.
git checkout <V1_COMMIT_HASH>
dvc checkout
cat reports/metrics.json
cat models/model.txt이제 다시 최신 커밋으로 돌아갑니다. 기본 브랜치 이름이 main이 아니라 master라면 자신의 저장소에 맞게 바꿉니다.
git checkout main # 또는 git checkout master
dvc checkout
cat reports/metrics.json
cat models/model.txt두 결과가 다르다면, 그 차이의 원인이 데이터 변경인지 확인할 수 있습니다. 이것이 데이터 버전관리의 실질적 가치입니다. 다만 완전한 재현성을 원한다면 여기서 끝나지 않습니다. Python 버전, 패키지 버전, 실행 명령, 난수 seed, 컨테이너 이미지까지 함께 남겨야 합니다.
흔한 실수와 오류 분석
첫째, latest.csv, final_model.pkl 같은 이름만 믿는 것입니다. 운영에서는 "최신"이 가장 위험한 단어가 될 수 있습니다. 최신이 언제의 최신인지, 누가 만들었는지, 어떤 검증을 통과했는지 알 수 없기 때문입니다.
둘째, 데이터 분할 기준을 기록하지 않는 것입니다. 같은 원본 데이터라도 train/test split이 달라지면 성능이 달라집니다. 분할 seed, 기준 날짜, 사용자 단위 분할 여부를 기록해야 합니다.
셋째, 모델 파일을 덮어쓰는 것입니다. model.pkl을 계속 덮어쓰면 롤백할 수 없습니다. 모델 버전 이름, 생성 시각, 실험 ID를 함께 남겨야 합니다.
넷째, 개인정보와 시크릿을 저장소에 넣는 것입니다. 원본 개인정보, API 키, DB 비밀번호, 임시 접근 토큰은 Git에 들어가면 안 됩니다. .gitignore, .dvcignore, 시크릿 관리 도구, 접근 권한, 저장소 암호화를 함께 사용해야 합니다.
다섯째, 외부 스토리지 URI를 무심코 공개하는 것입니다. URI 자체가 민감한 구조를 드러낼 수 있습니다. 공개 저장소에는 샘플 데이터만 두고, 실제 데이터 접근은 권한이 있는 환경에서만 가능하게 합니다.
여섯째, DVC 메타파일만 커밋하고 원격 스토리지에 실제 데이터를 올리지 않는 것입니다. 다른 사람이 같은 커밋을 받아도 dvc pull로 데이터를 가져올 수 없다면 재현성은 깨집니다. 중요한 데이터와 산출물은 커밋 후 dvc push까지 확인해야 합니다.
요약
데이터와 모델 버전관리는 재현성의 기반입니다. Git은 코드에 강하지만 대용량 데이터와 모델 파일에는 한계가 있습니다. DVC는 Git 메타데이터와 외부 스토리지를 연결해 프로젝트 단위의 데이터·모델 버전관리를 돕습니다. lakeFS는 데이터 레이크에 브랜치와 커밋 개념을 제공하고, Delta Lake는 테이블 단위의 트랜잭션과 time travel에 강합니다.
운영 가능한 모델 이력을 만들려면 모델 파일뿐 아니라 데이터 버전, 코드 커밋, 실험 ID, 지표, 평가 데이터, 실행 환경을 함께 기록해야 합니다. 어떤 모델이 어떤 데이터와 코드로 만들어졌는지 설명할 수 없다면, 그 모델은 운영할 준비가 되지 않은 것입니다.
퀴즈
Git만으로 대용량 학습 데이터셋을 직접 관리하기 어려운 이유는 무엇인가?
DVC에서 Git에 저장되는 것은 실제 데이터 파일인가, 데이터 메타파일인가?
모델 계보에 반드시 포함해야 할 정보 세 가지를 말해 보라.
champion/challenger 방식은 어떤 상황에서 유용한가?
latest_model.pkl이라는 파일명만으로 운영하면 어떤 문제가 생길 수 있는가?
체크리스트
학습, 검증, 테스트 데이터 버전을 기록했는가?
데이터 분할 기준과 random seed를 기록했는가?
전처리 코드와 피처 생성 코드를 Git 커밋으로 연결했는가?
모델 파일과 실험 ID, 성능 지표를 함께 저장했는가?
모델 상태값과 배포 승인 조건을 정의했는가?
개인정보, 시크릿, 대용량 원본 파일을 Git에 직접 넣지 않았는가?
DVC 원격 스토리지에 필요한 데이터와 산출물을 push했는가?
이전 모델로 되돌리는 절차를 실제로 테스트했는가?
다음 장 연결
이번 장에서는 데이터, 코드, 모델, 환경을 함께 추적하는 방법을 배웠습니다. 다음 장에서는 이 기록을 더 체계적으로 관리하는 실험 추적과 모델 레지스트리를 다룹니다. 여러 실험의 하이퍼파라미터와 지표를 비교하고, 어떤 모델을 운영 후보로 등록할지 결정하는 흐름으로 이어집니다.
참고
2DVC Tutorial: Data and Model Versioning
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.