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