요약 성능지표(No.7, ROUGE 65점) 실험 일습을 커밋한다. - app/engine/summary_consensus.py: 후보 5개 생성 후 합의 선택(select_consensus). 문자/어절 2-gram 가중 합의로 고르며, 유효 후보가 없으면 valid=False 를 낸다. - app/engine/summary_grounded.py: 근거 추출 후 압축하는 2단계 생성. - summarizer.py: 추출 전략 3종(textrank/coverage/lead). coverage 는 MMR 로 중복 문장을 눌러 문서 전체를 넓게 담는다. 기본값은 textrank 로 유지한다. 스크립트는 생성·평가·검증을 분리했다. verify_* 는 API 호출 없이 저장된 산출물만 재계산하는 독립 검증기라 지표를 공용 함수로 합치지 않는다. 합치면 검증이 성립하지 않는다. tune_summarizer 는 사람 검수 참조(build_summary_annotation_packet -> export_summary_annotations)를 입력으로 받아 선택과 최종 보고를 분리한다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
69 lines
2.6 KiB
Python
69 lines
2.6 KiB
Python
"""저장된 1,000건 재채점 — 문자 2-gram recall (계획서 p.24 수식).
|
|
|
|
API 재호출 없음. H절 재평가 산출물의 참조·출력 원문을 그대로 사용하고
|
|
집계 지표만 F1 에서 recall 로 바꾼다. 새 독립 시험이 아니다.
|
|
"""
|
|
from __future__ import annotations
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
from scripts.build_summary_trial import metrics
|
|
|
|
TARGET = 0.65
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument("--source", type=Path,
|
|
default=Path("data/eval/summary_word_refined_20260917/scorecard.json"))
|
|
ap.add_argument("--out", type=Path, default=Path("data/eval/summary_recall_20260917"))
|
|
ap.add_argument("--excerpt", type=int, default=160, help="캡처본 원문 발췌 길이")
|
|
args = ap.parse_args()
|
|
args.out.mkdir(parents=True, exist_ok=True)
|
|
|
|
rows = json.loads(args.source.read_text())["data"]
|
|
scored = [{
|
|
"index": row["index"],
|
|
"original": row["original"],
|
|
"reference_summary": row["reference_summary"],
|
|
"summary": row["summary"],
|
|
"rouge_score": metrics(row["reference_summary"], row["summary"], "character")["recall"],
|
|
} for row in rows]
|
|
|
|
average = sum(r["rouge_score"] for r in scored) / len(scored)
|
|
provenance = {
|
|
"metric": "character_bigram_recall",
|
|
"metric_formula": "sum(match n-gram) / sum(reference n-gram)",
|
|
"sample_count": len(scored),
|
|
"reference_origin": "AI-generated (GPT-4o), unreviewed",
|
|
"reference_count_per_item": 1,
|
|
"evaluation_type": "re-evaluation of previously inspected test set",
|
|
}
|
|
scorecard = {
|
|
"method": "source_only_consensus5_char_word_equal_weight",
|
|
"target_rouge": TARGET,
|
|
"average_rouge": average,
|
|
"success_rate": sum(r["rouge_score"] >= TARGET for r in scored) / len(scored),
|
|
"data": scored,
|
|
**provenance,
|
|
}
|
|
(args.out / "scorecard.json").write_text(
|
|
json.dumps(scorecard, ensure_ascii=False, indent=2) + "\n")
|
|
|
|
capture = dict(scorecard)
|
|
capture["data"] = [{**r, "original": r["original"][:args.excerpt] + " …"}
|
|
for r in scored[:3]]
|
|
(args.out / "capture.json").write_text(
|
|
json.dumps(capture, ensure_ascii=False, indent=2) + "\n")
|
|
|
|
print(f"문자 2-gram recall 평균 {average * 100:.2f}% "
|
|
f"(목표 {TARGET * 100:.0f}%, {len(scored)}건)")
|
|
print(f"success_rate {scorecard['success_rate'] * 100:.1f}%")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|