Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -36,7 +36,10 @@ CLAUDE_MODEL = "claude-sonnet-4-6" # 2026๋
5์ ํ์ฌ ๊ถ์ฅ ๋ชจ๋ธ
|
|
| 36 |
# ๋ฉ์์ง ํจ์ฑ์ 2๋ฒ ์ํ (2-hop)
|
| 37 |
|
| 38 |
def build_training_data_v2(n_normal=200, n_fraud=50, seed=42):
|
| 39 |
-
"""4์ธ๋ GNN์ฉ
|
|
|
|
|
|
|
|
|
|
| 40 |
np.random.seed(seed)
|
| 41 |
normal_arr = np.column_stack([
|
| 42 |
np.random.normal(100, 50, n_normal),
|
|
@@ -179,20 +182,22 @@ gnn_model = train_gnn()
|
|
| 179 |
FEATURES = ['๊ธ์ก', '์๊ฐ', '์ ๊ท์์ทจ์ธ', '๊ธ์ก๋น์จ']
|
| 180 |
|
| 181 |
def build_training_data():
|
| 182 |
-
"""ํ์ต ๋ฐ์ดํฐ ์์ฑ (
|
|
|
|
|
|
|
| 183 |
np.random.seed(42)
|
| 184 |
normal = pd.DataFrame({
|
| 185 |
-
'๊ธ์ก': np.random.normal(100, 50,
|
| 186 |
-
'์๊ฐ': np.random.normal(14, 4,
|
| 187 |
-
'์ ๊ท์์ทจ์ธ': np.random.binomial(1, 0.1,
|
| 188 |
-
'๊ธ์ก๋น์จ': np.random.normal(1, 0.5,
|
| 189 |
'๋ผ๋ฒจ': 0
|
| 190 |
})
|
| 191 |
fraud = pd.DataFrame({
|
| 192 |
-
'๊ธ์ก': np.random.normal(500, 200,
|
| 193 |
-
'์๊ฐ': np.random.normal(3, 2,
|
| 194 |
-
'์ ๊ท์์ทจ์ธ': np.random.binomial(1, 0.8,
|
| 195 |
-
'๊ธ์ก๋น์จ': np.random.normal(10, 5,
|
| 196 |
'๋ผ๋ฒจ': 1
|
| 197 |
})
|
| 198 |
return pd.concat([normal, fraud], ignore_index=True)
|
|
@@ -268,6 +273,47 @@ def card_header(gen_label, title, decision_text, bg_color, text_color, sub):
|
|
| 268 |
def formula_box(html):
|
| 269 |
return f"""<div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:'Courier New',monospace; font-size:12px; margin-bottom:10px; line-height:1.6;">{html}</div>"""
|
| 270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
CARD_STYLE = ("background:#fff; border:0.5px solid rgba(0,0,0,0.15); "
|
| 272 |
"border-radius:12px; padding:16px 20px; margin-bottom:14px;")
|
| 273 |
|
|
@@ -294,9 +340,22 @@ def render_gen1(amount, hour, new_payee_bin, ratio):
|
|
| 294 |
<td style="text-align:right; padding:6px 4px; font-weight:500; color:{td_color};">+{applied}</td>
|
| 295 |
</tr>"""
|
| 296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
return f"""
|
| 298 |
<div style="{CARD_STYLE}">
|
| 299 |
{card_header("GEN 1 ยท RULE-BASED", "๊ท์น ๊ธฐ๋ฐ ํ๋จ", dec, bg, fg, f"๋์ {score}์ / 100์ ")}
|
|
|
|
| 300 |
{formula_box("์ด์ = ฮฃ (๋ฐ๋๋ ๋ฃฐ์ ๊ฐ์ค์น) โ ์๊ณ๊ฐ ๋น๊ต (โฅ70 ์ฐจ๋จ / โฅ40 ์ถ๊ฐ์ธ์ฆ)")}
|
| 301 |
<table style="width:100%; font-size:13px; border-collapse:collapse;">
|
| 302 |
<thead>
|
|
@@ -351,9 +410,23 @@ def render_gen2(amount, hour, new_payee_bin, ratio):
|
|
| 351 |
f"<span style='font-weight:500;'>{prob:.4f} โ {prob*100:.2f}%</span>"
|
| 352 |
)
|
| 353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
return f"""
|
| 355 |
<div style="{CARD_STYLE}">
|
| 356 |
-
{card_header("GEN 2 ยท LOGISTIC REGRESSION", "๋ก์ง์คํฑ ํ๊ท", dec, bg, fg, f"์ฌ๊ธฐ ํ๋ฅ {prob*100:.2f}%")}
|
|
|
|
| 357 |
{formula_box("z = wโยท๊ธ์ก + wโยท์๊ฐ + wโยท์ ๊ท์์ทจ์ธ + wโยท๊ธ์ก๋น์จ + b<br>P(์ฌ๊ธฐ) = 1 / (1 + e<sup>-z</sup>)")}
|
| 358 |
<table style="width:100%; font-size:13px; border-collapse:collapse;">
|
| 359 |
<thead>
|
|
@@ -395,30 +468,113 @@ def render_gen3(amount, hour, new_payee_bin, ratio):
|
|
| 395 |
<span style="text-align:right; font-family:monospace; color:#666;">{imp:.3f}</span>
|
| 396 |
</div>"""
|
| 397 |
|
| 398 |
-
#
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
|
| 413 |
return f"""
|
| 414 |
<div style="{CARD_STYLE}">
|
| 415 |
-
{card_header("GEN 3 ยท XGBOOST (TREE ENSEMBLE)", "
|
|
|
|
| 416 |
{formula_box("F(x) = ฮฃ<sub>k=1..K</sub> f<sub>k</sub>(x), f<sub>k</sub> โ ํธ๋ฆฌ ๊ณต๊ฐ<br>P(์ฌ๊ธฐ) = sigmoid(F(x)) [K=10, max_depth=3, lr=0.1]")}
|
| 417 |
<p style="font-size:13px; color:#666; margin:12px 0 6px;">ํผ์ฒ ์ค์๋ (Gain ๊ธฐ๋ฐ)</p>
|
| 418 |
<div style="display:flex; flex-direction:column; gap:6px; font-size:13px;">{imp_bars}</div>
|
| 419 |
-
<p style="font-size:13px; color:#666; margin:14px 0 6px;">
|
| 420 |
-
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
| 422 |
</div>
|
| 423 |
"""
|
| 424 |
|
|
@@ -652,9 +808,24 @@ def render_gen4(amount, hour, new_payee_bin, ratio, prob3):
|
|
| 652 |
</table>
|
| 653 |
"""
|
| 654 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 655 |
return prob, f"""
|
| 656 |
<div style="{CARD_STYLE}">
|
| 657 |
{card_header("GEN 4 ยท GNN (GRAPH NEURAL NETWORK)", "๊ทธ๋ํ ์ ๊ฒฝ๋ง (numpy ํ์ต)", dec, bg, fg, f"์ฌ๊ธฐ ํ๋ฅ {prob*100:.2f}%")}
|
|
|
|
| 658 |
{formula_box("h<sub>v</sub><sup>(l+1)</sup> = ReLU(W<sup>(l)</sup> ยท AGG({{h<sub>u</sub><sup>(l)</sup> : u โ N(v)}}))<br>P(์ฌ๊ธฐ) = sigmoid(W<sub>mlp</sub> ยท h<sub>๊ฑฐ๋</sub><sup>(2)</sup>) [2-hop ๋ฉ์์ง ํจ์ฑ, 250๊ฑด ํ์ต๋จ]")}
|
| 659 |
<p style="font-size:13px; color:#666; margin:12px 0 6px;">2-hop ์ด์ ๊ทธ๋ํ</p>
|
| 660 |
{svg}
|
|
@@ -874,9 +1045,24 @@ def render_gen5_card(amount, hour, new_payee_bin, ratio, prior_avg, prior_dec,
|
|
| 874 |
f"{judgment_text}"
|
| 875 |
)
|
| 876 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 877 |
return f"""
|
| 878 |
<div style="{CARD_STYLE}">
|
| 879 |
{card_header("GEN 5 ยท FOUNDATION MODEL (LLM)", f"์ด๊ฑฐ๋ ์ถ๋ก ๋ชจ๋ธ ({source_label})", dec, bg, fg, f"์์ฌ๋ {prob*100:.0f}%")}
|
|
|
|
| 880 |
<p style="font-size:13px; color:#666; margin:4px 0 6px;">์ปจํ
์คํธ ํ ํฐํ</p>
|
| 881 |
<div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:monospace; font-size:11px; line-height:1.7; margin-bottom:12px;">{context_html}</div>
|
| 882 |
<p style="font-size:13px; color:#666; margin:4px 0 6px;">์ถ๋ก ์ฒด์ธ (Chain-of-Thought)</p>
|
|
|
|
| 36 |
# ๋ฉ์์ง ํจ์ฑ์ 2๋ฒ ์ํ (2-hop)
|
| 37 |
|
| 38 |
def build_training_data_v2(n_normal=200, n_fraud=50, seed=42):
|
| 39 |
+
"""4์ธ๋ GNN์ฉ ํ์ต ๋ฐ์ดํฐ (250๊ฑด, 1-3์ธ๋์ ๋์ผ ๋ถํฌยท๋์ผ ๊ท๋ชจ).
|
| 40 |
+
1-3์ธ๋์ build_training_data()์ ๊ฐ์ ์ ๊ท๋ถํฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ์ฌ
|
| 41 |
+
๋ชจ๋ ์ธ๋๊ฐ ๊ฐ์ ๋ฐ์ดํฐ ๊ท๋ชจ์ ๋ถํฌ๋ก ํ์ต๋๋๋ก ๋ณด์ฅ.
|
| 42 |
+
"""
|
| 43 |
np.random.seed(seed)
|
| 44 |
normal_arr = np.column_stack([
|
| 45 |
np.random.normal(100, 50, n_normal),
|
|
|
|
| 182 |
FEATURES = ['๊ธ์ก', '์๊ฐ', '์ ๊ท์์ทจ์ธ', '๊ธ์ก๋น์จ']
|
| 183 |
|
| 184 |
def build_training_data():
|
| 185 |
+
"""ํ์ต ๋ฐ์ดํฐ ์์ฑ (250๊ฑด: ์ ์ 200 + ์ฌ๊ธฐ 50)
|
| 186 |
+
1-3์ธ๋ ๋ชจ๋ ๋์ผํ 250๊ฑด์ผ๋ก ํ์ต๋์ด, 4์ธ๋ GNN๊ณผ ๋ฐ์ดํฐ ๊ท๋ชจ๊ฐ ๊ฐ์.
|
| 187 |
+
"""
|
| 188 |
np.random.seed(42)
|
| 189 |
normal = pd.DataFrame({
|
| 190 |
+
'๊ธ์ก': np.random.normal(100, 50, 200),
|
| 191 |
+
'์๊ฐ': np.random.normal(14, 4, 200),
|
| 192 |
+
'์ ๊ท์์ทจ์ธ': np.random.binomial(1, 0.1, 200),
|
| 193 |
+
'๊ธ์ก๋น์จ': np.random.normal(1, 0.5, 200),
|
| 194 |
'๋ผ๋ฒจ': 0
|
| 195 |
})
|
| 196 |
fraud = pd.DataFrame({
|
| 197 |
+
'๊ธ์ก': np.random.normal(500, 200, 50),
|
| 198 |
+
'์๊ฐ': np.random.normal(3, 2, 50),
|
| 199 |
+
'์ ๊ท์์ทจ์ธ': np.random.binomial(1, 0.8, 50),
|
| 200 |
+
'๊ธ์ก๋น์จ': np.random.normal(10, 5, 50),
|
| 201 |
'๋ผ๋ฒจ': 1
|
| 202 |
})
|
| 203 |
return pd.concat([normal, fraud], ignore_index=True)
|
|
|
|
| 273 |
def formula_box(html):
|
| 274 |
return f"""<div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:'Courier New',monospace; font-size:12px; margin-bottom:10px; line-height:1.6;">{html}</div>"""
|
| 275 |
|
| 276 |
+
def feature_setup_box(actor_label, actor_color, items, explanation):
|
| 277 |
+
"""๊ฐ ์ธ๋ ์นด๋ ์๋จ์ 'Feature/Rule์ด ์ด๋ป๊ฒ ๊ฒฐ์ ๋๋๊ฐ'๋ฅผ ํ์ํ๋ ๊ณตํต ๋ฐ์ค.
|
| 278 |
+
|
| 279 |
+
actor_label: '์ฌ๋์ด ๊ฒฐ์ ' / '๋ชจ๋ธ์ด ํ์ต' / 'ํ์ด๋ธ๋ฆฌ๋'
|
| 280 |
+
actor_color: ๋ฐฐ์ง ์ ('blue' | 'orange' | 'mixed')
|
| 281 |
+
items: [(ํญ๋ชฉ๋ช
, ๊ฒฐ์ ์ฃผ์ฒด, ์ค๋ช
)] ๋ฆฌ์คํธ
|
| 282 |
+
explanation: ๋ฐ์ค ํ๋จ์ ํ ์ค ๊ฐ์กฐ ๋ฉ์์ง
|
| 283 |
+
"""
|
| 284 |
+
color_map = {
|
| 285 |
+
'human': ('#E6F1FB', '#0C447C'), # ํ๋ = ์ฌ๋
|
| 286 |
+
'model': ('#FAECE7', '#993C1D'), # ์ฃผํฉ = ๋ชจ๋ธ
|
| 287 |
+
'mixed': ('#F1EFE8', '#5F5E5A'), # ํ์ = ํผํฉ
|
| 288 |
+
}
|
| 289 |
+
badge_bg, badge_fg = color_map.get(actor_color, color_map['mixed'])
|
| 290 |
+
|
| 291 |
+
rows = ""
|
| 292 |
+
for name, actor, desc in items:
|
| 293 |
+
a_bg, a_fg = color_map.get(actor, color_map['mixed'])
|
| 294 |
+
rows += (
|
| 295 |
+
f"<tr>"
|
| 296 |
+
f"<td style='padding:5px 8px; color:#444; width:30%;'>{name}</td>"
|
| 297 |
+
f"<td style='padding:5px 8px; width:20%;'>"
|
| 298 |
+
f"<span style='background:{a_bg}; color:{a_fg}; font-size:10px; padding:2px 8px; border-radius:6px; font-weight:500;'>{actor}</span>"
|
| 299 |
+
f"</td>"
|
| 300 |
+
f"<td style='padding:5px 8px; color:#666; font-size:12px;'>{desc}</td>"
|
| 301 |
+
f"</tr>"
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
return f"""
|
| 305 |
+
<div style="background:#FAFAF7; border:0.5px solid rgba(0,0,0,0.08); border-radius:8px; padding:10px 14px; margin-bottom:14px;">
|
| 306 |
+
<div style="display:flex; align-items:center; gap:10px; margin-bottom:8px;">
|
| 307 |
+
<p style="font-size:12px; font-weight:500; color:#444; margin:0;">โ๏ธ FeatureยทRule ๊ฒฐ์ ๋ฐฉ์</p>
|
| 308 |
+
<span style="background:{badge_bg}; color:{badge_fg}; font-size:10px; padding:3px 10px; border-radius:6px; font-weight:500;">{actor_label}</span>
|
| 309 |
+
</div>
|
| 310 |
+
<table style="width:100%; font-size:13px; border-collapse:collapse;">
|
| 311 |
+
<tbody>{rows}</tbody>
|
| 312 |
+
</table>
|
| 313 |
+
<p style="font-size:11px; color:#888; margin:8px 0 0; font-style:italic; line-height:1.5;">{explanation}</p>
|
| 314 |
+
</div>
|
| 315 |
+
"""
|
| 316 |
+
|
| 317 |
CARD_STYLE = ("background:#fff; border:0.5px solid rgba(0,0,0,0.15); "
|
| 318 |
"border-radius:12px; padding:16px 20px; margin-bottom:14px;")
|
| 319 |
|
|
|
|
| 340 |
<td style="text-align:right; padding:6px 4px; font-weight:500; color:{td_color};">+{applied}</td>
|
| 341 |
</tr>"""
|
| 342 |
|
| 343 |
+
gen1_setup = feature_setup_box(
|
| 344 |
+
actor_label="100% ์ฌ๋ ๊ฒฐ์ ",
|
| 345 |
+
actor_color='human',
|
| 346 |
+
items=[
|
| 347 |
+
("์
๋ ฅ Feature 4๊ฐ", "์ฌ๋", "๋๋ฉ์ธ ์ ๋ฌธ๊ฐ๊ฐ '๊ธ์กยท์๊ฐยท์ ๊ท์์ทจ์ธยท๊ธ์ก๋น์จ'์ ์ฌ๊ธฐ ํ๋จ ๊ธฐ๏ฟฝ๏ฟฝ๏ฟฝ์ผ๋ก ์ ์ "),
|
| 348 |
+
("๋ฃฐ ์กฐ๊ฑด (์๊ณ๊ฐ)", "์ฌ๋", "โฅ500๋ง์, โค6์ ๋๋ โฅ22์, =1, โฅ5๋ฐฐ โ ๋ชจ๋ ์ฌ๋์ด ์ง์ ๊ฒฐ์ "),
|
| 349 |
+
("๋ฃฐ๋ณ ๊ฐ์ค์น", "์ฌ๋", "40 / 30 / 20 / 10์ โ ๋๋ฉ์ธ ๊ฒฝํ์ ๋ฐ๋ผ ์ฌ๋์ด ๋ถ์ฌ"),
|
| 350 |
+
("ํ์ ์๊ณ๊ฐ", "์ฌ๋", "70์ ์ด์ ์ฐจ๋จ, 40์ ์ด์ ์ถ๊ฐ์ธ์ฆ โ ์ด์ํ์ด ๋น์ฆ๋์ค ํ๋จ์ผ๋ก ๊ฒฐ์ "),
|
| 351 |
+
],
|
| 352 |
+
explanation="๋ชจ๋ ๊ฒฐ์ ์ด ์ฌ๋์ ๋๋ฉ์ธ ์ง์์ ์์กด. ํ์ต ๋ฐ์ดํฐ๋ ์ฌ์ฉํ์ง ์์. ์ ์ฌ๊ธฐ ํจํด ๋ฑ์ฅ ์ ์ฌ๋์ด ๋ฃฐ์ ์ถ๊ฐํด์ผ ํจ."
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
return f"""
|
| 356 |
<div style="{CARD_STYLE}">
|
| 357 |
{card_header("GEN 1 ยท RULE-BASED", "๊ท์น ๊ธฐ๋ฐ ํ๋จ", dec, bg, fg, f"๋์ {score}์ / 100์ ")}
|
| 358 |
+
{gen1_setup}
|
| 359 |
{formula_box("์ด์ = ฮฃ (๋ฐ๋๋ ๋ฃฐ์ ๊ฐ์ค์น) โ ์๊ณ๊ฐ ๋น๊ต (โฅ70 ์ฐจ๋จ / โฅ40 ์ถ๊ฐ์ธ์ฆ)")}
|
| 360 |
<table style="width:100%; font-size:13px; border-collapse:collapse;">
|
| 361 |
<thead>
|
|
|
|
| 410 |
f"<span style='font-weight:500;'>{prob:.4f} โ {prob*100:.2f}%</span>"
|
| 411 |
)
|
| 412 |
|
| 413 |
+
gen2_setup = feature_setup_box(
|
| 414 |
+
actor_label="ํผ์ฒ๋ ์ฌ๋, ๊ฐ์ค์น๋ ๋ชจ๋ธ",
|
| 415 |
+
actor_color='mixed',
|
| 416 |
+
items=[
|
| 417 |
+
("์
๋ ฅ Feature 4๊ฐ", "์ฌ๋", "1์ธ๋์ ๋์ผํ 4๊ฐ ์ปฌ๋ผ์ ์ฌ๋์ด ์ ์ (ํผ์ฒ ์์ง๋์ด๋ง)"),
|
| 418 |
+
("ํ์ต ๋ฐ์ดํฐ", "์ฌ๋", "250๊ฑด์ ๊ฑฐ๋์ ์ฌ๊ธฐ/์ ์ ๋ผ๋ฒจ์ ์ฌ๋์ด ๋ถ์ฌ"),
|
| 419 |
+
("๊ฐ์ค์น wโ~wโ", "๋ชจ๋ธ", "fit() ํธ์ถ ์ L-BFGS ์๊ณ ๋ฆฌ์ฆ์ด ์๋ ํ์ต"),
|
| 420 |
+
("์ ํธ b", "๋ชจ๋ธ", "๋ฐ์ดํฐ์ ์ฌ๊ธฐ ๋น์จ(50/250=20%)์ ๋ง์ถฐ ์๋ ์กฐ์ "),
|
| 421 |
+
("ํ์ ์๊ณ๊ฐ", "์ฌ๋", "0.5(์ถ๊ฐ์ธ์ฆ) / 0.7(์ฐจ๋จ) โ ์ด์ํ์ด ๊ฒฐ์ "),
|
| 422 |
+
],
|
| 423 |
+
explanation="ํผ์ฒ๋ ์ฌ์ ํ ์ฌ๋์ด ์ ์. ๋ชจ๋ธ์ด ํ์ตํ๋ ๊ฑด '4๊ฐ ํผ์ฒ์ ์ด๋ค ๊ฐ์ค์น๋ฅผ ๊ณฑํด์ผ ์ฌ๊ธฐ๋ฅผ ์ ๋ง์ถ๋๊ฐ'๋ฟ. ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋ฉด ๊ฐ์ค์น๋ ์๋ ์ฌํ์ต๋์ง๋ง, ํผ์ฒ ์์ฒด๋ ์ฌ๋์ด ๋ค์ ์ค๊ณํด์ผ ํจ."
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
return f"""
|
| 427 |
<div style="{CARD_STYLE}">
|
| 428 |
+
{card_header("GEN 2 ยท LOGISTIC REGRESSION", "๋ก์ง์คํฑ ํ๊ท (์ ํ ๋ชจ๋ธ)", dec, bg, fg, f"์ฌ๊ธฐ ํ๋ฅ {prob*100:.2f}%")}
|
| 429 |
+
{gen2_setup}
|
| 430 |
{formula_box("z = wโยท๊ธ์ก + wโยท์๊ฐ + wโยท์ ๊ท์์ทจ์ธ + wโยท๊ธ์ก๋น์จ + b<br>P(์ฌ๊ธฐ) = 1 / (1 + e<sup>-z</sup>)")}
|
| 431 |
<table style="width:100%; font-size:13px; border-collapse:collapse;">
|
| 432 |
<thead>
|
|
|
|
| 468 |
<span style="text-align:right; font-family:monospace; color:#666;">{imp:.3f}</span>
|
| 469 |
</div>"""
|
| 470 |
|
| 471 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 472 |
+
# ์ง์ง ํ์ต๋ ํธ๋ฆฌ 10๊ฐ ์ ์ฒด์์ ๋ณธ ๊ฑฐ๋๊ฐ ๋๋ฌํ leaf ๊ฐ์ ์ถ์ถ
|
| 473 |
+
# โ ๋ถ์คํ
์ ๋ณธ์ง(ํธ๋ฆฌ๋ค์ด ๋์ ๋์ด ์ ์๋ฅผ ๋ง๋ ๋ค)์ ์ ์งํ๊ฒ ๋
ธ์ถ
|
| 474 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 475 |
+
booster = gen3_model.get_booster()
|
| 476 |
+
trees_df = booster.trees_to_dataframe()
|
| 477 |
+
input_values = {'๊ธ์ก': amount, '์๊ฐ': hour, '์ ๊ท์์ทจ์ธ': new_payee_bin, '๊ธ์ก๋น์จ': ratio}
|
| 478 |
+
|
| 479 |
+
tree_traces = [] # ๊ฐ ํธ๋ฆฌ์ (๋ถ๊ธฐ ๊ฒฝ๋ก, leaf ๊ฐ)์ ์ ์ฅ
|
| 480 |
+
for tree_id in range(10):
|
| 481 |
+
tree = trees_df[trees_df['Tree'] == tree_id].set_index('ID')
|
| 482 |
+
# ๋ฃจํธ ๋
ธ๋๋ถํฐ leaf๊น์ง ๋ฐ๋ผ๊ฐ๊ธฐ
|
| 483 |
+
current_id = f"{tree_id}-0"
|
| 484 |
+
path = []
|
| 485 |
+
leaf_val = 0.0
|
| 486 |
+
while True:
|
| 487 |
+
row = tree.loc[current_id]
|
| 488 |
+
if row['Feature'] == 'Leaf':
|
| 489 |
+
leaf_val = float(row['Gain'])
|
| 490 |
+
break
|
| 491 |
+
feature = row['Feature']
|
| 492 |
+
split = float(row['Split'])
|
| 493 |
+
input_v = input_values[feature]
|
| 494 |
+
if input_v < split:
|
| 495 |
+
path.append(f"[{feature} < {split:.2f}] Yes")
|
| 496 |
+
current_id = row['Yes']
|
| 497 |
+
else:
|
| 498 |
+
path.append(f"[{feature} < {split:.2f}] No")
|
| 499 |
+
current_id = row['No']
|
| 500 |
+
tree_traces.append((path, leaf_val))
|
| 501 |
+
|
| 502 |
+
# raw_score ํฉ์ฐ (์ค์ XGBoost ๋ด๋ถ ๋์ ์ฌํ)
|
| 503 |
+
raw_score = sum(leaf for _, leaf in tree_traces)
|
| 504 |
+
# XGBoost ๋ด๋ถ base_score ์ถ๊ฐ (๊ธฐ๋ณธ 0.5 โ logit 0)
|
| 505 |
+
# logit ํฉ์ฐ ํ sigmoid โ predict_proba์ ๊ฑฐ์ ์ผ์นํด์ผ ํจ
|
| 506 |
+
|
| 507 |
+
# ํธ๋ฆฌ๋ณ leaf ๊ฐ ๋์ ํ
์ด๋ธ
|
| 508 |
+
tree_rows = ""
|
| 509 |
+
cumulative = 0.0
|
| 510 |
+
for tree_id, (path, leaf) in enumerate(tree_traces):
|
| 511 |
+
cumulative += leaf
|
| 512 |
+
path_text = " โ ".join(path) + f" โ <b>leaf={leaf:+.4f}</b>"
|
| 513 |
+
leaf_color = "#4A1B0C" if leaf > 0 else "#04342C"
|
| 514 |
+
leaf_bg = "#FAECE7" if leaf > 0 else "#E1F5EE"
|
| 515 |
+
tree_rows += (
|
| 516 |
+
f"<tr style='background:{leaf_bg};'>"
|
| 517 |
+
f"<td style='padding:5px 6px; font-family:monospace;'>#{tree_id}</td>"
|
| 518 |
+
f"<td style='padding:5px 6px; font-size:11px; color:#555;'>{path_text}</td>"
|
| 519 |
+
f"<td style='text-align:right; padding:5px 6px; font-family:monospace; color:{leaf_color}; font-weight:500;'>{leaf:+.4f}</td>"
|
| 520 |
+
f"<td style='text-align:right; padding:5px 6px; font-family:monospace; color:#666;'>{cumulative:+.4f}</td>"
|
| 521 |
+
f"</tr>"
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
sigmoid_result = 1 / (1 + np.exp(-raw_score))
|
| 525 |
+
|
| 526 |
+
tree_table = f"""
|
| 527 |
+
<table style="width:100%; font-size:12px; border-collapse:collapse;">
|
| 528 |
+
<thead>
|
| 529 |
+
<tr style="background:#f5f5f0; border-bottom:0.5px solid rgba(0,0,0,0.15);">
|
| 530 |
+
<th style="text-align:left; padding:6px; font-weight:500; color:#666; width:8%;">ํธ๋ฆฌ</th>
|
| 531 |
+
<th style="text-align:left; padding:6px; font-weight:500; color:#666; width:60%;">๋ณธ ๊ฑฐ๋์ ๋ถ๊ธฐ ๊ฒฝ๋ก โ ๋๋ฌํ leaf</th>
|
| 532 |
+
<th style="text-align:right; padding:6px; font-weight:500; color:#666; width:14%;">leaf ๊ฐ</th>
|
| 533 |
+
<th style="text-align:right; padding:6px; font-weight:500; color:#666; width:18%;">๋์ raw score</th>
|
| 534 |
+
</tr>
|
| 535 |
+
</thead>
|
| 536 |
+
<tbody>{tree_rows}
|
| 537 |
+
<tr style="background:#F1EFE8; font-weight:500; border-top:1px solid rgba(0,0,0,0.3);">
|
| 538 |
+
<td colspan="3" style="text-align:right; padding:6px;">์ต์ข
raw score (10๊ฐ ํธ๋ฆฌ ํฉ์ฐ)</td>
|
| 539 |
+
<td style="text-align:right; padding:6px; font-family:monospace;">{raw_score:+.4f}</td>
|
| 540 |
+
</tr>
|
| 541 |
+
</tbody>
|
| 542 |
+
</table>
|
| 543 |
+
"""
|
| 544 |
+
|
| 545 |
+
final_calc = formula_box(
|
| 546 |
+
f"P(์ฌ๊ธฐ) = sigmoid({raw_score:+.4f}) "
|
| 547 |
+
f"= 1 / (1 + e<sup>{-raw_score:+.4f}</sup>) "
|
| 548 |
+
f"= <b>{sigmoid_result:.4f}</b> ({sigmoid_result*100:.2f}%)"
|
| 549 |
+
)
|
| 550 |
+
|
| 551 |
+
gen3_setup = feature_setup_box(
|
| 552 |
+
actor_label="ํผ์ฒ๋ ์ฌ๋, ํธ๋ฆฌ ๊ตฌ์กฐ๋ ๋ชจ๋ธ",
|
| 553 |
+
actor_color='mixed',
|
| 554 |
+
items=[
|
| 555 |
+
("์
๋ ฅ Feature 4๊ฐ", "์ฌ๋", "1ยท2์ธ๋์ ์์ ํ ๋์ผํ 4๊ฐ ์ปฌ๋ผ"),
|
| 556 |
+
("ํ์ต ๋ฐ์ดํฐ", "์ฌ๋", "1ยท2์ธ๋์ ๋์ผํ 250๊ฑด"),
|
| 557 |
+
("ํธ๋ฆฌ ๋ถ๊ธฐ ์๊ณ๊ฐ", "๋ชจ๋ธ", "Gain ์ต๋ํ๋ก ์๋ ๊ฒฐ์ (์: ์๊ฐ < 8.69, ๊ธ์ก๋น์จ < 1.77)"),
|
| 558 |
+
("๊ฐ leaf ๊ฐ", "๋ชจ๋ธ", "๊ฐ leaf์ ๋๋ฌํ ์ํ๋ค์ ์์ฐจ๋ก ์๋ ๊ณ์ฐ"),
|
| 559 |
+
("ํธ๋ฆฌ ๊ฐ์ / ๊น์ด", "์ฌ๋", "n_estimators=10, max_depth=3 (ํ์ดํผํ๋ผ๋ฏธํฐ)"),
|
| 560 |
+
("ํ์ ์๊ณ๊ฐ", "์ฌ๋", "0.5(์ถ๊ฐ์ธ์ฆ) / 0.7(์ฐจ๋จ)"),
|
| 561 |
+
],
|
| 562 |
+
explanation="2์ธ๋๋ณด๋ค ํ์ต๋๋ ๋ถ๋ถ์ด ํจ์ฌ ๋ง์์ง. ๋ถ๊ธฐ ์๊ณ๊ฐ(์ 4.8์ด ์๋๋ผ 1.77์ธ๊ฐ)๊ณผ leaf ๊ฐ ๋ชจ๋ ๋ฐ์ดํฐ์์ ์๋ ๋ฐ๊ฒฌ. ๋จ ์
๋ ฅ ํผ์ฒ ์์ฒด๋ ์ฌ์ ํ ์ฌ๋์ด ์ ํ 4๊ฐ."
|
| 563 |
+
)
|
| 564 |
|
| 565 |
return f"""
|
| 566 |
<div style="{CARD_STYLE}">
|
| 567 |
+
{card_header("GEN 3 ยท XGBOOST (TREE ENSEMBLE)", "XGBoost (ํธ๋ฆฌ 10๊ฐ ์์๋ธ)", dec, bg, fg, f"์ฌ๊ธฐ ํ๋ฅ {prob*100:.2f}%")}
|
| 568 |
+
{gen3_setup}
|
| 569 |
{formula_box("F(x) = ฮฃ<sub>k=1..K</sub> f<sub>k</sub>(x), f<sub>k</sub> โ ํธ๋ฆฌ ๊ณต๊ฐ<br>P(์ฌ๊ธฐ) = sigmoid(F(x)) [K=10, max_depth=3, lr=0.1]")}
|
| 570 |
<p style="font-size:13px; color:#666; margin:12px 0 6px;">ํผ์ฒ ์ค์๋ (Gain ๊ธฐ๋ฐ)</p>
|
| 571 |
<div style="display:flex; flex-direction:column; gap:6px; font-size:13px;">{imp_bars}</div>
|
| 572 |
+
<p style="font-size:13px; color:#666; margin:14px 0 6px;">๐ณ ํ์ต๋ ํธ๋ฆฌ 10๊ฐ์ leaf ๊ฐ ๋์ (๋ถ์คํ
ํต์ฌ)</p>
|
| 573 |
+
<p style="font-size:12px; color:#888; margin:0 0 8px; font-style:italic;">๊ฐ ํธ๋ฆฌ๊ฐ ์ด์ ํธ๋ฆฌ์ ์์ฐจ๋ฅผ ๋ณด๏ฟฝ๏ฟฝํ๋ฉฐ leaf ๊ฐ์ ๋ํด๊ฐ โ ๋์ ๋ raw score๋ฅผ sigmoid๋ก ๋ณํ</p>
|
| 574 |
+
{tree_table}
|
| 575 |
+
<p style="font-size:13px; color:#666; margin:14px 0 6px;">๐งฎ ์ต์ข
ํ๋ฅ ๊ณ์ฐ</p>
|
| 576 |
+
{final_calc}
|
| 577 |
+
<p style="font-size:12px; color:#888; margin:10px 0 0; font-style:italic;">๊ฐ์ : ๋น์ ํ ํจํดยทํผ์ฒ ์ํธ์์ฉ ์๋ ํ์ต. ํ๊ณ: ํ์ต ๋ฐ์ดํฐ ๋ถํฌ ๋ฐ์ ์ผ์ด์ค(์: ์ ์ธ ์๊ธ)๋ ์ฌ์ ํ ๋ชป ์ก์</p>
|
| 578 |
</div>
|
| 579 |
"""
|
| 580 |
|
|
|
|
| 808 |
</table>
|
| 809 |
"""
|
| 810 |
|
| 811 |
+
gen4_setup = feature_setup_box(
|
| 812 |
+
actor_label="๊ตฌ์กฐ๋ ์ฌ๋, ์๋ฒ ๋ฉ์ ๋ชจ๋ธ ์์ฑ",
|
| 813 |
+
actor_color='model',
|
| 814 |
+
items=[
|
| 815 |
+
("๊ทธ๋ํ ๊ตฌ์กฐ ์ ์", "์ฌ๋", "๋
ธ๋ ์ข
๋ฅ(๊ฑฐ๋ยท์ก๊ธ์ธยท์์ทจ์ธยท๋จ๋ง๊ธฐ)์ ์ฃ์ง ๊ด๊ณ๋ ์ฌ๋์ด ์ค๊ณ"),
|
| 816 |
+
("ํ์ต ๋ฐ์ดํฐ", "์ฌ๋", "1-3์ธ๋์ ๋์ผํ 250๊ฑด ๊ฑฐ๋์ ๋ผ๋ฒจ ๋ถ์ฌ"),
|
| 817 |
+
("๋
ธ๋ ์ด๊ธฐ ์๋ฒ ๋ฉ", "์ฌ๋", "๊ฐ ๋
ธ๋์ 4์ฐจ์ ์ด๊ธฐ๊ฐ์ ์ฌ๋์ด ์ธ์ฝ๋ฉ ๊ท์น ์์ฑ"),
|
| 818 |
+
("์๋์ธต 16์ฐจ์ Feature", "๋ชจ๋ธ", "์ฌ๋์ด ์ ์ ์ ํจ. ๋ชจ๋ธ์ด ํ์ต์ผ๋ก 16๊ฐ ์ต๋ช
์ฐจ์์ ์๋ ์์ฑ"),
|
| 819 |
+
("๊ฐ์ค์น Wโ, Wโ, W_mlp", "๋ชจ๋ธ", "337๊ฐ ํ๋ผ๋ฏธํฐ๋ฅผ backpropagation์ผ๋ก ์๋ ํ์ต"),
|
| 820 |
+
("ํ์ ์๊ณ๊ฐ", "์ฌ๋", "0.5(์ถ๊ฐ์ธ์ฆ) / 0.7(์ฐจ๋จ)"),
|
| 821 |
+
],
|
| 822 |
+
explanation="๊ฒฐ์ ์ ์ฐจ์ด: 1-3์ธ๋๋ ์ฌ๋์ด ์ ํ 4๊ฐ ํผ์ฒ๋ง ๋ดค์ง๋ง, 4์ธ๋๋ ๋ชจ๋ธ์ด 16+16=32๊ฐ์ ์ ์ต๋ช
ํผ์ฒ๋ฅผ ์ค์ค๋ก ๋ง๋ค์ด๋. ์ด ์ต๋ช
ํผ์ฒ๋ค์ ์๋ฏธ๋ ์ฌ๋์ด ์ฌํ์๋ ๋๋ถ๋ถ ํด์ ๋ถ๊ฐ."
|
| 823 |
+
)
|
| 824 |
+
|
| 825 |
return prob, f"""
|
| 826 |
<div style="{CARD_STYLE}">
|
| 827 |
{card_header("GEN 4 ยท GNN (GRAPH NEURAL NETWORK)", "๊ทธ๋ํ ์ ๊ฒฝ๋ง (numpy ํ์ต)", dec, bg, fg, f"์ฌ๊ธฐ ํ๋ฅ {prob*100:.2f}%")}
|
| 828 |
+
{gen4_setup}
|
| 829 |
{formula_box("h<sub>v</sub><sup>(l+1)</sup> = ReLU(W<sup>(l)</sup> ยท AGG({{h<sub>u</sub><sup>(l)</sup> : u โ N(v)}}))<br>P(์ฌ๊ธฐ) = sigmoid(W<sub>mlp</sub> ยท h<sub>๊ฑฐ๋</sub><sup>(2)</sup>) [2-hop ๋ฉ์์ง ํจ์ฑ, 250๊ฑด ํ์ต๋จ]")}
|
| 830 |
<p style="font-size:13px; color:#666; margin:12px 0 6px;">2-hop ์ด์ ๊ทธ๋ํ</p>
|
| 831 |
{svg}
|
|
|
|
| 1045 |
f"{judgment_text}"
|
| 1046 |
)
|
| 1047 |
|
| 1048 |
+
gen5_setup = feature_setup_box(
|
| 1049 |
+
actor_label="ํ๋กฌํํธ๋ง ์ฌ๋, ์ถ๋ก ์ ์ ์ ์ผ๋ก ๋ชจ๋ธ",
|
| 1050 |
+
actor_color='model',
|
| 1051 |
+
items=[
|
| 1052 |
+
("์์คํ
ํ๋กฌํํธ", "์ฌ๋", "'๋น์ ์ FDS ๋ถ์๊ฐ์
๋๋ค' ๋ฑ์ ์ญํ ๋ถ์ฌ๋ง ์ฌ๋์ด ์์ฑ"),
|
| 1053 |
+
("ํ์ต ๋ฐ์ดํฐ", "๋ชจ๋ธ", "Anthropic์ด ์ธํฐ๋ท ๊ท๋ชจ ๋ฐ์ดํฐ๋ก ์ฌ์ ํ์ต (์์กฐ ํ ํฐ)"),
|
| 1054 |
+
("๋๋ฉ์ธ ์ง์", "๋ชจ๋ธ", "๋ณด์ด์คํผ์ฑ ํจํด, ๋ถ๋์ฐ ๊ฑฐ๋ ์ ํ ๋ฑ์ ์ฌ์ ํ์ต์ผ๋ก ๋ณด์ "),
|
| 1055 |
+
("์ถ๋ก ๋จ๊ณ (CoT)", "๋ชจ๋ธ", "๊ฐ ๋จ๊ณ์์ ๋ฌด์์ ์ฃผ๋ชฉํ ์ง ๋ชจ๋ธ์ด ์ค์ค๋ก ๊ฒฐ์ "),
|
| 1056 |
+
("์ต์ข
ํ๋จ ๋ฌธ์ฅ", "๋ชจ๋ธ", "์์ฐ์ด๋ก ์๋ ์์ฑ"),
|
| 1057 |
+
("ํ์ ์๊ณ๊ฐ", "์ฌ๋", "0.5(์ถ๊ฐ์ธ์ฆ) / 0.7(์ฐจ๋จ)"),
|
| 1058 |
+
],
|
| 1059 |
+
explanation="๊ฐ์ฅ ๊ทน์ ์ธ ๋ณํ: ์ฌ๋์ด ์
๋ ฅ์ผ๋ก ์ฃผ๋ ๊ฑด '๊ฑฐ๋ ์ ๋ณด + ์ฌ์ ํ๋จ'๋ฟ. ๋ชจ๋ธ์ด ์ฌ์ ํ์ต๋ ๋๋ฉ์ธ ์ง์์ผ๋ก '์ ์ฌ๊ธฐ์ธ์ง/์๋์ง'๋ฅผ ์์ฐ์ด๋ก ์ถ๋ก . 4์ธ๋๊น์ง์ 250๊ฑด ํ์ต๊ณผ๋ ์ฐจ์์ด ๋ค๋ฅธ ๊ท๋ชจ์ ์ฌ์ ํ์ต์ด ๊น๋ ค ์์."
|
| 1060 |
+
)
|
| 1061 |
+
|
| 1062 |
return f"""
|
| 1063 |
<div style="{CARD_STYLE}">
|
| 1064 |
{card_header("GEN 5 ยท FOUNDATION MODEL (LLM)", f"์ด๊ฑฐ๋ ์ถ๋ก ๋ชจ๋ธ ({source_label})", dec, bg, fg, f"์์ฌ๋ {prob*100:.0f}%")}
|
| 1065 |
+
{gen5_setup}
|
| 1066 |
<p style="font-size:13px; color:#666; margin:4px 0 6px;">์ปจํ
์คํธ ํ ํฐํ</p>
|
| 1067 |
<div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:monospace; font-size:11px; line-height:1.7; margin-bottom:12px;">{context_html}</div>
|
| 1068 |
<p style="font-size:13px; color:#666; margin:4px 0 6px;">์ถ๋ก ์ฒด์ธ (Chain-of-Thought)</p>
|