Jupyter Notebook에서 프로덕션까지: 실제로 동작하는 AI 시스템 배포 방법
출처: The New Stack
AI에서 실험 단계에서 프로덕션 단계로 이동하려면 사고방식, 아키텍처, 엔지니어링 규율의 전환이 필요합니다. API 래퍼는 전혀 사용되지 않습니다.
Jupyter Notebook과 같은 환경에서는 모델이 매우 인터랙티브하고 상태를 유지하는 워크플로우에서 구축됩니다. 여기서는 가정이 암묵적이고, 의존성이 느슨하게 관리되며, 데이터가 로컬에 존재하고 정적인 경우가 많습니다. 반면 프로덕션 시스템은 데이터가 지속적으로 변하고, 트래픽이 예측 불가능하며, 실패가 불가피하고, 모든 구성 요소가 관찰 가능하고, 버전 관리되며, 복구 가능하도록 설계된 분산·동적 환경에서 동작합니다. Notebook 안에서 동작하는 것이 성공하는 이유는 환경이 통제돼 있기 때문이고, 프로덕션에서 성공하는 이유는 불확실성을 대비해 설계됐기 때문입니다.
실제로 작동하는 AI 시스템을 제공하려면 높은 정확도 지표 와 재현 가능한 학습 파이프라인, 컨테이너화된 환경, 확장 가능한 모델 서빙 인프라, 드리프트 및 성능 저하를 감시하는 견고한 모니터링, 머신러닝에 맞춘 CI/CD 실천, 모델이 예상치 못하게 동작할 때를 대비한 명확한 롤백 전략이 모두 필요합니다. 진짜 과제는 같은 모델이 잡음이 섞인 입력, 왜곡된 분포, 동시성, 지연 시간 요구사항, 변화하는 비즈니스 로직 등 현실 세계 제약 하에서도 안정적으로 (92% 이상) 정확도를 유지하도록 보장하는 것입니다. Notebook에서 프로덕션으로 가는 여정은 근본적으로 실험에서 시스템 엔지니어링으로의 진화라고 할 수 있습니다.
먼저 실험 단계부터 살펴보겠습니다. 실험은 AI 시스템이 탄생하는 곳이며, 향후 프로덕션 실패가 조용히 도입되는 곳이기도 합니다. 이 단계의 목표는 결정론적이고, 추적 가능하며, 재현 가능한 기반을 마련하는 것입니다. 실험이 혼란스럽다면 프로덕션은 그 혼란을 증폭시킬 것입니다.
이를 체계적으로 나눠 보겠습니다.
빠른 실험에서 Jupyter Notebook의 역할
Jupyter Notebook은 다음을 최적화하기 때문에 강력합니다.
- 빠른 반복;
- 인터랙티브 시각화;
- 인라인 실험;
- 즉각적인 피드백 루프.
가설을 빠르게 검증할 수 있게 해줍니다:
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
df = pd.read_csv("data.csv")
X = df.drop("target", axis=1)
y = df["target"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = RandomForestClassifier()
model.fit(X_train, y_train)
print("Accuracy:", model.score(X_test, y_test))
탐색 단계에서는 매우 유용합니다.
하지만 Notebook은 다음과 같은 문제점을 가지고 있습니다.
- Stateful (실행 순서가 중요);
- 숨겨진 변수에 의존하는 경우가 많음;
- 로컬 환경에 민감함;
- 구조화를 강제하기 어려움.
프로덕션 준비 수준으로 나아가려면 실험 자체가 규율을 갖춰야 합니다.
무작위성 및 환경 상태 제어
머신러닝 파이프라인에는 무작위성이 내재되어 있습니다.
- 데이터 셔플링
- 가중치 초기화
- 샘플링
- 병렬 실행
결과를 재현하려면 무작위성을 제어해야 합니다.
1단계: 랜덤 시드 설정
import numpy as np
import random
import torch
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
torch.cuda.manual_seed_all(SEED)
torch.use_deterministic_algorithms(True)
Scikit-learn 모델의 경우:
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(random_state=42)
가능한 한 결정론적인 동작을 보장합니다.
2단계: 의존성 고정
requirements.txt 파일을 생성합니다.
pip freeze > requirements.txt
또는 다음과 같은 환경 관리자를 사용합니다.
venvcondapoetry
예시:
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
실제 프로덕션과의 일치를 위해서는 컨테이너화(후에 Docker와 함께 다룸)가 환경 일치를 보장합니다.
데이터셋 버전 관리와 라인리지
두 가지 주요 문제점:
- 데이터셋이 조용히 변한다.
- 어떤 데이터셋 버전이 어떤 모델을 만들었는지 알 수 없다.
문제 상황 예시
- 모델을 재학습한다.
- 정확도가 떨어진다.
- 원인을 파악할 수 없다.
- 데이터가 바뀌었는가?
- 전처리가 바뀌었는가?
- 모델 자체가 바뀌었는가?
프로덕션에서는 이런 상황이 용납되지 않습니다.
최소 수준의 수동 버전 관리 (기본 규율)
data/
v1/
train.csv
v2/
train.csv
Git에 데이터셋 버전을 태그합니다.
git tag data-v1.0
DVC를 활용한 정식 데이터 버전 관리
DVC 초기화:
dvc init
dvc add data/train.csv
git add data/train.csv.dvc .gitignore
git commit -m "Track dataset v1"
DVC는 데이터를 외부에 저장하면서 Git으로 버전을 추적합니다.
이제 각 모델은 다음과 연결됩니다.
- 데이터셋 해시
- 커밋 해시
- 실험 파라미터
이렇게 하면 라인리지가 형성됩니다.
실험 추적 및 메타데이터 관리
50개의 실험을 수행하고 가장 좋은 하나만 수작업으로 기억한다면 위험합니다.
구조화된 추적이 필요합니다.
- 하이퍼파라미터
- 데이터셋 버전
- 메트릭
- 모델 아티팩트
- 실행 환경
MLflow 사용 예시
import mlflow
import mlflow.sklearn
from sklearn.ensemble import RandomForestClassifier
mlflow.set_experiment("rf_experiment")
mlflow.sklearn.autolog()
with mlflow.start_run():
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
accuracy = model.score(X_test, y_test)
mlflow.log_param("n_estimators", 100)
mlflow.log_metric("accuracy", accuracy)
mlflow.sklearn.log_model(model, "model")
이제 할 수 있는 일:
- 실행 간 비교
- 구성 재현
- 최적 모델 등록
- 모델을 스테이징 혹은 프로덕션으로 승격
실험 추적은 직관을 구조화된 지식으로 전환합니다.
재현성은 절대 타협할 수 없는 요구사항
재현성이란 동일한 코드, 데이터셋 버전, 파라미터, 환경에서 동일한 모델 아티팩트를 생성할 수 있어야 함을 의미합니다. 이를 위해서는:
- 결정론적 무작위성
- 버전 관리된 데이터셋
- 버전 관리된 코드
- 의존성 고정
- 하이퍼파라미터 로깅
- 모델 아티팩트 저장
재현 가능한 파이프라인 예시:
git checkout <commit-hash>
dvc pull
pip install -r requirements.txt
python train.py --config configs/v1.yaml
이 명령어가 동일한 모델을 재생성하지 못한다면 시스템은 프로덕션 준비가 되지 않은 것입니다.
사고방식의 전환
실험은 다음을 목표로 합니다.
- 통제된 반복
- 추적 가능한 결과
- 결정론적 프로세스
- 측정 가능한 변화
성숙한 AI 팀에서는 실험 단계 자체가 이미 작은 규모의 프로덕션 시스템과 유사한 규율을 갖추고 있습니다. 모델이 “충분히 좋다”고 판단되는 순간, 그 모델이 어떻게 만들어졌는지는 법적·운영적·재정적으로 중요한 의미를 갖게 됩니다. 바로 여기서 진정한 AI 엔지니어링이 시작됩니다.
실험 단계가 끝나면 우리는 모델을 배포용 아티팩트로 전환합니다. Notebook 안에서 훈련된 모델은 특정 런타임 세션에 묶인 메모리 객체에 불과합니다. 반면 프로덕션 환경에서 배포되는 것은 모델 자체보다 더 복합적인 형태입니다. 버전 관리된 아티팩트는 모델 가중치, 전처리 로직, 의존성, 메타데이터를 모두 포함한, 제어되고 이식 가능한 포맷을 의미합니다. 이 구분은 매우 중요합니다. Notebook은 반복 속도를 최적화하지만, 프로덕션 시스템은 신뢰성, 재현성, 확장성을 최적화합니다. 이 격차를 메우려면 의도적인 패키징이 필요합니다.
첫 번째 단계는 직렬화입니다. 학습이 끝난 후 모델을 결정론적으로 다시 로드할 수 있는 포맷으로 저장해야 합니다. 파이썬 기반 워크플로우에서는 보통 바이너리 아티팩트를 내보냅니다:
import joblib
joblib.dump(pipeline, "model_v1.pkl")
하지만 추정기만 직렬화하는 것은 흔한 실수입니다. 모델은 원시 입력에 직접 작동하지 않습니다. 특징 스케일링, 인코딩, 정규화, 컬럼 순서와 같은 전처리 단계에 의존합니다. 전처리 단계가 …