File size: 3,821 Bytes
fbd1091
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
Step 4: 검색 품질 평가 (Hit@K 메트릭)
실행: python scripts/04_evaluate.py
"""
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from rich.console import Console
from rich.table import Table
from core.search_engine import MenuSearchEngine

console = Console()

# (쿼리, 정답 menu_id) 평가셋 — 실제 SCR_ 형식 ID 기준 (real_menus.json 검증)
EVAL_SET = [
    # 국내주식 - 관심/시세 (search_count 상위)
    ("관심종목 보고싶어",                "SCR_1000"),   # 국내관심, sc=12,739,646
    ("삼성전자 호가 보여줘",             "SCR_1050"),   # 호가, sc=9,120,154
    ("종목 차트 보고싶어",               "SCR_1200"),   # 종목차트, sc=11,006,235
    # 국내주식 - 계좌/주문
    ("국내주식 잔고 확인",               "SCR_1300"),   # 국내잔고, sc=14,981,128
    ("미체결 주문 확인",                 "SCR_1301"),   # 미체결, sc=1,895,282
    ("오늘 손익 얼마야",                 "SCR_1309"),   # 실현손익, sc=875,319
    ("예약주문 걸어놓고 싶어",           "SCR_1180"),   # 예약주문, sc=107,490
    ("내 조건검색식 편집하고 싶어",      "SCR_1871"),   # 조건검색, sc=311,728
    # 해외주식
    ("미국 주식 주문하고 싶어",          "SCR_3030"),   # 해외주식주문, sc=1,615,638
    ("해외주식 잔고 확인",               "SCR_3032"),   # 해외잔고, sc=4,127,850
    ("애플 주식 현재가 호가",            "SCR_3010"),   # 해외호가, sc=1,662,209
    # 파생상품 (CFD)
    ("CFD 주문하고 싶어",               "SCR_2020"),   # 파생 CFD 주문, sc=117
    # 자산/뱅킹
    ("입출금 하고싶어",                  "SCR_5000"),   # 입출금, sc=1,210,154
    ("이체내역 조회",                    "SCR_5006"),   # 이체내역조회, sc=224,757
    # search_count 낮은 메뉴 (다양성 확보)
    ("주식더모으기 설정",               "SCR_1182"),   # 주식더모으기, sc=0
]


def evaluate(engine: MenuSearchEngine, k_values=(1, 3, 5)):
    hits = {k: 0 for k in k_values}
    details = []

    for query, correct_id in EVAL_SET:
        results = engine.search(query, top_n=max(k_values), threshold=0.0)
        returned_ids = [r["menu_id"] for r in results]
        top1 = results[0]["menu_name"] if results else "-"
        top1_sim = results[0]["similarity_pct"] if results else "-"

        row = {"query": query, "correct": correct_id, "top1": top1, "top1_sim": top1_sim}
        for k in k_values:
            row[f"hit@{k}"] = correct_id in returned_ids[:k]
            if row[f"hit@{k}"]:
                hits[k] += 1
        details.append(row)

    # 상세 결과 테이블
    table = Table(title="검색 품질 평가 상세", show_lines=True)
    table.add_column("쿼리", style="white", max_width=30)
    table.add_column("Top-1 결과", style="cyan")
    table.add_column("유사도", style="yellow")
    table.add_column("Hit@1", style="green")
    table.add_column("Hit@3", style="green")
    table.add_column("Hit@5", style="green")

    for row in details:
        table.add_row(
            row["query"],
            row["top1"],
            row["top1_sim"],
            "✅" if row["hit@1"] else "❌",
            "✅" if row["hit@3"] else "❌",
            "✅" if row["hit@5"] else "❌",
        )
    console.print(table)

    # 요약
    total = len(EVAL_SET)
    console.print("\n[bold]=== 평가 요약 ===[/bold]")
    for k in k_values:
        rate = hits[k] / total * 100
        color = "green" if rate >= 70 else "yellow" if rate >= 50 else "red"
        console.print(f"  Hit@{k}: [{color}]{hits[k]}/{total} ({rate:.1f}%)[/{color}]")


if __name__ == "__main__":
    engine = MenuSearchEngine.get_instance()
    evaluate(engine)