Spaces:
Sleeping
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 κ° μμ£Ό λμ€λμ§μ λ³ν |
|---|---|
| μκ³κ° | ` |
| νΉμ | 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 λΌμ°νΈ
GET /api/drift/snapshot?window=30
&accuracy_drop=0.15
&gap_rise=0.3
&entity_psi=0.1
β μλ΅: recent, previous, drift, entities[], alerts[]
GET /api/drift/timeline?bin=10
β μλ΅: bins[] (κ° bin μ idx, n, accuracy, mean_gap, start_at, end_at)
4. μλ¦Ό μμ β μ€μ μλ΅
κ²μ¦ μ λ°μ μ€μ λ°μ΄ν° (window=8):
{
"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 |
| 6. Entity λΆν¬ λ³ν ν | Entity Type Β· μ΅κ·Ό Β· μ΄μ Β· λΉμ¨ λ³ν Β· PSI Β· μν (μ κ·/μλ©Έ/λ³ν νΌ) |
6. νμΌ λ³κ²½ μμ½
| νμΌ | μ ν | λΆλ | λ΄μ© |
|---|---|---|---|
| drift_detection.py | μ κ· | ~180μ€ | snapshot() / timeline() / _window_stats / _entity_drift / _build_alerts |
| app.py / app_lite.py | μμ | +30μ€ Γ 2 | import + 2 λΌμ°νΈ |
| templates/index.html | μμ | +280μ€ | μ¬μ΄λλ° nav 1 + μ ν panel + JS (SVG μκ³μ΄) |
| templates/index.html | μμ | +30μ€ | μ 체 ꡬμ±λ 9λ²μ§Έ νμ΄νλΌμΈ + κ°μ΄λ κ°±μ |
| docs/drift_detection.pptx | μ κ· | 8 μ¬λΌμ΄λ | μ΄λ‘ + ꡬν + κ²μ¦ PPT |
| docs/drift_detection_theory_and_impl.py | μ κ· | ~350μ€ | PPT μμ± μ€ν¬λ¦½νΈ |
| docs/drift_detection.md | μ κ· | μ΄ λ¬Έμ | λ§ν¬λ€μ΄ μ 리본 |
7. κ²μ¦ μ μ°¨
# 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βalertgap_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Β§2.4 (4) β "User profile drift detection: νΉμ μ¬μ©μμ κ°μ΄ 컀μ§λ©΄ β μ μ± λ³κ²½/μ‘°μ§ λ³κ²½ μ νΈλ‘ μλ¦Ό" - PSI μ΄λ‘ : Karakoulas (2004) "Empirical Validation of Retail Credit-Scoring Models"
- 보쑰 μλ£:
docs/drift_detection.pptxβ μ¬λΌμ΄λλ‘ κ°μ λ΄μ©