diff --git a/docs/specs/2026-08-30-cpu-diarization-experiment.md b/docs/specs/2026-08-30-cpu-diarization-experiment.md index 969a944..042dde1 100644 --- a/docs/specs/2026-08-30-cpu-diarization-experiment.md +++ b/docs/specs/2026-08-30-cpu-diarization-experiment.md @@ -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 завершен либо зафиксировано, почему до него не дошел ни один