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

194 lines
7.8 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 23 — 정제 anchor + content summary
공식: 점수 = 0.5 × 키워드 + 0.5 × 내용
- 키워드 축: keyword_base 기반 (Phase 22 과 동일)
- 내용 축: MDX summary ↔ frame.content 의 ko-sroberta cosine 유사도
MDX summary = title + 첫 일반 문단 + slot labels (detect_mdx.build_summary 사용)
(Phase 25 template-fit-v1 과 같은 summary 방식 — axis 간 consistency 확보)
"""
import sys
import pickle
from pathlib import Path
import numpy as np
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,
)
from embeddings import embed_texts, cosine
from detect_mdx import detect_mdx_analysis
HERE = Path(__file__).parent
PNG_REL = "../../data/figma_previews/"
W_KW = 0.5
W_CONTENT = 0.5
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()
# Pre-embed all frame contents
fids = list(frames.keys())
frame_texts = [frames[fid].get("content", "") for fid in fids]
frame_vecs = embed_texts(frame_texts)
reports = []
hits = 0
for uid, display, correct_sid, *_ in TARGET_UNITS:
text = units_full[uid]
title = units_title[uid]
mdx_kws = extract_mdx_keywords(text, vocab, keyword_base=kb)
# content 축: detect_mdx.build_summary 기반 (title + 첫 문단 + slot labels)
analysis = detect_mdx_analysis(text, title, anchor_vocab=None)
mdx_summary = analysis['summary']
mdx_vec = embed_texts([mdx_summary])[0]
c_scores = {fids[i]: max(0.0, min(1.0, cosine(mdx_vec, frame_vecs[i])))
for i in range(len(fids))}
ranked = []
for fid, v in frames.items():
fig_kws = set(v["keywords"])
k_s, inter = keyword_score(mdx_kws, fig_kws, idf, tier)
c_s = c_scores[fid]
final = W_KW * k_s + W_CONTENT * c_s
ranked.append((fid, final, {
"kw": k_s, "content": c_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
margin = ranked[0][1] - ranked[1][1]
reports.append({
"uid": uid, "display": display, "correct_sid": correct_sid,
"mdx_kws": sorted(mdx_kws), "mdx_title": title,
"mdx_summary": mdx_summary,
"top3": ranked[:3], "margin": margin,
})
return {
"phase": 23, "desc": "정제 anchor + content summary",
"formula": f"{W_KW} × 키워드 + {W_CONTENT} × 내용(summary)",
"weights": (W_KW, W_CONTENT, 0.0),
"hits": hits, "reports": reports,
"frame_to_short": frame_to_short, "idx_data": idx_data, "frames": frames,
}
def write_md(r):
lines = []
lines.append("# Phase 23 — 정제 anchor + content summary")
lines.append("")
lines.append(f"**공식**: `{r['formula']}`")
lines.append(f"**결과: {r['hits']}/4 정답**")
lines.append("")
lines.append("**Phase 22 대비**:")
lines.append("- 내용(의미 유사도) 축 추가")
lines.append("- 내용: **MDX summary** ↔ frame.content 의 ko-sroberta cosine")
lines.append(" - summary = title + 첫 일반 문단 + slot labels (detect_mdx.build_summary 사용)")
lines.append(" - Phase 25 template-fit-v1 과 동일한 summary 방식 — axis consistency")
lines.append("")
# 요약
lines.append("## 1. TARGET별 결과")
lines.append("")
lines.append("| MDX | 정답 | 1위 (kw / content / 최종) | 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 "✗"
bd1 = rep["top3"][0][2]
lines.append(
f"| {rep['display']} | {rep['correct_sid']} | "
f"{top1} ({bd1['kw']:.2f}/{bd1['content']:.2f}/{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 제목**: {rep['mdx_title']}")
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"- **축별 점수**: 키워드 {bd['kw']:.3f} / 내용 {bd['content']:.3f}")
lines.append(f"- **최종**: **{score:.3f}** = {W_KW}×{bd['kw']:.3f} + {W_CONTENT}×{bd['content']:.3f}")
lines.append("")
# 한계
lines.append("## 4. 한계")
lines.append("")
lines.append("- **의미 유사 ≠ 구조 적합**: ko-sroberta는 '같은 도메인 주제'를 높게 본다. 하지만 3열 병렬과 2열 비교는 구조가 다름에도 cosine이 비슷할 수 있음.")
lines.append("- **구조 축 부재**: MDX 2개 병렬 vs Figma 3열 템플릿이 점수로 구분되지 않음.")
lines.append("- **재구성 불가 판정 없음**: 적합도가 낮아도 여전히 1위가 될 수 있음.")
lines.append("- **다음 단계**: Phase 25에서 legacy 구조 축을 추가.")
lines.append("")
out = HERE / "MATRIX_PHASE23.md"
out.write_text("\n".join(lines), encoding="utf-8")
print(f"완료: {out}")
def main():
result = run()
with open(HERE / "_phase23_results.pkl", "wb") as f:
pickle.dump(result, f)
write_md(result)
print(f"Phase 23: {result['hits']}/4 정답")
if __name__ == "__main__":
main()