Files
C.E.L_Slide_test2/tests/matching/phase22.py
T

175 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Phase 22 — 정제 anchor keywords only (baseline)
공식: 점수 = 1.0 × 키워드 (IDF-weighted Jaccard)
데이터:
- keyword_base.yaml (Milestone 1.6 결과, 268 canonical + variants)
- analysis.md mirror (templates_v1.anchor_sets 기반, Milestone 2 결과)
- MDX 추출: keyword_base variants → canonical 정규화
→ direct canonical hit + Kiwi token hit union
목적: 정제 anchor keyword 만으로 4 TARGET 매칭이 어디까지 되는지 baseline.
"""
import sys
import pickle
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from phase_common import (
TARGET_UNITS, load_keyword_base, load_32_frames, compute_df_idf_tier,
extract_mdx_keywords, keyword_score,
load_target_units, load_frame_index,
)
HERE = Path(__file__).parent
PNG_REL = "../../data/figma_previews/"
def run():
kb = load_keyword_base()
frames = load_32_frames()
df, idf, tier, N = compute_df_idf_tier(frames)
vocab = set()
for v in frames.values():
vocab.update(v["keywords"])
units_full, units_title = load_target_units()
idx_data, frame_to_short = load_frame_index()
reports = []
hits = 0
for uid, display, correct_sid, *_ in TARGET_UNITS:
text = units_full[uid]
mdx_kws = extract_mdx_keywords(text, vocab, keyword_base=kb)
ranked = []
for fid, v in frames.items():
fig_kws = set(v["keywords"])
k_s, inter = keyword_score(mdx_kws, fig_kws, idf, tier)
ranked.append((fid, k_s, {"kw": k_s, "inter": sorted(inter)}))
ranked.sort(key=lambda x: -x[1])
top_sid = frame_to_short.get(ranked[0][0], "?")
if top_sid == correct_sid:
hits += 1
top1_score = ranked[0][1]
top2_score = ranked[1][1] if len(ranked) > 1 else 0
margin = top1_score - top2_score
reports.append({
"uid": uid, "display": display, "correct_sid": correct_sid,
"mdx_kws": sorted(mdx_kws), "top3": ranked[:3],
"margin": margin,
})
return {
"phase": 22, "desc": "키워드만 (baseline)",
"formula": "1.0 × 키워드",
"hits": hits, "reports": reports,
"frame_to_short": frame_to_short, "idx_data": idx_data, "frames": frames,
}
def write_md(r):
lines = []
lines.append(f"# Phase 22 — 정제 anchor keywords only (baseline)")
lines.append("")
lines.append(f"**공식**: `점수 = 1.0 × 키워드` (IDF-weighted Jaccard)")
lines.append(f"**결과: {r['hits']}/4 정답**")
lines.append("")
lines.append("**데이터 기반**:")
lines.append("- `keyword_base.yaml` (Milestone 1.6 결과, 268 canonical + variants)")
lines.append("- 32 Figma frames 의 anchor keywords (analysis.md mirror — templates_v1.anchor_sets 기반)")
lines.append("")
lines.append("**MDX 키워드 추출**:")
lines.append("1. keyword_base variants → canonical 정규화")
lines.append("2. direct canonical substring hit (compound 보호: 설계Data, 공사비절감 등)")
lines.append("3. Kiwi 형태소 분석 → Figma vocab 교집합")
lines.append("4. direct hit Kiwi hit")
lines.append("")
# 요약 표
lines.append("## 1. TARGET별 결과")
lines.append("")
lines.append("| MDX | 정답 | 1위 | 2위 | margin |")
lines.append("|-----|------|-----|-----|--------|")
for rep in r["reports"]:
top1 = r["frame_to_short"].get(rep["top3"][0][0], "?")
top2 = r["frame_to_short"].get(rep["top3"][1][0], "?")
mark = "✓" if top1 == rep["correct_sid"] else "✗"
lines.append(
f"| {rep['display']} | {rep['correct_sid']} | {top1} ({rep['top3'][0][1]:.3f}) {mark} | "
f"{top2} ({rep['top3'][1][1]:.3f}) | {rep['margin']:.3f} |"
)
lines.append("")
# Top-3 이미지 매트릭스
lines.append("## 2. Top-3 매트릭스")
lines.append("")
lines.append("| 콘텐츠 | 1순위 | 2순위 | 3순위 |")
lines.append("|--------|-------|-------|-------|")
for rep in r["reports"]:
row = [f"**{rep['display']}**<br>정답 Frame **{rep['correct_sid']}**"]
for rank_idx in range(3):
fid, score, _ = rep["top3"][rank_idx]
sid = r["frame_to_short"].get(fid, "?")
info = r["idx_data"].get(sid, {})
png = info.get("png", "")
title = info.get("title_text", "").strip().replace("\n", " ")[:15] + "…"
inner = f"![{sid}]({PNG_REL}{png})<br>**{sid}** ({score:.3f})<br>{title}"
if sid == rep["correct_sid"]:
cell = f"<div style='background:#fff3cd;border:3px solid #dc2626;padding:8px;border-radius:6px'>🎯 <b>정답</b><br>{inner}</div>"
else:
cell = inner
row.append(cell)
lines.append("| " + " | ".join(row) + " |")
lines.append("")
# 상세
lines.append("## 3. 유닛별 세부")
lines.append("")
for rep in r["reports"]:
lines.append(f"### {rep['display']} — 정답 Frame **{rep['correct_sid']}**")
lines.append("")
lines.append(f"**MDX 키워드 ({len(rep['mdx_kws'])}개)**: {', '.join(rep['mdx_kws'])}")
lines.append("")
for rank, (fid, score, bd) in enumerate(rep["top3"], 1):
sid = r["frame_to_short"].get(fid, "?")
info = r["idx_data"].get(sid, {})
png = info.get("png", "")
fig_content = r["frames"][fid].get("content", "")
common = ", ".join(bd["inter"]) if bd["inter"] else "(없음)"
is_correct = sid == rep["correct_sid"]
prefix = "🎯 " if is_correct else ""
lines.append(f"**{prefix}{rank}위 Frame {sid}** {'(정답)' if is_correct else ''}")
lines.append("")
lines.append(f"![{sid}]({PNG_REL}{png})")
lines.append("")
lines.append(f"- **매칭 내용**: {fig_content}")
lines.append(f"- **매칭 키워드**: {common}")
lines.append(f"- **점수**: **{score:.3f}**")
lines.append("")
# 한계
lines.append("## 4. 한계")
lines.append("")
lines.append("- **정제 anchor 만으론 recall 한계**: Milestone 1.6 에서 구조어·weak·AI 요약어 49개 제외 후 anchor pool 이 좁아짐.")
lines.append(" - 원래 analysis.md 후보 키워드 (수동 큐레이션, messy) 대비 recall 낮을 수 있음")
lines.append(" - 대신 precision ↑ + evidence-based 방어 가능")
lines.append("- **IDF Jaccard 만으론 의미 관계 포착 못함**: 같은 주제를 다른 canonical 로 표현 시 매칭 실패 가능")
lines.append("- **구조적 호환성 무시**: 3열 병렬과 2열 비교를 구분 못 함")
lines.append("- **다음 단계**: Phase 23 에서 MDX summary embedding 추가")
lines.append("")
out = HERE / "MATRIX_PHASE22.md"
out.write_text("\n".join(lines), encoding="utf-8")
print(f"완료: {out}")
def main():
result = run()
with open(HERE / "_phase22_results.pkl", "wb") as f:
pickle.dump(result, f)
write_md(result)
print(f"Phase 22: {result['hits']}/4 정답")
if __name__ == "__main__":
main()