127 lines
6.8 KiB
Python
127 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
||
"""МАТЕРИАЛ ПАКА: 16 НОВЫХ единиц той же книги. $0 — глава уже скачана эксп-22.
|
||
|
||
Зачем новые единицы. Контраст `R2 glm-5` против боевого редактора дал −2.12 при шуме одного вызова
|
||
sd≈3.5: это вопрос МОЩНОСТИ, а не судьи. Пред-объявленный порог эксп-22 (2.03) он проходит,
|
||
собственный порог своего прохода (2.47) — нет, и вердикт зависит от того, какой взять. Удвоение
|
||
n с 16 до 32 либо разводит интервал от порога, либо честно говорит «эффект меньше, чем стоит
|
||
различать». Оба ответа годны; зависимость вердикта от выбора порога — нет.
|
||
|
||
Методика отбора та же, что в эксп-21 §0 и эксп-22: абзацные чанки, дедуп по подписи без заголовка
|
||
главы, ключ = хеш источника, отбор из СЕРЕДИНЫ распределения длин. Отличие ровно одно: главы,
|
||
из которых эксп-22 уже брал единицы, исключаются целиком — иначе «новые» единицы были бы соседними
|
||
абзацами тех же глав, и прирост n оказался бы мнимым (соседние куски одной главы делят персонажей,
|
||
сцену и лексику, то есть коррелируют сильнее случайных).
|
||
|
||
Гейт прав эксп-22 (снятые главы верхнего дециля эротических маркеров) продолжает действовать: он
|
||
применяется в `chapters()`, который здесь переиспользуется как есть.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
ZONE = Path(__file__).resolve().parent
|
||
sys.path.insert(0, str(REPO / "eval" / "tenant_panel"))
|
||
|
||
import material as M22 # noqa: E402
|
||
|
||
N_NEW = 16
|
||
|
||
|
||
def used_chapters() -> set[int]:
|
||
"""Главы, из которых эксп-22 уже взял единицы."""
|
||
return {u["chapter"] for u in M22.units_zh()}
|
||
|
||
|
||
def used_uids() -> set[str]:
|
||
return {u["uid"] for u in M22.units_zh()}
|
||
|
||
|
||
def rights_dropped(pool: set[int]) -> set[int]:
|
||
"""Гейт прав по СВОЕМУ пулу: верхний дециль по плотности эротических маркеров.
|
||
|
||
⚠ Число снятых глав НЕ переносится из эксп-22. Там гейт снял две главы — это был верхний
|
||
дециль пула из 18. Пул этого пака другой, и перенос константы «2» пропустил бы в замер главы,
|
||
которые объявленное правило снимает. Правило одно и то же, число считается заново.
|
||
"""
|
||
allch = {c["idx"]: c for c in M22.chapters(all_=True)}
|
||
ranked = sorted(pool, key=lambda i: (-M22.se_density(allch[i]["text"]), i))
|
||
k = max(1, round(len(pool) * 0.10))
|
||
return set(ranked[:k])
|
||
|
||
|
||
def units_new(n: int = N_NEW) -> list[dict]:
|
||
"""N новых единиц из глав, которых эксп-22 не касался и которые прошли гейт прав."""
|
||
allidx = {c["idx"] for c in M22.chapters(all_=True)}
|
||
free = allidx - used_chapters() - M22.se_dropped(M22.chapters(all_=True))
|
||
dropped = rights_dropped(free)
|
||
busy = used_chapters() | dropped | M22.se_dropped(M22.chapters(all_=True))
|
||
globals()["RIGHTS_DROPPED"] = sorted(dropped)
|
||
uniq: dict[str, dict] = {}
|
||
seen: set[str] = set()
|
||
for c in M22.chunks():
|
||
if c["chapter"] in busy:
|
||
continue
|
||
sig = M22._dedup_sig(c["text"]) # noqa: SLF001
|
||
if sig in seen:
|
||
continue
|
||
seen.add(sig)
|
||
uid = M22.uid_of(c["text"])
|
||
uniq.setdefault(uid, dict(source=c["text"], uid=uid, chapter=c["chapter"]))
|
||
keys = sorted(uniq, key=lambda k: (len(uniq[k]["source"]), k))
|
||
if len(keys) < n:
|
||
raise SystemExit(f"свободных единиц {len(keys)}, нужно {n} — материал не набирается")
|
||
lo = (len(keys) - n) // 2
|
||
picked = [uniq[k] for k in keys[lo:lo + n]]
|
||
# одна глава — не более одной единицы: иначе внутри «новых» появится та же корреляция,
|
||
# ради снятия которой исключались главы эксп-22
|
||
out, taken = [], set()
|
||
for u in picked:
|
||
if u["chapter"] in taken:
|
||
continue
|
||
taken.add(u["chapter"])
|
||
out.append(u)
|
||
if len(out) < n: # добираем по тому же порядку, соблюдая правило главы
|
||
for k in keys[lo + n:] + keys[:lo][::-1]:
|
||
u = uniq[k]
|
||
if u["chapter"] in taken:
|
||
continue
|
||
taken.add(u["chapter"])
|
||
out.append(u)
|
||
if len(out) == n:
|
||
break
|
||
return out[:n]
|
||
|
||
|
||
def selfcheck() -> int:
|
||
bad = 0
|
||
us = units_new()
|
||
if len(us) != N_NEW:
|
||
print(f"[ПРОВАЛ] единиц {len(us)}, нужно {N_NEW}")
|
||
bad += 1
|
||
if set(u["uid"] for u in us) & used_uids():
|
||
print("[ПРОВАЛ] единица повторяет эксп-22")
|
||
bad += 1
|
||
if set(u["chapter"] for u in us) & used_chapters():
|
||
print("[ПРОВАЛ] глава повторяет эксп-22")
|
||
bad += 1
|
||
if len({u["chapter"] for u in us}) != len(us):
|
||
print("[ПРОВАЛ] две единицы из одной главы")
|
||
bad += 1
|
||
sim = M22._pairwise_max_sim(us) # noqa: SLF001
|
||
if sim > 0.5:
|
||
print(f"[ПРОВАЛ] единицы слишком похожи: макс. близость {sim:.3f}")
|
||
bad += 1
|
||
ln = sorted(len(u["source"]) for u in us)
|
||
print(f"новых единиц {len(us)} из {len({u['chapter'] for u in us})} глав · "
|
||
f"знаков {ln[0]}…{ln[-1]} медиана {ln[len(ln) // 2]} · макс. близость {sim:.3f}")
|
||
print(f"главы: {sorted(u['chapter'] for u in us)}")
|
||
print(f"снято гейтом прав из своего пула: {globals().get('RIGHTS_DROPPED')}")
|
||
print(f"провалов {bad}")
|
||
return bad
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(1 if selfcheck() else 0)
|