Accept D39.51 measurements as D39.52: format example fixes the language slip and joins the build, P1 consistency fails to separate
This commit is contained in:
parent
7692457e9a
commit
21e7099133
4 changed files with 306 additions and 2 deletions
File diff suppressed because one or more lines are too long
|
|
@ -1158,3 +1158,7 @@ API-529-долг закрыт: 8-осевой refute-by-default воркфлоу
|
|||
## D39.51 — Дизайн «дисциплина банкноты + дыра языка ответа» РАТИФИЦИРОВАН, стройка санкционирована (владелец: «идём», 26.07): языковой экран на ВЫХОДЕ + счётчик OffLanguage + фильтр свёртки банкноты + нормализация кавычек в свёртке (Q1) + агрегация из чекпойнтов (A1) + kwic_width в пар-данные (файл zh-ru НЕ шипуем) + doc-хвост. Судья ОТЛОЖЕН; мини-прогон D39.40 — В ХОЛОДНОЙ конфигурации; allow_short — на нём (26.07, оркестратор №8). ✅
|
||||
|
||||
**Отчёт:** `docs/archive/reports/PACK20_BANKNOTE_DESIGN_2026-07-26.md` (ревью-шапка). Принципиальные решения: (1) **гарантия языка — на выходе, не на входе** (предикат offLanguage по `*unicode.RangeTable` через конфиг гейта `target_script`, обязателен при enabled, вне снапшота, $0; направление отказа объявлено: отвергнутый законный латинизм дешевле англо-канона); (2) **кавычки — в свёртке (Q1), не в парсере/NormalizeSourceKey** (Q2/Q3 платят волной за косметику; Q1 $0 и ретроактивен; NormVersion не двигается — тест-страж); (3) **агрегация банкноты — производное из чекпойнтов (A1), без новых таблиц**; инвариант «прогон без новых черновиков даёт байт-идентичную карту» — пином; предложения отвергнутых попыток входят (решение: предложение = улика, не канон); (4) **судья-с-декоем НЕ строится** (≈$0.025/книга — цена не аргумент; его положительное решение = решение о переводе за владельца, против D39.47; популяция неизвестна до холодного старта; сначала P1); (5) **три расхождения сессии с записками оркестратора приняты** (allow_short = базовая версия = перепокупка книги → на холодный прогон; kwic-файл zh-ru не шипуем; судья отложен). **Замеры полигону:** пример формата в промпте (≈$0.009, критерий до прогона: 0 не-целевых строк ×3 против 22/40) · P1 consistency ($0: Σchunks-предложивших / chunks-содержащих; порог засчитывается при ≥80% термов и ≥60% мусора; арм-без-фактора = голая частота обязана провалиться; положительный исход = право на ПОРЯДОК, не на выброс строк) · приёмка предиката на живом сырье 147 вызовов (живо, подтверждено: ровно 27 строк §3.4 и ни одной сверх). **Мини-прогон D39.40 = холодная конфигурация** (пустой сид, банкнота+терминолог ON, --verify-bank, ОТДЕЛЬНАЯ БД) — пять ответов за ≤$0.23: банкнота без цензуры · совместный recall (замер 1 состоится) · язык при пустом якоре (счётчик OffLanguage как прибор) · побочки allow_short · второй сэмпл для агрегации. Приёмка стройки: мутации M-Я1/M-Я2 + регресс 16 живых строк + пин общности (латинская цель переворачивает вердикты) + пин инварианта агрегации + сырьё-приёмка.
|
||||
|
||||
## D39.52 — Замеры D39.51 закрыты ($0.00960; пакет-8 итого $0.12273): (а) строка примера формата ЧИНИТ срыв языка (22/120 → 0/120) — ИДЁТ В ПРОМПТ РОЛИ данными вместе со стройкой пп.1–7; (б) P1 consistency НЕ разделяет (вырожденность 0.740, критерий провален, частота — ноль) — права на порядок подачи нет, пере-замер на холодном прогоне $0 (26.07, оркестратор №8). ✅
|
||||
|
||||
Приёмка воспроизведением из сырья: (а) 0/120 не-целевых строк в арме с примером — независимый пересчёт; фактор один (дифф запросов — только три строки примера; пример-терм 师父 не входит ни в выборку, ни в якорь, ни в сид); мощность 0.104 названа до прогона, удвоение повторов не берём (страховка = экран + OffLanguage живьём). (б) ре-ран скорера: 54/73 вырождено, критерий ≥80/≥60 не выполнен нигде, частотный арм — независимое подтверждение §5.1 на другом наборе и другой разметке (три слепых модельных разметчика, согласие 0.973–0.986; оговорка «разметчики — модели» названа). **Хвост в пак-21: утечка алфавита** — «甲等 → категория A» (7 строк): языковой предикат пропускает (кириллица есть), подписанные dst латиницы не содержат вовсе; кандидаты — банк-линт латиницы в dst или строгая форма предиката (§2.2-Б дизайна). **Вопросы холодного мини-прогона пополнены:** + пере-замер P1 на популяции без цензуры ($0 из чекпойнтов). Стройка бэкенда: пп.1–7 §8 + п.8 «строка примера в промпте роли» — санкционирована, ждёт релея.
|
||||
|
|
|
|||
|
|
@ -784,3 +784,189 @@ Precision 0/10. **Оговорка, обязательная:** я подста
|
|||
ответа без якоря (§3.4) — на en→ru он опаснее всего; и `kwic_width` как пар-данные (§6.3).
|
||||
4. **Чего второй книгой не купить:** цену расширения эмиссии. Она упирается в холодный старт с
|
||||
пустым банком на ЛЮБОЙ паре, и это по-прежнему единственная неизмеренная цифра.
|
||||
---
|
||||
|
||||
## §7 Добор по D39.51: два замера. ПРЕ-РЕГИСТРАЦИЯ (написана ДО трат и ДО счёта)
|
||||
|
||||
Санкция D39.51 (26.07): (а) пример формата в промпте роли — арм «3×40 без якоря + строка примера на
|
||||
целевом письме», 3 повтора, ≈$0.009; (б) P1 consistency — $0 из сохранённых артефактов.
|
||||
|
||||
### §7.1 Замер (а): помогает ли строка примера удержать язык ответа
|
||||
|
||||
**Фактор ровно один, проверено диффом, а не глазом.** Вариант промпта собран копией
|
||||
`backend/prompts/zh-ru/terminologist.md` в скретчпад (репозиторий не тронут); единственное отличие —
|
||||
три строки после спецификации формата:
|
||||
|
||||
```
|
||||
Пример строки ответа:
|
||||
|
||||
师父 наставник
|
||||
```
|
||||
|
||||
Батчи, ключи и user-часть запроса **побайтно те же**, что у контрольного арма `gNOANCHOR`
|
||||
(проверено сравнением построенных запросов). Пример-терм `师父` не входит ни в выборку, ни в якорь,
|
||||
ни в сид и **не встречается в срезе ни разу** — подсказать эталон он не может по построению.
|
||||
|
||||
**Арм без фактора** — существующий `gNOANCHOR` (3×40, без якоря, без примера, 3 повтора, 120 строк
|
||||
ответа, из них 22 не на целевом письме).
|
||||
|
||||
**Предикат «не-целевая строка»** (тот же, которым отвечал бэкендерам): в принятом парсером ответе нет
|
||||
ни одной руны кириллицы. Смешанные строки (кириллица + латиница) считаются отдельно и в критерий не
|
||||
входят — предикат их по построению пропускает.
|
||||
|
||||
**Критерий, названный ДО прогона:** арм с примером проходит, если даёт **0 не-целевых строк** из 120.
|
||||
Любая не-целевая строка = фактор не сработал.
|
||||
|
||||
**Мощность — называю ДО прогона, потому что после будет поздно.** Срыв в контроле случался не в
|
||||
каждом прогоне: 1 прогон из 3, **2 батча из 9**, 22 строки из 120. Если считать батч независимой
|
||||
попыткой, то при НУЛЕВОМ эффекте вероятность получить 0 срывов на 9 батчах равна (7/9)⁹ = **0.104**.
|
||||
То есть даже чистый проход — это «согласуется с починкой» с одним шансом из десяти на совпадение, а
|
||||
не доказательство. Провал же (хоть одна не-целевая строка) — сильная улика против фактора.
|
||||
Усиление до 6 повторов уронило бы ложный проход до ~0.011 и стоило бы ещё ≈$0.009; санкция названа
|
||||
на 3 повтора, поэтому гоню 3 и цифру ложного прохода публикую рядом с результатом.
|
||||
|
||||
### §7.2 Замер (б): P1 consistency как разделитель термов и мусора
|
||||
|
||||
**Метрика.** Для каждой из 74 поверхностей банкноты: Σ единиц, где чтец объявил строку термином,
|
||||
делить на Σ единиц, где строка физически встречается.
|
||||
|
||||
**Отклонение от формулировки санкции, названное заранее.** Санкция говорит «чанков». Тексты боевых
|
||||
чанков прогона не сохранены (в БД лежат хеши и вердикты, не исходник чанка), поэтому единицей взята
|
||||
**ГЛАВА** — атрибуция главы у чекпойнта банкноты есть точная. Цена отклонения измерена, а не
|
||||
оценена: доля поверхностей, встречающихся в ≤1 единице, составляет 0.743 по главам и 0.730 по
|
||||
чанкам моей нарезки — то есть более мелкая единица метрику не спасает, а вырожденность есть
|
||||
свойство десятиглавного среза, не единицы.
|
||||
|
||||
**Разметка.** 74 поверхности размечены на TERM/JUNK **тремя независимыми разметчиками вслепую**:
|
||||
им дана строка исходника, предложенный черновиком перевод и до двух контекстов — и НЕ даны ни
|
||||
частота, ни главы, ни сама метрика (иначе метка коррелировала бы с арм-без-фактора). Метка —
|
||||
большинство из трёх; согласие публикуется; расхождения решаю сам по контекстам и помечаю.
|
||||
Это прямой ответ на слабость, записанную мной же в §2.5 как непокрытую («разметчик один»).
|
||||
|
||||
**Критерий, названный ДО счёта (из санкции):** P1 засчитывается как разделитель, если существует
|
||||
порог, при котором выше него оказывается **≥80% TERM**, а ниже — **≥60% JUNK**.
|
||||
|
||||
**Арм без фактора:** голая частота на том же наборе и том же критерии. Она **обязана провалиться** —
|
||||
иначе P1 не несёт информации сверх частоты, и вся конструкция беспредметна (§5.1 уже показал, что
|
||||
частота термы и не-термы не разделяет; здесь это проверяется на ДРУГОМ наборе и другой разметке).
|
||||
|
||||
**Заранее названный стоп.** Если вырожденных поверхностей (встречаются в ≤1 главе, поэтому P1 = 1.0
|
||||
по построению) окажется больше половины набора, критерий §7.2 объявляется **неприменимым на этих
|
||||
артефактах**: на такой популяции «≥80% TERM выше порога» достигается тривиально и ничего не значит.
|
||||
В этом случае пишется, чего стоит измерить P1 по-настоящему, а цифра не подгоняется.
|
||||
|
||||
### §7.3 Результат (а): пример формата язык УДЕРЖИВАЕТ — критерий выполнен
|
||||
|
||||
Факт **$0.00960** (смета ≤$0.0232, названная до трат), 9 вызовов, 120 принятых строк ответа.
|
||||
|
||||
| Арм | Строк | НЕ на целевом письме | Смешанных (кир.+лат.) |
|
||||
|---|---|---|---|
|
||||
| 3×40 без якоря, **без примера** (контроль) | 120 | **22** | 3 |
|
||||
| 3×40 без якоря, **со строкой примера** | 120 | **0** | 7 |
|
||||
|
||||
**Критерий §7.1 (0 не-целевых строк) ВЫПОЛНЕН.** Три строки промпта — `Пример строки ответа: 师父
|
||||
наставник` — сняли полный срыв в английский там, где его не удержал ни якорь (его нет), ни объявление
|
||||
`{{target_lang}}`, ни весь русскоязычный промпт.
|
||||
|
||||
**Мощность, названная ДО прогона, и она же ограничение вывода.** Срыв в контроле происходил не в
|
||||
каждом прогоне: 2 батча из 9. Значит при НУЛЕВОМ эффекте шанс получить 0 срывов на 9 батчах равен
|
||||
(7/9)⁹ = **0.104**. Читать результат следует как «согласуется с починкой, один шанс из десяти на
|
||||
совпадение», а не как доказательство. Удвоение повторов уронило бы эту цифру до ~0.011 и стоило бы
|
||||
ещё ≈$0.010 — решение не моё, но цифра названа.
|
||||
|
||||
### §7.4 Побочный результат (а): пример НЕ чинит утечку алфавита, и экран её не поймает
|
||||
|
||||
Смешанных строк стало не меньше, а больше (7 против 3), и все они — одно и то же место:
|
||||
|
||||
| Терм | Подпись владельца | Арм с примером | Контроль |
|
||||
|---|---|---|---|
|
||||
| `甲等` | «класс **А**» (кириллица) | «категория **A**» (латиница) | «Ранг **A**» |
|
||||
| `乙等` | «класс **Б**» | «Класс **B**» · «уровень **B**» | «Категория **B**» |
|
||||
| `丙等` | «класс **В**» | «Класс **C**» · «уровень **C**» | «Категория **C**» |
|
||||
|
||||
Сравнение 7 против 3 **причинным считать нельзя**: в контроле те же самые термы в прогоне r2 уехали
|
||||
целиком в английский («Grade C», «Grade B») и попали в колонку «не на целевом письме», а не
|
||||
«смешанных». То есть поведение одно и то же в обоих армах — модель берёт буквенные обозначения из
|
||||
ЛАТИНСКОГО алфавита, — а пример лишь перевёл окружающее слово на русский.
|
||||
|
||||
Значение для стройки: **предикат «в ответе есть руны целевого письма» пропускает все семь.**
|
||||
«категория A» с латинской `A` от подписанного «класс А» с кириллической `А` отличается байтом и не
|
||||
сойдётся с каноном никогда, а языковой экран промолчит. Ни один подписанный dst сида (53 строки)
|
||||
латинских букв не содержит вовсе — то есть эталон здесь однозначен, и правило «в целевой строке
|
||||
латиница допустима только внутри латинского имени собственного» проверяемо. Это не возражение против
|
||||
экрана: это второй дефект, который экран по построению не покрывает, и теперь он измерен дважды.
|
||||
|
||||
### §7.5 Результат (б): P1 consistency НЕ разделяет термы и мусор
|
||||
|
||||
**Разметка — три независимых слепых разметчика**, попарное согласие **0.973 · 0.986 · 0.986**,
|
||||
единогласно 72 из 74; расхождения ровно два (`第三蛊`, `镇族蛊虫`), взято большинство. Метка:
|
||||
**37 TERM · 37 JUNK** — набор сбалансирован сам собой, подгонки не потребовалось.
|
||||
|
||||
**Заранее названный стоп сработал.** Вырожденных поверхностей (встречаются в ≤1 главе среза, поэтому
|
||||
P1 = 1.0 по построению) — **54 из 73 = 0.740**, что больше половины. И вырожденность бесполезна не
|
||||
«в среднем», а адресно: среди этих 54 ровно **27 TERM и 27 JUNK**. Обе метки получают одинаковый
|
||||
максимум, разделять там нечем.
|
||||
|
||||
| Набор | Метрика | Лучший порог | TERM выше | JUNK ниже | Критерий ≥0.80/≥0.60 |
|
||||
|---|---|---|---|---|---|
|
||||
| весь (73) | **P1** | — | 1.000 | 0.000 | **НЕ ВЫПОЛНЕН** |
|
||||
| весь (73) | частота (арм-без-фактора) | >4 | 0.189 | 0.861 | НЕ ВЫПОЛНЕН |
|
||||
| невырожденные (19) | **P1** | >0.5 | 0.200 | 1.000 | **НЕ ВЫПОЛНЕН** |
|
||||
| невырожденные (19) | частота (арм-без-фактора) | >7 | 0.300 | 0.778 | НЕ ВЫПОЛНЕН |
|
||||
|
||||
Порог — не единственный способ прочитать метрику, поэтому считаю и площадь под кривой (вероятность,
|
||||
что случайный TERM получит балл выше случайного JUNK; 0.5 = ноль информации):
|
||||
|
||||
| | Весь набор | Невырожденные (19) |
|
||||
|---|---|---|
|
||||
| **P1 consistency** | **0.438** | 0.583 |
|
||||
| Частота (арм-без-фактора) | **0.500** | 0.433 |
|
||||
|
||||
**P1 на полном наборе ниже случайного угадывания.** Арм-без-фактора отработал ровно как обязан был:
|
||||
голая частота даёт 0.500 — ноль информации, независимое подтверждение §5.1 на ДРУГОМ наборе и ДРУГОЙ
|
||||
разметке. На 19 невырожденных P1 даёт 0.583 — формально выше монетки, фактически 10 против 9
|
||||
объектов, и лучшее, чего метрика достигает, это 2 TERM из 10 выше порога.
|
||||
|
||||
**Вердикт: положительного P1 нет.** Права на порядок подачи метрика не заработала. Внутри среза
|
||||
видно и почему: два верхних места по P1 действительно заняты термами (`灵泉` 1.00, `学堂家老` 0.75),
|
||||
но на самом дне рядом с `无` (0.10, JUNK) и `少年` (0.14, JUNK) стоит `魔道巨擘` (0.00, TERM), а вся
|
||||
середина 0.50 — ровная смесь.
|
||||
|
||||
**Отклонение от формулировки санкции, названное в §7.2 и подтверждённое замером.** Единица — глава,
|
||||
а не чанк: тексты боевых чанков не сохранены. Цена отклонения измерена: доля вырожденных 0.740 по
|
||||
главам против 0.730 по чанкам моей нарезки — более мелкая единица метрику не спасает.
|
||||
|
||||
**Чего стоит измерить P1 по-настоящему:** данных банкноты существует только на 10 глав, а метрика
|
||||
требует, чтобы поверхность встречалась во МНОГИХ единицах. Нужен прогон черновой волны с включённой
|
||||
банкнотой на объёме, где типичная поверхность попадает в 5–10 единиц, — то есть та же полная книга,
|
||||
что и для холодного старта. Отдельного замера P1 при этом не потребуется: он считается $0 из
|
||||
чекпойнтов того же прогона.
|
||||
|
||||
### §7.6 Деньги добора и критик полноты
|
||||
|
||||
| Замер | Вызовов | $ |
|
||||
|---|---|---|
|
||||
| (а) арм с примером формата | 9 | **0.00960** |
|
||||
| (б) P1 consistency | 0 | **0** |
|
||||
| **Добор итого** | 9 | **0.00960** |
|
||||
| **Пакет-8 нарастающим итогом** | 156 | **$0.12273** |
|
||||
|
||||
| Проверка критика | Итог |
|
||||
|---|---|
|
||||
| Фактор в (а) ровно один | доказано диффом собранных запросов: ключи и user-часть побайтно те же, отличие — три строки |
|
||||
| Пример не подсказывает эталон | `师父` нет ни в выборке, ни в каноне, ни в сиде, и не встречается в срезе ни разу |
|
||||
| Мощность (а) названа ДО прогона | да, 0.104 при нулевом эффекте |
|
||||
| Разметчик в (б) не один | три независимых, слепых, согласие 0.973–0.986 |
|
||||
| Разметка не видела метрику | да: разметчикам не давали ни частоту, ни главы, ни P1 |
|
||||
| Арм-без-фактора в (б) провалился, как обязан | да, AUC 0.500 |
|
||||
| **НЕ покрыто** | (а) одна модель, 3 повтора, одна пара; разметчики (б) — модели, а не люди, и их согласие 0.98 может отражать общий системный вкус, а не истину |
|
||||
|
||||
> **Ревью-приписка к §7 (оркестратор №8, 26.07): ОБА ЗАМЕРА ПРИНЯТЫ, D39.52.** Воспроизведено: (а) арм
|
||||
> с примером — 0 из 120 строк не на целевом письме (независимый пересчёт по raw_example своим кодом);
|
||||
> (б) ре-ран `p1_consistency.py` на сохранённых артефактах d51 — вырожденность 54/73 = 0.740, критерий
|
||||
> не выполнен на полном и невырожденном наборах, частотный арм-без-фактора нулевой. Оговорка мощности
|
||||
> (а) — 0.104 — принята как названная честно; удвоение повторов НЕ берём: гарантия — экран п.1, живой
|
||||
> монитор — счётчик OffLanguage холодного прогона. Побочная находка «утечка алфавита» (7 строк вида
|
||||
> «категория A»; предикат п.1 их пропускает, подписанные dst латиницы не содержат) — записана хвостом
|
||||
> в пак-21 (кандидаты: банк-линт по латинице в dst / строгая форма §2.2-Б).
|
||||
|
||||
|
|
|
|||
112
eval/pkg7/p1_consistency.py
Normal file
112
eval/pkg7/p1_consistency.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Полигон, пакет-8 (добор D39.51-б): P1 consistency как разделитель термов и мусора. $0.
|
||||
|
||||
Метрика: Σ единиц, где чтец объявил строку термином / Σ единиц, где строка встречается. Единица —
|
||||
ГЛАВА (тексты боевых чанков прогона не сохранены; отклонение и его цена названы в §7.2 отчёта).
|
||||
|
||||
Арм без фактора — голая частота на том же наборе и том же критерии: если частота проходит, P1 не
|
||||
несёт информации сверх неё и вся конструкция беспредметна.
|
||||
|
||||
Критерий из санкции, названный ДО счёта: существует порог, выше которого ≥80% TERM, а ниже — ≥60%
|
||||
JUNK. Дополнительно печатается вырожденность (строки, встречающиеся в ≤1 единице, получают 1.0 по
|
||||
построению и разделять не могут) — при её доле >0.5 критерий объявляется неприменимым.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def best_threshold(scores: dict[str, float], labels: dict[str, str], want_term=0.80, want_junk=0.60):
|
||||
"""Ищем порог, дающий ≥want_term доли TERM выше и ≥want_junk доли JUNK ниже. Возвращаем лучший
|
||||
по сумме двух долей — и признак, выполнен ли критерий хоть на одном пороге."""
|
||||
terms = [s for s, l in labels.items() if l == "TERM" and s in scores]
|
||||
junk = [s for s, l in labels.items() if l == "JUNK" and s in scores]
|
||||
if not terms or not junk:
|
||||
return None
|
||||
cands = sorted({scores[s] for s in scores})
|
||||
best = None
|
||||
for t in cands:
|
||||
# "выше порога" = строго больше или равно; пробуем оба, берём лучший — порог как таковой
|
||||
# заранее не назывался, назывались только доли.
|
||||
for mode in (">=", ">"):
|
||||
hi = (lambda v: v >= t) if mode == ">=" else (lambda v: v > t)
|
||||
tp = sum(1 for s in terms if hi(scores[s])) / len(terms)
|
||||
tn = sum(1 for s in junk if not hi(scores[s])) / len(junk)
|
||||
row = (tp, tn, t, mode, tp >= want_term and tn >= want_junk)
|
||||
if best is None or (row[0] + row[1]) > (best[0] + best[1]):
|
||||
best = row
|
||||
return best
|
||||
|
||||
|
||||
def report(name: str, scores: dict[str, float], labels: dict[str, str]) -> dict:
|
||||
b = best_threshold(scores, labels)
|
||||
if b is None:
|
||||
print(f"{name}: нет обоих классов")
|
||||
return {}
|
||||
tp, tn, t, mode, ok = b
|
||||
n_t = sum(1 for s, l in labels.items() if l == "TERM" and s in scores)
|
||||
n_j = sum(1 for s, l in labels.items() if l == "JUNK" and s in scores)
|
||||
print(f"{name}: лучший порог {mode}{t:.3f} → TERM выше {tp:.3f} ({n_t} шт) · JUNK ниже {tn:.3f} ({n_j} шт)"
|
||||
f" критерий (≥0.80/≥0.60): {'ВЫПОЛНЕН' if ok else 'НЕ ВЫПОЛНЕН'}")
|
||||
return {"tpr": tp, "tnr": tn, "threshold": t, "mode": mode, "passes": ok, "n_term": n_t, "n_junk": n_j}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--surfaces", required=True, type=Path)
|
||||
ap.add_argument("--labels", required=True, type=Path)
|
||||
ap.add_argument("--chunks", required=True, type=Path)
|
||||
ap.add_argument("--out", type=Path)
|
||||
a = ap.parse_args()
|
||||
|
||||
items = json.loads(a.surfaces.read_text(encoding="utf-8"))
|
||||
labels = json.loads(a.labels.read_text(encoding="utf-8"))["majority"]
|
||||
rows = [json.loads(l) for l in a.chunks.open(encoding="utf-8") if l.strip()]
|
||||
by_ch: dict[int, str] = {}
|
||||
for r in rows:
|
||||
by_ch[r["chapter"]] = by_ch.get(r["chapter"], "") + "\n" + r["source"]
|
||||
|
||||
p1, freq, degen = {}, {}, []
|
||||
for it in items:
|
||||
occ = it["occ_chapters"]
|
||||
prop = [c for c in it["proposed_chapters"] if c in occ]
|
||||
if not occ:
|
||||
continue # предложена там, где не встречается — вне определения метрики
|
||||
p1[it["src"]] = len(prop) / len(occ)
|
||||
freq[it["src"]] = sum(by_ch[c].count(it["src"]) for c in occ)
|
||||
if len(occ) <= 1:
|
||||
degen.append(it["src"])
|
||||
|
||||
n = len(p1)
|
||||
print(f"поверхностей в счёте: {n} TERM={sum(1 for s in p1 if labels.get(s)=='TERM')} "
|
||||
f"JUNK={sum(1 for s in p1 if labels.get(s)=='JUNK')}")
|
||||
print(f"ВЫРОЖДЕННЫХ (встречаются в ≤1 главе → P1 = 1.0 по построению): {len(degen)} = {len(degen)/n:.3f}")
|
||||
dt = sum(1 for s in degen if labels.get(s) == "TERM")
|
||||
print(f" из них TERM {dt}, JUNK {len(degen)-dt} — обе метки получают одинаковый максимум")
|
||||
|
||||
print("\n--- весь набор ---")
|
||||
r_all = report("P1 consistency ", p1, labels)
|
||||
r_fq = report("частота (арм-без-фактора)", freq, labels)
|
||||
|
||||
live = {s: v for s, v in p1.items() if s not in degen}
|
||||
live_f = {s: v for s, v in freq.items() if s not in degen}
|
||||
print(f"\n--- только НЕвырожденные ({len(live)} поверхностей) ---")
|
||||
r_live = report("P1 consistency ", live, labels)
|
||||
r_lf = report("частота (арм-без-фактора)", live_f, labels)
|
||||
|
||||
print("\nневырожденные поверхности (P1 · частота · метка):")
|
||||
for s in sorted(live, key=lambda s: -live[s]):
|
||||
print(f" {s}\t{live[s]:.2f}\t{live_f[s]:3d}\t{labels.get(s)}")
|
||||
|
||||
if a.out:
|
||||
a.out.write_text(json.dumps({"p1": p1, "freq": freq, "degenerate": degen,
|
||||
"all": {"p1": r_all, "freq": r_fq},
|
||||
"live": {"p1": r_live, "freq": r_lf}},
|
||||
ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Reference in a new issue