wip: phase_z2 evidence 파이프라인 + matching 실험(phase2~26) + 프론트 trace 패널 진행분 스냅샷

- src: phase_z2 composition/mapper/pipeline/placement_planner/retry, ai_fallback(prompts/schema/validate), mdx_text_atoms 신규
- Front: PipelineTracePanel 신규, FramePanel/SlideCanvas/Home/designAgentApi 등 갱신 + 테스트 4종 추가
- templates/phase_z2: catalog(component_expansion_registry, node_slot_mapping 신규), frames, families, slide_base 갱신
- tests/matching: phase2~26 매칭 실험 스크립트·리포트·온톨로지 전체 (미커밋 진행분)
- tests: b4_v4 evidence, task5~28.5 시리즈, regression(imp95 baseline) 등 신규 테스트 대량 추가
- docs/reference: MDX 구조 인벤토리, MDX→Frame 구조 계약 문서
- scripts: mdx 계약/parity/coverage/viewport 체크, gitea comment, run sync 유틸
- .gitignore: tmp*.json, chromedriver, .orchestrator, *.pkl, Front_test* 등 임시/스냅샷 제외

미완성 작업의 보존용 스냅샷 커밋 (2026-07-02)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 17:03:42 +09:00
co-authored by Claude Opus 4.8
parent 97b7833a1b
commit b836e79ee1
527 changed files with 673036 additions and 717 deletions
@@ -0,0 +1,134 @@
"""Pipeline Step 15 (Logistic Regression) — TARGET 4 로 가중치 학습.
방법:
1. TARGET 4 섹션 × 32 프레임 = 128 샘플
2. Feature: (standalone_score, group_score, related_score) — 각 [0, 1]
3. Label: 1 if 정답 프레임 else 0 (정답 4, 오답 124)
4. Logistic Regression + Linear Regression 두 가지 fit
5. 학습 가중치를 sum=1 로 정규화 → 현재 0.30/0.50/0.20 와 비교
6. 학습 가중치로 예측 시 TARGET 정답률 검증
"""
from pathlib import Path
import numpy as np
import yaml
from sklearn.linear_model import LogisticRegression, LinearRegression
HERE = Path(__file__).parent
TARGET_SIDS = ['01-2', '02-2.2', '03-1', '03-2']
def main():
v1 = yaml.safe_load((HERE / 'mdx_matching_result.yaml').read_text(encoding='utf-8'))
auto = yaml.safe_load((HERE / 'auto_anchor_candidates.yaml').read_text(encoding='utf-8'))
answer_map = v1['meta']['answer_map']
frame_num = {fid: info['frame_number'] for fid, info in auto['frame_stats'].items()}
# ─── 데이터 구성 ───
rows = [] # (sid, frame_id, frame_num, s, g, r, label)
for sid in TARGET_SIDS:
sec = v1['mdx_sections'][sid]
answer = answer_map[sid]
for frame_id, detail in sec['per_frame_detail'].items():
s = detail['standalone']['score']
g = detail['keyword_group'].get('score_avg', 0)
r = detail['related']['score']
label = 1 if frame_num[frame_id] == answer else 0
rows.append((sid, frame_id, frame_num[frame_id], s, g, r, label))
X = np.array([[r[3], r[4], r[5]] for r in rows])
y = np.array([r[6] for r in rows])
print('=' * 75)
print(f'데이터: {len(y)} 샘플 (정답 {y.sum()}, 오답 {len(y)-y.sum()}) — TARGET 4 × 32 frames')
print('=' * 75)
print()
# ─── Logistic Regression ───
clf = LogisticRegression(penalty='l2', C=1.0, fit_intercept=True, max_iter=2000)
clf.fit(X, y)
w_lr = clf.coef_[0]
bias_lr = clf.intercept_[0]
w_lr_norm = w_lr / w_lr.sum()
print(f'[Logistic Regression (L2, C=1.0)]')
print(f' raw weights: standalone={w_lr[0]:+.3f} group={w_lr[1]:+.3f} related={w_lr[2]:+.3f}')
print(f' bias: {bias_lr:+.3f}')
print(f' 정규화 (sum=1): standalone={w_lr_norm[0]:.3f} group={w_lr_norm[1]:.3f} related={w_lr_norm[2]:.3f}')
print()
# ─── Linear Regression (OLS, no intercept) ───
ols = LinearRegression(fit_intercept=False)
ols.fit(X, y)
w_ols = ols.coef_
w_ols_norm = w_ols / w_ols.sum()
print(f'[Linear Regression (OLS, no intercept)]')
print(f' raw weights: standalone={w_ols[0]:+.3f} group={w_ols[1]:+.3f} related={w_ols[2]:+.3f}')
print(f' 정규화 (sum=1): standalone={w_ols_norm[0]:.3f} group={w_ols_norm[1]:.3f} related={w_ols_norm[2]:.3f}')
print()
# ─── 현재 vs 학습 가중치 비교 ───
print('[비교]')
print(f' standalone group related')
print(f' 현재 (수동): 0.300 0.500 0.200')
print(f' Logistic: {w_lr_norm[0]:.3f} {w_lr_norm[1]:.3f} {w_lr_norm[2]:.3f}')
print(f' OLS: {w_ols_norm[0]:.3f} {w_ols_norm[1]:.3f} {w_ols_norm[2]:.3f}')
print()
# ─── 학습 가중치로 TARGET 정답률 검증 ───
def evaluate(w_normalized, name):
hits = 0
for sid in TARGET_SIDS:
sec = v1['mdx_sections'][sid]
answer = answer_map[sid]
scores = {}
for fid, detail in sec['per_frame_detail'].items():
s = detail['standalone']['score']
g = detail['keyword_group'].get('score_avg', 0)
r = detail['related']['score']
scores[fid] = w_normalized[0]*s + w_normalized[1]*g + w_normalized[2]*r
top_fid = max(scores, key=scores.get)
if frame_num[top_fid] == answer:
hits += 1
return hits
hits_current = evaluate([0.30, 0.50, 0.20], '현재')
hits_lr = evaluate(w_lr_norm, 'Logistic')
hits_ols = evaluate(w_ols_norm, 'OLS')
print('[TARGET 4 정답률 (학습 가중치로 재예측)]')
print(f' 현재 (0.30/0.50/0.20): {hits_current}/4')
print(f' Logistic Regression: {hits_lr}/4')
print(f' OLS Linear Regression: {hits_ols}/4')
print()
# ─── LOOCV (Leave-One-Out Cross-Validation) — 과적합 체크 ───
print('[LOOCV — 과적합 확인]')
print(' 각 TARGET 을 hold-out, 나머지 3개로 학습 후 테스트')
loocv_hits = 0
for hold_out_idx, hold_out_sid in enumerate(TARGET_SIDS):
train_X = np.array([[r[3], r[4], r[5]] for r in rows if r[0] != hold_out_sid])
train_y = np.array([r[6] for r in rows if r[0] != hold_out_sid])
clf_cv = LogisticRegression(penalty='l2', C=1.0, fit_intercept=True, max_iter=2000)
clf_cv.fit(train_X, train_y)
w_cv = clf_cv.coef_[0] / clf_cv.coef_[0].sum()
sec = v1['mdx_sections'][hold_out_sid]
answer = answer_map[hold_out_sid]
scores = {}
for fid, detail in sec['per_frame_detail'].items():
s = detail['standalone']['score']
g = detail['keyword_group'].get('score_avg', 0)
r = detail['related']['score']
scores[fid] = w_cv[0]*s + w_cv[1]*g + w_cv[2]*r
top_fid = max(scores, key=scores.get)
correct = frame_num[top_fid] == answer
if correct:
loocv_hits += 1
print(f' hold-out {hold_out_sid}: weights={w_cv.round(3)} 정답? {"" if correct else ""}')
print(f' LOOCV 정답률: {loocv_hits}/4')
if __name__ == '__main__':
main()