hpce-dev / scripts /sim_bundle.py
์ด๋™ํ˜„
[REFACTOR] ํŽ˜๋ฅด์†Œ๋‚˜ ๋ผ๋ฒจ ํ‚ค extra_intents โ†’ expected_intents ๋ฆฌ๋„ค์ด๋ฐ
d504def
Raw
History Blame Contribute Delete
4.49 kB
from __future__ import annotations
"""
๊ฒฐํ•ฉ(bundle-v3) Intent ๋ถ„ํฌ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ (๋กœ์ง ๊ณ ๋„ํ™”์šฉ).
๊ฐ ํŽ˜๋ฅด์†Œ๋‚˜๊ฐ€ ์„ค๋ฌธ(๋Œ€ํ‘œ ๋‹ต๋ณ€)์— ์‘๋‹ตํ•˜๊ณ  ๋Œ€ํ‘œ ํ–‰๋™ ์‹œํ€€์Šค๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์„ ๋•Œ,
๊ธฐ๋Œ€ intent(expected_intents)๊ฐ€ ์ƒ์œ„ ๋ถ„ํฌ์— ๋œจ๋Š”์ง€ ์ ์ˆ˜ํ™”ํ•ด "๋‚ฉ๋“ ๊ฐ€๋Šฅํ•œ ๋ถ„ํฌ"์ธ์ง€ ํ‰๊ฐ€ํ•œ๋‹ค.
์ง€ํ‘œ:
- cov@5 / cov@10 : expected_intents ์ค‘ final top-5/top-10์— ๋“  ๋น„์œจ
- avg_rank : expected_intents์˜ ํ‰๊ท  final ์ˆœ์œ„ (๋‚ฎ์„์ˆ˜๋ก ์ข‹์Œ)
- ํŽ˜๋ฅด์†Œ๋‚˜๋ณ„ + ์ „์ฒด ํ‰๊ท 
์‹คํ–‰: python scripts/sim_bundle.py [--behavior] (--behavior: ๋Œ€ํ‘œ ํ–‰๋™ ์‹œํ€€์Šค๊นŒ์ง€ ๋ฐ˜์˜)
"""
import argparse
import random
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.engines import config # noqa: E402
from core.extractor import get_extractor # noqa: E402
from core.inference import infer_batch, infer_with_behavior, to_topn_with_others # noqa: E402
from scripts.build_bundle_dataset import PERSONAS # noqa: E402
SID = "bundle-v3"
def _sample_answers(answer_dist: dict, rng: random.Random) -> dict[str, str]:
"""answer_dist ๋ถ„ํฌ์—์„œ 1๋ช…์˜ ์‘๋‹ต์„ ์ƒ˜ํ”Œ๋ง (์‹ค์ œ ์‹œ์—ฐ์ž ์‘๋‹ต์„ ๋ชจ์‚ฌ)."""
return {qid: rng.choices(list(d), weights=list(d.values()), k=1)[0]
for qid, d in answer_dist.items()}
def _behavior_map() -> dict[str, tuple[str, str]]:
"""behavior_id โ†’ (event_type, entity). BACK/EXIT ํฌํ•จ."""
bc = config.get_behaviors(SID)
m: dict[str, tuple[str, str]] = {}
for b in bc["step1"]["behaviors"]:
m[b["id"]] = (b["event_type"], b["entity"])
for items in bc["step2"]["by_parent"].values():
for b in items:
m[b["id"]] = (b["event_type"], b["entity"])
for b in bc["step2"].get("common", []):
m[b["id"]] = (b["event_type"], b["entity"])
m.setdefault("BACK", ("navigate_back", "back_to_step1"))
m.setdefault("EXIT", ("app_exit", "session_end"))
return m
def _intent_names() -> dict[str, str]:
return {i["id"]: i["name"] for i in config.get_taxonomy(SID)["intents"]}
def _rank_of(scores: list, intent_id: str) -> int:
"""final ์ˆœ์œ„(1-๊ธฐ๋ฐ˜). ์—†์œผ๋ฉด 999."""
for s in scores:
if s.intent_id == intent_id:
return s.rank
return 999
def run(use_behavior: bool, k: int = 40, seed: int = 7) -> None:
"""ํŽ˜๋ฅด์†Œ๋‚˜๋งˆ๋‹ค answer_dist์—์„œ k๋ช…์„ ์ƒ˜ํ”Œ๋งํ•ด cov@5/10ยทavg_rank ํ‰๊ท  (๋ถ„ํฌ ์ถฉ์‹ค)."""
bmap = _behavior_map()
ext = get_extractor()
rng = random.Random(seed)
p_cov5, p_cov10, rank_all = [], [], []
for p in PERSONAS:
expected = p["expected_intents"]
c5s, c10s, rks = [], [], []
for j in range(k):
answers = _sample_answers(p["answer_dist"], rng)
if use_behavior:
seq = rng.choice(p["action_seqs"])
sess = f"__sim__{p['id']}_{j}"
ext.reset(sess)
for bid in seq:
et, ent = bmap.get(bid, (None, None))
if et:
ext.add_event(sess, et, ent)
_, scores = infer_with_behavior(answers, sess, SID)
ext.reset(sess)
else:
_, scores = infer_batch(answers, SID)
top_ids = [s.intent_id for s in sorted(scores, key=lambda s: s.final_score, reverse=True)]
top5, top10 = set(top_ids[:5]), set(top_ids[:10])
c5s.append(sum(1 for e in expected if e in top5) / len(expected))
c10s.append(sum(1 for e in expected if e in top10) / len(expected))
rks.extend(_rank_of(scores, e) for e in expected)
cov5, cov10 = sum(c5s) / k, sum(c10s) / k
p_cov5.append(cov5); p_cov10.append(cov10); rank_all.extend(rks)
print(f" {p['id']} {p['name'][:22]:22} cov@5={cov5:.2f} cov@10={cov10:.2f} "
f"avg_rank={sum(rks)/len(rks):4.1f}")
n = len(PERSONAS)
print("=" * 64)
print(f" ์ „์ฒด ํ‰๊ท  cov@5={sum(p_cov5)/n:.3f} cov@10={sum(p_cov10)/n:.3f} "
f"avg_rank={sum(rank_all)/len(rank_all):.1f} ({'ํ–‰๋™๋ฐ˜์˜' if use_behavior else '์„ค๋ฌธ๋งŒ'}, k={k})")
print("=" * 64)
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--behavior", action="store_true", help="๋Œ€ํ‘œ ํ–‰๋™ ์‹œํ€€์Šค๊นŒ์ง€ ๋ฐ˜์˜")
args = ap.parse_args()
run(args.behavior)