docs(diarization): разделен CPU-эксперимент по машинам

- Зачем:
  - один агент не может последовательно работать на двух ноутбуках.
- Что:
  - этапы 0-4 закреплены за Intel, этапы 5-6 - за зависимой Ryzen-задачей.
  - добавлен проверяемый handoff через отчет, commit и приватную ZIP-капсулу.
  - устранен штатный возврат с Ryzen на Intel и ограничен финальный бюджет.
- Проверка:
  - `.venv/bin/pytest -q tests/test_diarization_calibration.py tests/test_speaker_diarizer.py tests/test_diarization.py` (61 passed).
This commit is contained in:
Dmitry Dementiev committed 2026-08-31 00:59:23 +03:00
1 parent fde4c48859
commit 558c1baf89
1 file changed
+139 -63
@@ -40,8 +40,8 @@
- расширить исследовательский стенд для новых ручек, повторов и счетчиков;
- независимо проверить сокращение работы, модель с надежной оценкой числа
говорящих и способы ускорить embedding-инференс;
- собрать не больше трех комбинаций на Intel и перенести не больше двух на
Ryzen;
- собрать не больше трех комбинаций на Intel, передать их в Ryzen-этап и
оставить там не больше двух финалистов после первичного A/B;
- выбрать один итоговый вариант либо зафиксировать, почему ни один вариант не
прошел;
- выпустить публичный обезличенный отчет и сохранить полную переносимую капсулу
@@ -105,6 +105,7 @@
- размер batch, где он применим;
- машину, число физических ядер, CPUID/ISA и версии библиотек;
- номер повтора и позицию в расписании;
- этап `intel` или `ryzen`; для Ryzen - исходный commit handoff;
- хеш экспериментальной сборки или патча sherpa-onnx;
- хеши моделей и приватных входов по прежнему контракту.
@@ -125,18 +126,78 @@
идентификатору, а расписание оставшихся сохраняет заданный 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/`.
`.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. Проверить механизмы без дорогих замеров
@@ -199,21 +260,24 @@ embedding-заданий. Ускорение без соответствующе
| M2 | CAMPPlus | счетчик, выбранный для CAMPPlus, + `FastClustering(N)` |
| M3 | TitaNet small | счетчик, выбранный для TitaNet, + `FastClustering(N)` |
M1 намеренно отсутствует: отдельный контроль `Prepare/Finalize` перенесен в
дешевую проверку этапа 0 и не получает CPU-ячейку.
M0, M2 и M3 получают по одному прогону трех фрагментов. Быстрая модель проходит
дальше, если выдерживает обязательные условия и достигает порога продолжения
относительно M0. Малая разница не уточняется дополнительными повторами.
Регрессия диагностических метрик требует прослушать отмеченные пятиминутные
случаи до сборки комбинации.
### Этап 3. Проверить исполнение эмбеддингов
### Этап 3. Проверить исполнение эмбеддингов на Intel
#### QDQ WeSpeaker
S8S8 QDQ сравнивается с FP32 на обеих машинах при шаге `0.1`. На Intel
достаточно одного A/B по трем фрагментам. Изолированный Ryzen A/B выполняется
до сборки комбинаций этапа 4: по одному прогону baseline и кандидата на трех
фрагментах. Проверка на Ryzen обязательна даже при проигрыше на Intel: выигрыш
зависит от VNNI/AVX-512.
S8S8 QDQ сравнивается с FP32 на Intel при шаге `0.1`; достаточно одного A/B по
трем фрагментам. Если QDQ проходит обязательные условия и проверку памяти, он
остается допустимым для C2 даже без порога продолжения на Intel: возможный
выигрыш на Ryzen зависит от VNNI/AVX-512. Изолированный Ryzen A/B выполняется
уже после handoff, в этапе 5.
U8U8 допускается как единственный запасной вариант, если S8S8 достигает порога
продолжения по времени диаризации, но не проходит качество. Другие форматы
@@ -221,12 +285,10 @@ U8U8 допускается как единственный запасной в
#### Shared-session streams
На Intel и Ryzen сравниваются четыре конфигурации при общем бюджете `P`. На
Intel достаточно одного A/B по трем фрагментам. Изолированный Ryzen A/B также
выполняется до этапа 4 и состоит из одного прогона baseline и одного прогона
кандидата на трех фрагментах. Порядок baseline и кандидата меняется между
парами. Эти прогоны в
этапе 5 не повторяются.
На Intel сравниваются четыре конфигурации при общем бюджете `P`; достаточно
одного A/B по трем фрагментам. Лучшая shared-session конфигурация, прошедшая
обязательные условия и проверку памяти, остается допустимой для C2 даже без
порога продолжения. Изолированный Ryzen A/B выполняется в этапе 5.
| Ячейка | Outer workers | Intra-op | Session |
|---|---:|---:|---|
@@ -255,48 +317,51 @@ Batch TitaNet открывается только после прохожден
| Ячейка | Состав |
|---|---|
| C0 | рабочий шаг + WeSpeaker FP32 + последовательный инференс |
| C1 | рабочий шаг + одна сильнейшая прошедшая ветвь |
| C2 | рабочий шаг + все совместимые прошедшие ветви |
| C1 | рабочий шаг + сильнейшая ветвь по Intel |
| C2 | рабочий шаг + заранее определенный ISA-стек |
Сильнейшая ветвь определяется по сокращению времени диаризации: для быстрой
модели используется Intel, для QDQ и shared-session - обязательный изолированный
Ryzen A/B из этапа 3. Это намеренно грубое межмашинное ранжирование: Intel
дешево отсеивает быстрые модели, а ISA-чувствительные ветви оцениваются там, где
их выигрыш проявляется. Оно не заменяет финальный Ryzen A/B. QDQ совместим
только с WeSpeaker. Batch совместим только с TitaNet. Shared-session добавляется
к выбранной модели после проверки сохранности всех входов и результатов на этой
модели. Если C1 и C2 совпадают, дубликат не запускается. C0 совпадает с baseline
и тоже не запускается, если рабочим остался шаг `0.1`.
Для 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`.
Каждая существующая комбинация получает один прогон трех фрагментов. Если C0
отличается от baseline, он всегда переносится на Ryzen после обязательных
условий, а из C1 и C2 туда идет максимум один вариант с лучшим временем
диаризации. Если C0 совпадает с baseline, на Ryzen переносятся максимум два
разных варианта C1/C2. При равенстве по скорости выбирается меньший RSS и
меньшее изменение sherpa-onnx. Если прошел только новый шаг окна, воронка
продолжается с одним C0. Если порога продолжения не достигла ни одна ветвь,
эксперимент фиксирует `stop` до финального Ryzen A/B.
Каждая существующая комбинация получает один прогон трех фрагментов на Intel.
Все комбинации, выдержавшие обязательные условия и память, передаются в handoff;
их остается не больше трех. Если прошел только новый шаг окна, передается один
C0. Если новых комбинаций нет, Intel-агент фиксирует `stop-before-ryzen`:
Ryzen-задача подтверждает итог по handoff и не запускает модели.
### Этап 5. Принять финалиста на Ryzen
На 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 она выбывает
только при нарушении обязательных условий или правила памяти.
- совпадающий baseline;
- не больше двух комбинаций из этапа 4.
Затем первичный A/B получают все переданные комбинации, но не больше трех.
Каждый A/B состоит из одного прогона baseline и одного прогона кандидата на трех
фрагментах. Порядок меняется для следующей пары. Вариант, который нарушает
обязательные условия или не достигает порога продолжения, выбывает без
дополнительных прогонов.
Изолированные QDQ и shared-session A/B уже выполнены в этапе 3 и здесь не
повторяются.
Первичный A/B состоит из одного прогона baseline и одного прогона кандидата на
трех фрагментах. Порядок меняется для следующей пары. Вариант, который не
достигает порога продолжения, выбывает без дополнительных прогонов.
Не больше двух вариантов, прошедших первичный A/B, получают по пять чередующихся
A/B-циклов: C0, если он отличается от baseline, и лучший по времени диаризации
из C1/C2; иначе два лучших варианта C1/C2. Один цикл содержит все три фрагмента;
результат цикла - сумма времени диаризации. Итогом служат медиана пяти парных
ускорений и MAD. Доверительные интервалы и новые повторы после пяти циклов не
требуются.
Не больше двух лучших вариантов, прошедших первичный A/B, получают по пять
чередующихся A/B-циклов. Они выбираются по времени диаризации, при равенстве по
скорости - по меньшему RSS, затем по меньшему числу отличий от baseline. При
полном равенстве порядок C0, C1, C2 служит последним тай-брейком. Один цикл
содержит все три фрагмента; результат цикла - сумма времени диаризации. Итогом
служат медиана пяти парных ускорений и MAD. Доверительные интервалы и новые
повторы после пяти циклов не требуются.
Итоговая комбинация проходит условие скорости, если достигает итогового порога
на Ryzen. Примерно двукратное ускорение остается ориентиром. На Intel процент
@@ -381,7 +446,7 @@ limits, температуры, throttling, swap и число потоков.
Peak RSS записывается для каждой ячейки и сравнивается с baseline той же машины.
RSS выше 2 ГиБ на машине с 16 ГиБ RAM требует разбора, но сам по себе не
отбрасывает вариант. Эксперимент останавливает ячейку при OOM, активной
отбрасывает вариант. Правило памяти останавливает ячейку при OOM, активной
подкачке, росте RSS между повторами без плато или линейном росте памяти с числом
workers или session.
@@ -398,14 +463,18 @@ workers или session.
равенстве по скорости выбирается более простой и экономный по памяти вариант.
Ни один экспериментальный результат автоматически не меняет production-профиль.
Возможны четыре исхода:
`stop-before-ryzen` - промежуточный итог Intel-этапа; после проверки handoff он
становится итогом `stop`. `needs-info` - состояние заблокированной задачи, а не
результат эксперимента.
Возможны четыре итоговых исхода:
- `go`: один вариант достигает итогового порога на Ryzen и проходит все
проверки;
- `needs-decision`: Ryzen проходит итоговый порог, но Intel показывает
практически значимую регрессию;
- `stop`: ни один вариант не проходит скорость, качество или память; причины
сохраняются по каждой ветви;
- `stop`: baseline 1.13.6 не принят либо ни один вариант не проходит скорость,
качество или память; причины сохраняются по каждой ветви;
- `new map`: сокращение окон и цены эмбеддинга упирается в качество раньше
порога скорости, поэтому отдельно рассматривается end-to-end архитектура.
@@ -424,8 +493,9 @@ uv run pytest -q \
повторы, смешанные результаты счетчиков, ветвь только с шагом окна, совместимый
стек, наследование происхождения QDQ-материала и запрет общего исходника с
контрольными записями, обязательные условия, два показателя остаточных кластеров,
контроль общего бюджета потоков, продолжение незавершенного запуска и отсутствие
приватного текста в ошибках.
контроль общего бюджета потоков, продолжение незавершенного запуска, границу
этапов Intel/Ryzen, не больше трех handoff-кандидатов и отсутствие приватного
текста в ошибках.
До моделей выполняются:
@@ -437,9 +507,10 @@ uv run python scripts/benchmarks/diarization_calibration.py \
--validate-only
```
После эксперимента публичный отчет должен позволять восстановить все решения по
агрегатам и хешам. Полное воспроизведение использует приватную капсулу из
закрытой папки Яндекс Диска.
Intel-отчет должен позволять восстановить отсев и точные рецепты переданных
комбинаций по агрегатам и хешам. Финальный Ryzen-отчет ссылается на него и
добавляет первичный A/B, финальные циклы и решение. Полное воспроизведение
использует приватную капсулу из закрытой папки Яндекс Диска.
## Отклоненные варианты
@@ -451,16 +522,21 @@ uv run python scripts/benchmarks/diarization_calibration.py \
которые могут складываться с сокращением работы.
- Только современный CPU отклонен: он скрывает регрессии на AVX2 и не дает
понять, зависит ли выигрыш от ISA.
- Полная матрица на Ryzen отклонена: старый Intel дешевле для первичного отсева,
а Ryzen нужен для ISA-чувствительных A/B и финальной комбинации.
- Полная матрица на Ryzen отклонена: старый Intel дешевле для первичного отсева.
Ryzen получает максимум три комбинации для одного первичного A/B и оставляет
максимум две для дорогих циклов.
- Сохранение текущего профиля остается допустимым итогом, если практического
победителя нет.
## Критерии завершения задачи эксперимента
## Критерии завершения задач эксперимента
- [ ] Стенд и тесты реализуют описанную воронку и не меняют пользовательский
контракт диаризации.
- [ ] Совпадающий baseline воспроизведен на Intel и Ryzen.
- [ ] Intel-задача принимает baseline и завершает этапы 0-4 либо фиксирует
подтвержденную регрессию как `stop-before-ryzen`; в обоих случаях коммитит
обезличенный отчет и публикует полный handoff-комментарий.
- [ ] Ryzen-задача начинает с указанного commit и проверенной капсулы, затем
воспроизводит свой baseline без возврата к обычным Intel-замерам.
- [ ] Все пройденные и остановленные ветви имеют явную причину и ссылку на
ячейки результата.
- [ ] Финальный A/B завершен либо зафиксировано, почему до него не дошел ни один