컴북스 데이터 수령 전 가능한 작업을 선행 구현. 데이터 도착 즉시 학습·검증에 착수할 수 있도록 알고리즘·평가 하니스·문서를 준비. 알고리즘/파이프라인: - 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>
60 lines
2.0 KiB
Python
60 lines
2.0 KiB
Python
"""HF 선호학습 데이터 파이프라인 단위테스트 (계획서 2단계 고도화)."""
|
|
from __future__ import annotations
|
|
|
|
from app.engine.preference import (
|
|
PreferenceCandidate,
|
|
build_candidate,
|
|
to_dpo_record,
|
|
to_dpo_dataset,
|
|
validate_labeled,
|
|
dataset_stats,
|
|
)
|
|
|
|
|
|
def test_build_candidate_is_pending():
|
|
c = build_candidate("p1", "원문", "글A", "글B", suggested_rejected="b")
|
|
assert c.label_status == "pending"
|
|
assert "원문" in c.prompt
|
|
assert to_dpo_record(c) is None # 미라벨은 학습 레코드 없음
|
|
|
|
|
|
def test_labeled_becomes_dpo_record():
|
|
c = build_candidate("p1", "원문", "정상글", "표절글", suggested_rejected="b")
|
|
c.label_status = "labeled"
|
|
c.chosen = "정상글"
|
|
c.rejected = "표절글"
|
|
rec = to_dpo_record(c)
|
|
assert rec is not None
|
|
assert rec["chosen"] == "정상글" and rec["rejected"] == "표절글"
|
|
|
|
|
|
def test_validate_catches_same_chosen_rejected():
|
|
c = PreferenceCandidate("p1", "p", "a", "b", label_status="labeled", chosen="x", rejected="x")
|
|
errors = validate_labeled(c)
|
|
assert any("chosen == rejected" in e for e in errors)
|
|
|
|
|
|
def test_validate_catches_missing():
|
|
c = PreferenceCandidate("p1", "p", "a", "b", label_status="labeled", chosen="x", rejected=None)
|
|
assert any("rejected 누락" in e for e in validate_labeled(c))
|
|
|
|
|
|
def test_dataset_stats():
|
|
pending = build_candidate("p1", "o", "a", "b")
|
|
labeled = build_candidate("p2", "o", "a", "b")
|
|
labeled.label_status = "labeled"
|
|
labeled.chosen, labeled.rejected = "a", "b"
|
|
stats = dataset_stats([pending, labeled])
|
|
assert stats["total"] == 2
|
|
assert stats["labeled"] == 1
|
|
assert stats["pending"] == 1
|
|
assert stats["trainable"] == 1
|
|
assert stats["errors"] == []
|
|
|
|
|
|
def test_to_dpo_dataset_filters_pending():
|
|
cs = [build_candidate(f"p{i}", "o", "a", "b") for i in range(3)]
|
|
cs[0].label_status = "labeled"
|
|
cs[0].chosen, cs[0].rejected = "a", "b"
|
|
assert len(to_dpo_dataset(cs)) == 1
|