Download join_eval.py from sonsdf/accounting-standards-assistant: direct link, hf CLI and curl.
- Browser
- Download file 4.11 kB
-
https://huggingface.co/spaces/sonsdf/accounting-standards-assistant/resolve/main/join_eval.py
- Command line
-
hf download hf://spaces/sonsdf/accounting-standards-assistant/join_eval.py
-
curl -L -o join_eval.py https://huggingface.co/spaces/sonsdf/accounting-standards-assistant/resolve/main/join_eval.py
4.11 kB
| # -*- coding: utf-8 -*- | |
| """조인 평가 + 회귀 검증 공용 모듈. | |
| 각 정규화 단계 전후로 호출해 조인율을 재측정하고, (board, lineno, refpos) | |
| 단위로 '이전에 조인되던 인용이 깨졌는지'(회귀)를 정확히 비교한다. | |
| """ | |
| import json | |
| import re | |
| BOARDS = ["016001", "016002", "016003", "016005", "016006"] | |
| DATA = __import__("pathlib").Path(__file__).resolve().parent / "data" / "parsed" | |
| def load_std_index(): | |
| s3001 = [json.loads(l) for l in (DATA / "3001.jsonl").open(encoding="utf-8")] | |
| s3003 = [json.loads(l) for l in (DATA / "3003.jsonl").open(encoding="utf-8")] | |
| return { | |
| "k_para": {r["ref_key"] for r in s3001 if r["record_type"] == "paragraph"}, | |
| "k_tsec": {r["section_key"] for r in s3001 if r["record_type"] == "term"}, | |
| "k_nos": {r["standard_no"] for r in s3001}, | |
| "g_para": {r["ref_key"] for r in s3003}, | |
| "g_ch": {re.search(r"제(\d+)장", r["standard_no"]).group(1) for r in s3003}, | |
| } | |
| def classify(ref): | |
| """참조 유형 판정: (kind, precision). precision=True면 문단/용어/장문단(정밀).""" | |
| if re.match(r"^제\d{1,2}장", ref): | |
| if "문단" in ref: | |
| return "장문단", True | |
| return ("장제목", False) if "'" in ref else ("단독장", False) | |
| if re.match(r"^제\d{3,4}호", ref): | |
| if ref.endswith("용어의 정의"): | |
| return "용어섹션", True | |
| if "문단" in ref: | |
| return "문단", True | |
| return ("제목", False) if "'" in ref else ("단독호", False) | |
| return "기타", False | |
| def matches(ref, idx): | |
| """현재 ref가 기준서 레코드와 조인되는지 (정밀은 exact, 상위는 식별).""" | |
| kind, _ = classify(ref) | |
| if kind == "장문단": | |
| return ref in idx["g_para"] | |
| if kind in ("장제목", "단독장"): | |
| ch = re.match(r"제(\d+)장", ref).group(1) | |
| return ch in idx["g_ch"] | |
| if kind == "용어섹션": | |
| return ref in idx["k_tsec"] | |
| if kind == "문단": | |
| return ref in idx["k_para"] | |
| if kind in ("제목", "단독호"): | |
| no = re.match(r"제(\d+)호", ref).group(1) | |
| return no in idx["k_nos"] | |
| return False | |
| def evaluate(): | |
| """현재 JSONL 상태로 조인 측정. 반환: dict(rates, joined_precise set, joined_any set).""" | |
| idx = load_std_index() | |
| prec_tot = prec_hit = any_tot = any_hit = 0 | |
| joined_precise = set() # (board, lineno, refpos) | |
| joined_any = set() | |
| per_kind = {} | |
| for b in BOARDS: | |
| for ln, line in enumerate((DATA / (b + ".jsonl")).open(encoding="utf-8")): | |
| refs = json.loads(line).get("standard_refs", []) | |
| for pos, ref in enumerate(refs): | |
| kind, precise = classify(ref) | |
| ok = matches(ref, idx) | |
| d = per_kind.setdefault(kind, [0, 0]) | |
| d[1] += 1 | |
| if ok: | |
| d[0] += 1 | |
| joined_any.add((b, ln, pos)) | |
| any_tot += 1 | |
| any_hit += ok | |
| if precise: | |
| prec_tot += 1 | |
| prec_hit += ok | |
| if ok: | |
| joined_precise.add((b, ln, pos)) | |
| return { | |
| "prec": (prec_hit, prec_tot), "any": (any_hit, any_tot), | |
| "per_kind": per_kind, | |
| "joined_precise": joined_precise, "joined_any": joined_any, | |
| } | |
| def report(r, label=""): | |
| ph, pt = r["prec"]; ah, at = r["any"] | |
| print(f"[{label}] 정밀 {ph}/{pt}={100*ph/pt:.1f}% | 전체 {ah}/{at}={100*ah/at:.1f}%") | |
| for k in ["문단", "용어섹션", "제목", "단독호", "장문단", "장제목", "단독장"]: | |
| if k in r["per_kind"]: | |
| h, t = r["per_kind"][k] | |
| print(f" {k}: {h}/{t} ({100*h/t:.0f}%)") | |
| def regression(before, after): | |
| """이전에 조인되던 (board,lineno,refpos)가 깨진 건수.""" | |
| lost_prec = before["joined_precise"] - after["joined_precise"] | |
| lost_any = before["joined_any"] - after["joined_any"] | |
| return lost_prec, lost_any | |
| if __name__ == "__main__": | |
| report(evaluate(), "현재") | |