๋์ ๋ ์: ๊ฐ ํ๋ก๋ธ๊ฐ ๋ฌด์์ ํ๋์ง, ์ธ์ ์จ์ผ ํ๋์ง, ์ถ๋ ฅ์ ์ด๋ป๊ฒ ์ฝ์ด์ผ ํ๋์ง ์๊ณ ์ถ์ ์ฐ๊ตฌ์ยทML ์์ง๋์ดยท๋ฆฌ๋ทฐ์ด.
๊ด๋ จ ํ์ผ: README_KO (API ๋ ํผ๋ฐ์ค) ยท CHANGELOG ยท CHRONICLE (๊ฐ๋ฐ ์ฐ๋๊ธฐ)
English version: GUIDE.md
๋ฒ์: ์ด ๊ฐ์ด๋๋ 28๊ฐ ํ๋ก๋ธ ์ ์ฒด๋ฅผ ๋ค๋ฃน๋๋ค. ์๋์ ๋์ค๋ "4๊ฐ ํ๋ก๋ธ" ๊ฐ์ ํํ์ ํน์ ๊ทธ๋ฃน์ ๋ถ๋ถ ์์น์ด์ง ์ด๊ณ๊ฐ ์๋๋๋ค.
๋๋ถ๋ถ์ ํ๊ฐ ๋ฌด๊ฒฐ์ฑ ๋๊ตฌ๋ ๊ฑฐ์ง์์ฑ๋ง ์ก์ต๋๋ค โ ์ค์ ๋ณด๋ค ์ข์ ๋ณด์ด๋ ๊ฒฐ๊ณผ. Measurement Mirror๋ ์๋ฐฉํฅ์ ๋ชจ๋ ์ก์ต๋๋ค:
| ๋ฐฉํฅ | ์คํจ ์์ | ๊ฑฐ์ธ์ ๋ฐ์ |
|---|---|---|
| ๊ฑฐ์ง์์ฑ | n=9, acc=55.6%๋ฅผ ํ๊ธฐ์ ์ฑ๊ณผ๋ก ๋ณด๊ณ | โฃa Wilson CI๊ฐ ์ฐ์ฐ ์์ค์์ ์ ๋ฐ |
| ๊ฑฐ์ง์์ฑ | ์คํ 1ํ ์คํจ โ "์ด ์ ๊ทผ๋ฒ์ ์ฃฝ์๋ค" | โฌ negative_audit๊ฐ ๋ ๋ฆฝ ๊ฐ๋ โฅ3๊ฐ ์๊ตฌ |
| ํ์ ์ ์ฐฉ์ | LLM ํ์ ์๊ฐ ํญ์ ์ฒซ ๋ฒ์งธ ์๋ต์ ์ ํ | โฎ judge_bias_check๊ฐ ์์น ํธํฅ ์ ๋ฐ |
์ฑ๊ธํ ์์ฑ ์ข ๊ฒฐ์ ์กฐ์๋ ์์ฑ๋งํผ ๋์ฉ๋๋ค. ๋ ๋ค ์ฐ๊ตฌ ์์์ ๋ญ๋นํ๊ณ ๋ถ์ผ ์ ์ฒด๋ฅผ ์๋ชป๋ ๋ฐฉํฅ์ผ๋ก ์ด๋๋ ํ์์ ๋๋ค.
โ preregister โ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ธฐ ์ ์ ๊ธฐ์ค์ ๋ด์ธ
โ
โผ
ใ prereg_lint โ ์ฐ์ฐ์ ์ฐ๊ธฐ ์ ์ ๋ด์ธ์ ํ์ง์ ๋ฆฐํธ
โ (kill-condition ๋์ ยท ์ ์ธ์ฐ์ฐ ์ดํ ๋ฐ ยท
โผ ๋น๊ตฌ์กฐํ kill ยท ์ n ยท ์ฒดํฌ ๋ฏธ์ ์ธ)
์คํ ์คํ
โ
โผ
audit / full_audit โ ๊ฒฐ๊ณผ๊ฐ ๋์จ ํ ์ ์ฒด ํ๋ก๋ธ ์คํ
โโ โฃa ํต๊ณ ์ ํจ์ฑ
โโ โ ์ฌ์ ๋ฑ๋ก ํ์ธ
โโ โฉ GRIM ์ฐ์ ํ์ธ
โโ โช ๋ฐ์ฆ๊ฐ๋ฅ์ฑ / kill-condition
โโ โซ ์ฒ ํ cascade
โ
โโโ ์์ฑ ๊ฒฐ๋ก โ publish + anchor (์ธ๋ถ ํด์ ๋ณด๊ด)
โ
โโโ ์์ฑ ๊ฒฐ๋ก โ negative_audit (โฌ) ๋ก ์ข
๊ฒฐ ๊ฒ์ดํธ
โ ๋
๋ฆฝ ๊ฐ๋ โฅ min_angles ํ์
โ โ
โ โผ
โ ๋์ค์ ๋ฌดํจํ ์: retract() โ cascade_check()
โ
โโโ LLM judge ํ๊ฐ โ judge_run()์ด โญโฎโฏโฐ ์๋ ๋ฐํ
์ฒด์ธ ์ฐ๊ฒฐ ์ํธ๋ฆฌ๋ฅผ ์์ฅ์ ๋ด์ธ
| ๋จ๊ณ | ๋ฐฉ๋ฒ | ์ฉ๋ |
|---|---|---|
| ํ ๊ฒ์ฆ | mm.verify(ledger, data) / mm verify --file data.json |
์์ท ๊ฐ์ฌ โ data์ ์
๋ ฅ์ด ์๋ ๋ชจ๋ ํ๋ก๋ธ ์คํ |
| ๊ทธ๋ฃน๋จ์ ๊ฒ์ฆ | mm.verify(ledger, data, groups=["judge"]) / --groups judge |
ํ๋์ ๊ฒ์ฆ ๊ด์ฌ์ฌ์ ์ง์ค |
| ๊ฐ๋ณ๊ฒ์ฆ | mm.grim_check(...), mm.judge_swap_check(...) ๋ฑ |
์ ๋ฐ ์ ์ด, ์ปค์คํ ํ์ดํ๋ผ์ธ |
๊ฒ์ฆ ๊ทธ๋ฃน (mm verify --list-groups ๋๋ mm.GROUPS):
| ๊ทธ๋ฃน | ํ๋ก๋ธ | ๋ตํ๋ ์ง๋ฌธ |
|---|---|---|
ledger |
โ โซ + ์ฒด์ธ | ์ฌ์ ๋ฑ๋ก ๊ธฐ๋ก์ด ๋ฌด๊ฒฐํ๊ณ ์ฒ ํ๋์ง ์์๋? |
stats |
โฃ โค โฆ โง โจ โฉ | ์ซ์๊ฐ ํต๊ณ์ ์ผ๋ก ์ง์ง์ธ๊ฐ? |
design |
โก โข โฅ โช | ์คํ์ด ๊ณต์ ํ๊ฒ ์ค๊ณ๋๋? |
negative |
โฌ | ์ด ์์ฑ ์ข ๊ฒฐ์ด ์ฑ๊ธํ์ง ์๋? |
judge |
โญ โฎ โฏ โฐ โฑ | LLM ํ์ ์๊ฐ ์ ๋ขฐํ ๋งํ๊ฐ? |
ranking |
โฒ โณ | ๋ฆฌ๋๋ณด๋๊ฐ ์ง์ง์ธ๊ฐ? |
verify()๋ ์
๋ ฅ ์ฃผ๋ํ์
๋๋ค: data ๋์
๋๋ฆฌ์ ํค๊ฐ ์๋ ํ๋ก๋ธ๋ง ์คํ๋๋ฏ๋ก,
ํ ๊ฒ์ฆ์ ์
๋ ฅ ๋๋ฝ์ผ๋ก ์๋ฌ๋์ง ์์ต๋๋ค โ ๋ฐ์ดํฐ๊ฐ ์ง์ํ๋ ๋งํผ๋ง ๋๋๋ค.
group_of(finding)์ผ๋ก ์ด๋ค Finding์ด๋ ์์ ๊ทธ๋ฃน์ ์ ์ ์์ต๋๋ค.
ํ๋ก๋ธ๋ ์ก์๋ด๋ ๋ฌด๊ฒฐ์ฑ ์คํจ์ ์ข ๋ฅ๋ณ๋ก ๋ฌถ์์ต๋๋ค.
์ก์๋ด๋ ๊ฒ: ์ฌํ ์งํ ๊ต์ฒด ยท ํ๋ณธ ๋ฏธ๋ฌ ยท ์กฐ์๋ ๊ธฐ์ค ยท pass-threshold ๋ฏธ๋ฌ
์ฌ์ ๋ฑ๋ก์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ธฐ ์ ์ ํ๊ฐ ๊ณํ์ ๋ด์ธํฉ๋๋ค. SHA-256 ๋ด์ธ๊ณผ ์ฒด์ธ ๋งํฌ๊ฐ ์๋ณ์กฐ๋ฅผ ํ์ง ๊ฐ๋ฅํ๊ฒ ๋ง๋ญ๋๋ค: ์ฌํ์ ์ด๋ค ํ๋๋ ๋ณ๊ฒฝํ๋ฉด ๊ฐ์ง๋ฉ๋๋ค.
# ์คํ ์
mm.preregister("ledger.jsonl", "my_model",
metric="acc", # ์ปค๋ฐํ๋ ๋จ ํ๋์ ์งํ
min_n=200, # ํ์ฉ๋๋ ์ต์ ํ๋ณธ ํฌ๊ธฐ
baseline=0.5, # ๊ณต์ ํ ๋น๊ต ๊ธฐ์ค์
pass_threshold=0.60) # ์ฑ๊ณต์ ์ฃผ์ฅํ๊ธฐ ์ํ ์ต์ ๊ธฐ์ค
# ์คํ ํ
findings = mm.audit("ledger.jsonl", "my_model",
reported_metric="acc", # ๋ฑ๋ก๋ ์งํ์ ์ผ์นํด์ผ ํจ
reported_acc=0.72,
n=500)
mm.report("my_model", findings)audit() ์ถ๋ ฅ ๋ ๋ฒจ:
FAIL [โ pre-registration(metric-swap)]โacc๋ฑ๋ก ํf1๋ณด๊ณFAIL [โ pre-registration(min_n)]โn=9 < ๋ฑ๋ก๋ min_n=200FAIL [โ pre-registration(pass-threshold)]โ ์ค์ค๋ก ์ค์ ํ ๊ธฐ์ค ๋ฏธ๋ฌFAIL [โ seal-tamper]โ ๋ฑ๋ก ํ ์์ฅ ํ์ผ ์์ ๋จ
ํํ ์ค์: metric="acc"๋ก ๋ฑ๋ก ํ, ๋์ค์ ๋ ์ข์ ๋ณด์ด๋ ์งํ(f1, auc)๋ฅผ
๋ณด๊ณ . ๋ด์ธ์ด ๋ช ๋ฌ ํ์๋ ์ด๊ฒ์ ์ก์๋
๋๋ค.
์ก์๋ด๋ ๊ฒ: ์ญ์ ๋ ์ํธ๋ฆฌ ยท ์ฝ์ ๋ ์ํธ๋ฆฌ ยท ์ด๋ค ์ํธ๋ฆฌ๋ ๋ด์ฉ ์์
findings = mm.verify_chain("ledger.jsonl")
mm.report("์์ฅ ๋ฌด๊ฒฐ์ฑ", findings)์ฒด์ธ์ด ์๋ํ๋ ์๋ฆฌ: ๋ชจ๋ ์ํธ๋ฆฌ๊ฐ ์์ ์ ๋ด์ธ์ ๊ณ์ฐํ๊ธฐ ์ ์
์ด์ ์ํธ๋ฆฌ์ SHA-256์ธ prev_seal์ ํฌํจํฉ๋๋ค. N๋ฒ ์ํธ๋ฆฌ๋ฅผ ์ญ์ ํ๋ฉด
N+1๋ฒ ์ํธ๋ฆฌ์์ ์ฒด์ธ์ด ๋๊น๋๋ค. ๊ฐ์ง ์ํธ๋ฆฌ๋ฅผ ์ฝ์
ํด๋ ๋๊น๋๋ค.
์คํ ์์ : ๋ชจ๋ ์คํ ํ CI์์, ๊ทธ๋ฆฌ๊ณ ๊ฒฐ๊ณผ ๊ฒ์ฌ ์ ์.
์ก์๋ด๋ ๊ฒ: ์์ฅ ํ์ผ ํต์งธ ๊ต์ฒด โ ์ฒด์ธ ํด์๊ฐ ํผ์์๋ ์ก์ง ๋ชปํ๋ ์ ์ผํ ๊ณต๊ฒฉ
์ฒด์ธ ํด์๋ ํ์ผ ์์ ์์ ์ ๊ฐ์งํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋๊ตฐ๊ฐ ํ์ผ์ ํต์งธ๋ก ์ญ์ ํ๊ณ
์๋ก ์์ํ๋ฉด, ๊ธฐ์ ์ ์ผ๋ก ์ฒด์ธ์ ์ ํจํฉ๋๋ค(์ genesis). anchor_hash(์ ์ฒด ํ์ผ
๋ฐ์ดํธ์ SHA-256)๊ฐ ์ด๊ฒ์ ์ก์๋
๋๋ค.
# ๋ณ์กฐ ๋ฐฉ์ง ์ค๋
์ท ์ถ๋ ฅ โ ์ ๋ขฐํ๋ ๊ณณ์ ํ์ดํ
a = mm.anchor("ledger.jsonl")
# โ {"_type": "anchor", "anchor_hash": "sha256hex...", "chain_ok": true, ...}# ๊ฒ์ฌ ์ ์ธ๋ถ ์ ์ฅ์์ ํ์ดํ
mm anchor | gh gist create - # GitHub Gist ํ์์คํฌํ
mm anchor >> ~/Dropbox/mm_anchors.jsonl # ๋ก์ปฌ ๋ฐฑ์
mm anchor --pretty # ์ฌ๋์ด ์ฝ๊ธฐ ์ฌ์ด ํํ๊ถ์ฅ ์ฌํญ: ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ฌํ๊ธฐ ์ง์ ์ mm anchor๋ฅผ ์คํํ์ธ์. ์ธ๋ถ ํ์์คํฌํ๊ฐ
๊ฒ์ฌ ์์ ์ ์์ฅ์ด ๋ฌด์์ ๋ด๊ณ ์์๋์ง๋ฅผ ์ฆ๋ช
ํฉ๋๋ค.
falsifiability_check(โช)๋ kill-condition์ด ์กด์ฌํ๋์ง๋ฅผ ๋ฌป์ต๋๋ค. prereg_lint๋
๋ด์ธ์ด ์๋ ๊ฒ์ฌ๊ฐ ๋ฐํํ ๋งํผ ์ ๋๋ก ๋๋์ง, ๋ฐ๊ฐ ์๋ฏธ ์๋์ง๋ฅผ ๋ฌป์ต๋๋ค โ
๋ฌด์๋ฏธ ์ฐ์ฐ์ด ์๋ ๊ฒฐํจ ํด๋์ค๋ค:
| ๋ ๋ฒจ | ๊ฒฐํจ |
|---|---|
| FAIL | kill-condition ๋ฌธ์ฅ์ด metric ํ๋๋ก ๋์(์๋ชป๋ ํธ์ถ โ ์ฌ๋ ๋์ ๊ธฐ์ค์ด ๋ณด์ด๋ ํ์์ ์์) |
| FAIL | pass ๋ฐ๊ฐ ์ ์ธ๋ ์ฐ์ฐ(chance) ์ดํ(๋์ด๋ ์๋ฌด๊ฒ๋ ์ฆ๋ช
๋ชป ํจ; chance= ํ์ โ baseline๋ง์ผ๋ก ๋ฐ๋ฅ ์๋) |
| WARN | ์ ๋ kill์ ๊ตฌ์กฐํ๋ kill_threshold ์์ด ์์ ํ
์คํธ๋ก๋ง โ ์์ํ ์๋ํ๊ฐ ๋ถ๊ฐ |
| WARN | min_n์ด ์ํ๋ณธ ๋ฐ๋ฅ(20) ๋ฏธ๋ง |
| INFO | pre_seal_checks ๋ฏธ์ ์ธ(reachability-smoke ยท mass-balance-audit ยท neutral-control ยท manipulation-check ยท positive-control) |
mm.preregister("ledger.jsonl", "my_model",
metric="acc", min_n=240, baseline=0.5, pass_threshold=0.60,
kill_threshold={"metric": "acc", "threshold": 0.55, "direction": "below"},
pre_seal_checks=["reachability-smoke", "neutral-control"])
for f in mm.prereg_lint("ledger.jsonl", "my_model"): # claim_id=None โ ์ ์ฃผ์ฅ ๋ฆฐํธ
print(f)์๋์ ์ผ๋ก verify() ์ฐ์ฐยท๊ทธ๋ฃน์ ๋ฃ์ง ์์์ต๋๋ค: ์ด๊ฒ์ ์ฐ์ฐ ์ ์ฒดํฌ์ด๊ณ ,
verify()/audit()์ ๋ณด๊ณ ์์ ์ ๋๋๋ค. MCP mm_register ์์์ ์๋ ๋ฐํํ๋ฉฐ(์๋ต์
lint ๋๋ด), mirror-stack compute ๊ฒ์ดํธ๋ ใ FAIL์ BLOCKํฉ๋๋ค. FAIL์ด๋ฉด ์ claim_id๋ก
๊ณ ์ณ์ ์ฌ๋ด์ธ โ first-write-wins๋ผ ์ด๋ฏธ ๋ด์ธ๋ ๊ฒ์ ๊ณ ์น ์ ์์ต๋๋ค.
์ก์๋ด๋ ๊ฒ: ํต๊ณ์ ์ผ๋ก ์ฐ์ฐ๊ณผ ๊ตฌ๋ณ ๋ถ๊ฐํ ๊ฒฐ๊ณผ (์ํ๋ณธ ์ ๊ธฐ๋ฃจ)
Wilson ์ค์ฝ์ด ์ ๋ขฐ๊ตฌ๊ฐ์ ์์ n์์ ์ ๊ท๊ทผ์ฌ๋ณด๋ค ์ ํํฉ๋๋ค. 95% CI๊ฐ ๊ธฐ์ค์ ์ ํฌํจํ๋ฉด ๊ฒฐ๊ณผ๋ ํต๊ณ์ ์ผ๋ก ๋ฌด์๋ฏธํฉ๋๋ค.
# audit() ๋ด๋ถ์์ ์๋ ์คํ
findings = mm.audit("ledger.jsonl", "my_model",
reported_metric="acc", reported_acc=0.72, n=15)
# โ ๏ธ [โฃa small-sample CI] n=15, acc=0.72 โ 95%CI [0.467, 0.887]
# โ baseline(0.5). ์ฐ์ฐ๊ณผ ๊ตฌ๋ณ ๋ถ๊ฐ.๊ฒฝํ ๋ฒ์น: n=15์์๋ acc=0.72๋ 0.5์ ๊ตฌ๋ณ๋์ง ์์ต๋๋ค. +10pp ๊ฐ์ ์ ์ ์๋ฏธํ๊ฒ ๋ณด์ด๋ ค๋ฉด nโฅ200์ด ํ์ํฉ๋๋ค.
์ก์๋ด๋ ๊ฒ: ๊ด์ฌ ์๋ ์ต์ ํจ๊ณผ๋ฅผ ํ์งํ๊ธฐ์ n์ด ๋๋ฌด ์์ (๊ฑฐ์ง์์ฑ ๊ฐ๋)
์ด๊ฒ์ด ๊ฑฐ์ธ์ ํต์ฌ ๊ฑฐ์ง์์ฑ ํ๋ก๋ธ์ ๋๋ค. ํต๊ณ์ ๊ฒ์ ๋ ฅ์ด ๋ถ์กฑํ ์คํ์์ ๋์จ ์์ฑ ๊ฒฐ๊ณผ๋ ์๋ฏธ๊ฐ ์์ต๋๋ค โ ์ง์ง ํจ๊ณผ๋ฅผ ๋์ณค์ ์ ์์ต๋๋ค.
f = mm.power_check(n=50, baseline=0.5, min_detectable_effect=0.05)
# โ ๏ธ [โง power] n=50์ 80% ๊ฒ์ ๋ ฅ์ผ๋ก ฮ=+0.05 ํ์ง์ ๋ถ์กฑ.
# ํ์ nโฅ388. (ฮฑ=0.05, power=0.80)
# ๋ค์ํ ํจ๊ณผ ํฌ๊ธฐ์์ 80% ๊ฒ์ ๋ ฅ์ ํ์ํ n:
# ฮ=+0.20 โ nโ50 | ฮ=+0.10 โ nโ200 | ฮ=+0.05 โ nโ388
# full_audit์์ ํ์ฑํ
findings = mm.full_audit("ledger.jsonl", "my_model", ...,
min_detectable_effect=0.05)ํํ ์ค์: n=30์ผ๋ก ฮ=+0.05์ ๋ํด ํ ์คํธํ ํ "์ด ๋ฐฉ๋ฒ์ ๋์์ด ์ ๋๋ค" (์์ฑ ๊ฒฐ๋ก )๋ฅผ ์ข ๊ฒฐ. ๊ฒ์ ๋ ฅ์ด 14%์์ผ๋ฏ๋ก ์ง์ง ํจ๊ณผ๋ฅผ ๋์ณค์ ํ๋ฅ ์ด 86%์ ๋๋ค.
์ก์๋ด๋ ๊ฒ: ๊ฐ์ ์์ฅ์์ k>1 ์คํ์ ํ ๋์ ๋ค์ค๋น๊ต ๋ฌธ์
5๊ฐ ์คํ์ ๋๋ ค ๊ฐ์ฅ ์ข์ ๊ฒ์ ๊ณ ๋ฅด๋ฉด, ์คํจ ฮฑ๋ 0.05๊ฐ ์๋๋ผ 1 โ (1โ0.05)^5 โ 0.23์ ๋๋ค. Bonferroni ๊ต์ ์ ํ ์คํธ๋น ฮฑ/k๋ฅผ ์๊ตฌํฉ๋๋ค.
f = mm.multiple_comparisons_check("ledger.jsonl", alpha=0.05)
# โ ๏ธ [โจ multiple-comparisons] ์์ฅ์ k=5๊ฐ ์คํ.
# Bonferroni ๊ต์ ฮฑ = 0.010 (0.05๊ฐ ์๋). ๋ ์๊ฒฉํ ๊ธฐ์ค ์ฌ์ฉ.
# full_audit์์ ํ์ฑํ
findings = mm.full_audit("ledger.jsonl", "my_model", ...,
check_multiplicity=True)์ฐธ๊ณ : ๊ฐ์ claim_id์ ์ฌ๋ฑ๋ก์ k=1๋ก ์นด์ดํธ(first-write-wins ์ ์ฑ
๊ณผ ์ผ๊ด).
์๋ก ๋ค๋ฅธ claim_id๋ง ์นด์ดํธ๋ฉ๋๋ค.
์ก์๋ด๋ ๊ฒ: ํ์ฝํ ๊ธฐ์ค์ ยท ๋์ ๊ฒฐ๊ณผ ยท ์ญ์ ๋ ๋น๊ต
์๋์ ์ผ๋ก ์ฝํ ๊ธฐ์ค์ ๋๋น "๊ฐํ ๊ฐ์ "์ ๊ฐ์ ์ด ์๋๋๋ค. ์ค์ฐจ ๋ฒ์ ์์ ๋๋ "๋ ์ข์" ๊ฒฐ๊ณผ๋ ๋์ ์ ๋๋ค.
# ํ์ฝํ ๊ธฐ์ค์ : ๋ด ๋ชจ๋ธ์ด ๋ง๊ฐ์ง ๊ฒฝ์์๋ฅผ ์ด๊น
f = mm.baseline_fairness("random_baseline", 0.60, 0.50) # OK โ ๋ช
ํํ ์น๋ฆฌ
f = mm.baseline_fairness("vs_gru_ode", 0.998, 0.996) # FAIL โ ๋์ (ฮ=0.002)
# ์ญ์ : ๋ด๊ฐ ์ง
f = mm.baseline_fairness("strong_model", 0.72, 0.86) # FAIL โ ๊ธฐ์ค์ ์น
# ์ด์ง์ด ์๋ ์งํ (๋ฎ์์๋ก ์ข์, ์: MSE)
f = mm.baseline_fairness("vs_baseline_mse", 0.12, 0.15, higher_better=False)๋ ๋ฒจ:
FAIL [โก fair-baseline] X winsโ ๊ธฐ์ค์ ์ด ๋๋ฅผ ๋ฅ๊ฐFAIL [โก fair-baseline] Tiedโ ฮ < margin (๊ธฐ๋ณธ๊ฐ 0.01)OKโ ๋ช ํํ ์น๋ฆฌ
์ก์๋ด๋ ๊ฒ: ์ถ๊ฐ ๊ฒํ ๊ฐ ํ์ํ ์์ฌ์ค๋ฝ๊ฒ ํฐ ๊ฐ์
"๋๋ฌด ์ข์ ๋ณด์ด๋ฉด, ๊ทธ๊ฒ ๋ณดํต ์ฌ์ค์ ๋๋ค": ๋ฐ์ดํฐ ๋์, ํ๊ฐ์ ์ค์ผ, ๋ณด์/์งํ ์ ๋ ฌ ๋ฒ๊ทธ๊ฐ ํํ ์์ธ์ ๋๋ค.
f = mm.too_good_check("my_model", claimed=0.95, baseline=0.50)
# โ ๏ธ [โฆ too-good] ๊ธฐ์ค์ ๋๋น ฮ=+0.45 โ ์์ฌ์ค๋ฝ๊ฒ ํผ.
# ์กฐ์ฌ: ๋ฐ์ดํฐ ๋์? ๋ณด์ ํดํน? ์งํ ์ ๋ ฌ ๋ฒ๊ทธ?๊ธฐ๋ณธ ์๊ณ๊ฐ: ฮ > 0.30์ด๋ฉด WARN. full_audit() ๋ด๋ถ์์ ํญ์ ์๋ ์คํ๋ฉ๋๋ค.
์ก์๋ด๋ ๊ฒ: ํ๋ จ ๋ณด์/์์ค์ ํ๊ฐ ์งํ๊ฐ ์ง์ ํฌํจ๋จ
ํ๊ฐ ์งํ๋ฅผ ์ง์ ์ต์ ํํ๋ฉด ๊ฒฐ๊ณผ๋ ์๊ธฐ์ถฉ์กฑ์ ์ ๋๋ค: ๋ชจ๋ธ์ ๊ธฐ์ ๊ณผ์ ๋ฅผ ํ์ตํ๊ธฐ ๋๋ฌธ์ด ์๋๋ผ, ์งํ๋ฅผ ์ต์ ํํ๋๋ก ํ๋ จ๋๊ธฐ ๋๋ฌธ์ ์ข์ ์ ์๋ฅผ ๋ฐ์ต๋๋ค.
f = mm.gaming_check(metric="accuracy",
reward_terms=["cross_entropy", "accuracy"])
# ๐ด [โข gaming] 'accuracy'๊ฐ reward_terms์ ํฌํจ๋จ.
# ๊ฒฐ๊ณผ๋ ์๊ธฐ์ถฉ์กฑ์ โ ์งํ๊ฐ ํ๋ จ ๋ชฉ์ ํจ์์ ์ง์ ์์.
f = mm.gaming_check(metric="bleu", reward_terms=["rl_reward", "fluency"])
# โ
OK โ bleu๊ฐ ๋ณด์์ ์์์ก์๋ด๋ ๊ฒ: ํ๋ จ/ํ ์คํธ ์ธํธ ์ค๋ณต (๋ฐ์ดํฐ ์ค์ผ)
์๋์ ์ค๋ณต๋ ์ ํ๋๋ฅผ ํฌ๊ฒ ๋ถํ๋ฆด ์ ์์ต๋๋ค. ์์ดํ ์ ํด์ฑํ์ฌ ๊ต์งํฉ์ ๊ณ์ฐํฉ๋๋ค โ ํด์ ๊ฐ๋ฅํ ์์ดํ ํ์ ์ด๋ฉด ๋ชจ๋ ์๋ํฉ๋๋ค.
# ๋ฌธ์์ด ์์ดํ
train = ["๋ฌธ์ฅ A", "๋ฌธ์ฅ B", "๋ฌธ์ฅ C"]
test = ["๋ฌธ์ฅ C", "๋ฌธ์ฅ D", "๋ฌธ์ฅ E"]
f = mm.leakage_check(train, test)
# ๐ด [โฃa leakage] ํ
์คํธ ์์ดํ
์ 1/3 (33.3%)์ด ํ๋ จ์
์ ์์.
# ์ ์, ํํ, ํด์ ๊ฐ๋ฅํ ๋ชจ๋ ํ์
์๋
f = mm.leakage_check(list(range(100)), list(range(50, 150))) # 50% ์ค๋ณต โ FAIL์ก์๋ด๋ ๊ฒ: ์๋ ๊ฐ ๋ถ์์ ํ ์ ํธ ยท ๊ธฐ์ค์ ์ด ์๋ ๋ฒ์ ์์ ๋ค์ด์ด
acc=0.48~0.72 ๋ฒ์๋ก ์๋๋ง๋ค ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง๋ฉด ์ ๋ขฐํ ์ ์์ต๋๋ค. ๊ธฐ์ค์ ์ด ์๋ ๋ฒ์ ์์ ์์ผ๋ฉด, ๋ค๋ฅธ ์ด๊ธฐํ์์ ๊ฒฐ๊ณผ๊ฐ ์ฐ์ฐ๊ณผ ๊ตฌ๋ณ๋์ง ์์ต๋๋ค.
f = mm.multiseed_check([0.48, 0.72, 0.65], baseline=0.5)
# ๐ด [โค multi-seed] ๊ธฐ์ค์ 0.500์ด ์๋ ๋ฒ์ [0.480, 0.720] ์์ ์์.
# ์ฐ์ฐ ์ด์์ ์ ํธ๊ฐ ๊ฐ๊ฑดํ์ง ์์.
f = mm.multiseed_check([0.68, 0.71, 0.72], baseline=0.5) # OK
f = mm.multiseed_check([0.70, 0.85, 0.75], baseline=0.5,
cv_threshold=0.05) # CV ์๊ณ๊ฐ ์กฐ์ ๊ท์น: CV(๋ณ๋๊ณ์) > 10%์ด๋ฉด ๊ธฐ๋ณธ์ผ๋ก WARN.
์ก์๋ด๋ ๊ฒ: ๊ฒ์ฆ๋ ๋ฒ์๋ณด๋ค ๋๊ฒ ์ฃผ์ฅ (๊ณผ๋ ์ผ๋ฐํ)
"๊ณผ์ A์์ ์๋" โ "์ผ๋ฐ ์ถ๋ก ". claimed_scope๋ tested_scope์ ๋ถ๋ถ์งํฉ์ด๊ฑฐ๋
๊ฐ์์ผ ํฉ๋๋ค.
f = mm.scope_check(claimed_scope=["reasoning", "math"],
tested_scope=["musr_task_a"])
# ๐ด [โฅ scope] ๊ณผ๋์ฃผ์ฅ: {'reasoning', 'math'} ๋ฏธ๊ฒ์ฆ.
# ๊ฒ์ฆ๋จ: {'musr_task_a'} ๋ฟ.
f = mm.scope_check(claimed_scope=["task_a"],
tested_scope=["task_a", "held_out_b"]) # OK์ก์๋ด๋ ๊ฒ: ์ฐ์ ์ ์ผ๋ก ๋ถ๊ฐ๋ฅํ ์ ํ๋/ํ๊ท ๊ฐ โ ์กฐ์๋์๊ฑฐ๋ n์ ์๋ชป ๊ธฐ์ฌํ ๊ฐ๋ฅ์ฑ
GRIM (Granularity-Related Inconsistency of Means): acc = k/N์ด ์ด๋ค ์ ์ k์
๋ํด ์ฑ๋ฆฝํ๋ค๋ฉด(N = nยทitems), round(k/N, d) == acc๊ฐ ๋ฐ๋์ ์ฑ๋ฆฝํด์ผ ํฉ๋๋ค.
์ด๋ฅผ ๋ง์กฑํ๋ ์ ์ k๊ฐ ์๋ค๋ฉด ๊ทธ ๊ฐ์ ๋ถ๊ฐ๋ฅํฉ๋๋ค. ๋น์จยทํผ์ผํธยท์ ์(๋ฆฌ์ปคํธ ๋ฑ)
๋ฐ์ดํฐ์ ํ๊ท ์ ๋ชจ๋ ์๋ํฉ๋๋ค.
f = mm.grim_check(reported_acc=0.33, n=10)
# ๐ด [โฉ GRIM] acc=0.33์ n=10์์ ์ฐ์ ์ ์ผ๋ก ๋ถ๊ฐ๋ฅ.
# round(k/10, 2) = 0.33์ ๋ง์กฑํ๋ ์ ์ k ์์.
# (ํ๋ณด: k=3 โ 0.30, k=4 โ 0.40). ์์น ์กฐ์ ๋๋ n ์ค๊ธฐ์ฌ.
f = mm.grim_check(reported_acc=0.30, n=10) # OK โ round(3/10, 2) = 0.30
# ๋ค๋ฌธํญ ์ฒ๋์ ํ๊ท : granularity๋ nยทitems
f = mm.grim_check(5.90, n=40, items=3) # N=120
# ์์์ ์๋ฆฌ์ ์๋ ์ถ๋ก ; n_decimals๋ก ์ฌ์ ์ ๊ฐ๋ฅ
f = mm.grim_check(0.333, n=10, n_decimals=3)audit() ๋ด๋ถ์์ ์๋ ์คํ โ FAIL๋ง ์ถ๊ฐ, OK๋ ์กฐ์ฉํ ํต๊ณผ.
๋ฒ์ โ ์ํ๋ณธ ์ ์ฉ. GRIM์ ํ์ granularity์์ ๋์ต๋๋ค: N์ด ์์ผ๋ฉด ๋๋ฌ ๊ฐ๋ฅํ ๊ฐ์ด ๋ช ๊ฐ๋ฟ์ด๋ผ ๋ถ๊ฐ๋ฅํ ๊ฐ์ด ํ์ด๋์ต๋๋ค. N์ด ์ปค์ง๋ฉด ๋๋ฌ ๊ฐ๋ฅํ ๊ฐ์ด ๋นฝ๋นฝ์ด ์ฑ์์ ธ GRIM์ ๋์ด ๋ฉ๋๋ค โ d์๋ฆฌ๋ก ๋ณด๊ณ ๋ ๊ฐ์
N โณ 10^d(์: 2์๋ฆฌ ํ๊ท ์์ n โฅ 100)๋ถํฐ ์๋ฌด๊ฒ๋ ๋ชป ์ก์ต๋๋ค. ๋ํ ์ฐ์ ์ ๋ถ๊ฐ๋ฅ๋ง ์ก์ง ๋ถํฌ์ ์กฐ์์ ๋ชป ์ก์ต๋๋ค: ๋๊ท๋ชจ ์กฐ์ ๋ฐ์ดํฐ์ (์: AI ์์ฑ)์ ๋ณดํต GRIM์ ํต๊ณผํ๊ณ , ๋์ ๋์งํธ ํจํด/๋ถํฌ ํฌ๋ ์์ ๊ฑธ๋ฆฝ๋๋ค(์ฌ๊ธฐ ๋ฒ์ ๋ฐ). ์ค์ฆ ํ์ธ: ๋ฌด์์ 2์๋ฆฌ ํ๊ท ์ n=20์์ ~79%๊ฐ GRIM-๋ถ๊ฐ๋ฅ์ด์ง๋ง nโฅ100์์๋ ~0%. GRIM์ ์ํ๋ณธ ์ฐ์ ๊ฒ์ดํธ์ด์ง ๋ฒ์ฉ ์ฌ๊ธฐ ํ์ง๊ธฐ๊ฐ ์๋๋๋ค.
์ด ์ธ ํ๋ก๋ธ๊ฐ "์ฃผ์ฅ ์์ ์ฃผ๊ธฐ ๋ฌด๊ฒฐ์ฑ" ์์คํ ์ ํต์ฌ์ ๋๋ค. ์ ์ ํฉ์น๋ฉด Measurement Mirror๊ฐ "ํต๊ณ ์ฒดํฌ๋ฆฌ์คํธ"์์ ๋ฌด์์ด ์ฃผ์ฅ๋๊ณ , ๋ฌด์์ด ๊ทธ ์ฃผ์ฅ์ ์ฃฝ์ผ ์ ์์ผ๋ฉฐ, ๊ธฐ๋ฐ์ด ๋ฌด๋์ก๋์ง๋ฅผ ์ถ์ ํ๋ ๊ฐ์ฌ ์ธํ๋ผ๋ก ๋ฐ๋๋๋ค.
์ก์๋ด๋ ๊ฒ: ๋ฐ์ฆ ๋ถ๊ฐ๋ฅํ ์ฃผ์ฅ (kill-condition ์์) ยท ์ด๋ฏธ ์๊ธฐ๋ถ์ ํ ์ฃผ์ฅ
kill-condition ์๋ ์ฃผ์ฅ์ ์์น์ ์ผ๋ก ํ๋ ธ์์ ์ฆ๋ช
ํ ์ ์์ต๋๋ค โ ๋ฐ์ฆ๋ถ๊ฐ๋ฅํฉ๋๋ค.
๋ฑ๋ก๋ kill_threshold๋ฅผ ๊ฒฐ๊ณผ๊ฐ ๋ฐํ์ํค๋ ์ฃผ์ฅ์ ๊ฒ์ฌ ์์ ์ ์๊ธฐ๋ถ์ ๋ฉ๋๋ค.
# ์คํ ์ kill-condition ๋ฑ๋ก
mm.preregister("ledger.jsonl", "my_model",
metric="acc", min_n=200, baseline=0.5, pass_threshold=0.60,
# ์ฌ๋์ด ์ฝ๋ ์ค๋ช
(์ ํ์ฌํญ์ด์ง๋ง ๊ถ์ฅ)
kill_condition="held-out ํ
์คํธ์์ ์ ํ๋ 0.55 ๋ฏธ๋ง",
# ๊ตฌ์กฐํ ํํ: audit ์์ ์ ์๋ ํ๊ฐ (๊ถ์ฅ)
kill_threshold={"metric": "acc",
"threshold": 0.55,
"direction": "below"})
# audit ์ โ โช ์๋ ์คํ
findings = mm.audit("ledger.jsonl", "my_model",
reported_metric="acc", reported_acc=0.50, n=500)
# ๐ด [โช falsifiability] Kill condition triggered: acc=0.5 < 0.55.
# Claim 'my_model' is falsified by its own pre-registered criterion.
# ๋จ๋
ํ์ธ (์ ์ฒด audit ์ ๋๋ ํน์ ์ฟผ๋ฆฌ์ฉ)
f = mm.falsifiability_check("ledger.jsonl", "my_model", reported_acc=0.50)๋ ๋ฒจ:
FAILโ kill_threshold ๋ฑ๋ก๋จ AND reported_acc๊ฐ ๋ฐํWARNโ kill-condition ์์ ์์ ("๋ฐ์ฆ๋ถ๊ฐ๋ฅ") OR threshold ์ค์ ๋์ง๋ง ๊ฒฐ๊ณผ ๋ฏธ์ ๊ณตOKโ threshold ๋ฏธ๋ฐํ, ๋๋ ํ ์คํธ ์ ์ฉ ์กฐ๊ฑด ๋ฑ๋ก๋จ
direction ํ๋ผ๋ฏธํฐ:
"below":reported_acc < threshold์ผ ๋ FAIL (์ ํ๋ํ, ๋์์๋ก ์ข์)"above":reported_acc > threshold์ผ ๋ FAIL (์ค๋ฅํ, ์: MSE, ๋ฎ์์๋ก ์ข์)
CLI:
mm register my_model --metric acc --min-n 200 --baseline 0.5 --pass 0.60 \
--kill "์ ํ๋ 0.55 ๋ฏธ๋ง์ด๋ฉด ์ฌ๋ง" \
--kill-threshold 0.55 --kill-direction below์ก์๋ด๋ ๊ฒ: ์ฒ ํ๋ ๊ธฐ๋ฐ ์์ ์ธ์์ง ์ฃผ์ฅ (์ค๋๋ ์ ์ด ์์กด์ฑ)
์ฐ๊ตฌ๋ ๋์ ๋ฉ๋๋ค. ์ฃผ์ฅ B๋ ์ฃผ์ฅ A ์์ ์ธ์์ง๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ฃผ์ฅ A๊ฐ ์ฒ ํ๋๋ฉด (๋ฐ์ดํฐ์ ์ค์ผ ๋ฐ๊ฒฌ, ๋ฐฉ๋ฒ๋ก ๊ฒฐํจ ๋ฐ๊ฒฌ), A์ ์์กดํ๋ ๋ชจ๋ ์ฃผ์ฅ์ ์๋์ผ๋ก STALE๋ก ํ์๋์ด์ผ ํฉ๋๋ค โ ์ค๋ น ์๋ ํ์ ๊ฒ์ฌ๋ ๊ฒ์ด๋ผ๋.
# ์คํ ์ ์์กด ๊ด๊ณ ๋ฑ๋ก
mm.preregister("ledger.jsonl", "dataset_v1",
metric="quality_score", min_n=100, baseline=0.0, pass_threshold=0.8)
mm.preregister("ledger.jsonl", "v1๋ก_ํ๋ จ๋_๋ชจ๋ธ",
metric="acc", min_n=200, baseline=0.5, pass_threshold=0.60,
depends_on=["dataset_v1"]) # โ ์์กด์ฑ ๋ด์ธ
mm.preregister("ledger.jsonl", "๋
ผ๋ฌธ_๊ฒฐ๊ณผ",
metric="acc", min_n=500, baseline=0.5, pass_threshold=0.70,
depends_on=["v1๋ก_ํ๋ จ๋_๋ชจ๋ธ"]) # โ ์ ์ด ์ฒด์ธ
# ๋์ค์: ๋ฐ์ดํฐ์
์ค์ผ ๋ฐ๊ฒฌ
mm.retract("ledger.jsonl", "dataset_v1",
reason="์ ์ฒ๋ฆฌ ๋จ๊ณ์์ ํ๋ จ/ํ
์คํธ 12% ์ค๋ณต ๋ฐ๊ฒฌ")
# Cascade check โ ์์กด์ฑ ์ฒด์ธ์ ์ง์ ์ ํ์ ์์
f = mm.cascade_check("ledger.jsonl", "๋
ผ๋ฌธ_๊ฒฐ๊ณผ")
# โ ๏ธ [โซ retraction-cascade] Claim '๋
ผ๋ฌธ_๊ฒฐ๊ณผ' is STALE:
# depends (transitively) on retracted claim(s): 'dataset_v1'
f = mm.cascade_check("ledger.jsonl", "dataset_v1")
# ๐ด [โซ retraction-cascade] Claim 'dataset_v1' has been retracted.cascade_check ๋ ๋ฒจ:
FAILโ ์ฃผ์ฅ ์์ฒด๊ฐ ์ฒ ํ๋จWARNโ ์ฃผ์ฅ์ด STALE (์ ์ด ์์กด์ฑ์ด ์ฒ ํ๋จ)OKโ ์ฒ ํ ์ํ ์์
ํต์ฌ ํน์ฑ:
- ์ฒ ํ ์ํธ๋ฆฌ๋ ์ฒด์ธ ์ฐ๊ฒฐ๋ฉ๋๋ค โ ์ฒ ํ ๊ธฐ๋ก ์ญ์ ์
verify_chain()์ด ๊ฐ์ง. ์กฐ์ฉํ ์ฒ ํ๋ฅผ ์์ ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค. - ์ ํ๋ ๊ฒ์ฌ ์์์ ๋ฌด๊ด โ 2019๋ ๋ฐ์ดํฐ์ ์ ๊ธฐ๋ฐ์ผ๋ก ํ 2020๋ ๋ ผ๋ฌธ์ด 2024๋ ์ ์ฒ ํ๋๋ฉด ์ฆ์ STALE๋ก ํ์.
audit()๋ด๋ถ์์ ์๋ ์คํ (WARN/FAIL๋ง ์ถ๊ฐ).
CLI:
# ์์กด์ฑ ํฌํจ ๋ฑ๋ก
mm register model_v2 --metric acc --min-n 200 --baseline 0.5 --pass 0.60 \
--depends-on dataset_v1 baseline_eval
# ์ฒ ํ
mm retract dataset_v1 --reason "ํ๋ จ/ํ
์คํธ ์ค๋ณต ๋ฐ๊ฒฌ"์ก์๋ด๋ ๊ฒ: ์ฑ๊ธํ ์์ฑ ์ข ๊ฒฐ (๊ฐ๋๊ฐ ๋ถ์กฑํ Resolved-Negative)
์ฐ๊ตฌ์์ ๊ฐ์ฅ ์ก๊ธฐ ์ด๋ ค์ด ๊ฑฐ์ง์์ฑ: ๋จ ํ ๋ฒ์ ์์ฑ ์คํ ํ "X๋ ์๋ํ์ง ์๋๋ค" ์ ์ธ. ๋จ์ผ ์คํจ๋ frame ๊ฒฐํจ์ผ ์ ์์ผ๋ฉฐ, ๋ณดํธ์ ๋ฒฝ์ด ์๋๋๋ค. ์์ฑ ๊ฒฐ๋ก ์ด ์ ๋ขฐ๋ฐ์ผ๋ ค๋ฉด ์ฌ๋ฌ ๋ ๋ฆฝ ๊ฐ๋๊ฐ ์๋ ดํด์ผ ํฉ๋๋ค.
# ๊ฐ ๊ฐ๋๋ ๋์ผํ ๊ฐ์ค์ ๋ค๋ฅธ ๊ด์ ์์ ํ
์คํธํ๋ ๋ณ๊ฐ์ ๋
๋ฆฝ ์คํ
# (๋ค๋ฅธ ๋ฐ์ดํฐ์
, ๋ฐฉ๋ฒ๋ก , ์กฐ๊ฑด)
for angle_id in ["oee_wave_ode", "oee_bilinear_coev", "oee_alife_sim",
"oee_gray_scott", "oee_hp_folding"]:
mm.preregister("ledger.jsonl", angle_id,
metric="oee_score", min_n=50, baseline=0.5, pass_threshold=0.0)
# ๋ชจ๋ ๊ฐ๋๊ฐ ์์ฑ์ผ๋ก ์๋ ดํ ํ โ ์ข
๊ฒฐ ๊ฒ์ดํธ
f = mm.negative_audit("ledger.jsonl",
angles=["oee_wave_ode", "oee_bilinear_coev",
"oee_alife_sim", "oee_gray_scott", "oee_hp_folding"],
min_angles=3)
# โ
[โฌ negative-audit] 5/5 independent pre-registered angle(s) verified โ
# negative conclusion is supported.
# ์ ํ์ฌํญ: ์์ฑ ๊ฒฐ๋ก ์ด ๊ณผ๋ ์ผ๋ฐํ๋์ง ์์๋์ง ๋ฒ์ ํ์ธ
f = mm.negative_audit("ledger.jsonl",
angles=["oee_wave_ode", "oee_bilinear_coev", "oee_alife_sim"],
conclusion_scope=["all_substrates", "all_ALife"],
tested_scope=["in_silico_digital"])
# ๐ด [โฌ negative-audit] conclusion scope includes untested domain(s):
# ['all_substrates', 'all_ALife'].๋ ๋ฒจ:
FAILโmin_angles๋ณด๋ค ๊ฐ๋ ์๊ฐ ์ ์ (์ฑ๊ธ์ข ๊ฒฐ ์ํ)FAILโ ๊ฐ๋๊ฐ ์ฌ์ ๋ฑ๋ก๋์ง ์์ (๋ ๋ฆฝ ์ฆ๊ฑฐ๋ก ์ ๋ขฐ ๋ถ๊ฐ)FAILโconclusion_scope โ tested_scope(๊ณผ๋ ์ผ๋ฐํ๋ ์์ฑ)WARNโ ๊ฐ๋ ์๋ ์ถฉ๋ถํ์ง๋ง ์ผ๋ถ ์ฒ ํ๋จ (์ฝํ๋ ์ผ์ด์ค)OKโ ์ ์ฒด ํต๊ณผ
CLI:
mm negative \
--angles oee_wave_ode oee_bilinear_coev oee_alife_sim \
--min-angles 3full_audit()๋ฅผ ํตํ ํ์ฑํ:
findings = mm.full_audit("ledger.jsonl", "main_claim", ...,
angles=["exp1", "exp2", "exp3"])
# โฌ ๊ฒฐ๊ณผ๊ฐ ์๋์ผ๋ก ์ถ๊ฐ๋จ์ด 4๊ฐ ํ๋ก๋ธ๋ ํ๊ฐ๋ฐ๋ ๋ชจ๋ธ์ด ์๋๋ผ ํ์ ์ ์์ฒด๋ฅผ ๊ฐ์ฌํฉ๋๋ค. LLM ํ์ ์๋ ์ซ์ ์งํ๋ก๋ ์ก์ ์ ์๋ ๊ณ ์ ํ ์คํจ ํจํด์ ๊ฐ์ง๋๋ค: ํ๋ฅ ์ ๋ค์ง๊ธฐ, ์์น ํธํฅ, ๋ฐ ๊ฐ ๋ถ์ผ์น, ํดํ ์ ์ ๋ถํฌ.
4๊ฐ ํ๋ก๋ธ๋ ๋ชจ๋ mm.py์ ์์ด ์์กด์ฑ์ด ์์ผ๋ฉฐ ์ ์ ๋ฆฌ์คํธ๋ฅผ ์ง์ ๋ฐ์ต๋๋ค.
์ ํ ๋ชจ๋ judge.py๋ LLM ํธ์ถ๊ณผ 4๊ฐ ํ๋ก๋ธ ์ฐ๊ฒฐ์ ์๋ ์ฒ๋ฆฌํฉ๋๋ค.
์ค์น:
pip install "measure-mirror[judge]" # openai ยท anthropic ํจํค์ง ์ถ๊ฐ์ก์๋ด๋ ๊ฒ: ์ฌ์คํ ์ ๋์ผ ์์ดํ ์ ๋ค๋ฅธ ํ์ ์ ๋ด๋ฆฌ๋ ํ๋ฅ ์ ํ์ ์.
๋์ผ ์์ดํ ์ ํ์ ์๋ฅผ ๋ ๋ฒ ์คํํ์ ๋ ๋ค์ง๊ธฐ ๋น์จ์ด ๋์ผ๋ฉด ํ์ ์ ์ถ๋ ฅ์ ๋ ธ์ด์ฆ์ ๋๋ค. ๋ ธ์ด์ฆ ์ ์๋ก ๋ง๋ ์์๋ ์ง๊ณ ์์น๊ฐ ์์ ์ ์ผ๋ก ๋ณด์ฌ๋ ์๋ฏธ๊ฐ ์์ต๋๋ค.
# score_pairs: [(๋ฐ1 ์ ์, ๋ฐ2 ์ ์), ...] โ ๋์ผ ์์ดํ
2ํ ํ์
# pairwise: 0 = A ์น, 1 = B ์น
# rating: ์ ์ ์ ์
score_pairs = [(1, 1), (0, 0), (1, 1), (0, 0), (1, 0)] # 1ํ ๋ค์ง๊ธฐ / 5
f = mm.judge_consistency_check(score_pairs, flip_threshold=0.20)
# โ
[โญ judge-consistency] Judge flip rate 20.0% โค 20.0% (1/5 flips). Consistent.
score_pairs_bad = [(1, 0), (0, 1), (1, 0), (0, 1), (1, 0)] # ์ ๋ถ ๋ค์งํ
f = mm.judge_consistency_check(score_pairs_bad, flip_threshold=0.20)
# ๐ด [โญ judge-consistency] Judge flip rate 100.0% > 20.0%.
# Judge is unreliable โ scores cannot be trusted.ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
score_pairs |
ํ์ | [(๋ฐ1, ๋ฐ2), ...] โ ์์ดํ
๋น 1์ |
flip_threshold |
0.20 | ํ์ฉ ์ต๋ ๋ค์ง๊ธฐ ๋น์จ |
๋ ๋ฒจ:
FAILโ flip_rate > flip_thresholdWARNโ ๋น score_pairs (ํ๋จ ๋ถ๊ฐ)OKโ flip_rate โค flip_threshold
์ก์๋ด๋ ๊ฒ: ๋ด์ฉ์ ๊ด๊ณ์์ด A ๋๋ B ์์น๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ์ ํธํ๋ ํ์ ์.
pairwise ํ๊ฐ์์ ํ์ ์๋ ๊ณ ์ ๋ ์์(A, B)๋ก ์๋ต์ ๋ฐ์ต๋๋ค. ํธํฅ๋ ํ์ ์๋ "์ฒซ ๋ฒ์งธ ๋ต์ด ๋ ์ข๋ค"๋ผ๋ ์ง๋ฆ๊ธธ์ ์๋๋ค. ์ด๋ ์ ํธ๋๋ ์์น๋ฅผ ์ฐจ์งํ๋ ํ๋ณด๋ฅผ ๋ถ๋นํ๊ฒ ์ ๋ฆฌํ๊ฒ ๋ง๋ญ๋๋ค.
# pairwise_results: [0, 1, 0, ...] โ 0 = A ์น, 1 = B ์น
results = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1] # 50/50 โ ํธํฅ ์์
f = mm.judge_bias_check(results)
# โ
[โฎ judge-bias] Position A win rate 50.0% โ no significant position bias.
results = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] # A๊ฐ 90% ์น
f = mm.judge_bias_check(results, bias_threshold=0.60)
# ๐ด [โฎ judge-bias] Position A win rate 90.0% > 60.0%.
# Strong position bias detected (9/10 items favor A).์ํ ๋ฐฉ๋ฒ: ๊ฐ ์์ ์๋ฐฉํฅ(AB, BA)์ผ๋ก ์คํํ๊ณ ํ๊ท ๋ ๋๋ค. ํธํฅ ์๋ ํ์ ์๋ผ๋ฉด ์์ ๋ณ๊ฒฝ ์ ํ์ ๋น๋๊ฐ ๋ฐ์ ๋ผ์ผ ํฉ๋๋ค.
ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
pairwise_results |
ํ์ | [0, 1, ...] โ ๋น๊ต๋น 1๊ฒฐ๊ณผ |
bias_threshold |
0.60 | ์์น ํธํฅ ํ๋๊ทธ ์๊ณ๊ฐ |
๋ ๋ฒจ:
FAILโ A ๋๋ B ์น๋ฅ > bias_thresholdWARNโ ๋น resultsOKโ ์๋ฐฉํฅ ์น๋ฅ ๋ชจ๋ ์๊ณ๊ฐ ์ดํ
์ก์๋ด๋ ๊ฒ: ๋ ํ์ ์(๋๋ ๋์ผ ํ์ ์์ ๋ ๋ฐ)๊ฐ ์ฐ์ฐ ์์ค์ ๋์ด ๋ถ์ผ์น.
Cohen's ฮบ๋ ๋ฌด์์ ์ฐ์ฐ์ผ๋ก ๊ธฐ๋๋๋ ์ผ์น ์ด์์ ์ผ์น๋ฅผ ์ธก์ ํฉ๋๋ค. ฮบ โ 0์ด๋ฉด ๋ ํ๊ฐ์๋ ์ฌ์ค์ ๋ ๋ฆฝ์ ์ธ ๋์ ๋ณ์์ ๋๋ค โ ์ ์๋ฅผ ํ๊ท ๋ด๊ฑฐ๋ ๋จ์ผ ์ ํธ๋ก ๋ณด๊ณ ํ๋ ๊ฒ์ด ์๋ฏธ๋ฅผ ์์ต๋๋ค.
| ฮบ | ํด์ |
|---|---|
| < 0.20 | ๋ถ๋ โ ์ฌ์ค์ ๋ฌด์์ |
| 0.20 โ 0.40 | ๋ณดํต |
| 0.40 โ 0.60 | ์ค๊ฐ (๊ธฐ๋ณธ ์๊ณ๊ฐ) |
| 0.60 โ 0.80 | ์ํธ |
| > 0.80 | ๊ฑฐ์ ์๋ฒฝ |
# ratings_matrix: [(ํ์ ์1_์ ์, ํ์ ์2_์ ์), ...] โ ์์ดํ
๋น 1ํ
# ์์ ์ผ์น
matrix = [(1, 1), (0, 0), (1, 1), (0, 0), (1, 1)]
f = mm.inter_rater_agreement(matrix)
# โ
[โฏ inter-rater] Cohen's ฮบ=1.000 โฅ 0.40 โ acceptable inter-rater agreement.
# ๋ณดํต ์ผ์น (ฮบ โ 0.33 < 0.40)
matrix = [(0, 0), (0, 1), (0, 0), (1, 1), (1, 0), (1, 1)]
f = mm.inter_rater_agreement(matrix, min_kappa=0.40)
# โ ๏ธ [โฏ inter-rater] Cohen's ฮบ=0.333 < 0.40 โ fair agreement only.
# ๋ถ๋ ์ผ์น โ FAIL
matrix = [(0, 1), (1, 0), (0, 1), (1, 0), (0, 1)]
f = mm.inter_rater_agreement(matrix)
# ๐ด [โฏ inter-rater] Cohen's ฮบ=-1.000 < 0.20 โ poor agreement.ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
ratings_matrix |
ํ์ | [(r1, r2), ...] โ โฅ 3 ์์ดํ
ํ์ |
min_kappa |
0.40 | ํ์ฉ ์ต์ ฮบ |
๋ ๋ฒจ:
FAILโ ฮบ < 0.20 ๋๋ ์์ดํ < 3WARNโ 0.20 โค ฮบ < min_kappaOKโ ฮบ โฅ min_kappa
์ก์๋ด๋ ๊ฒ: ๊ฑฐ์ ๋ชจ๋ ๊ฒ์ ๋์ผํ ์ ์๋ฅผ ๋ถ์ฌํ๋ ํดํ ํ์ ์.
ํ๋ณ๋ ฅ์ด ์ฌ์ค์ ์๋ ํ์ ์๋ ์ ํธ๋ฅผ ์ ๊ณตํ์ง ์์ต๋๋ค. ๊ทธ ์ ์๋ก ๋ง๋ ์์๋ ๋ฌด์์ ์์์ ๋๋ฑํฉ๋๋ค.
# scores: [8, 7, 8, 9, ...] โ ํ ํ์ ์์ ๋ชจ๋ ์ ์
# ๊ฑด๊ฐํ ๋ถํฌ
scores = [3, 7, 5, 8, 4, 6, 9, 2, 7, 5, 3, 8, 6, 4, 7]
f = mm.judge_score_sanity(scores)
# โ
[โฐ judge-score-sanity] 8 distinct values across 15 scores (53.3% unique).
# ํดํ: ์ ์ ๋์
scores = [8] * 20
f = mm.judge_score_sanity(scores)
# ๐ด [โฐ judge-score-sanity] All 20 scores identical (8).
# Judge is not discriminating โ scores are meaningless.
# ๊ทผ์ฌ ํดํ: 95%๊ฐ 8
scores = [8] * 19 + [7]
f = mm.judge_score_sanity(scores)
# โ ๏ธ [โฐ judge-score-sanity] 95% of scores are '8' โ near-degenerate distribution.ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
scores |
ํ์ | ํ ํ์ ์ ๋ฐ์ ๋ชจ๋ ์ ์ ๋ชฉ๋ก |
min_unique_ratio |
0.10 | ์ด ์ ์ ๋๋น ๊ณ ์ ๊ฐ ๋น์จ ์ต์๊ฐ |
๋ ๋ฒจ:
FAILโ ์ ์ ๋์ WARNโ ์ต๋ค ๊ฐ ์ง์ค > 90% ๋๋ ๊ณ ์ ๋น์จ < min_unique_ratioOKโ ๋ถํฌ ์ํธ
์ก์๋ด๋ ๊ฒ: ํ์ ์ด ์ฌ๋กฏ์ ๋ฐ๋ฅด๊ณ ๋ด์ฉ์ ๋ฐ๋ฅด์ง ์๋ ํ์ ์ โ ๊ฐ์ฅ ์ด๋ ค์ด ์ผ์ด์ค์ธ "๊ฒฐ์ ๋ก ์ ยท๊ท ํ์ ยท๋ด์ฉ ๋ฌด์" ํ์ ์(๋ค๋ฅธ ๋ชจ๋ ํ๋ก๋ธ ํต๊ณผ) ํฌํจ.
๊ฐ ์์ ๋ ๋ฒ ํ์ ํฉ๋๋ค: (A, B) ์์๋ก ํ ๋ฒ, ์์น๋ฅผ ๊ตํํ (B, A)๋ก ํ ๋ฒ. ๋ด์ฉ์ ์ฝ๋ ํ์ ์๋ ํ์ ์ ๋ค์ง์ด์ผ ํฉ๋๋ค โ ๊ฐ์ ์๋ต์ด ์ด๋ ์ฌ๋กฏ์์๋ ์ด๊ฒจ์ผ ํ๋๊น. ํ์ ์ด ์ฌ๋กฏ์ ๋จธ๋ฌด๋ฅด๋ฉด ๋ด์ฉ์ด ์๋ ์์น๋ฅผ ์ฝ๊ณ ์๋ ๊ฒ์ ๋๋ค.
lock = forward[i] == swapped[i] (๊ฐ์ ์ฌ๋กฏ์ด ๋ ๋ฒ ๋ค ์น๋ฆฌ)
lock_rate โ 0.0 โ ๋ด์ฉ ์ฃผ๋ (ํ์ ์ด ์๋ต์ ๋ฐ๋ผ๊ฐ) โ OK
lock_rate โ 0.5 โ ๋
ธ์ด์ฆ (์ด๋ ์ชฝ๋ ์ ๋ฐ๋ผ๊ฐ) โ WARN
lock_rate โ 1.0 โ ์์น ๊ณ ์ฐฉ (ํ์ ์ด ์ฌ๋กฏ์ ๋ฐ๋ผ๊ฐ) โ FAIL
์น๋ฅ ์ง๊ณ(โฎ)๋ก ๋ถ์กฑํ ์ด์ โ ์๋ต์ ์ ํ ์ ์ฝ๋ ๊ฒฐ์ ๋ก ์ ํ์ ์(์: ํ๋กฌํํธ๋ง ๋ณด๊ณ ๊ฒฐ์ )๋ ์๋ฒฝํ ์ผ๊ด๋๊ณ (โญ OK), ์น๋ฅ ๋ ๊ท ํ์ด๊ณ (โฎ OK), ์๊ธฐ ์์ ๊ณผ ์ผ์นํ๊ณ (โฏ ฮบ=1.0), ์ ์๋ ๋ค์ํฉ๋๋ค(โฐ OK). ์ค์ง ์ค์๋ง์ด ์ ์ฒด๋ฅผ ๋๋ฌ๋ ๋๋ค:
# ๋ด์ฉ ์ฃผ๋ ํ์ ์: ์ค์ํ๋ฉด ๋ชจ๋ ํ์ ์ด ๋ค์งํ
forward = [0, 1, 0, 1, 0, 1]
swapped = [1, 0, 1, 0, 1, 0]
f = mm.judge_swap_check(forward, swapped)
# โ
[โฑ judge-swap] Position-lock rate 0.0% โค 35.0%. Content-driven.
# ๋ด์ฉ ๋ฌด์ ํ์ ์: ์๋ฐฉํฅ ํ์ ์ด ๋์ผ
forward = [0, 1, 0, 1, 0, 1]
swapped = [0, 1, 0, 1, 0, 1]
f = mm.judge_swap_check(forward, swapped)
# ๐ด [โฑ judge-swap] Position-lock rate 100.0% > 65.0%.
# ํ์ ์๊ฐ ๋ด์ฉ์ด ์๋ ์์น๋ฅผ ์ฝ๊ณ ์์.python examples/demo_judge.py๋ฅผ ์คํํ๋ฉด mock ๋ด์ฉ-๋ฌด์ ํ์ ์๊ฐ โญโฎโฏโฐ์
์ ๋ถ ํต๊ณผํ๊ณ โฑ์๋ง ์ ๋ฐ๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค โ API ํค ๋ถํ์.
ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
forward_results |
ํ์ | [0, 1, ...] โ ์๋ (A, B) ์์ ์น์ |
swapped_results |
ํ์ | [0, 1, ...] โ ๊ตํ๋ (B, A) ์์ ์น์ |
position_lock_threshold |
0.65 | lock_rate ์ด๊ณผ ์ FAIL |
noise_threshold |
0.35 | lock_rate ์ด๊ณผ ์ WARN |
{0, 1} ๋ฐ์ ๊ฐ(-1 ํ์ฑ ์คํจ ๋ฑ)์ด ํฌํจ๋ ์์ ์ ์ธ๋ฉ๋๋ค.
๋ ๋ฒจ:
FAILโ lock_rate > position_lock_threshold ๋๋ ๊ธธ์ด ๋ถ์ผ์นWARNโ ๋ ธ์ด์ฆ ๋ฐด๋ ๋ด lock_rate ๋๋ ์ ํจ ์ ์์OKโ lock_rate โค noise_threshold
judge_run์ ์ ์ ์์ง์ ๋ฒ๊ฑฐ๋ก์์ ์์ ์ค๋๋ค. judge ํจ์๋ฅผ ํธ์ถํ๊ณ ,
4๊ฐ ํ๋ก๋ธ๋ฅผ ์๋์ผ๋ก ๋ฐํํ ๋ค, ์ฒด์ธ ์ฐ๊ฒฐ๋ _type: judge_run ์ํธ๋ฆฌ๋ฅผ
์์ฅ์ ๋ด์ธํฉ๋๋ค.
from measure_mirror.judge import anthropic_judge, openai_judge, judge_run
# 1๋จ๊ณ: judge callable ์์ฑ
judge_fn = anthropic_judge(
model="claude-opus-4-8",
system_prompt="๋น์ ์ ์๊ฒฉํ๊ณ ๊ณต์ ํ ํ๊ฐ์์
๋๋ค.",
pairwise=True, # True = A/B ๋น๊ต; False = 1-10 ์ ์
)
# 2๋จ๊ณ: ์์ดํ
์ค๋น
pairs = [
{"prompt": "๊ฒฝ์ฌํ๊ฐ๋ฒ์ ์ค๋ช
ํ์ธ์",
"a": "๋ชจ๋ธ A์ ์๋ต", "b": "๋ชจ๋ธ B์ ์๋ต"},
...
]
# 3๋จ๊ณ: ์คํ + ์๋ ํ๋ก๋ธ
result = judge_run(
"mm_ledger.jsonl", # ์์ฅ ๊ฒฝ๋ก โ ์ํธ๋ฆฌ๊ฐ ์ฒด์ธ ์ฐ๊ฒฐยท๋ด์ธ๋จ
"my_llm_eval_v1", # claim_id โ ์ฌ์ ๋ฑ๋ก ์ํธ๋ฆฌ์ ์ฐ๊ฒฐ
judge_fn=judge_fn,
items=pairs,
runs=2, # ์์ดํ
๋น 2ํ ํธ์ถ โ โญ + โฏ ํ์ฑํ
pairwise=True, # โฎ ํธํฅ ๊ฒ์ฌ ํ์ฑํ
swap_positions=True, # ABโBA ์ถ๊ฐ ํจ์ค โ โฑ ์ค์ ๊ฒ์ฌ ํ์ฑํ
)
for f in result["findings"]:
print(f" {f.level} [{f.probe}] {f.msg}")
print(result["scores"]) # ๋ฐ-1 ์ ์ (์์ดํ
๋น 1๊ฐ)
print(result["score_pairs"]) # (๋ฐ1, ๋ฐ2) ์ โ runs=1์ด๋ฉด None
print(result["ledger_entry"]) # ๋ด์ธ๋ ์์ฅ ์ํธ๋ฆฌ๋ฐํ๊ฐ ํค:
| ํค | ํ์ | ๋ด์ฉ |
|---|---|---|
findings |
list[Finding] |
ํ๋ก๋ธ ๊ฒฐ๊ณผ (โญโฎโฏโฐโฑ + judge-parse) |
scores |
list[int] |
๋ฐ-1 ์์ ์ ์ (์์ดํ ๋น 1๊ฐ, -1 ํฌํจ ๊ฐ๋ฅ) |
score_pairs |
list[tuple] or None |
(๋ฐ1, ๋ฐ2) ์; runs=1์ด๋ฉด None |
swap_scores |
list[int] or None |
๊ตํ ์์ ์ ์; swap_positions ์๋๋ฉด None |
parse_failures |
int |
ํ์ฑ ๋ถ๊ฐ๋ก ์ ์ธ๋ ์์ดํ ์ |
n_items |
int |
ํ๊ฐ๋ ์์ดํ ์ |
runs |
int |
๋ฐ๋ณต ํ์ |
pairwise |
bool |
pairwise ๋ชจ๋ ์ฌ๋ถ |
ledger_entry |
dict |
์์ฅ์ ์ถ๊ฐ๋ ์ฒด์ธ ์ฐ๊ฒฐ ์ํธ๋ฆฌ |
์กฐ๊ฑด๋ณ ํ์ฑ ํ๋ก๋ธ:
| ํ๋ก๋ธ | ์กฐ๊ฑด |
|---|---|
โญ judge_consistency_check |
runs โฅ 2์ผ ๋ ํญ์ |
โฎ judge_bias_check |
pairwise=True์ผ ๋ ํญ์ |
โฏ inter_rater_agreement |
์๋๋ฐํ ์ ํจ โ ๋จ๋ ์ ์ฉ, ์๋ก ๋ค๋ฅธ ๋ ํ์ ์์ฉ (๊ฐ์ ํ์ ์ ์ฌ์คํ์ โญ์ ๋ชซ) |
โฐ judge_score_sanity |
ํญ์ |
โฑ judge_swap_check |
swap_positions=True์ผ ๋ (pairwise ์ ์ฉ) |
judge-parse |
ํ์ฑ ์คํจ์จ >10% ์ WARN; ์ ๋ถ ์คํจ ์ FAIL |
ํ์ฑ ์คํจ ์ฒ๋ฆฌ โ ํ์ฑ ๋ถ๊ฐ ์๋ต์ -1๋ก ๊ธฐ๋ก๋ฉ๋๋ค. ์ด๋ ๋ฐ์์๋ -1์ด ๋์จ
์์ดํ
์ ๋ชจ๋ ํ๋ก๋ธ์์ ์ ์ธ๋์ด, ํ์ฑ ๋
ธ์ด์ฆ๊ฐ โฎ ํธํฅ์ด๋ โฐ ๊ฑด์ ์ฑ ๊ฒฐ๊ณผ๋ฅผ
์๊ณกํ ์ ์์ต๋๋ค. ์ ์ธ ์๋ ์์ฅ ์ํธ๋ฆฌ(parse_failures)์ ๊ธฐ๋ก๋ฉ๋๋ค.
ํ์ ์ ํ๋ก๋ธ(๊ทธ๋ฃน 6)๋ ๊ฐ๋ณ ํ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์ด ๋ ํ๋ก๋ธ๋ ๊ทธ ํ์ ์ผ๋ก ๋ง๋ ์์ ์์ฒด๋ฅผ ๊ฐ์ฌํฉ๋๋ค โ ๋๋ถ๋ถ์ ๋ฐํ ์ฃผ์ฅ์ด ์ค์ ๋ก ์ฌ๋ ๋ฆฌ๋๋ณด๋ ๋ ์ด์ด์ ๋๋ค.
์ก์๋ด๋ ๊ฒ: pairwise ํ ๋๋จผํธ์ ์ํ ์ ํธ(A>B>C>A) โ ์ผ๊ด๋ ํ์ง ์ฒ๋๊ฐ ์๋ ํ์ ์.
ํ์ ์๊ฐ ์ ์ด์์ ๋ชจ๋ธ์ ์๋ณ ๋น๊ต๋ก ์์ ๋งค๊ธธ ๋, ์ง๊ณ๋ ์ ํธ๋ ์ดํ์ ์์๋ฅผ ์ด๋ค์ผ ํฉ๋๋ค. ์ํ์ด ์์ผ๋ฉด ๊ทธ ํ์ ์ผ๋ก ๋ง๋ ๋ฆฌ๋๋ณด๋๋ ๋์ง ์์์ ์ฐ๋ฌผ์ ๋๋ค: ๋ธ๋ํท์ ๋ค๋ฅธ ์์๋ก ๋๋ฆฌ๋ฉด ๋ค๋ฅธ ์ฑํผ์ธ์ด ๋์ต๋๋ค.
# matches: [(๋ชจ๋ธa, ๋ชจ๋ธb, ์น์), ...] โ ์น์ 0 = ์ฒซ์งธ, 1 = ๋์งธ
# ๊ฐ์ ์์ ๋ฐ๋ณต ๋์ง์ ๋ค์๊ฒฐ๋ก ์ง๊ณ๋ฉ๋๋ค.
matches = [("gpt", "claude", 0), # gpt > claude
("claude", "llama", 0), # claude > llama
("gpt", "llama", 0)] # gpt > llama โ ์ดํ์ โ
f = mm.judge_transitivity_check(matches)
# โ
[โฒ judge-transitivity] 3๊ฐ ๋ชจ๋ธ ์ ํธ ๊ทธ๋ํ๊ฐ ๋น์ํ โ ์ผ๊ด๋ ์์ ์กด์ฌ.
matches = [("gpt", "claude", 0),
("claude", "llama", 0),
("llama", "gpt", 0)] # llama > gpt โ ์ํ!
f = mm.judge_transitivity_check(matches)
# ๐ด [โฒ judge-transitivity] ์ํ ์ ํธ ์ ๋ฐ: gpt > claude > llama > gpt.
# ํ์ ์์๊ฒ ์ผ๊ด๋ ํ์ง ์ฒ๋๊ฐ ์์.์ ํํ ๋๋ฅ ์ธ ์(์๋ฐฉํฅ ๋์ ์น๋ฆฌ)์ ์ฃ์ง๋ฅผ ๋ง๋ค์ง ์์ ๊ฑฐ์ง ์ํ์ ์ผ์ผํฌ ์ ์์ผ๋ฉฐ, OK ๋ฉ์์ง์ ์ ์ธ๋ ๋๋ฅ ์๊ฐ ๋ณด๊ณ ๋ฉ๋๋ค.
๋ ๋ฒจ:
FAILโ ์ํ 1๊ฐ ์ด์ (์์ ๊ฒฝ๋ก ํ์)WARNโ ๋ชจ๋ธ 3๊ฐ ๋ฏธ๋ง ๋๋ ๋์ง ์์OKโ ์ ํธ ๊ทธ๋ํ ๋น์ํ
์ก์๋ด๋ ๊ฒ: ์์ ์ ๊ธฐ๋ฃจ โ ๊ฐ์ ํฌ๊ธฐ ํ๋ณธ์ ๋ค์ ๋ฝ์ผ๋ฉด ๋ค์งํ๋ "๋ชจ๋ธ A๊ฐ B๋ฅผ ์ด๊น" ์ฃผ์ฅ.
๋ถํธ์คํธ๋ฉ ๋ฆฌ์ํ๋ง: ์์ดํ
์ธ๋ฑ์ค๋ฅผ ๋ณต์์ถ์ถ๋ก n_bootํ ๋ค์ ๋ฝ์ ๊ด์ธก๋
์น์๊ฐ ์ผ๋ง๋ ์์ฃผ ์น์๋ก ์ ์ง๋๋์ง ์ธก์ ํฉ๋๋ค. ๊ฒฐ์ ๋ก ์ (์๋ ๊ณ ์ RNG) โ
๊ฐ์ ์
๋ ฅ์ ํญ์ ๊ฐ์ Finding์ ๋ด์ด ๊ฑฐ์ธ์ ์ฌํ์ฑ ๊ท์จ์ ์งํต๋๋ค.
# ๊ฐ์ ์์ดํ
์ ๋ํ ๋ ๋ชจ๋ธ์ ์์ดํ
๋ณ ์ ์ (์ธ๋ฑ์ค๋ก ์ง์ง์)
scores_a = [9, 8, 9, 9, 8, 9, 8, 9] # ์ผ๊ด๋๊ฒ ๋์
scores_b = [3, 2, 3, 2, 3, 2, 3, 2] # ์ผ๊ด๋๊ฒ ๋ฎ์
f = mm.ranking_stability_check(scores_a, scores_b)
# โ
[โณ ranking-stability] ์์ 'A > B'๊ฐ 1000ํ ๋ถํธ์คํธ๋ฉ ์ค 100.0% ์ ์ง (n=8).
scores_a = [5, 9, 1, 8, 2, 7, 3] # ๊ณ ๋ถ์ฐ,
scores_b = [6, 1, 9, 2, 8, 3, 7] # ํฉ๊ณ ๊ฑฐ์ ๋๋ฅ
f = mm.ranking_stability_check(scores_a, scores_b)
# ๐ด [โณ ranking-stability] ์์๊ฐ 1000ํ ์ค 52.4%๋ง ์ ์ง (n=7). ์์๋ ๋
ธ์ด์ฆ.ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
scores_a, scores_b |
ํ์ | ์ง์ง์ด์ง ์์ดํ ๋ณ ์ ์; ๋์ผ ๊ธธ์ด, โฅ 5 ์์ดํ |
n_boot |
1000 | ๋ถํธ์คํธ๋ฉ ํ์ |
seed |
0 | RNG ์๋ (๊ฒฐ์ ๋ก ) |
min_stability |
0.95 | ์๊ตฌ๋๋ ์น์ ์ ์ง ๋น์จ |
๋ ๋ฒจ:
FAILโ ๊ธธ์ด ๋ถ์ผ์น ยท ํฉ๊ณ ๋๋ฅ ยท ์์ ์ฑ < 0.80WARNโ ์์ดํ 5๊ฐ ๋ฏธ๋ง ยท 0.80 โค ์์ ์ฑ < min_stabilityOKโ ์์ ์ฑ โฅ min_stability
์๊ฐ ํ ์คํธ: 5๊ฐ์ง ํฉ์ฑ ์๋ ค์ง-์ข์/๋์จ ์ผ์ด์ค๋ฅผ ์คํํ๊ณ ์์ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ฆ. ์ค์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฌํ๊ธฐ ์ ์ ๊ฑฐ์ธ ์์ฒด์ ํ๊ท๊ฐ ์์์ ํ์ธํฉ๋๋ค.
findings = mm.calibrate()
mm.report("๊ฑฐ์ธ ๊ฑด๊ฐ ์ํ", findings)
# โ
[โ calibrate] 5/5 synthetic cases correct โ mirror is calibrated.mm calibratewitness() ์ ๋๋ CI์์ ๋๊ตฌ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋์ง ํ์ธํ๊ธฐ ์ํด ์คํ.
์ปค๋งจ๋๋ฅผ ์คํํ๊ณ , ์ถ๋ ฅ์ ์บก์ฒํ๋ฉฐ, ๋ณ์กฐ ๋ฐฉ์ง ์คํ ๊ธฐ๋ก์ ๋ด์ธํฉ๋๋ค. ์ด๋ค ์ปค๋งจ๋๊ฐ, ์ธ์ , ์ ํํ ๋ฌด์์ ์์ฑํ๋์ง๋ฅผ ์ฆ๋ช ํฉ๋๋ค.
entry = mm.witness("ledger.jsonl", "my_model",
["python", "evaluate.py", "--model", "my_model"])
# stdout/stderr/returncode๊ฐ ๋ฐ๋๋ฉด entry["output_hash"]๊ฐ ๋ฌ๋ผ์ง
# ์ํธ๋ฆฌ๋ ์ฒด์ธ ์ฐ๊ฒฐ๋จ โ ์ญ์ ์ verify_chain()์ด ๊ฐ์ง# CLI: ๋จผ์ ๋ณด์ ํ ์คํ ๋ฐ ๋ด์ธ (--no-calibrate๋ก ๋ณด์ ๊ฑด๋๋)
mm run my_model -- python evaluate.py --model my_modelํ์ฉ: ๊ฒฐ๊ณผ ๊ฒ์ฌ ์ ์ ํ๊ฐ ์คํฌ๋ฆฝํธ์ ์ ํํ ์ถ๋ ฅ์ ๋ด์ธ. ๋๊ตฐ๊ฐ ์์น์ ์๋ฌธ์ ์ ๊ธฐํ๋ฉด, ์ฆ์ธ ๊ธฐ๋ก์ด ์คํฌ๋ฆฝํธ๊ฐ ๋ฌด์์ ์์ฑํ๋์ง ์ฆ๋ช ํฉ๋๋ค.
์ฒด์ธ ์ฐ๊ฒฐ ์ฒ ํ ์ํธ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์์ โซ cascade_check๋ฅผ ์ฐธ์กฐํ์ธ์.
์ฃผ์ฅ์ ์ ์ฒด ๋ฌด๊ฒฐ์ฑ ์ํ๋ฅผ ๋ ผ๋ฌธยทREADMEยท๋ฆด๋ฆฌ์ค ๋ ธํธ์ ์ฝ์ ๊ฐ๋ฅํ ํ๋์ ๊ฒ์ฆ ๊ฐ๋ฅ ์ฐ์ถ๋ฌผ๋ก ์์ถํด ๋ด์ธ๋ ์ธ์ฆ์๋ฅผ ๋ฐํํฉ๋๋ค.
# ๊ตฌ์กฐ ์ธ์ฆ์ (์ฌ์ ๋ฑ๋ก ๋ด์ธ + ์ฒด์ธ + ์ฒ ํ ์ํ)
cert = mm.certificate("ledger.jsonl", "my_model")
# ์์ ์ธ์ฆ์ โ ๊ฐ์ฌ ๊ฒฐ๊ณผ๊น์ง ํฌํจ
findings = mm.audit("ledger.jsonl", "my_model",
reported_metric="acc", reported_acc=0.72, n=500)
cert = mm.certificate("ledger.jsonl", "my_model", findings=findings)mm certify my_model --pretty # ๊ตฌ์กฐ๋ง
mm certify my_model --acc 0.72 --n 500 # + ๊ฐ์ฌ ๊ฒฐ๊ณผ ํฌํจ
mm certify my_model | gh gist create - # ์ธ๋ถ ๊ณต๊ฐ| ํ์ | ๋ฐ๋ ์กฐ๊ฑด |
|---|---|
REJECTED |
์ฒด์ธ ํ์ ยท ๋ด์ธ ๋ณ์กฐ ยท ์ฒ ํ๋จ ยท FAIL ์กด์ฌ |
UNVERIFIED |
์ฌ์ ๋ฑ๋ก ์์ |
CERTIFIED-WITH-WARNINGS |
์ค๋๋ ์์กด์ฑ ๋๋ WARN ์กด์ฌ |
CERTIFIED |
๋ชจ๋ ๊ฒ์ฌ ํต๊ณผ |
ํต์ฌ ์์ฑ:
- ์์ฅ์
anchor_hash๋ฅผ ํฌํจ โ ์ธ์ฆ์๋ ํน์ ์์ฅ ์ํ ํ๋๋ฅผ ๋ณด์ฆ. ์์ฅ ๋ณ๊ฒฝ ํ์๋ ์ฌ๋ฐํ ํ์. - ์ธ์ฆ์ ์์ฒด๊ฐ ๋ด์ธ๋จ(SHA-256) โ ํ๋ ์์ ์ ์ฆ์ ํ์ง.
- ์์ฅ์ ์ถ๊ฐ๋์ง ์์;
anchor()์ฒ๋ผ ์ถ๋ ฅ ์ฐ์ถ๋ฌผ.
์ธ์ฆ์๋ฅผ ์๋ฒ ๋ ๊ฐ๋ฅํ ๋ฐฐ์ง๋ก ๋ ๋๋งํฉ๋๋ค โ verdict์ ์๊ฐํ.
cert = mm.certificate("ledger.jsonl", "my_model")
mm.badge(cert) # markdown โ README์ฉ shields.io ์ด๋ฏธ์ง
mm.badge(cert, fmt="svg") # ์์ฒด์๊ฒฐ SVG, ์คํ๋ผ์ธ ์๋mm certify my_model --badge markdown >> README.md
mm certify my_model --badge svg > badge.svg| ํ์ | ์์ |
|---|---|
CERTIFIED |
brightgreen |
CERTIFIED-WITH-WARNINGS |
yellow |
UNVERIFIED |
lightgrey |
REJECTED |
red |
SVG ๋ฒ์ ์ ์ธ์ฆ์ seal๊ณผ anchor-hash ์ ๋์ฌ๋ฅผ <title> ํดํ์ ๋ด์ฅํฉ๋๋ค โ
๋ชจ๋ ๋ฐฐ์ง๋ ์์ ์ด ๋ ๋๋งํ ๋ด์ธ ์ธ์ฆ์๋ก ์ถ์ ๊ฐ๋ฅํฉ๋๋ค. SVG ํ์์ ์ธ๋ถ
์๋น์ค๊ฐ ํ์ ์์ต๋๋ค.
๋ถ๋ฅ ๊ฒฐ๊ณผ์ ๋ํ ์๋-ํฌ-์๋ ํ๋ฆ.
from measure_mirror import mm
LEDGER = "experiment_ledger.jsonl"
# โโโ 1. ์คํ ์ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
mm.preregister(LEDGER, "bert_sentiment",
metric="acc",
min_n=500,
baseline=0.5,
pass_threshold=0.70,
kill_condition="held-out์์ ์ ํ๋ 0.65 ๋ฏธ๋ง",
kill_threshold={"metric": "acc",
"threshold": 0.65,
"direction": "below"},
depends_on=["sst2_dataset_v3"]) # ๋ฐ์ดํฐ์
์์กด์ฑ
# โโโ 2. ์คํ ๋ฐ ์ฆ์ธ ๊ธฐ๋ก (์ ํ์ฌํญ์ด์ง๋ง ๊ถ์ฅ) โโโโโโโโโโโโโโโ
mm.witness(LEDGER, "bert_sentiment",
["python", "train_and_eval.py", "--dataset", "sst2"])
# โโโ 3. ๊ฒฐ๊ณผ๊ฐ ๋์จ ํ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
findings = mm.full_audit(
LEDGER, "bert_sentiment",
reported_metric="acc", reported_acc=0.78, n=872,
baseline=0.5,
competing_name="LogReg ๊ธฐ์ค์ ", competing_acc=0.73, # โก
reward_terms=["cross_entropy"], # โข
train_items=train_ids, test_items=test_ids, # โฃa ๋์
seed_results=[0.77, 0.78, 0.79], # โค
claimed_scope=["sentiment"], # โฅ
tested_scope=["sst2", "yelp_polarity"],
min_detectable_effect=0.03, # โง
check_multiplicity=True, # โจ
)
mm.report("BERT ๊ฐ์ฑ๋ถ์", findings)
# โโโ 4. ์ ์ถ ์ ์ต์ปค โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import subprocess
subprocess.run(["mm", "anchor", "--pretty"], check=True)
# โ gist, s3, dropbox์ ํ์ดํํ์ฌ ํ์์คํฌํ๋ ์ธ๋ถ ์ฆ๋ช
์์ฑ# conftest.py
pytest_plugins = ["measure_mirror.pytest_plugin"]
# test_eval_integrity.py
from measure_mirror import mm
from measure_mirror.pytest_plugin import assert_clean
LEDGER = "production_ledger.jsonl"
def test_model_integrity():
findings = mm.audit(LEDGER, "prod_model_v3",
reported_metric="acc", reported_acc=0.78, n=1000)
assert_clean(findings) # FAIL findings โ pytest ์คํจ โ CI ๋นจ๊ฐ๋ถ
def test_ledger_chain():
findings = mm.verify_chain(LEDGER)
assert_clean(findings)
def test_mirror_health():
findings = mm.calibrate()
assert_clean(findings)LEDGER = "oee_research_ledger.jsonl"
# ๊ฐ ๋
๋ฆฝ ๊ฐ๋๋ฅผ ์คํ ์ ๋ฑ๋ก
angles = [
("oee_angle_wave", "wave ODE โ ์ฐ์ ์ฅ"),
("oee_angle_bilinear", "bilinear ๊ณต์งํ"),
("oee_angle_alife", "๋์งํธ ALife (DISHTINY ์คํ์ผ)"),
("oee_angle_folding", "HP ๋จ๋ฐฑ์ง ํด๋ฉ ๊ฒฝ๊ด"),
("oee_angle_gs", "Gray-Scott ๋ฐ์-ํ์ฐ"),
]
for cid, desc in angles:
mm.preregister(LEDGER, cid,
metric="oee_score", min_n=50, baseline=0.5, pass_threshold=0.0,
kill_condition=f"{desc}: OEE๊ฐ ์๊ณ๊ฐ ์ด์")
# ... 5๊ฐ ์คํ ๋ชจ๋ ์คํ, ๋ชจ๋ ์์ฑ์ผ๋ก ์๋ ด ...
# ์์ฑ ์ข
๊ฒฐ ๊ฒ์ดํธ
f = mm.negative_audit(LEDGER,
angles=[cid for cid, _ in angles],
min_angles=3,
conclusion_scope=["in_silico_์์_OEE"],
tested_scope=["digital_field", "alife_sim",
"protein_HP", "reaction_diffusion"])
mm.report("OEE Resolved-Negative", [f])
# ์ต์ข
์์ฑ ๊ฒฐ๋ก ์ต์ปค
import subprocess, json
snap = json.loads(subprocess.check_output(["mm", "anchor", "--ledger", LEDGER]))
print(f"์ต์ปค๋จ: {snap['anchor_hash'][:12]}...")LEDGER = "shared_lab_ledger.jsonl"
# โโโ ๊ธฐ์ค ๋ฐ์ดํฐ์
์์ ์ค์ผ ๋ฐ๊ฒฌ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
mm.retract(LEDGER, "imagenet_baseline_v1",
reason="์ ์ฒ๋ฆฌ ๋จ๊ณ์์ ํ๋ จ/ํ
์คํธ 12% ์ค๋ณต ๋ฐ๊ฒฌ")
# โโโ ์ด์ ์ค๋๋(STALE) ๊ฒ์ฌ ๊ฒฐ๊ณผ ํ์ธ โโโโโโโโโโโโโโโโโโโโโโโ
published_claims = ["vit_paper_2023", "resnet_ablation", "downstream_nlp"]
for claim_id in published_claims:
f = mm.cascade_check(LEDGER, claim_id)
if f.level != "OK":
print(f"โ ๏ธ {claim_id}: {f.msg}")
# ์ถ๋ ฅ:
# โ ๏ธ vit_paper_2023: Claim 'vit_paper_2023' is STALE: depends (transitively)
# on retracted claim(s): 'imagenet_baseline_v1'
# โ ๏ธ downstream_nlp: Claim 'downstream_nlp' is STALE: ...MCP ํธํ AI(Claude Code, Cursor, Windsurf ๋ฑ)๊ฐ ๋ชจ๋ ํ๋ก๋ธ๋ฅผ ๋ํ ์ค์ ์ง์ ํธ์ถํ ์ ์์ต๋๋ค. ์์ด์ ํธ๊ฐ ์ฝ๋๋ฅผ ์์ฑํ์ง ์๊ณ ๋ ์ฃผ์ฅ์ ๊ฐ์ฌํ ์ ์์ต๋๋ค.
// .mcp.json
{
"mcpServers": {
"measure-mirror": {
"command": "python",
"args": ["-m", "measure_mirror.mcp_server"],
"cwd": "/path/to/measure-mirror"
}
}
}์์ด์ ํธ ๋ํ ์์:
์ฌ์ฉ์: "๋ด ๋ชจ๋ธ์ด n=200์์ SQuAD 87.3% ๋์์ด์. ๋ฏฟ์ ์ ์๋์?"
์์ด์ ํธ: [mm_register, mm_audit ํธ์ถ]
โ โ ๏ธ [โฃa small-sample CI] n=200, acc=0.873 โ 95%CI [0.820, 0.915]
๊ธฐ์ค์ (0.5) ์ด๊ณผ. OK.
โ โ ๏ธ [โฆ too-good] ๊ธฐ์ค์ ๋๋น ฮ=+0.373 โ ์์ฌ์ค๋ฝ๊ฒ ํผ.
์กฐ์ฌ: ๋ฐ์ดํฐ ๋์? ๋ณด์ ํดํน?
[mm_grim_check ํธ์ถ: reported_acc=0.873, n=200]
โ ๐ด FAIL โ n=200์์ acc=0.873์ ๋ง์กฑํ๋ ์ ์ k ์์.
round(175/200, 3) = 0.875 โ 0.873.
"87.3%์ n=200์์ GRIM ๋ถ๊ฐ๋ฅํ ๊ฐ์
๋๋ค. n ๋๋ acc ์ค ํ๋๊ฐ ์๋ชป ๋ณด๊ณ ๋์ต๋๋ค."
| # | ํจ์ | ์ก์๋ด๋ ๊ฒ | audit() ์๋ ์คํ? |
|---|---|---|---|
| โ | preregister/audit |
์งํ ๊ต์ฒด, min_n, pass ๊ธฐ์ค, ๋ด์ธ ์๋ณ์กฐ | โ |
| โ | verify_chain |
์ํธ๋ฆฌ ์ญ์ /์ฝ์ /์๋ณ์กฐ | ์๋ |
| โก | baseline_fairness |
ํ์ฝ/๋์ /์ญ์ ๋ ๊ธฐ์ค์ | full_audit |
| โข | gaming_check |
์งํ๊ฐ ๋ณด์/์์ค์ ์ง์ ํฌํจ | full_audit |
| โฃa | Wilson CI (๋ด๋ถ) | ์ํ๋ณธ ์ฐ์ฐ ์์ค ๊ฒฐ๊ณผ | โ |
| โฃa | direction (๋ด๋ถ) | ๊ธฐ์ค์ ๋ณด๋ค ๋์จ (์ญ์ ํธ) | โ |
| โฃa | leakage_check |
ํ๋ จโฉํ ์คํธ ์ค๋ณต | full_audit |
| โค | multiseed_check |
๋ถ์์ ํ ์๋, ๊ธฐ์ค์ ์ด ๋ฒ์ ์ | full_audit |
| โฅ | scope_check |
๊ณผ๋ ์ผ๋ฐํ๋ ์ฃผ์ฅ | full_audit |
| โฆ | too_good_check |
์์ฌ์ค๋ฝ๊ฒ ํฐ ฮ | full_audit |
| โง | power_check |
n์ด ํจ๊ณผ ํ์ง์ ๋ถ์กฑ | full_audit |
| โจ | multiple_comparisons_check |
k>1 ์คํ Bonferroni ๊ฒฝ๋ณด | full_audit |
| โฉ | grim_check |
์ฐ์ ๋ถ๊ฐ๋ฅ ์์น | โ (FAIL๋ง) |
| โช | falsifiability_check |
kill-condition ์์; ๋ฐํ๋ kill threshold | โ (์ฌ์ ๋ฑ๋ก ์ ํจ ์) |
| ใ | prereg_lint |
๊ธฐํ ๋ด์ธ: kill-condition ๋์, ์ ์ธ์ฐ์ฐ ์ดํ ๋ฐ, ๋น๊ตฌ์กฐํ kill, ์ n, ์ฒดํฌ ๋ฏธ์ ์ธ | ๋
๋ฆฝ ์คํ (์ฐ์ฐ ์ ; MCP mm_register์์ ์๋) |
| โซ | cascade_check |
์ฒ ํ๋ ์ฃผ์ฅ ๋๋ ์ค๋๋ ์์กด์ฑ | โ (WARN/FAIL๋ง) |
| โฌ | negative_audit |
์ฑ๊ธํ ์์ฑ ์ข ๊ฒฐ; ๋ฒ์ ์ด๊ณผ | full_audit(angles=...) |
| โญ | judge_consistency_check |
LLM ํ์ ์ ๋ค์ง๊ธฐ์จ ์ด๊ณผ (์ ๋ขฐ ๋ถ๊ฐ ํ์ ์) | ๋จ๋ |
| โฎ | judge_bias_check |
ํ์ ์๊ฐ A ๋๋ B ์์น๋ฅผ ์ฒด๊ณ์ ์ผ๋ก ์ ํธ | ๋จ๋ |
| โฏ | inter_rater_agreement |
Cohen's ฮบ ๋ฏธ๋ฌ (ํ๊ฐ์ ๊ฐ ์ผ์น๋ ๋ถ์กฑ) | ๋จ๋ |
| โฐ | judge_score_sanity |
ํ์ ์๊ฐ ๋์ผ/๊ทผ์ฌ ์ ์๋ง ๋ถ์ฌ (ํดํ ๋ถํฌ) | ๋จ๋ |
| โฑ | judge_swap_check |
ํ์ ์ด ๋ด์ฉ ์๋ ์ฌ๋กฏ์ ๋ฐ๋ฆ (ABโBA ์ค์) | judge_run(swap_positions=True) |
| โฒ | judge_transitivity_check |
ํ ๋๋จผํธ์ A>B>C>A ์ํ ์ ํธ | ๋จ๋
/ mm judge |
| โณ | ranking_stability_check |
๋ถํธ์คํธ๋ฉ ๋ฆฌ์ํ๋ง์์ ๋ค์งํ๋ ์์ | ๋จ๋
/ mm judge |
| โ | anchor |
์์ฅ ํ์ผ ์์ ๊ต์ฒด | ์๋ (๊ฒ์ฌ ์ ) |
| โ | calibrate |
๊ฑฐ์ธ ์์ฒด์ ํ๊ท | ์๋ (witness ์ ) |
| โ | witness |
์คํ ๊ธฐ๋ก: ๋ฌด์์ด ์คํ๋๊ณ , ์ธ์ , ์ถ๋ ฅ ํด์ | ์๋ |
| โ | retract |
์ฒ ํ ๊ธฐ๋ก ์์ฑ (์ฒด์ธ ์ฐ๊ฒฐ) | ์๋ |
| โ | certificate |
์ฃผ์ฅ๋น ๋ด์ธ๋ ํ์ ์ฐ์ถ๋ฌผ (anchor ๊ณ ์ ) | ์๋ (๊ฒ์ฌ ์ ) |
| โ | badge |
์๋ฒ ๋ ๊ฐ๋ฅํ verdict ๋ฐฐ์ง (markdown / SVG) | ์๋ (mm certify --badge) |
์ฝ๋๋ฒ ์ด์ค ์ ์ฒด ์ฌ๊ฐ๋ ์ ์ฑ :
FAILโ ํ๋ ์คํฑ; ๊ฒฐ๊ณผ๊ฐ ๋ฌดํจ์ด๊ฑฐ๋ ์๊ธฐ๋ชจ์WARNโ ์ฃผ์ ํ์; ๊ฒฐ๊ณผ๊ฐ ์ ํจํ ์ ์์ง๋ง ๊ฒํ ํ์OKโ ์ด ํญ๋ชฉ์ ํต๊ณผ; ์ด ์ฐจ์์ ๊นจ๋ํจ
์ธก์ ๊ฑฐ์ธ์ ๊ณ ์ ์กฐ์์ ๋ํด ๋ ๋ํํ์ต๋๋ค. ์ค๊ณ์ ์ง๋๋ค. ์ด ์น์
์ ๊ทธ ํจ๋ฐฐ์
์ ์งํ ์ง๋์
๋๋ค โ OK๋ฅผ ๋ฏฟ๊ธฐ ์ ์ ์ฝ์ผ์ธ์.
๊ทผ๋ณธ ํ๊ณ: ์ธก์ ๊ฑฐ์ธ์ ๋ด์ ์ผ๊ด์ฑ์ ๊ฒ์ฌํ์ง ์์ฒ ์ง์ค์ฑ์ ๊ฒ์ฌํ์ง ์์ต๋๋ค.
๋ณด๊ณ ๋ ์ซ์๋ค์ด ์๋ก/์ฐ์ ๊ณผ ๋ชจ์๋์ง ์๋์ง๋ ๊ฒ์ฆํ์ง๋ง, ๊ทธ ์ซ์๊ฐ ์ง์ง ์คํ์์
๋์๋์ง๋ ๊ฒ์ฆ ๋ชป ํฉ๋๋ค. ๋ชจ๋ ์ซ์๋ฅผ ์ผ๊ด๋๊ฒ ์ง์ด๋ด๋ฉด ์ ์ฒด ๊ฐ์ฌ๊ฐ OK๋ฅผ
๋ฐํํฉ๋๋ค. ์ด๊ฑด ๊ณ ์น ์ ์๋ ๋ฒ๊ทธ๊ฐ ์๋๋๋ค โ ์กฐ์์ด ๋ด์ ์ผ๋ก ์ผ๊ด๋๋ฉด ์ด๋ค
๋๊ตฌ๋ ๋ณด๊ณ ๋ ์ซ์๋ง์ผ๋ก๋ ์ฌ๊ธฐ๋ฅผ ํ์ง ๋ชป ํฉ๋๋ค.
๊ฒ์ฆ๋ ๋ ๋ํ ๊ฒฐ๊ณผ:
| ๊ณต๊ฒฉ | ๊ฒฐ๊ณผ |
|---|---|
GRIM ํต๊ณผํ๋ ๊ฐ์ง ํ๊ท (ํฐ n, ๊ฐ์ k/N์ ์ค๋
) |
ํต๊ณผ โ GRIM์ ์N ๊ฒ์ดํธ(โฉ ๋ฒ์ ์ฐธ์กฐ) |
์์ ์กฐ์ ์ฐ๊ตฌ: ์ฌ์ ๋ฑ๋กยทํฐ nยท๊ณต์ baselineยทkill-conditionยท์์ ์๋ |
full_audit OK ํต๊ณผ โ ๋ชจ๋ ์ซ์๋ฅผ ์ผ๊ด๋๊ฒ ์ง์ด๋ |
๊ฑฐ์ง๋งํ๋ ์คํฌ๋ฆฝํธ๋ฅผ witness() |
ํต๊ณผ โ witness๋ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋ผ ์ด ์ถ๋ ฅ์ ๋๋ค๋ฅผ ๋ด์ธํ์ง, ์ถ๋ ฅ์ด ์ง์ค์์ ๋ด์ธ ๋ชป ํจ |
๋ชจ๋ ๋ฐฉ์ด(์ฌ์ ๋ฑ๋กยทauditยทwitnessยทanchor)๋ ๋ฐ์ดํฐยท์ฝ๋๊ฐ ์ ์งํ๋ค๋ ๊ฐ์ ์์ ์ญ๋๋ค. ์ธก์ ๊ฑฐ์ธ์ *๊ธฐ๋ก(paper trail)*์ ๋จ๋จํ๊ฒ ํ์ง ๊ทผ์ ์ ์ง์ค์ ๋จ๋จํ๊ฒ ํ์ง ์์ต๋๋ค.
๊ทธ๋๋ ์ฌ๋ ๊ฒ โ ์ฌ๊ธฐ๊ฐ ๊ฒธ์ํ๋๋ก ๊ฐ์๋ฉ๋๋ค. ํต๊ณผํ๋ ค๋ฉด ๊ฑฐ์ง๋ง์ด ๊ฒธ์ํด์ผ ํฉ๋๋ค. ์์ฌ์ ๋ถ๋ฆฌ๋ฉด ํ๋ก๋ธ๊ฐ ์ก์ต๋๋ค:
| ์์ฌ๋ธ ๊ฑฐ์ง๋ง | ์ก๋ ํ๋ก๋ธ |
|---|---|
| baseline ๋๋น ์์ฌ์ค๋ฝ๊ฒ ํฐ ฮ | โฆ too_good_check |
| ์์ ํ๋ณธ | โฉ grim_check, โฃa Wilson CI |
| ๋ถ์์ /๋นํ์ค์ ์ผ๋ก ์๋ฒฝํ ์๋ | โค multiseed_check |
| ๊ฒ์ฆ๋ณด๋ค ๋์ ์ฃผ์ฅ | โฅ scope_check |
๊ทธ๋ฌ๋ ์ธก์ ๊ฑฐ์ธ์ ์ง์ง ์ผ์ ์์ ํ ์ฌ๊ธฐ๋ฅผ ์ก๋ ๊ฒ ์๋๋ผ(๋๋ฌผ๊ณ ํ ์ ์๋
๊ฒฝ์ฐ) ์ ์งํ ์๊ธฐ๊ธฐ๋ง์ ์ก๋ ๊ฒ์
๋๋ค โ p-ํดํน, ์ฒด๋ฆฌํฝ, ์n ๊ณผ์ , ์ฑ๊ธํ
์ข
๊ฒฐ. ์ด๊ฒ ์ฐ๊ตฌ ๋ถ์ ์ง์ ํํ ํํ์ด๊ณ , ๋๊ตฌ๊ฐ ๊ทธ๊ฑธ ์ก์ต๋๋ค. OK๋
*"์ซ์๊ฐ ๋ด์ ์ผ๋ก ์ ์งํ๊ณ ๊ณผ๋ํ์ง ์๋ค"*๋ก ์ฝ์ผ์ธ์, ์ ๋ *"์ด๊ฑด ์ง์ค์ด๋ค"*๋ก
์ฝ์ง ๋ง์ธ์.
(์ด ์ง๋๋ ๋๊ตฌ๊ฐ ๊นจ์ง ๋๊น์ง ๋ ๋ํํด์ ๊ทธ๋ ธ์ต๋๋ค โ ์ฐ๋ฆฌ๊ฐ ๋๊ตฌ์ ํ ์ ์๋ ๊ฐ์ฅ ์ธก์ ๊ฑฐ์ธ๋ค์ด ์ผ์ด์์ต๋๋ค.)
์ค์ค๋ก์ ํ๋ก์ ํธ๋ฅผ ์ ์งํ๊ฒ ์ฃฝ์ด๋ ๊ณผ์ ์์ ๋ง๋ค์ด์ก์ต๋๋ค. ์ ์์๋ค์ด ์์ ๋ค์๊ฒ ๋จผ์ ์ ์ฉํ์ต๋๋ค.
โ ๊ฐ๋ฐ ์ฐ๋๊ธฐ