textmachine/eval/role_topology/qe_guard_units.py

66 lines
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
"""Ф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()