76 lines
4.4 KiB
Python
76 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
||
"""АРМ G С ЗАКАЗАННЫМ РОУТЕРОМ: QE-скорер вместо детерминированного гейта. $0 (модель локальная).
|
||
|
||
⚠ Заведён 07.08 после того, как адверсариальное ревью показало: обоснование `guarded.py`
|
||
(«QE-ранкера в зоне нет, выдумывать его полигон не вправе») ЛОЖНО. Ранкер есть — `qe_bench.QE`
|
||
на `metricx-24-hybrid-large`, венв `~/.venvs/tm-qe`, шесть файлов калибровок на диске с 06.08.
|
||
Я подменил заказанный роутер, не проверив собственную посылку. Здесь заказ исполняется буквой.
|
||
|
||
Промт стр.116: «QE/гейты скорят ВСЕ окна → стиль-пасс ТОЛЬКО флагнутым, редактура принимается,
|
||
только если внешний гейт видит УЛУЧШЕНИЕ (guarded APE, max(draft, edited))».
|
||
Здесь и роутинг, и страж идут по ОДНОЙ величине — предсказанной QE ошибке, — а не по разным,
|
||
как в `guarded.py`, где страж смотрел на механику, а редактор оптимизировал язык.
|
||
|
||
⚠ ШКАЛА ПЕРЕВЁРНУТА: MetricX предсказывает ВЕЛИЧИНУ ОШИБКИ, больше — хуже.
|
||
|
||
Запуск: --score посчитать QE по всем армам на всех единицах (кэшируется на диск), $0
|
||
--build собрать арм G_qe и напечатать цену и качество, $0
|
||
"""
|
||
from __future__ import annotations
|
||
import json
|
||
import statistics as st
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||
import bakeoff as BO # noqa: E402
|
||
|
||
OUT = Path.home() / "books" / "role-topology"
|
||
CACHE = OUT / "qe-arms.json"
|
||
ARMS = ("F3", "A", "A_law", "B", "D", "D_")
|
||
|
||
|
||
def cmd_score() -> None:
|
||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||
from qe_bench import QE # noqa: PLC0415
|
||
cache = json.loads(CACHE.read_text(encoding="utf-8")) if CACHE.exists() else {}
|
||
qe = QE()
|
||
for u in BO.units():
|
||
for arm in ARMS:
|
||
k = f"{arm}|{u['uid']}"
|
||
if k in cache:
|
||
continue
|
||
cache[k] = qe.score(u["source"], BO.text3_of(arm, u))
|
||
CACHE.write_text(json.dumps(cache, ensure_ascii=False), encoding="utf-8")
|
||
print(f" {k} {cache[k]:.3f}", flush=True)
|
||
print(f"готово · {len(cache)} оценок → {CACHE}")
|
||
|
||
|
||
def cmd_build() -> None:
|
||
cache = json.loads(CACHE.read_text(encoding="utf-8"))
|
||
us = BO.units()
|
||
q = lambda a, u: cache[f"{a}|{u['uid']}"] # noqa: E731
|
||
med = st.median([q("F3", u) for u in us])
|
||
print(f"QE черновика: медиана {med:.3f} · разброс "
|
||
f"{min(q('F3', u) for u in us):.2f}…{max(q('F3', u) for u in us):.2f}")
|
||
print(f"\n{'арм':7s}{'QE медиана':>12s}{'лучше черновика':>18s}")
|
||
for a in ARMS:
|
||
v = [q(a, u) for u in us]
|
||
better = sum(1 for u in us if q(a, u) < q("F3", u))
|
||
print(f"{a:7s}{st.median(v):12.3f}{better:>14d}/16")
|
||
# РОУТИНГ: стиль-пасс только окнам, чей QE хуже медианы; СТРАЖ: принять, только если QE упал
|
||
for thr_name, thr in (("медиана", med), ("верхняя треть", sorted(q("F3", u) for u in us)[10])):
|
||
routed = [u for u in us if q("F3", u) > thr]
|
||
acc = [u for u in routed if q("A", u) < q("F3", u)]
|
||
print(f"\nпорог = {thr_name} ({thr:.3f}): флагнуто {len(routed)}/16 · "
|
||
f"страж принял {len(acc)}/{len(routed)}")
|
||
yield_ = {u["uid"]: ("A" if u in acc else "F3") for u in us}
|
||
(OUT / f"arm-Gqe-{thr_name.split()[0]}.json").write_text(
|
||
json.dumps(dict(threshold=thr, routed=len(routed), accepted=len(acc), pick=yield_),
|
||
ensure_ascii=False), encoding="utf-8")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
a = sys.argv[1:] or ["--build"]
|
||
{"--score": cmd_score, "--build": cmd_build}.get(a[0], lambda: print(__doc__))()
|