textmachine/eval/promptlang/p4_pairs.py

115 lines
5.9 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 детерминированная батарея верности на ГОТОВЫХ парах база↔митигация P4 (09.07).
Это тот источник, на который промт сессии указывал прямо: `~/books/gu-zhenren/research15-probes/`.
Генерация за них уже оплачена 09.07 — здесь не тратится ни цента.
Судья отвечает на «исказила ли митигация СМЫСЛ». Половина этого вопроса — про ПРОПУСКИ и обрывы —
решается детерминированно и бесплатно: митигация, которая режет текст, видна по длине и по числу
предложений относительно ОДНОГО И ТОГО ЖЕ исходника. Оставшаяся половина (искажение при сохранённом
объёме) без судьи не берётся, и это здесь честно помечено, а не замазано.
"""
from __future__ import annotations
import collections
import json
import re
import statistics
import sys
import unicodedata
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
sys.path.insert(0, str(REPO / "eval"))
from refusal_bench import split_sentences
RAW = Path.home() / "books" / "gu-zhenren" / "research15-probes" / "probes" / "raw"
ARM_NAME = {"G0": "база", "G1": "инстр.ПОСЛЕ", "G2": "few-shot", "G3": "яз.констрейнт", "G4": "комбо",
"D0": "база", "D1": "инстр.ПОСЛЕ", "D2": "few-shot", "DP": "префилл"}
def cjk_share(s: str) -> float:
if not s:
return 1.0
return sum(1 for c in s if "CJK UNIFIED" in (unicodedata.name(c, "") or "")) / len(s)
def load():
out = {}
for f in sorted(RAW.glob("echo-*.json")):
d = json.load(open(f))
t = d.get("text") or ""
# исходник фрагмента лежит в user-сообщении между шапкой и (для арма G1) постинструкцией
u = d.get("user") or ""
src = u.split("Переведи следующий фрагмент:\n\n", 1)[-1]
src = src.split("\n\nНапоминание:")[0]
out[d["tag"]] = dict(tag=d["tag"], text=t, src=src, cjk=cjk_share(t), echo=cjk_share(t) > 0.15)
return out
def metrics(src: str, out: str) -> dict:
src_ns = len(re.sub(r"\s", "", src))
out_ns = len(re.sub(r"\s", "", out))
return dict(chars=len(out), len_ratio=round(out_ns / src_ns, 3) if src_ns else None,
sent_src=len(split_sentences(src)), sent_out=len(split_sentences(out)),
cjk=round(cjk_share(out), 4))
def main() -> int:
d = load()
groups = collections.defaultdict(dict)
for tag, r in d.items():
m = re.match(r"echo-grok-(gu-\d+)-(G\d)$", tag)
if m:
groups[("grok", m.group(1))][m.group(2)] = r
continue
m = re.match(r"echo-ds-gu008-(D\w+)-s(\d)$", tag)
if m:
groups[("deepseek", "gu-008-s" + m.group(2))][m.group(1)] = r
print("=== ПАРЫ база↔митигация из P4 (генерация оплачена 09.07; здесь $0) ===\n")
print(f"{'семейство':<9}{'фрагмент':<14}{'арм':<14}{'len_ratio':>10}{'предл. вых/исх':>16}"
f"{'симв':>7}{'Δlen_ratio к базе':>19}")
rows = []
for (fam, frag), arms in sorted(groups.items()):
base_key = "G0" if fam == "grok" else "D0"
base = arms.get(base_key)
if not base or base["echo"] or not base["text"]:
continue
bm = metrics(base["src"], base["text"])
print(f"{fam:<9}{frag:<14}{ARM_NAME[base_key]:<14}{bm['len_ratio']:>10}"
f"{str(bm['sent_out']) + '/' + str(bm['sent_src']):>16}{bm['chars']:>7}{'':>19}")
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
m = arms.get(a)
if not m or m["echo"] or not m["text"]:
continue
mm = metrics(base["src"], m["text"])
delta = mm["len_ratio"] - bm["len_ratio"]
rows.append(dict(fam=fam, frag=frag, arm=a, d_len=delta,
d_sent=mm["sent_out"] - bm["sent_out"],
base_lr=bm["len_ratio"], mit_lr=mm["len_ratio"]))
print(f"{'':<9}{'':<14}{ARM_NAME[a]:<14}{mm['len_ratio']:>10}"
f"{str(mm['sent_out']) + '/' + str(mm['sent_src']):>16}{mm['chars']:>7}{delta:>+19.3f}")
print()
print(f"ВСЕГО ПАР: {len(rows)}\n")
print("=== СВОДКА ПО МИТИГАЦИИ: режет ли она текст относительно базы на ТОМ ЖЕ исходнике ===")
print(f"{'митигация':<16}{'пар':>5}{'медиана Δlen_ratio':>21}{'пар с Δ<-0.15':>15}"
f"{'пар с Δ>+0.15':>15}{'медиана Δпредл.':>17}")
for a in ("G1", "G2", "G4", "D1", "D2", "DP"):
rs = [r for r in rows if r["arm"] == a]
if not rs:
continue
dl = [r["d_len"] for r in rs]
ds = [r["d_sent"] for r in rs]
print(f"{ARM_NAME[a]:<16}{len(rs):>5}{statistics.median(dl):>+21.3f}"
f"{sum(1 for x in dl if x < -0.15):>15}{sum(1 for x in dl if x > 0.15):>15}"
f"{statistics.median(ds):>+17.1f}")
print("\nЧТЕНИЕ: Δlen_ratio < 0.15 = митигированный перевод заметно КОРОЧЕ базы на том же")
print("исходнике — подпись пропуска. Δ > +0.15 — многословнее (отсебятина или развёрнутость).")
print("⚠ Ось «искажение при сохранённом объёме» этим прибором НЕ берётся — только судьёй.")
return 0
if __name__ == "__main__":
raise SystemExit(main())