Module 9
Evidently AI로 데이터 드리프트 감지
MLOps 과정 | 직접 구현 → 전문 도구로 교체
Section 1
왜 Evidently AI인가?
지금까지 배운 것
Module 7
드리프트 개념
PSI 이론
→
Module 8
detect_drift.py
직접 구현
→
Module 9
Evidently AI
전문 도구
Module 8: 원리를 이해하기 위해 직접 구현 (~170줄)
Module 9: 현업에서 사용하는 전문 도구로 교체 (~50줄)
직접 구현 vs Evidently
| 항목 | detect_drift.py (직접) | Evidently AI |
| 통계 검정 | 평균 비교 (단순) | Wasserstein, KS, Jensen-Shannon (자동) |
| 결과 확인 | 터미널 출력만 | 터미널 + HTML 시각화 |
| 코드량 | ~170줄 | ~50줄 |
| 데이터 타입 | 수동 구분 | 자동 감지 |
| 현업 사용 | 교육용 | GitHub 7,300+ Stars |
원리를 직접 구현해서 이해했으니, 이제 실무 도구로 넘어갑니다.
Evidently AI란?
ML 모니터링을 위한 오픈소스 라이브러리
학습 데이터(Reference)와 운영 데이터(Current)를 비교하여
드리프트를 자동으로 감지하고, HTML 리포트를 생성합니다.
- GitHub Stars: 7,300+
- MLOps 표준 도구로 자리잡음
- 데이터 드리프트 감지 분야에서 가장 널리 사용
- Evidently Cloud (SaaS)도 제공
3가지만 알면 됩니다
Reference Data
"정상" 기준 데이터
loan_data.csv
(모델 학습에 사용)
Current Data
비교 대상 데이터
prediction_logs.csv
(운영 중 수집된 데이터)
Report
두 데이터 비교 결과
HTML 리포트
(시각화 + 통계 수치)
Reference
(학습 데이터)
→
Evidently
비교 분석
←
Current
(운영 데이터)
통계 검정을 자동으로 선택
데이터 타입과 크기에 따라 최적의 검정 방법을 자동 선택합니다.
우리가 직접 고를 필요 없습니다!
| 데이터 타입 | 크기 | 자동 선택되는 검정 |
| 수치형 | 1,000건 이상 | Wasserstein distance (normed) |
| 수치형 | 1,000건 미만 | K-S 검정 (p-value) |
| 범주형 | - | Jensen-Shannon distance |
Module 8: "평균 차이 20% 이상이면 드리프트" (단순 규칙)
Evidently: 통계적으로 검증된 방법 (더 정확)
설치
pip install evidently
# 설치 확인
python -c "import evidently; print(evidently.__version__)"
# 출력: 0.7.21
핵심 코드 5줄
import pandas as pd
from evidently import Report
from evidently.presets import DataDriftPreset
# 데이터 로드
train_df = pd.read_csv("data/loan_data.csv")
pred_df = pd.read_csv("data/prediction_logs.csv")
feature_cols = ["나이", "연소득", "근속연수", "신용점수",
"기존대출건수", "연간카드사용액", "부채비율",
"대출신청액", "대출기간",
"성별", "주거형태", "대출목적", "상환방식"]
ref = train_df[feature_cols] # Reference (학습)
cur = pred_df[feature_cols] # Current (운영)
# === 이 3줄이 전부입니다! ===
report = Report(metrics=[DataDriftPreset()])
snapshot = report.run(reference_data=ref, current_data=cur)
snapshot.save_html("drift_report.html")
핵심 3줄 해석
# 1. Report 객체 생성 — "어떤 분석을 할지" 지정
report = Report(metrics=[DataDriftPreset()])
# 2. 분석 실행 — Reference와 Current를 비교
# Snapshot 객체가 반환됨
snapshot = report.run(reference_data=ref, current_data=cur)
# 3. HTML 리포트 저장
snapshot.save_html("drift_report.html")
DataDriftPreset() = "데이터 드리프트 분석해줘"
Evidently가 알아서 모든 피처의 통계 검정을 수행합니다.
import 경로 주의!
v0.7 (현재) - 정상
from evidently import Report
from evidently.presets import DataDriftPreset
구버전 - 에러!
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
인터넷에서 Evidently 예제를 검색하면 구버전 코드가 많이 나옵니다.
반드시 from evidently import Report를 사용하세요!
detect_drift_evidently.py 실행
cd 01-loan-api-server
python detect_drift_evidently.py
이 스크립트는 핵심 3줄 + 결과를 보기 좋게 출력하는 코드입니다.
터미널 출력과 HTML 리포트를 모두 생성합니다.
실행 결과
드리프트 피처: 11/13개 (84.6%) → 재학습이 필요합니다!
HTML 리포트
drift_report.html을 브라우저에서 열면:
- 전체 드리프트 요약 — 몇 개 피처에서 감지됐는지
- 피처별 분포 비교 그래프 — Reference vs Current 히스토그램
- 통계 검정 결과 — 검정 방법, 점수
- 드리프트 여부 — 피처별 DRIFT / OK
터미널은 숫자만 보이지만, HTML에서는
"왜 이 피처에서 드리프트가 발생했는지" 그래프로 바로 이해 가능
결과 추출: metric_results
import re
for key, val in snapshot.metric_results.items():
# "Value drift for 나이" 형태의 결과만 추출
if val.display_name.startswith("Value drift for "):
col_name = val.display_name.replace("Value drift for ", "")
# widget에서 드리프트 정보 추출
label = val.widget[0].params["counters"][0]["label"]
is_drift = "detected" in label and "not detected" not in label
method = re.search(r"method: (.+?)\.", label).group(1)
score = float(re.search(r"score: (.+?)$", label).group(1))
print(f"{col_name}: {'DRIFT!' if is_drift else 'OK'}")
widget label 해석
각 피처의 결과는 문자열로 들어있습니다:
드리프트 감지 시
"Data drift detected.
Drift detection method: Wasserstein distance (normed).
Drift score: 0.543"
드리프트 미감지 시
"Data drift not detected.
Drift detection method: Jensen-Shannon distance.
Drift score: 0.026"
정규표현식으로 method와 score를 파싱합니다.
v0.7 API 주의점
# 전체 드리프트 비율 가져오기
for key, val in snapshot.metric_results.items():
if val.display_name == "Count of Drifted Columns":
# get_share()는 SingleValue 객체를 반환!
# 반드시 .value를 붙여야 합니다
drift_share = val.get_share().value # 0.846
drift_count = val.get_count().value # 11.0
잘못된 코드 (에러!)
share = val.get_share()
# SingleValue 객체 → float 변환 불가!
올바른 코드
share = val.get_share().value
# 0.846 → 정상!
1단계만 교체하면 됩니다
| 단계 | Module 8 | Module 9 (교체) |
| 1. 드리프트 감지 |
detect_drift.py |
detect_drift_evidently.py |
| 2. 재학습 |
retrain.py |
동일 |
| 3. 배포 |
deploy_model.py |
동일 |
드리프트 감지
detect_drift_evidently.py
→
재학습
retrain.py
→
배포
deploy_model.py
교체 후 실행 순서
cd 01-loan-api-server
# 1. 드리프트 감지 (Evidently 버전)
python detect_drift_evidently.py
# → drift_report.html을 브라우저에서 열어 확인
# 2. 재학습 필요시
python retrain.py
# 3. 배포 결정시
python deploy_model.py
기존 파이프라인을 유지하면서 드리프트 감지만 전문 도구로 업그레이드
Section 7
Evidently 추가 기능
다양한 분석 Preset
| Preset | 용도 | 언제 사용 |
| DataDriftPreset | 입력 데이터 분포 변화 | 이번 실습 |
| DataQualityPreset | 결측값, 이상치, 품질 체크 | 데이터 파이프라인 |
| TargetDriftPreset | 예측 결과 분포 변화 | 모델 성능 저하 감지 |
| ClassificationPreset | 분류 모델 성능 리포트 | 모델 평가 |
| RegressionPreset | 회귀 모델 성능 리포트 | 모델 평가 |
여러 Preset 동시 사용
from evidently import Report
from evidently.presets import DataDriftPreset, DataQualityPreset
# 여러 Preset을 동시에 사용 가능
report = Report(metrics=[
DataDriftPreset(),
DataQualityPreset(),
])
snapshot = report.run(reference_data=ref, current_data=cur)
snapshot.save_html("full_report.html")
metrics 리스트에 원하는 Preset을 추가하기만 하면 됩니다.
한 번에 드리프트 + 데이터 품질을 함께 확인할 수 있습니다.
Module 9 정리
- Evidently AI = 현업 표준 드리프트 감지 도구
- 핵심 코드 3줄이면 드리프트 감지 가능
- 통계 검정을 자동 선택 (Wasserstein, KS, Jensen-Shannon)
- HTML 리포트로 시각적 분석 가능
- 기존 파이프라인에서 1단계만 교체하면 적용 완료
- import 경로:
from evidently import Report
Module 8에서 원리를 이해했고,
Module 9에서 실무 도구를 익혔습니다.
이것이 MLOps 학습의 올바른 흐름입니다!
실습
cd 01-loan-api-server
# 1. Evidently 설치
pip install evidently
# 2. 드리프트 감지 실행
python detect_drift_evidently.py
# 3. HTML 리포트 확인
# drift_report.html을 브라우저에서 열기
# 4. 재학습이 필요하면
python retrain.py
python deploy_model.py
확인 포인트:
- 어떤 피처에서 드리프트가 감지되었는가?
- 각 피처에 어떤 통계 검정이 사용되었는가?
- HTML 리포트에서 분포 그래프를 확인했는가?