textmachine/eval/role_topology/qe_route.py

76 lines
4.4 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.

#!/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__))()