# Drift Detection — 사용자 분류 패턴 변화 감지 > SPEC §2.4 (4) 구현. 모듈 `drift_detection.py` · 라우트 `/api/drift/*` · 탭 "Drift 탐지". --- ## 1. 문제 — 분류 패턴은 시간이 지나면 변한다 ### 실제 시나리오 | 시점 | 상황 | 결과 | |---|---|---| | 1개월차 | AI: "이메일=S 민감" → 사용자 동의 | 정확도 90% | | 3개월차 | 회사 정책 변경: "이메일=O 일반" | 사용자 매번 정정, 갭 누적 | | 그 동안 | AI 는 변화 모름 | **학습 자동 적용 전까지 정확도 ↓** | | 더 심각 | 사람이 정확도 표를 매일 봐야 알아챔 — 안 봄 | **품질이 조용히 무너짐** | > **핵심 통찰**: 변화를 자동 감지·알림하지 않으면 분류기 품질이 조용히 무너진다. ### ML 용어 — drift 의 3 종류 - **Concept drift** — "정답의 의미" 가 변함. (위 시나리오: 이메일 등급 기준 변경) - **Data drift** — 입력 데이터 분포가 변함. (예: 새로운 문서 유형 등장) - **User drift** — 사용자 판단 기준이 변함. (조직 정책 / 새 담당자 / 학습 등) 본 모듈은 결정 이력에서 위 세 가지를 통합적으로 감지합니다 (정확도·갭·entity 분포). --- ## 2. 알고리즘 — 3 지표로 drift 측정 ### 윈도우 분할 결정 이력을 시간순 정렬 → 최근 N건 (window **A**) vs 이전 N건 (window **B**) 으로 분할. ``` 시간 ←──────────────────────────────────→ ┌──── window B ────┐┌──── window A ────┐ [b1, b2, ..., bN] [a1, a2, ..., aN] (이전) (최근) ``` ### 지표 1 — 정확도 drift ``` acc_A = (AI = User in A) / |A| acc_B = (AI = User in B) / |B| drift = acc_A − acc_B ``` | 의미 | AI 가 사용자와 얼마나 맞고 있나의 변화 | |---|---| | **임계값** | `acc_drift ≤ −0.15` → `alert` (빠른 하락) | | | `acc_drift ≥ +0.15` → `info` (학습 효과 / 패턴 안정화) | ### 지표 2 — 평균 갭 drift ``` gap_A = mean(|AI − User|) in A # O=0, S=1, C=2 절대 차이 gap_B = mean(|AI − User|) in B drift = gap_A − gap_B ``` | 의미 | 사용자가 더 큰 폭으로 정정하는가 | |---|---| | **임계값** | `gap_drift ≥ +0.3` → `warn` (O→C 같은 두 단계 정정 증가) | ### 지표 3 — Entity 분포 drift (PSI) ``` ratio_A = count_A(ent) / total_A ratio_B = count_B(ent) / total_B psi = (ratio_A − ratio_B) · log( (ratio_A + ε) / (ratio_B + ε) ) ``` | 의미 | 어떤 PII 가 자주 나오는지의 변화 | |---|---| | **임계값** | `|psi| ≥ 0.1` → `info`, `≥ 0.2` → `warn` | | **특수** | `appeared`: 이전에 없던 entity 가 ≥3건 등장 → `info` | | | `disappeared`: 이전 ≥5건 → 최근 0건 → `info` | > PSI = Population Stability Index — 두 분포의 안정성 측정 (production ML 표준 지표). ### 시계열 `sliding window` (기본 10건씩, 5건 단위로 이동) 로 `accuracy` · `mean_gap` 의 시간 추이를 계산. SVG 차트로 시각화 (파란선=정확도, 주황선=평균 갭). --- ## 3. 데이터 흐름 ``` [결정 이력] [윈도우 분할] [3 지표 계산] [시계열] [알림 생성] decisions.db → 최근 N (A) → acc / gap / PSI → sliding bin → alert/warn/info 시간순 정렬 이전 N (B) (기본 10건) 임계값 기반 ``` ### API 라우트 ```http GET /api/drift/snapshot?window=30 &accuracy_drop=0.15 &gap_rise=0.3 &entity_psi=0.1 ``` → 응답: `recent`, `previous`, `drift`, `entities[]`, `alerts[]` ```http GET /api/drift/timeline?bin=10 ``` → 응답: `bins[]` (각 bin 의 `idx`, `n`, `accuracy`, `mean_gap`, `start_at`, `end_at`) --- ## 4. 알림 예시 — 실제 응답 검증 시 받은 실제 데이터 (window=8): ```json { "ok": true, "window_size": 8, "recent": { "n": 8, "accuracy": 0.375, "mean_gap": 1.00 }, "previous": { "n": 8, "accuracy": 0.750, "mean_gap": 0.25 }, "drift": { "accuracy_drift": -0.375, // ← 37.5%p 하락 "gap_drift": +0.75 // ← 갭이 0.75 증가 }, "alerts": [ { "severity": "alert", "type": "accuracy_drop", "message": "정확도 37.5%p 하락 — 분류기가 사용자 의도와 점점 어긋남." }, { "severity": "warn", "type": "gap_rise", "message": "평균 갭이 0.75 증가 — 사용자가 더 큰 폭으로 정정 중." } ] } ``` ### 이 응답이 의미하는 것 - 최근 8건의 정확도가 37.5% 로, 이전 8건의 75% 보다 절반. - 평균 갭도 0.25 → 1.00 으로 4배. - → **사용자가 AI 와 점점 다른 방향으로 매기는 패턴. 학습 또는 룰 검토 필요 신호.** --- ## 5. UI 구조 — "Drift 탐지" 탭 | 영역 | 내용 | |---|---| | **1. 초보 가이드** | `details/open` — drift 란? + 3 종류 + 3 지표 + 임계값 | | **2. 윈도우/임계값 설정** | window 크기 + acc/gap/PSI 임계값 input + "Drift 분석 실행" 버튼 | | **3. 알림 영역** | 🚨 `alert` (빨강) · ⚠️ `warn` (주황) · ℹ️ `info` (회색) 카드 list. 좌측 4px 보더 | | **4. 윈도우 비교 카드** (3 컬럼) | 📊 최근 · 📊 이전 · ↕ Drift (A−B). 각 카드: n / 정확도 / 평균갭 / entity 합계 | | **5. 시계열 SVG** | 파란선=정확도 (좌축 0~1) · 주황선=평균갭 (우축 0~maxGap) · 점 표시 + bin size 조정 | | **6. Entity 분포 변화 표** | Entity Type · 최근 · 이전 · 비율 변화 · PSI · 상태 (신규/소멸/변화 큼) | --- ## 6. 파일 변경 요약 | 파일 | 유형 | 분량 | 내용 | |---|---|---|---| | [drift_detection.py](../drift_detection.py) | 신규 | ~180줄 | `snapshot()` / `timeline()` / `_window_stats` / `_entity_drift` / `_build_alerts` | | [app.py](../app.py) / [app_lite.py](../app_lite.py) | 수정 | +30줄 × 2 | import + 2 라우트 | | [templates/index.html](../templates/index.html) | 수정 | +280줄 | 사이드바 nav 1 + 새 탭 panel + JS (SVG 시계열) | | [templates/index.html](../templates/index.html) | 수정 | +30줄 | 전체 구성도 9번째 파이프라인 + 가이드 갱신 | | [docs/drift_detection.pptx](drift_detection.pptx) | 신규 | 8 슬라이드 | 이론 + 구현 + 검증 PPT | | [docs/drift_detection_theory_and_impl.py](drift_detection_theory_and_impl.py) | 신규 | ~350줄 | PPT 생성 스크립트 | | [docs/drift_detection.md](drift_detection.md) | 신규 | 이 문서 | 마크다운 정리본 | --- ## 7. 검증 절차 ```bash # 1) 결정 이력 누적 — 파일 분석 탭에서 [C][S][O] 클릭으로 최소 20+ 건 # (window=10 기준) # 2) 사이드바 → 운영 → "Drift 탐지" 클릭 # 3) window 크기 + 임계값 설정 후 "Drift 분석 실행" # → 알림 카드, 윈도우 비교 3컬럼, Entity 표, 시계열 SVG 모두 표시 # 4) bin size 변경 후 "시계열 새로고침" — 정확도·갭 추이 확인 # 5) curl 직접 검증 curl "http://127.0.0.1:5050/api/drift/snapshot?window=8" curl "http://127.0.0.1:5050/api/drift/timeline?bin=5" ``` ### 검증 결과 (현재 노트북) - `accuracy_drift = −37.5%p` → `alert` - `gap_drift = +0.75` → `warn` - 총 알림 2건 (실제 결정 이력 기반) --- ## 8. 향후 보완 (선택) | 항목 | 분량 | 효과 | |---|---|---| | 주기적 자동 실행 (cron + Slack/email 알림) | 1일 | 사람이 탭을 안 봐도 알람 | | KS 검정 / CUSUM | 2일 | 통계적으로 더 엄밀한 변화점 탐지 | | 사용자별 분리 (`user_id` 별 drift) | 1일 | 다중 사용자 환경 | | Drift 원인 자동 분류 (concept vs data vs user) | 2~3일 | 진단 자동화 | | 알림 영속 (`alerts.db` + 대시보드) | 1일 | 이력 추적 | --- ## 9. SPEC §2.4 완료 상태 | 항목 | 모듈 | 상태 | |---|---|---| | (1) sample weight (학습 시 가중치) | `train.py` | ✅ | | (2) Platt calibration (신뢰도 보정) | `platt_calibration.py` | ✅ | | (3) Rule mining (자동 룰 후보) | `rule_mining.py` | ✅ | | **(4) Drift detection** (패턴 변화 감지) | **`drift_detection.py`** | ✅ **이번 구현** | **SPEC §2.4 갭 활용 4가지 전부 구현 완료.** --- ## 참고 - SPEC: [`SPEC_파일분류_PoC.md`](../SPEC_파일분류_PoC.md) §2.4 (4) — "User profile drift detection: 특정 사용자의 갭이 커지면 → 정책 변경/조직 변경 신호로 알림" - PSI 이론: Karakoulas (2004) "Empirical Validation of Retail Credit-Scoring Models" - 보조 자료: [`docs/drift_detection.pptx`](drift_detection.pptx) — 슬라이드로 같은 내용