File size: 4,486 Bytes
e000be4 d504def e000be4 d504def e000be4 d504def e000be4 | 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | 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)
|