Spaces:
Running
Running
| """ | |
| 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) | |