diff --git a/docs/specs/2026-08-30-cpu-diarization-experiment.md b/docs/specs/2026-08-30-cpu-diarization-experiment.md new file mode 100644 index 0000000..042dde1 --- /dev/null +++ b/docs/specs/2026-08-30-cpu-diarization-experiment.md @@ -0,0 +1,550 @@ +# Следующий CPU-эксперимент диаризации + +**Дата:** 2026-08-30 + +**Статус:** согласованная постановка эксперимента. Production-конфигурация не +менялась. + +## Проблема + +Текущий профиль Pyannote FP32 + WeSpeaker тратит большую часть времени +диаризации на эмбеддинги для сильно перекрывающихся окон. Отдельные исследования +нашли три независимых рычага: увеличить шаг окна, удешевить эмбеддинги и изменить +их исполнение. Полный перебор всех сочетаний слишком дорог и смешивает проверку +механизма с выбором итогового профиля. + +Нужен один эксперимент с последовательным отсевом. Он должен показать, есть ли +комбинация, которая сокращает время диаризации на Ryzen минимум на 30%, сохраняет +принятую проверку качества и остается работоспособной на старом Intel. Ориентир +для сильного результата - ускорение примерно в два раза. + +## Сначала прочитать + +- [ADR-007](../adr/007-word-level-speaker-diarization.md) - когда работа касается + действующего профиля и проверки качества. +- [Предыдущий CPU-тест](../benchmarks/2026-08-30-lightweight-diarization-cpu.md) - + при работе с корпусом, метриками и приватными результатами. +- [Шаг окна](../research/2026-08-30-sherpa-window-shift.md) - при добавлении + `window_shift_ratio` и счетчиков работы. +- [Оценка числа говорящих](../research/2026-08-30-diarization-speaker-count.md) - + при реализации `Prepare/Finalize`, eigengap и NME. +- [Embedding-инференс](../research/2026-08-30-diarization-embedding-inference.md) - + при квантизации, конкурентных stream и batch TitaNet. +- [Решение об аппаратной матрице](https://git.dementev.space/ddmitry/local-transcriber/issues/35#issuecomment-2243) + - при подготовке машин и разборе памяти. + +## Цель и границы + +Эксперимент должен: + +- расширить исследовательский стенд для новых ручек, повторов и счетчиков; +- независимо проверить сокращение работы, модель с надежной оценкой числа + говорящих и способы ускорить embedding-инференс; +- собрать не больше трех комбинаций на Intel, передать их в Ryzen-этап и + оставить там не больше двух финалистов после первичного A/B; +- выбрать один итоговый вариант либо зафиксировать, почему ни один вариант не + прошел; +- выпустить публичный обезличенный отчет и сохранить полную переносимую капсулу + в закрытой папке Яндекс Диска. + +В эксперимент не входят: + +- изменение CLI, `.transcriber.toml` или модели по умолчанию; +- автоматический профиль по ISA или отдельные пользовательские профили; +- параллельный запуск ASR и диаризации, GPU, NPU и Mac; +- новая полная спектральная кластеризация, NeMo runtime и k-means; +- CAMPPlus batch без маски и несколько сессий как самостоятельное направление; +- production-переход на победителя по результату одного эксперимента. + +Если каскад не достигает нужной скорости из-за качества, следующий шаг - +отдельная карта по end-to-end диаризации. Этот эксперимент ее не начинает. + +## Совпадающий baseline + +Каждое сравнение использует baseline, запущенный на той же машине, в том же +режиме питания и на том же стенде: + +- `sherpa-onnx` 1.13.6; +- Pyannote Segmentation 3.0 FP32; +- WeSpeaker ResNet34 LM FP32; +- `window_shift_ratio=0.1`; +- `FastClustering` с порогом `0.89` и автоматическим числом кластеров; +- последовательный embedding-инференс; +- не больше `P` потоков, где `P` - число физических ядер машины. + +Версию `sherpa-onnx`, uv override и `sherpa-onnx-core` обновляют синхронно. +Значение `0.1` сохраняет действующее поведение. Изменение зависимости само по +себе не считается ускорением. На тех же трех фрагментах W0 сравнивается со +свежим запуском 1.13.5 на той же машине. После канонической нумерации кластеров +сверяются интервалы, назначение говорящих словам и прежние диагностические +метрики. Различия направляются в слуховую проверку отмеченных мест. Baseline +1.13.6 принимается только при `3 / 2 / 2` и отсутствии подтвержденной регрессии. + +Время диаризации во всей спецификации означает wall time одного вызова +`diarizer.process(samples)`. ASR, загрузка моделей, декодирование и подготовка +данных в него не входят. + +Три порога используются во всех этапах: + +- разница до 5% считается равенством по скорости; +- 10% - порог продолжения после дешевого отсева; +- 30% - итоговый порог `go` на Ryzen. + +## Стенд и данные + +Существующий `scripts/benchmarks/diarization_calibration.py` остается точкой +входа, но получает новую версию manifest. Прежний формат жестко закрепляет +матрицу FP32/INT8 x WeSpeaker/TitaNet, обязательный свип порогов и +`sherpa-onnx` 1.13.5, поэтому старые результаты с новым экспериментом не +продолжаются. + +Новая версия manifest и идентификатор ячейки должны учитывать: + +- вариант шага окна, модель, счетчик `N` и способ embedding-инференса; +- число outer workers, вид session и бюджеты intra-op/inter-op; +- размер batch, где он применим; +- машину, число физических ядер, CPUID/ISA и версии библиотек; +- номер повтора и позицию в расписании; +- этап `intel` или `ryzen`; для Ryzen - исходный commit handoff; +- хеш экспериментальной сборки или патча sherpa-onnx; +- хеши моделей и приватных входов по прежнему контракту. + +Для QDQ идентичность также включает хеши калибровочных источников, +препроцессинг, reader, параметры `quant_pre_process` и `quantize_static`, список +исключенных узлов, версии инструментов и хеш готового графа. Для каждого +калибровочного элемента manifest хранит идентификатор и SHA-256 исходной записи, +а также использованный временной интервал. Вырезанный или перекодированный файл +наследует эти данные от исходника. Ни один калибровочный элемент не может +происходить из трех контрольных записей, даже из другого интервала. +`--validate-only` отклоняет общий исходник; если происхождение нельзя подтвердить +или отдельного материала нет, ветвь QDQ пропускается и не блокирует остальные +направления. Проверка показывает только хеши и рецепт без приватных путей. + +Каждая измеряемая ячейка выполняется в новом дочернем процессе. Стенд сохраняет +сырые результаты каждого повтора и не заменяет их одной агрегированной строкой. +При продолжении запуска успешные ячейки пропускаются по стабильному +идентификатору, а расписание оставшихся сохраняет заданный A/B-порядок. Режим +`--validate-only` показывает полный план без приватных путей и текста. + +### Приватная капсула + +Капсула передается одним ZIP-архивом; `capsule_sha256` означает SHA-256 этого +файла. В корне лежит `capsule.json` с идентификатором капсулы, исходным commit, +хешем публичного Intel-отчета и SHA-256 каждого файла внутри. Все пути в manifest +относительны корня распакованной капсулы. + +Архив содержит: + +- manifest и полный набор входов для трех пятиминутных и трех полных прогонов; +- ASR-sidecar, опорные транскрипты и разметку, нужную для метрик и прослушивания; +- калибровочный материал и готовый QDQ-граф, если QDQ передается в Ryzen; +- сырые Intel-результаты, интервалы, worker requests и журналы, нужные для + продолжения и разбора; +- локально созданные или измененные модельные артефакты. + +Публично загружаемые базовые модели можно не дублировать в архиве, если manifest +фиксирует репозиторий, revision и SHA-256. После распаковки `--validate-only` +сверяет внешний хеш архива, `capsule.json`, относительные пути и все внутренние +хеши до запуска моделей. + +### Приватность + +За пределами Git остаются manifest, записи, WAV-кеш, ASR-sidecar, опорные +транскрипты, сырые интервалы, результаты, worker requests, журналы и материалы +слуховой проверки. Рабочий каталог стенда нельзя размещать в обычной +`.scratch`: репозиторий игнорирует там только служебный каталог +`.scratch/handoffs/`, не связанный с переносом эксперимента между ноутбуками. + +Публичный отчет содержит хеши, агрегаты, конфигурацию и выводы. В него не входят +локальные пути, распознанный текст, временные интервалы, имена участников и +фрагменты приватных записей. Перед внешним ревью используется только публичный +отчет и код стенда. + +## Разделение работы между ноутбуками + +Эксперимент выполняют две зависимые задачи. Один агент не меняет машину в ходе +работы: + +| Задача | Машина | Этапы | Результат | +|---|---|---|---| +| [Intel #41](https://git.dementev.space/ddmitry/local-transcriber/issues/41) | Core i7-6820HQ | 0-4 | стенд, Intel-отчет и handoff | +| [Ryzen #42](https://git.dementev.space/ddmitry/local-transcriber/issues/42) | Ryzen 7 8845H | 5-6 | финальный отчет и решение | + +Intel-агент коммитит в обычную ветку своей задачи код стенда и обезличенный +отчет. Специальная общая ветка для handoff не создается. Manifest, сырые +результаты и другие приватные материалы в Git не попадают. В финальном +комментарии к Intel-задаче агент оставляет только индекс: + +- `intel_outcome`: `handoff` или `stop-before-ryzen`; +- ветку, точный commit и PR; +- путь и SHA-256 публичного Intel-отчета; +- число кандидатов и ссылку на их рецепты в отчете; +- непрозрачный идентификатор приватной капсулы и ее SHA-256; +- ссылку на ограничения в отчете. + +При расхождении источником истины служит файл отчета в указанном commit, +комментарий остается указателем. Он не содержит приватных путей, текста или +интервалов. Пользователь передает капсулу на второй ноутбук отдельно и сливает +Intel-PR обычным merge, без squash или rebase. После этого #41 закрывается и +средствами Gitea разблокирует #42. Ryzen-агент сначала читает обе задачи и все +комментарии к первой, обновляет `master` и проверяет присутствие handoff-commit +в его истории. Затем он сверяет SHA-256 капсулы и запускает `--validate-only`. +При наличии кандидатов агент создает обычную ветку #42 от `master`. +Несовпадение commit, капсулы или плана переводит задачу в `needs-info`; +недостающие данные агент не восстанавливает догадками. + +Штатный путь идет только с Intel на Ryzen. Если на Ryzen обнаружена ошибка +стенда, которая делает Intel-результаты недействительными, эксперимент +останавливается и требует отдельного исправления. Обычный отсев или проигрыш +кандидата не возвращает работу на Intel. + +## Воронка вариантов + +### Этап 0. Проверить механизмы без дорогих замеров + +До CPU-матрицы стенд должен пройти дешевые проверки: + +1. `sherpa-onnx` 1.13.6 импортируется, принимает пять значений шага и сохраняет + прежнее поведение при `0.1`. +2. Счетчик eigengap и NME проходят синтетические фикстуры из исследования. + Затем оба считаются на независимых копиях трех матриц CAMPPlus и трех матриц + TitaNet, подготовленных без повторного инференса. Для каждой модели отдельно + остается счетчик, который дает точные `3 / 2 / 2`. Если проходят оба, при + разнице времени до 10% выбирается более простой eigengap, иначе более + быстрый. Модель, для которой не проходит ни один счетчик, выбывает из + автоматического режима. +3. `Prepare/Finalize` сохраняет пары строк и штатную финализацию. Для известного + `N` метки строк и итоговая разметка совпадают с отдельным штатным вызовом на + той же матрице. +4. WeSpeaker S8S8 QDQ сохраняет входы, выходы, opset, пользовательские метаданные + и загружается через `SpeakerEmbeddingExtractor`. +5. Shared-session возвращает по одному результату на каждый вход без гонок, + пропусков и нечисловых значений. Сравнение ведется с последовательными + вызовами на том же наборе входов. + +Числовой дрейф эмбеддингов записывается, но отдельный произвольный порог для него +не вводится. Решение принимается по одинаковому числу кластеров, меткам строк и +итоговой разметке. + +### Этап 1. Найти безопасный шаг окна на Intel + +На Core i7-6820HQ проверяется текущая связка WeSpeaker с сеткой: + +| Ячейка | `window_shift_ratio` | Роль | +|---|---:|---| +| W0 | `0.10` | совпадающий baseline | +| W1 | `0.15` | осторожное сокращение окон | +| W2 | `0.20` | примерно половина исходных окон | +| W3 | `0.25` | агрессивный кандидат | +| W4 | `0.50` | граничная проверка | + +Для каждой ячейки выполняется один прогон трех пятиминутных фрагментов. Стенд +записывает фактический шаг в сэмплах, расчетное число segmentation-окон и число +embedding-заданий. Ускорение без соответствующего изменения счетчиков считается +необъясненным и не проходит дальше до разбора. + +Все значения, прошедшие обязательные условия, ранжируются по времени +диаризации. При равенстве по скорости выбирается меньший шаг: он меньше отходит +от принятой разметки. Если лучший по времени шаг ухудшает purity или число слов +без говорящего, до сборки комбинаций прослушиваются отмеченные пятиминутные +случаи. Рабочим становится лучший шаг, достигший порога продолжения. Если такого +нет, рабочим остается `0.1`, а ветвь шага считается проигравшей. + +### Этап 2. Выбрать модель и счетчик говорящих на Intel + +При `window_shift_ratio=0.1` сравниваются: + +| Ячейка | Эмбеддинги | Оценка числа говорящих | +|---|---|---| +| M0 | WeSpeaker FP32 | текущий `FastClustering`, порог `0.89` | +| M2 | CAMPPlus | счетчик, выбранный для CAMPPlus, + `FastClustering(N)` | +| M3 | TitaNet small | счетчик, выбранный для TitaNet, + `FastClustering(N)` | + +M1 намеренно отсутствует: отдельный контроль `Prepare/Finalize` перенесен в +дешевую проверку этапа 0 и не получает CPU-ячейку. + +M0, M2 и M3 получают по одному прогону трех фрагментов. Быстрая модель проходит +дальше, если выдерживает обязательные условия и достигает порога продолжения +относительно M0. Малая разница не уточняется дополнительными повторами. +Регрессия диагностических метрик требует прослушать отмеченные пятиминутные +случаи до сборки комбинации. + +### Этап 3. Проверить исполнение эмбеддингов на Intel + +#### QDQ WeSpeaker + +S8S8 QDQ сравнивается с FP32 на Intel при шаге `0.1`; достаточно одного A/B по +трем фрагментам. Если QDQ проходит обязательные условия и проверку памяти, он +остается допустимым для C2 даже без порога продолжения на Intel: возможный +выигрыш на Ryzen зависит от VNNI/AVX-512. Изолированный Ryzen A/B выполняется +уже после handoff, в этапе 5. + +U8U8 допускается как единственный запасной вариант, если S8S8 достигает порога +продолжения по времени диаризации, но не проходит качество. Другие форматы +квантизации в матрицу не добавляются. + +#### Shared-session streams + +На Intel сравниваются четыре конфигурации при общем бюджете `P`; достаточно +одного A/B по трем фрагментам. Лучшая shared-session конфигурация, прошедшая +обязательные условия и проверку памяти, остается допустимой для C2 даже без +порога продолжения. Изолированный Ryzen A/B выполняется в этапе 5. + +| Ячейка | Outer workers | Intra-op | Session | +|---|---:|---:|---| +| E0 | `1` | `P` | одна, текущий внутренний параллелизм | +| E1 | `2` | общий pool `P` | одна | +| E2 | `P` | `1` | одна | +| E3 | `P` | `1` на session | отдельные session, отрицательный контроль | + +`inter_op_threads=1` во всех ячейках. Измеряется время полного набора +embedding-вызовов. Latency одного удачного stream для решения не используется. +E3 не становится кандидатом, если только не опровергает ожидаемый рост RSS и не +выигрывает у shared-session по времени диаризации. + +#### Batch TitaNet + +Batch TitaNet открывается только после прохождения M3. Сначала сравниваются batch +`1` и `4` на одинаковом наборе переменных длин. Batch проходит к CPU-замеру, +если сохраняет число кластеров, метки строк и итоговую разметку на всех трех +контрольных матрицах. CAMPPlus batch и подбор размера больше `4` отложены. + +### Этап 4. Собрать комбинации на Intel + +Полного декартова перебора нет. На Intel собираются максимум три явно заданные +комбинации: + +| Ячейка | Состав | +|---|---| +| C0 | рабочий шаг + WeSpeaker FP32 + последовательный инференс | +| C1 | рабочий шаг + сильнейшая ветвь по Intel | +| C2 | рабочий шаг + заранее определенный ISA-стек | + +Для C1 рассматриваются ветви, достигшие порога продолжения на Intel; сильнейшая +определяется по сокращению времени диаризации. C2 начинается с рабочего шага и +WeSpeaker FP32. Если QDQ выдержал обязательные условия и память, он заменяет +FP32 независимо от скорости на Intel. Затем добавляется лучшая допустимая +shared-session конфигурация для WeSpeaker. Если ни QDQ, ни shared-session не +допустимы, C2 собирается как совместимый стек прошедших Intel-ветвей. Так +ISA-кандидаты доходят до Ryzen без предварительного Ryzen-замера. Batch совместим +только с TitaNet. Если C1 и C2 совпадают, дубликат не запускается. C0 совпадает +с baseline и тоже не запускается, если рабочим остался шаг `0.1`. + +Каждая существующая комбинация получает один прогон трех фрагментов на Intel. +Все комбинации, выдержавшие обязательные условия и память, передаются в handoff; +их остается не больше трех. Если прошел только новый шаг окна, передается один +C0. Если новых комбинаций нет, Intel-агент фиксирует `stop-before-ryzen`: +Ryzen-задача подтверждает итог по handoff и не запускает модели. + +### Этап 5. Принять финалиста на Ryzen + +Ryzen-агент начинает с восстановления handoff. Если передано ноль кандидатов и +Intel-итог равен `stop-before-ryzen`, после проверки commit, SHA-256 капсулы и +`--validate-only` он фиксирует итог `stop` и закрывает задачу без запуска +моделей. При наличии кандидатов агент воспроизводит совпадающий baseline и +выполняет изолированные QDQ и shared-session A/B для используемых механизмов. +Каждый такой A/B состоит из одного прогона baseline и одного прогона кандидата +на трех фрагментах. Порядок baseline и кандидата меняется между парами. Эти A/B +проверяют обязательные условия и объясняют вклад механизма. Проигрыш по скорости +сам по себе не снимает переданную комбинацию; до первичного A/B она выбывает +только при нарушении обязательных условий или правила памяти. + +Затем первичный A/B получают все переданные комбинации, но не больше трех. +Каждый A/B состоит из одного прогона baseline и одного прогона кандидата на трех +фрагментах. Порядок меняется для следующей пары. Вариант, который нарушает +обязательные условия или не достигает порога продолжения, выбывает без +дополнительных прогонов. + +Не больше двух лучших вариантов, прошедших первичный A/B, получают по пять +чередующихся A/B-циклов. Они выбираются по времени диаризации, при равенстве по +скорости - по меньшему RSS, затем по меньшему числу отличий от baseline. При +полном равенстве порядок C0, C1, C2 служит последним тай-брейком. Один цикл +содержит все три фрагмента; результат цикла - сумма времени диаризации. Итогом +служат медиана пяти парных ускорений и MAD. Доверительные интервалы и новые +повторы после пяти циклов не требуются. + +Итоговая комбинация проходит условие скорости, если достигает итогового порога +на Ryzen. Примерно двукратное ускорение остается ориентиром. На Intel процент +допустимого замедления заранее не задается. Если финалист медленнее Intel +baseline минимум на порог продолжения, эксперимент возвращает `needs-decision` +вместо автоматического `go`: приемлемость фактической регрессии решает владелец +после замеров. + +### Этап 6. Проверить полные записи + +Один победитель и совпадающий baseline 1.13.6 однократно обрабатывают три полные +записи на Ryzen. Эти прогоны проверяют перенос результата за границы +пятиминутных фрагментов; единственное время полного прогона не заменяет +итоговый порог скорости. + +В слуховой проверке baseline и кандидат сопоставляются по пяти группам: + +- короткие ответы и перебивки; +- смены говорящего; +- перекрывающуюся речь; +- устойчивость соответствия голоса кластеру; +- слова без говорящего и малые остаточные кластеры. + +Вердикт и краткие причины сохраняются в приватной капсуле. Публичный отчет +содержит только итог `пройдено` или `не пройдено` по каждой категории. + +## Метрики + +### Работа и скорость + +Для каждой ячейки сохраняются: + +- wall time, user/system/total CPU, средняя загрузка ядер и RTF; +- времена segmentation, embeddings, clustering и всего вызова; +- фактический шаг, число segmentation-окон и embedding-заданий; +- число строк подготовленной матрицы, выбранные `p` и `N`, время счетчика; +- число embedding-вызовов, batch, workers, session и бюджеты потоков. + +### Качество + +Обязательные условия: + +- точное число кластеров `3 / 2 / 2` на контрольных фрагментах; +- неизменный распознанный текст с точностью до прежней нормализации пробелов; +- повторяемые `p`, `N`, метки строк и финализация на одной матрице; +- конечные эмбеддинги и отсутствие потерь или дублей входов. + +Диагностика: + +- `mapped speaker purity` на двух записях с опорной разметкой; +- число слов без говорящего; +- число и длительность малых кластеров; +- доля времени речи в кластерах сверх ожидаемых; +- доля назначенных слов в тех же кластерах. + +Последние два показателя получают разные имена и единицы. Дополнительными +считаются кластеры после `N` крупнейших по времени речи. Доля времени использует +общее время речи в знаменателе, доля слов - все слова, назначенные известным +кластерам. + +Прокси-метрики не получают произвольных жестких порогов. Ухудшение purity или +рост слов без говорящего относительно совпадающего baseline помечает случаи для +слуховой проверки. Победитель не принимается, если слуховая проверка +подтверждает регрессию коротких реплик, смен говорящего или устойчивости +кластеров. + +ASR выполняется один раз при подготовке sidecar и не входит в измеряемые ячейки. +Каждая ячейка использует тот же набор слов. Проверка неизменности текста +сравнивает сборку слов после назначения говорящих с sidecar и ловит потерю, +дубль или перестановку слов; она не запускает ASR повторно. + +### Память и машина + +Для каждого запуска фиксируются точный SKU, физические и логические ядра, RAM, +CPUID/ISA, ОС, версии библиотек, питание, power mode, governor, turbo, power +limits, температуры, throttling, swap и число потоков. + +Обе машины работают от сети в стабильном `balanced`-режиме. A/B чередуются, +чтобы прогрев не давал преимущества одному варианту. Прогон с throttling +или сменой режима питания не учитывается и повторяется один раз после +стабилизации. Абсолютный температурный порог не задается. + +Peak RSS записывается для каждой ячейки и сравнивается с baseline той же машины. +RSS выше 2 ГиБ на машине с 16 ГиБ RAM требует разбора, но сам по себе не +отбрасывает вариант. Правило памяти останавливает ячейку при OOM, активной +подкачке, росте RSS между повторами без плато или линейном росте памяти с числом +workers или session. + +## Правила решения + +Результаты рассматриваются в таком порядке: + +1. обязательные условия качества и корректности; +2. медианное сокращение времени диаризации на Ryzen; +3. память; +4. сложность будущей реализации и сопровождения. + +Практически незначимая разница не получает дополнительных прогонов. При +равенстве по скорости выбирается более простой и экономный по памяти вариант. +Ни один экспериментальный результат автоматически не меняет production-профиль. + +`stop-before-ryzen` - промежуточный итог Intel-этапа; после проверки handoff он +становится итогом `stop`. `needs-info` - состояние заблокированной задачи, а не +результат эксперимента. + +Возможны четыре итоговых исхода: + +- `go`: один вариант достигает итогового порога на Ryzen и проходит все + проверки; +- `needs-decision`: Ryzen проходит итоговый порог, но Intel показывает + практически значимую регрессию; +- `stop`: baseline 1.13.6 не принят либо ни один вариант не проходит скорость, + качество или память; причины сохраняются по каждой ветви; +- `new map`: сокращение окон и цены эмбеддинга упирается в качество раньше + порога скорости, поэтому отдельно рассматривается end-to-end архитектура. + +## Проверка реализации стенда + +Минимальная автоматическая проверка: + +```bash +uv run pytest -q \ + tests/test_diarization_calibration.py \ + tests/test_speaker_diarizer.py \ + tests/test_diarization.py +``` + +Тесты должны покрывать новую схему manifest и идентичность ячеек, расписание и +повторы, смешанные результаты счетчиков, ветвь только с шагом окна, совместимый +стек, наследование происхождения QDQ-материала и запрет общего исходника с +контрольными записями, обязательные условия, два показателя остаточных кластеров, +контроль общего бюджета потоков, продолжение незавершенного запуска, границу +этапов Intel/Ryzen, не больше трех handoff-кандидатов и отсутствие приватного +текста в ошибках. + +До моделей выполняются: + +```bash +uv run python scripts/benchmarks/diarization_calibration.py --help + +uv run python scripts/benchmarks/diarization_calibration.py \ + --manifest /private/path/manifest.json \ + --validate-only +``` + +Intel-отчет должен позволять восстановить отсев и точные рецепты переданных +комбинаций по агрегатам и хешам. Финальный Ryzen-отчет ссылается на него и +добавляет первичный A/B, финальные циклы и решение. Полное воспроизведение +использует приватную капсулу из закрытой папки Яндекс Диска. + +## Отклоненные варианты + +- Полный декартов перебор отклонен: большая часть ячеек повторяет уже + проигравшие механизмы и не влияет на выбор. +- Пять повторов каждой ячейки отклонены: дорогая точность нужна только для + финального A/B. Первичный отсев использует порог продолжения. +- Только шаг окна отклонен: он не проверяет более дешевые модели и исполнение, + которые могут складываться с сокращением работы. +- Только современный CPU отклонен: он скрывает регрессии на AVX2 и не дает + понять, зависит ли выигрыш от ISA. +- Полная матрица на Ryzen отклонена: старый Intel дешевле для первичного отсева. + Ryzen получает максимум три комбинации для одного первичного A/B и оставляет + максимум две для дорогих циклов. +- Сохранение текущего профиля остается допустимым итогом, если практического + победителя нет. + +## Критерии завершения задач эксперимента + +- [ ] Стенд и тесты реализуют описанную воронку и не меняют пользовательский + контракт диаризации. +- [ ] Intel-задача принимает baseline и завершает этапы 0-4 либо фиксирует + подтвержденную регрессию как `stop-before-ryzen`; в обоих случаях коммитит + обезличенный отчет и публикует полный handoff-комментарий. +- [ ] Ryzen-задача начинает с указанного commit и проверенной капсулы, затем + воспроизводит свой baseline без возврата к обычным Intel-замерам. +- [ ] Все пройденные и остановленные ветви имеют явную причину и ссылку на + ячейки результата. +- [ ] Финальный A/B завершен либо зафиксировано, почему до него не дошел ни один + вариант. +- [ ] При наличии победителя выполнена проверка полных записей и слуховая + приемка. +- [ ] Публичный отчет свободен от приватных данных; полная капсула сохранена + только в закрытой папке Яндекс Диска. +- [ ] Итог формулирует следующее решение: готовить production-изменение, + вынести измеренную регрессию на Intel на решение владельца, сохранить текущий + профиль или открыть новую карту end-to-end диаризации.