66 lines
4 KiB
Python
66 lines
4 KiB
Python
#!/usr/bin/env python3
|
||
"""Ф0.6 (часть) — калибровка гейта арма G НА ЦЕЛОЙ ЕДИНИЦЕ, без выравнивания. $0.
|
||
|
||
Зачем отдельно от `qe_guard.py`. Посегментный замер требует сопоставить черновиковый сегмент с
|
||
финальным, а редактор меняет разбиение — и любое сопоставление вносит смещение:
|
||
• точное совпадение групп теряет 596 сегментов из 1143, причём СИСТЕМАТИЧЕСКИ там, где редактор
|
||
перестраивал предложения, то есть где он и работал (смещение в сторону консервативных правок);
|
||
• пересечение групп теряет ноль, но при слиянии предложений даёт финальной стороне более широкий
|
||
кусок источника, чем черновой (смещение В ПОЛЬЗУ финала).
|
||
Единица целиком не требует выравнивания вовсе ⇒ у неё нет ни того, ни другого смещения. Это и есть
|
||
арбитр между двумя посегментными числами.
|
||
|
||
Что решается. Арм G принимает правку, только если внешний гейт видит улучшение. Здесь меряется,
|
||
что гейт скажет о РЕАЛЬНОЙ редакторской правке, которая уже сделана и лежит в корпусе пакета-6.
|
||
|
||
⚠ Шкала перевёрнута: Δ = QE(final) − QE(draft); Δ<0 значит «редактор улучшил по мнению гейта».
|
||
|
||
Запуск (офлайн, $0, ~25 мин на CPU):
|
||
~/.venvs/tm-qe/bin/python eval/role_topology/qe_guard_units.py
|
||
"""
|
||
from __future__ import annotations
|
||
import json
|
||
import os
|
||
import statistics
|
||
import sys
|
||
from math import comb
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||
|
||
from qe_bench import QE, MODEL # noqa: E402
|
||
|
||
CORPUS = Path.home() / "books" / "gu-zhenren" / "labels" / "raw" / "corpus.jsonl"
|
||
OUT = Path.home() / "books" / "role-topology"
|
||
|
||
|
||
def two_sided_sign_p(better: int, n: int) -> float:
|
||
k = max(better, n - better)
|
||
return min(1.0, 2 * sum(comb(n, i) for i in range(k, n + 1)) / (2 ** n))
|
||
|
||
|
||
def main() -> None:
|
||
units = [json.loads(l) for l in CORPUS.read_text(encoding="utf-8").splitlines() if l.strip()]
|
||
units = [u for u in units if all((u.get(k) or "").strip()
|
||
for k in ("source", "draft", "final"))]
|
||
qe = QE()
|
||
rows = [dict(draft=qe.score(u["source"], u["draft"]), final=qe.score(u["source"], u["final"]))
|
||
for u in units]
|
||
d = [r["final"] - r["draft"] for r in rows]
|
||
n = len(d)
|
||
better = sum(1 for x in d if x < 0)
|
||
print(f"КАЛИБРОВКА ГЕЙТА АРМА G НА ЦЕЛОЙ ЕДИНИЦЕ — модель {MODEL}")
|
||
print(f"n = {n} единиц, выравнивание не применялось ⇒ потерь и смещения нет по построению\n")
|
||
print(f"медиана Δ (final − draft) {statistics.median(d):+.4f}")
|
||
print(f"среднее Δ {statistics.mean(d):+.4f}")
|
||
print(f"редактор УЛУЧШИЛ по гейту {better}/{n} = {better / n:.0%}")
|
||
print(f"двусторонний знаковый тест p = {two_sided_sign_p(better, n):.4f}")
|
||
print(f"гейт видит паритет (|Δ|<0.25) {sum(1 for x in d if abs(x) < 0.25)}/{n}")
|
||
print(f"|Δ| < 1.0 {sum(1 for x in d if abs(x) < 1.0)}/{n}")
|
||
(OUT / "qe-guard-units-all.json").write_text(json.dumps(rows), encoding="utf-8")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
os.environ.setdefault("PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION", "python")
|
||
main()
|