컴북스 데이터 수령 전 가능한 작업을 선행 구현. 데이터 도착 즉시 학습·검증에 착수할 수 있도록 알고리즘·평가 하니스·문서를 준비. 알고리즘/파이프라인: - engine/clustering.py: 군집화 기반 부분 표절(요소 교체) 판별, detect 응답에 partial_signal 필드 노출 (계획서 2단계 표절검출 고도화) - engine/summarizer.py + POST /v1/summary: TextRank 추출적 요약 + 통합(LLM) 골격 (과제2 ② 스토리 요약/분석) - engine/preference.py + scripts/build_preference_dataset.py: Human Feedback 선호학습(DPO) 데이터 파이프라인 골격 평가 하니스 (정답셋 도착 즉시 측정): - engine/rouge.py + scripts/eval_rouge.py: 요약 ROUGE (지표 No.7) - engine/metadata_eval.py + scripts/eval_metadata_f1.py: 메타 추출 F1 (지표 No.3, KLUE NER 호환) - engine/case_coverage.py + scripts/analyze_case_coverage.py: 39 케이스 갭 분석 → 컴북스 데이터 요청 목록 (정밀도 97% 근거) 문서: - docs/DATA_REQUEST_SPEC.md: 요청 데이터 스펙 + 요약 정답셋/HF 라벨 가이드 - docs/INTEGRATION_INTERFACE.md: 2단계 통합 인터페이스 - docs/SW_COPYRIGHT_REGISTRATION.md: SW 저작권 등록 준비 - docs/PHASE2_PROGRESS.md, docs/CASE_COVERAGE.md 테스트 31 → 67건 전부 통과. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
1.8 KiB
Python
51 lines
1.8 KiB
Python
"""ROUGE 평가 모듈 단위테스트 (성능지표 No.7)."""
|
|
from __future__ import annotations
|
|
|
|
from app.engine.rouge import rouge_n, rouge_l, evaluate_pairs, tokenize
|
|
|
|
|
|
def test_identical_is_perfect():
|
|
s = rouge_n("홍길동은 활빈당을 만들었다", "홍길동은 활빈당을 만들었다", n=1)
|
|
assert s.f1 == 1.0
|
|
assert s.recall == 1.0
|
|
|
|
|
|
def test_no_overlap_is_zero():
|
|
s = rouge_n("우주선이 행성을 탐사한다", "사랑은 아름다운 감정이다", n=1, mode="char")
|
|
assert s.f1 == 0.0
|
|
|
|
|
|
def test_partial_overlap_between_zero_and_one():
|
|
s = rouge_n("홍길동은 활빈당을 만들어 재물을 빼앗았다",
|
|
"홍길동은 활빈당을 조직했다", n=1)
|
|
assert 0.0 < s.f1 < 1.0
|
|
|
|
|
|
def test_rouge2_stricter_than_rouge1():
|
|
sys = "홍길동은 활빈당을 만들어 재물을 빼앗았다"
|
|
ref = "홍길동은 재물을 활빈당으로 만들어 빼앗았다" # 단어 순서 섞임
|
|
r1 = rouge_n(sys, ref, n=1).f1
|
|
r2 = rouge_n(sys, ref, n=2).f1
|
|
assert r2 <= r1 # bigram 은 순서에 민감 → 더 낮거나 같음
|
|
|
|
|
|
def test_rouge_l_rewards_sequence():
|
|
s = rouge_l("홍길동은 활빈당을 만들어 재물을 빼앗았다",
|
|
"홍길동은 활빈당을 만들어 재물을 빼앗았다")
|
|
assert s.f1 == 1.0
|
|
|
|
|
|
def test_evaluate_pairs_aggregates():
|
|
pairs = [
|
|
("홍길동은 활빈당을 만들었다", "홍길동은 활빈당을 만들었다"),
|
|
("어린왕자가 여우를 만났다", "어린왕자가 여우를 만났다"),
|
|
]
|
|
out = evaluate_pairs(pairs)
|
|
assert set(out) == {"rouge1", "rouge2", "rougeL"}
|
|
assert out["rouge1"]["f1"] == 1.0
|
|
|
|
|
|
def test_tokenize_char_fallback():
|
|
toks = tokenize("Hello 월드 123", mode="char")
|
|
assert "hello" in toks and "월드" in toks and "123" in toks
|