Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
159dbdcf7f |
No files matched your search
@@ -353,8 +353,7 @@ faster-whisper её не поддерживает, и запуск с `--device
|
|||||||
| `parakeet-v3` | ~600 MB | 7,6×* | 25 языков | ✅ |
|
| `parakeet-v3` | ~600 MB | 7,6×* | 25 языков | ✅ |
|
||||||
|
|
||||||
\* Наблюдение на AMD Ryzen 7 8845H, Windows, `int8`, три записи общей
|
\* Наблюдение на AMD Ryzen 7 8845H, Windows, `int8`, три записи общей
|
||||||
длительностью 43:37. Замер описывает только эту тестовую машину; скорость на
|
длительностью 43:37. Это не приёмочный замер для целевого Intel Core i5.
|
||||||
других CPU требует отдельного прогона.
|
|
||||||
Методика и качественное сравнение:
|
Методика и качественное сравнение:
|
||||||
[benchmark GigaAM и Whisper](docs/benchmarks/2026-08-11-gigaam-model-comparison.md).
|
[benchmark GigaAM и Whisper](docs/benchmarks/2026-08-11-gigaam-model-comparison.md).
|
||||||
|
|
||||||
|
|||||||
+11
-24
@@ -13,12 +13,7 @@
|
|||||||
**Промежуточный статус 2026-08-11:** короткая матрица на трёх записях завершена
|
**Промежуточный статус 2026-08-11:** короткая матрица на трёх записях завершена
|
||||||
и описана в [benchmark GigaAM и Whisper](benchmarks/2026-08-11-gigaam-model-comparison.md).
|
и описана в [benchmark GigaAM и Whisper](benchmarks/2026-08-11-gigaam-model-comparison.md).
|
||||||
Multilingual Large добавлена как явный качественный профиль; решение о default
|
Multilingual Large добавлена как явный качественный профиль; решение о default
|
||||||
по-прежнему ждет длинных негативных примеров и проверки производительности на
|
по-прежнему ждёт целевого Intel Core i5 и длинных негативных примеров.
|
||||||
нескольких доступных классах CPU.
|
|
||||||
|
|
||||||
Под CPU-дефолтом в этом разделе понимается программный профиль: backend, модель
|
|
||||||
и `compute_type`, выбранные без явных аргументов пользователя. Целевого
|
|
||||||
процессора и обязательного эталонного SKU у проекта нет.
|
|
||||||
|
|
||||||
**Проблема:** текущий CPU-путь через OpenVINO Whisper medium нестабилен на
|
**Проблема:** текущий CPU-путь через OpenVINO Whisper medium нестабилен на
|
||||||
длинных записях с тихими участками: модель генерирует правдоподобные повторы и
|
длинных записях с тихими участками: модель генерирует правдоподобные повторы и
|
||||||
@@ -73,16 +68,10 @@ Multilingual Large добавлена как явный качественный
|
|||||||
|
|
||||||
**Матрица производительности:**
|
**Матрица производительности:**
|
||||||
|
|
||||||
- использовать минимум два доступных x86-ориентира: консервативный baseline на
|
- обязательный прогон на реальном Intel Core i5 11-го поколения; точный SKU,
|
||||||
Intel Core i7-6820HQ и современный baseline на AMD Ryzen 7 8845H;
|
число ядер, объём RAM, power mode и число потоков записать вместе с результатом;
|
||||||
- для каждого прогона записывать точный SKU, физические ядра и логические
|
- Ryzen 7 8845H разработчика и ограничение числа потоков использовать только для
|
||||||
потоки, CPUID/ISA, объем RAM, power mode и фактическое число потоков;
|
предварительного smoke-теста, не как приёмку производительности i5;
|
||||||
- Ryzen использовать как основную машину для итераций и как современный
|
|
||||||
контролируемый baseline. Результат относится только к зафиксированной
|
|
||||||
конфигурации;
|
|
||||||
- при доступности отдельно собирать результаты на других x86-ноутбуках и Apple
|
|
||||||
Silicon как данные о переносимости; в контролируемую матрицу входят два
|
|
||||||
предыдущих x86 baseline;
|
|
||||||
- измерять отдельно cold start/загрузку модели и warm transcription, медиану
|
- измерять отдельно cold start/загрузку модели и warm transcription, медиану
|
||||||
трёх прогонов, RTFx, peak RSS и размер скачиваемой модели;
|
трёх прогонов, RTFx, peak RSS и размер скачиваемой модели;
|
||||||
- короткий 15-минутный фрагмент нужен для итераций, полные записи 22–81 мин —
|
- короткий 15-минутный фрагмент нужен для итераций, полные записи 22–81 мин —
|
||||||
@@ -103,9 +92,8 @@ Multilingual Large добавлена как явный качественный
|
|||||||
|
|
||||||
**Если подтвердится бенчмарком:**
|
**Если подтвердится бенчмарком:**
|
||||||
|
|
||||||
- Если E2E RNN-T сохраняет качество и приемлемую скорость на выбранной
|
- Если E2E RNN-T сохраняет качество и приемлемую скорость на i5 — сделать его
|
||||||
CPU-матрице - сделать его русским CPU-дефолтом, OpenVINO оставить явной
|
русским CPU-дефолтом, OpenVINO оставить явной опцией.
|
||||||
опцией.
|
|
||||||
- Если лучший вариант зависит от языка — выбрать language-aware default:
|
- Если лучший вариант зависит от языка — выбрать language-aware default:
|
||||||
GigaAM v3 для русского, multilingual-модель для смешанной речи.
|
GigaAM v3 для русского, multilingual-модель для смешанной речи.
|
||||||
- Если модели GigaAM проигрывают по пунктуации/смыслу — сохранить текущий
|
- Если модели GigaAM проигрывают по пунктуации/смыслу — сохранить текущий
|
||||||
@@ -147,11 +135,10 @@ Multilingual Large добавлена как явный качественный
|
|||||||
- Переключить русский CPU-дефолт на победителя сравнительного GigaAM-бенчмарка.
|
- Переключить русский CPU-дефолт на победителя сравнительного GigaAM-бенчмарка.
|
||||||
- Предупреждать пользователя при `--device openvino-cpu` для файлов >30 мин.
|
- Предупреждать пользователя при `--device openvino-cpu` для файлов >30 мин.
|
||||||
|
|
||||||
**Приоритет:** высокий - проблема затрагивает текущий OpenVINO CPU-путь, а
|
**Приоритет:** высокий — проблема затрагивает текущий OpenVINO CPU-путь, а
|
||||||
пользователи работают на ноутбуках разных поколений и производителей.
|
целевая аудитория включает ноутбуки с Intel Core i5 11-го поколения. GigaAM v3
|
||||||
Среди них встречаются и более скромные машины уровня Intel Core i5 11-го
|
остаётся рабочей явной альтернативой для русского, но выбор безопасного
|
||||||
поколения. GigaAM v3 остается рабочей явной альтернативой для русского, но
|
auto/default требует сравнительного бенчмарка.
|
||||||
выбор безопасного auto/default требует сравнительного бенчмарка.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
# Ускорение извлечения голосовых эмбеддингов на x86 CPU
|
|
||||||
|
|
||||||
**Дата:** 2026-08-30
|
|
||||||
|
|
||||||
**Статус:** исследовательская записка. Production-код и конфигурация не
|
|
||||||
менялись.
|
|
||||||
|
|
||||||
**Метод:** проверены `sherpa-onnx==1.13.5`, `onnxruntime==1.28.0`, официальные
|
|
||||||
скрипты экспорта и исходники владельцев моделей. Три официальных release asset
|
|
||||||
прочитаны потоком без сохранения на диск: WeSpeaker ResNet34 LM, CAMPPlus en и
|
|
||||||
TitaNet small. Их входы, выходы и метаданные разобраны через ORT 1.28.0; для
|
|
||||||
каждого выполнен CPU smoke-test с batch size 2.
|
|
||||||
|
|
||||||
## Вопрос и краткий ответ
|
|
||||||
|
|
||||||
Текущий путь не использует batch: sherpa-onnx последовательно создает stream и
|
|
||||||
вызывает один `Session::Run` для каждой пары "окно, локальный говорящий".
|
|
||||||
Публичный `SpeakerEmbeddingExtractor` также принимает один stream за вызов.
|
|
||||||
|
|
||||||
Из трех направлений достойны прототипа два:
|
|
||||||
|
|
||||||
1. статическая INT8-квантизация действующего WeSpeaker в формате S8S8 QDQ;
|
|
||||||
2. несколько независимых stream поверх **одной** ORT session с ограниченным
|
|
||||||
общим числом потоков.
|
|
||||||
|
|
||||||
Настоящий batch технически лучше всего подготовлен у TitaNet: граф принимает
|
|
||||||
вектор длин. Но TitaNet пока допустим только при явно известном числе говорящих,
|
|
||||||
а sherpa-onnx все равно фиксирует batch size 1. Поэтому batch TitaNet -
|
|
||||||
условный следующий прототип; действующую конфигурацию он не ускоряет.
|
|
||||||
|
|
||||||
Несколько отдельных session не стоит прототипировать первым: это не уменьшает
|
|
||||||
работу, создает отдельные пулы потоков и арены памяти, повышает риск
|
|
||||||
oversubscription и
|
|
||||||
роста RSS. Для WeSpeaker и CAMPPlus batch с простым дополнением также не готов:
|
|
||||||
их графы не принимают длины или маску.
|
|
||||||
|
|
||||||
## Что именно выполняется последовательно
|
|
||||||
|
|
||||||
Приложение создает один `OfflineSpeakerDiarization` и задает одно и то же
|
|
||||||
`num_threads` отдельно для segmentation и embedding
|
|
||||||
([адаптер проекта](../../src/local_transcriber/speaker_diarizer.py)). Внутри
|
|
||||||
sherpa-onnx стадии идут строго `segmentation → embeddings → clustering`, а
|
|
||||||
`ComputeEmbeddings` проходит список пар обычным циклом: создает stream,
|
|
||||||
добавляет все неперекрывающиеся фрагменты одного локального говорящего,
|
|
||||||
завершает input и синхронно вызывает `Compute`
|
|
||||||
([реализация Pyannote в sherpa-onnx 1.13.5](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h)).
|
|
||||||
|
|
||||||
Публичный C++ и Python API экспортирует `create_stream`, `is_ready` и
|
|
||||||
`compute(stream)`. Метода `compute_batch(streams)` нет
|
|
||||||
([C++ API](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/speaker-embedding-extractor.h),
|
|
||||||
[Python binding](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/python/csrc/speaker-embedding-extractor.cc)).
|
|
||||||
Python binding освобождает GIL на `compute`, поэтому внешний параллельный вызов
|
|
||||||
в принципе возможен; готовый диаризатор промежуточные stream наружу не отдает.
|
|
||||||
|
|
||||||
Для embedding создается одна ORT session. Sherpa-onnx передает заданное число и
|
|
||||||
в `intra_op_num_threads`, и в `inter_op_num_threads`
|
|
||||||
([настройка session](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/session.cc)).
|
|
||||||
ORT по умолчанию исполняет граф последовательно; `inter_op` начинает
|
|
||||||
распараллеливать узлы только при `ORT_PARALLEL`, тогда как `intra_op`
|
|
||||||
распараллеливает оператор
|
|
||||||
([официальная документация ORT](https://onnxruntime.ai/docs/performance/tune-performance/threading.html)).
|
|
||||||
Значит, в текущем CPU-пути основная ручка - `intra_op`.
|
|
||||||
|
|
||||||
## Контракты трех графов
|
|
||||||
|
|
||||||
| Модель | Вход и выход официального экспорта | Следствие для batch |
|
|
||||||
|---|---|---|
|
|
||||||
| WeSpeaker ResNet34 LM | Release asset имеет один `float32`-вход `[B, T, 80]` и выход `[B, 256]` ([asset](https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/wespeaker_en_voxceleb_resnet34_LM.onnx)). Скрипт, которым sherpa-onnx добавляет метаданные, проверяет символические `B`, `T`, размерность 80 и единственный выход. Метаданные включают `framework=wespeaker`, `sample_rate`, `output_dim`, `normalize_samples=0` ([проверка sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/scripts/wespeaker/add_meta_data.py), [экспорт WeSpeaker](https://github.com/wenet-e2e/wespeaker/blob/master/wespeaker/bin/export_onnx.py)). | Равные `T` можно сложить по `B`. Длины или маску граф не принимает; дополнение участвует в pooling и меняет эмбеддинг. |
|
|
||||||
| CAMPPlus en | Release asset имеет `float32`-вход `[N, T, 80]` и выход с динамической первой осью `[*, 512]` ([asset](https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_campplus_sv_en_voxceleb_16k.onnx)). Метаданные: `framework=3d-speaker`, `output_dim=512`, `normalize_samples=1`, `feature_normalize_type=global-mean` ([экспорт sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/scripts/3dspeaker/export-onnx.py)). | Несмотря на опечатку экспортного скрипта (`embedding`/`embeddings`), release asset принимает batch равной длины и возвращает все строки. Для разных длин граф по-прежнему не принимает маску или длины. |
|
|
||||||
| NeMo TitaNet small | Release asset имеет входы `float32 [N, 80, T]` и `int64 [N]`; выходы — logits `[N, 16681]` и embedding `[N, 192]` ([asset](https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/nemo_en_titanet_small.onnx), [контракт C++-обертки](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/speaker-embedding-extractor-nemo-model.h)). Метаданные содержат `framework=nemo`, `output_dim`, `feat_dim`, sample rate, параметры окна и нормализации ([экспорт](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/scripts/nemo/speaker-verification/export-onnx.py)). | Граф принимает batch и вектор длин. Но sherpa-onnx создает обе входные формы с `N=1` и возвращает только первую строку ([реализация](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/speaker-embedding-extractor-nemo-impl.h)). |
|
|
||||||
|
|
||||||
Smoke-test с синтетическими `float32`-признаками и одним intra-op thread дал
|
|
||||||
для WeSpeaker `(2, 256)`, для CAMPPlus `(2, 512)` и для TitaNet `(2, 192)`.
|
|
||||||
У WeSpeaker и CAMPPlus результат batch size 2 совпал с конкатенацией двух
|
|
||||||
последовательных `Run` (`max_abs_diff=0`). У TitaNet элемент длиной 100 кадров
|
|
||||||
также совпал, но элемент длиной 80, дополненный до 100 кадров, отличался от
|
|
||||||
отдельного непаддированного вызова (`max_abs_diff=0,0269`). Это не тест качества
|
|
||||||
на голосе, а подтверждение, что допустимый порог для variable-length batch нужно
|
|
||||||
определить в прототипе, а не считать эквивалентность гарантированной контрактом.
|
|
||||||
|
|
||||||
WeSpeaker ResNet34 состоит из Conv2d-блоков и statistics pooling
|
|
||||||
([исходник WeSpeaker](https://github.com/wenet-e2e/wespeaker/blob/master/wespeaker/models/resnet.py));
|
|
||||||
CAMPPlus сочетает Conv2d, TDNN/Conv1d и statistics pooling
|
|
||||||
([исходник 3D-Speaker](https://github.com/modelscope/3D-Speaker/blob/main/speakerlab/models/campplus/DTDNN.py));
|
|
||||||
TitaNet использует depth-wise separable Conv1d, squeeze-and-excitation и
|
|
||||||
attention statistics pooling
|
|
||||||
([документация NeMo](https://docs.nvidia.com/nemo-framework/user-guide/25.02/nemotoolkit/asr/speaker_recognition/models.html)).
|
|
||||||
Этого недостаточно, чтобы утверждать: каждый оператор будет квантизован и
|
|
||||||
ускорен.
|
|
||||||
|
|
||||||
В [официальном выпуске speaker-recognition models](https://github.com/k2-fsa/sherpa-onnx/releases/tag/speaker-recongition-models)
|
|
||||||
есть только FP32-файлы WeSpeaker, CAMPPlus и TitaNet; готовых INT8 embedding
|
|
||||||
assets нет. Статический INT8 здесь означает собственный производный артефакт с
|
|
||||||
отдельной проверкой лицензии, метаданных и качества.
|
|
||||||
|
|
||||||
## Оценка способов ускорения
|
|
||||||
|
|
||||||
| Способ | Техническая доступность | Вердикт |
|
|
||||||
|---|---|---|
|
|
||||||
| Настоящий batch | Release-графы WeSpeaker и CAMPPlus поддерживают batch равной длины; TitaNet принимает batch и второй вход с длинами. Sherpa-onnx для всех трех жестко формирует `N=1` и не имеет batch API. | Прототипировать только TitaNet и только условно для режима с известным числом говорящих. Для WeSpeaker/CAMPPlus без маски возможны лишь группы с одинаковым `T`; ожидаемая наполняемость неизвестна. |
|
|
||||||
| Несколько stream, одна session | Stream независимы, `Compute` использует локальные тензоры, а ORT разрешает нескольким потокам одновременно вызывать `Run` одной session ([архитектура ORT](https://onnxruntime.ai/docs/reference/high-level-design.html), [контракт 1.28.0](https://github.com/microsoft/onnxruntime/blob/v1.28.0/onnxruntime/core/session/inference_session.h)). Текущий цикл sherpa-onnx этого не делает. | Достойно узкого прототипа на WeSpeaker. У одной session общий intra-op pool; нужно сравнить внутренний и внешний параллелизм. Полный pool для каждого worker не нужен. |
|
|
||||||
| Несколько session | Можно создать несколько extractor, но по умолчанию каждая session получает собственный intra-op pool и CPU arena. ORT отдельно предлагает общий pool против конкуренции session pools и общий allocator против роста памяти ([thread management](https://onnxruntime.ai/docs/performance/tune-performance/threading.html), [C API guide](https://onnxruntime.ai/docs/get-started/with-c.html#share-allocator-s-between-sessions)). | Не брать как самостоятельный кандидат. Допустима одна контрольная ячейка против shared-session, чтобы подтвердить отказ по реальному времени и RSS. |
|
|
||||||
| Статический INT8 | ORT предоставляет `quantize_static`, использует calibration data и рекомендует static quantization для CNN. Для CPU первым выбором служит S8S8 QDQ; квантизация может ухудшить точность и даже скорость ([официальное руководство](https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html)). | Главный кандидат для действующего WeSpeaker. CAMPPlus и TitaNet квантизовать только после продуктового решения использовать соответствующую модель. |
|
|
||||||
|
|
||||||
Batch сокращает число `Session::Run` и может лучше загрузить SIMD, но число
|
|
||||||
эмбеддингов и основная модельная арифметика сохраняются; padding может даже
|
|
||||||
добавить работу. Несколько stream или session лишь перекрывают независимые
|
|
||||||
вызовы. Только INT8 меняет арифметику и объем движения весов. Увеличение шага
|
|
||||||
окна или отказ от части эмбеддингов действительно сокращают работу. Их нужно
|
|
||||||
рассматривать в отдельном исследовании шага окна.
|
|
||||||
|
|
||||||
## ISA, oversubscription и память
|
|
||||||
|
|
||||||
AVX2, VNNI и AVX-512 служат уровнями runtime dispatch одного формата модели.
|
|
||||||
MLAS в ORT 1.28.0 проверяет CPUID во время запуска и выбирает отдельные AVX2,
|
|
||||||
AVX-VNNI, AVX-512 и
|
|
||||||
AVX512-VNNI kernels
|
|
||||||
([runtime dispatch](https://github.com/microsoft/onnxruntime/blob/v1.28.0/onnxruntime/core/mlas/lib/platform.cpp)).
|
|
||||||
Один QDQ-граф поэтому нужно измерять на разных CPU. Отдельная сборка под каждый
|
|
||||||
из них не нужна.
|
|
||||||
|
|
||||||
Официальное руководство ORT предупреждает:
|
|
||||||
|
|
||||||
- S8S8 QDQ остается первым выбором CPU. Если он дает значимую потерю
|
|
||||||
точности, ORT советует проверить U8U8;
|
|
||||||
- отдельный путь U8S8 на AVX2 и AVX-512 без VNNI использует `VPMADDUBSW` и
|
|
||||||
может дать saturation; если до него дойдет матрица, этот риск снижают
|
|
||||||
`reduce_range` или переход на U8U8;
|
|
||||||
- на x86 с VNNI этой проблемы нет, а INT8 обычно выигрывает больше;
|
|
||||||
- на старом CPU без подходящих инструкций quantize/dequantize overhead способен
|
|
||||||
сделать модель медленнее
|
|
||||||
([разделы Data type selection и FAQ](https://onnxruntime.ai/docs/performance/model-optimizations/quantization.html)).
|
|
||||||
|
|
||||||
Доступный Core i7-6820HQ официально заявляет только SSE4.1/4.2 и AVX2
|
|
||||||
([Intel ARK](https://www.intel.com/content/www/us/en/products/sku/88970/intel-core-i76820hq-processor-8m-cache-up-to-3-60-ghz/specifications.html)).
|
|
||||||
Современный класс должен выбираться по фактическим CPUID-флагам. Надписи
|
|
||||||
"11-е поколение" недостаточно: минимум нужен `avx512_vnni` или `avx_vnni`. Для доступного
|
|
||||||
Zen 4 AMD документирует AVX-512 и AVX512_VNNI как путь ускоренного integer GEMM
|
|
||||||
([AMD AOCL](https://docs.amd.com/r/en-US/68552-AOCL-api-guide/Hardware-Utilization)).
|
|
||||||
|
|
||||||
В параллельном прототипе нужно измерять RSS. Уже последовательный тест показал,
|
|
||||||
что TitaNet требует примерно на 147–149 МиБ больше RSS, чем WeSpeaker, а FP32
|
|
||||||
WeSpeaker на старом Intel использовал в среднем 6,84 логического ядра из восьми
|
|
||||||
([локальный benchmark](../benchmarks/2026-08-30-lightweight-diarization-cpu.md)).
|
|
||||||
Конкурентные `Run` одной session делят общий intra-op pool, но все равно
|
|
||||||
конкурируют за его очереди и добавляют вызывающие потоки. Несколько session уже
|
|
||||||
создают отдельные пулы и дополнительно размножают арены и подготовленные веса.
|
|
||||||
Точный рост не следует считать линейным - его нужно измерить.
|
|
||||||
|
|
||||||
## Что остается неизвестным
|
|
||||||
|
|
||||||
- Реальный процент Conv/MatMul, который ORT сможет перевести в INT8, и выигрыш
|
|
||||||
каждого графа на двух ISA-классах.
|
|
||||||
- Сохранятся ли обязательные пользовательские метаданные после квантизации; без
|
|
||||||
`framework`, `output_dim` и параметров обработки признаков sherpa-onnx модель
|
|
||||||
не загрузит.
|
|
||||||
- Точность статического INT8 после калибровки и необходимость исключить отдельные
|
|
||||||
узлы через quantization debugging.
|
|
||||||
- Распределение `T` у пар "окно, локальный говорящий". Без него нельзя оценить
|
|
||||||
полезность exact-length buckets для WeSpeaker/CAMPPlus.
|
|
||||||
- Выигрыш shared-session concurrency после ограничения общего числа потоков,
|
|
||||||
его пиковый RSS и стабильность повторных конкурентных вызовов TitaNet.
|
|
||||||
|
|
||||||
## Рекомендация и минимальная граница прототипа
|
|
||||||
|
|
||||||
### 1. INT8 WeSpeaker - первый кандидат
|
|
||||||
|
|
||||||
На отдельном стенде, не меняя загрузчик проекта:
|
|
||||||
|
|
||||||
- выполнить `quant_pre_process`, затем `quantize_static` в S8S8 QDQ;
|
|
||||||
- проверить opset, вход/выход, все пользовательские метаданные и загрузку через
|
|
||||||
`SpeakerEmbeddingExtractor`;
|
|
||||||
- сравнить FP32 и INT8 по реальному времени embedding-стадии, процессорному
|
|
||||||
времени, пиковому RSS и
|
|
||||||
расстоянию между эмбеддингами;
|
|
||||||
- только после технического выигрыша повторить действующие проверки числа
|
|
||||||
голосовых кластеров, purity и слов без говорящего.
|
|
||||||
|
|
||||||
При значительной потере точности S8S8 проверить U8U8. Отдельный U8S8 с
|
|
||||||
`reduce_range` в первую матрицу не входит.
|
|
||||||
|
|
||||||
### 2. Shared-session streams - второй кандидат
|
|
||||||
|
|
||||||
Изолированный стенд или минимальная upstream-поправка должен сохранить одну
|
|
||||||
модель и одну ORT session, подготовить независимые stream и сравнить:
|
|
||||||
|
|
||||||
```text
|
|
||||||
outer workers / shared intra-op pool: 1 / P, 2 / P, P / 1
|
|
||||||
inter-op threads: 1
|
|
||||||
```
|
|
||||||
|
|
||||||
Здесь `P` - число физических ядер. Первая ячейка показывает текущий внутренний
|
|
||||||
параллелизм, вторая проверяет конкурентные `Run` на общем pool, третья переносит
|
|
||||||
параллелизм наружу и не создает intra-op pool. Измерять нужно полный набор
|
|
||||||
независимых embedding-вызовов. Latency одного удачного stream для решения
|
|
||||||
недостаточно. Отдельные session оставить одной отрицательной контрольной ячейкой
|
|
||||||
`P workers / 1 thread`.
|
|
||||||
|
|
||||||
### 3. Batch TitaNet - условный кандидат
|
|
||||||
|
|
||||||
Если отдельное решение разрешит TitaNet для режима с известным числом
|
|
||||||
говорящих, прямой ORT-стенд должен сначала доказать на размерах batch `1 / 4`,
|
|
||||||
что batch переменной длины с дополнением и `x_lens` дает те же эмбеддинги в
|
|
||||||
заданном допуске, что и последовательные вызовы. Затем измерить пропускную
|
|
||||||
способность и RSS. До этого менять
|
|
||||||
sherpa-onnx API ради TitaNet преждевременно. CAMPPlus и batch без mask в эту
|
|
||||||
границу не входят.
|
|
||||||
|
|
||||||
Минимальная доступная CPU-матрица для обоих непосредственных кандидатов:
|
|
||||||
|
|
||||||
| Класс | Конкретный доступный ориентир | Зачем |
|
|
||||||
|---|---|---|
|
|
||||||
| Консервативный x86 baseline: AVX2, без VNNI/AVX-512 | Intel Core i7-6820HQ, 4C/8T | Проверить нижнюю границу INT8 и конкуренцию на старом CPU. |
|
|
||||||
| Современный x86 baseline: VNNI + AVX-512 | AMD Ryzen 7 8845H из предыдущих замеров, с обязательной фиксацией `lscpu`/CPUID | Проверить специализированный INT8 kernel и масштабирование на 8C/16T. |
|
|
||||||
|
|
||||||
Эти две машины доступны для воспроизводимых прогонов; ни одна из них не является
|
|
||||||
целевым SKU проекта. Ноутбук с Intel Core i5 11-го поколения - лишь пример
|
|
||||||
распространенного пользовательского класса и не обязательная приемочная машина.
|
|
||||||
Для первого отбора кандидата двух ориентиров достаточно; на любой дополнительной
|
|
||||||
машине нужно записывать точную модель и CPUID. Переносить полученный коэффициент
|
|
||||||
ускорения на весь x86 нельзя.
|
|
||||||
@@ -1,402 +0,0 @@
|
|||||||
# Оценка числа говорящих для быстрых эмбеддингов
|
|
||||||
|
|
||||||
**Дата:** 2026-08-30
|
|
||||||
|
|
||||||
**Статус:** исследовательская записка. Production-код и конфигурация не
|
|
||||||
менялись.
|
|
||||||
|
|
||||||
## Вопрос
|
|
||||||
|
|
||||||
Как отделить оценку числа говорящих от порога `FastClustering`, чтобы
|
|
||||||
использовать быстрые CAMPPlus или TitaNet и не запускать сегментацию и
|
|
||||||
извлечение эмбеддингов второй раз?
|
|
||||||
|
|
||||||
## Краткий вывод
|
|
||||||
|
|
||||||
Нужна двухшаговая схема: один раз получить ровно те эмбеддинги, которые
|
|
||||||
подготовил конвейер sherpa-onnx, отдельно оценить по ним число голосовых
|
|
||||||
кластеров `N`, затем передать `N` штатному `FastClustering`. Для первого
|
|
||||||
прототипа рекомендован **счетчик NME**, без полной спектральной кластеризации:
|
|
||||||
ядро алгоритма из статьи реализовать на NumPy, а итоговые метки по-прежнему
|
|
||||||
получить через `FastClustering(num_clusters=N)`.
|
|
||||||
Счетчик eigengap с фиксированным прореживанием нужен как дешевый контрольный
|
|
||||||
вариант. Пока это гипотеза для прототипа, а не выбор production-алгоритма.
|
|
||||||
|
|
||||||
Публичного промежуточного интерфейса для этого в sherpa-onnx 1.13.5 нет.
|
|
||||||
`SpeakerEmbeddingExtractor` и `FastClustering` публичны, но
|
|
||||||
`OfflineSpeakerDiarization.process()` возвращает только готовые интервалы.
|
|
||||||
Матрица эмбеддингов и соответствие строк локальным говорящим остаются внутри
|
|
||||||
реализации. После `set_config()` повторный `process()` снова выполнит модельный
|
|
||||||
инференс. Python-адаптер проекта сам по себе не может дать дешевую повторную
|
|
||||||
кластеризацию; будущему прототипу нужен узкий исследовательский интерфейс на
|
|
||||||
уровне sherpa-onnx.
|
|
||||||
|
|
||||||
## Что подтверждено про sherpa-onnx
|
|
||||||
|
|
||||||
### `FastClustering` уже умеет второй шаг
|
|
||||||
|
|
||||||
В версии 1.13.5 алгоритм нормирует строки, строит все попарные косинусные
|
|
||||||
расстояния, выполняет AHC методом полной связи и затем выбирает один из двух
|
|
||||||
способов разрезать одно и то же дерево:
|
|
||||||
|
|
||||||
- при `num_clusters > 0`: ровно на заданное число кластеров;
|
|
||||||
- иначе: по высоте `threshold`.
|
|
||||||
|
|
||||||
Так работает
|
|
||||||
[`fast-clustering.cc`](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/fast-clustering.cc)
|
|
||||||
и закреплено в комментарии к
|
|
||||||
[`FastClusteringConfig`](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/fast-clustering-config.h):
|
|
||||||
при известном числе кластеров порог игнорируется. Python-модуль публично
|
|
||||||
принимает C-contiguous матрицу `float32` размера `M x D` и возвращает метку для
|
|
||||||
каждой строки
|
|
||||||
([исходник Python-модуля](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/python/csrc/fast-clustering.cc)).
|
|
||||||
Значит, передавать оцененное `N` в существующий кластеризатор не требует новой
|
|
||||||
реализации распределения эмбеддингов по кластерам.
|
|
||||||
|
|
||||||
Порог `FastClustering` не принадлежит модели эмбеддингов: он задает высоту
|
|
||||||
дерева полной связи в шкале косинусного расстояния. Официальный пример CAMPPlus
|
|
||||||
для проверки говорящего лишь вычисляет косинусное сходство и не задает
|
|
||||||
универсальной границы решения
|
|
||||||
([`infer_sv.py`](https://github.com/modelscope/3D-Speaker/blob/065629c313eaf1a01c65c640c46d77e61e9607b4/speakerlab/bin/infer_sv.py)).
|
|
||||||
Официальный рецепт диаризации 3D-Speaker отдельно задает `pval=0.012` для
|
|
||||||
спектральной кластеризации и `mer_cos=0.8` для последующего слияния центров
|
|
||||||
([`infer_diarization.py`](https://github.com/modelscope/3D-Speaker/blob/065629c313eaf1a01c65c640c46d77e61e9607b4/speakerlab/bin/infer_diarization.py)).
|
|
||||||
Эти величины имеют другой смысл и не могут быть перенесены в sherpa как
|
|
||||||
`порог модели`.
|
|
||||||
|
|
||||||
### Публичных деталей недостаточно для повтора всего конвейера
|
|
||||||
|
|
||||||
Публичный `SpeakerEmbeddingExtractor` позволяет создать поток, подать в него
|
|
||||||
аудио и получить один эмбеддинг
|
|
||||||
([Python-модуль](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/python/csrc/speaker-embedding-extractor.cc),
|
|
||||||
[C/C++ API](https://k2-fsa.github.io/sherpa/onnx/c-api/html/speaker_embedding.html)).
|
|
||||||
Интерфейс пригоден для самостоятельно выбранного фрагмента, но не выдает
|
|
||||||
материал, который выбрал конвейер диаризации.
|
|
||||||
|
|
||||||
Внутри `OfflineSpeakerDiarization` после сегментации:
|
|
||||||
|
|
||||||
1. из перекрывающихся окон строятся пары `окно x локальный говорящий`;
|
|
||||||
2. речь с одновременными голосами исключается;
|
|
||||||
3. для каждой пары собирается свой поток и вычисляется эмбеддинг;
|
|
||||||
4. вся матрица передается в `FastClustering`;
|
|
||||||
5. метки строк переводятся обратно в разметку говорящих.
|
|
||||||
|
|
||||||
Последовательность и временная матрица видны в
|
|
||||||
[`OfflineSpeakerDiarizationPyannoteImpl::Process`](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L87-L204),
|
|
||||||
а сборка строк описана в
|
|
||||||
[`GetChunkSpeakerSampleIndexes` и `ComputeEmbeddings`](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L376-L528).
|
|
||||||
Оба метода закрыты внутри реализации.
|
|
||||||
|
|
||||||
Python-класс `OfflineSpeakerDiarization` экспортирует только `sample_rate`,
|
|
||||||
`set_config` и `process`; `process` возвращает готовый результат, а не признаки
|
|
||||||
([Python-модуль 1.13.5](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L73-L128)).
|
|
||||||
Внутренний `SetConfig` заменяет только объект кластеризации, тогда как каждый
|
|
||||||
`Process` заново вызывает сегментацию и `ComputeEmbeddings`
|
|
||||||
([реализация](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L79-L175)).
|
|
||||||
Публичная C API также документирует только создание, настройку и получение
|
|
||||||
итоговых сегментов
|
|
||||||
([официальная страница](https://k2-fsa.github.io/sherpa/onnx/c-api/html/speaker_diarization.html)).
|
|
||||||
|
|
||||||
Итог: публичный API позволяет **кластеризовать имеющуюся матрицу повторно**.
|
|
||||||
Извлечь или повторно обработать промежуточную матрицу штатного конвейера
|
|
||||||
диаризации через него нельзя.
|
|
||||||
|
|
||||||
## Варианты оценки `N`
|
|
||||||
|
|
||||||
### 1. Отдельный порог для каждой модели эмбеддингов
|
|
||||||
|
|
||||||
Автоматический режим `FastClustering` дешев, не добавляет зависимостей и за один
|
|
||||||
проход оценивает число кластеров и назначает метки. На порог влияют архитектура
|
|
||||||
модели эмбеддингов, длительность и состав выбранных окон, акустика и
|
|
||||||
распределение голосов. Локальная калибровка уже не нашла общего рабочего порога
|
|
||||||
для CAMPPlus, а TitaNet не прошел автоматическую сетку
|
|
||||||
([исходное исследование](2026-08-14-diarization-cpu-cost.md),
|
|
||||||
[повторный CPU-тест](../benchmarks/2026-08-30-lightweight-diarization-cpu.md)).
|
|
||||||
|
|
||||||
Вводить еще один `официальный порог модели` оснований нет: исходные рецепты
|
|
||||||
публикуют параметры конкретного конвейера кластеризации, а не переносимую
|
|
||||||
границу для метода полной связи в sherpa. Вариант остается контрольным, но не
|
|
||||||
решает тикет.
|
|
||||||
|
|
||||||
### 2. Счетчик eigengap
|
|
||||||
|
|
||||||
Официальный CAMPPlus-конвейер 3D-Speaker строит матрицу косинусного сходства.
|
|
||||||
В каждой строке он оставляет фиксированную долю ближайших соседей,
|
|
||||||
симметризует матрицу, затем вычисляет ненормированный лапласиан и несколько
|
|
||||||
наименьших собственных пар.
|
|
||||||
Число говорящих задает позиция максимального разрыва собственных значений
|
|
||||||
(`eigengap`) в заданных пределах, после чего собственные векторы
|
|
||||||
кластеризуются k-means
|
|
||||||
([`SpectralCluster`](https://github.com/modelscope/3D-Speaker/blob/065629c313eaf1a01c65c640c46d77e61e9607b4/speakerlab/process/cluster.py#L21-L106)).
|
|
||||||
|
|
||||||
Для нашей двухшаговой схемы нужны только собственные значения и оценка `N`;
|
|
||||||
k-means можно не выполнять. Одного спектрального разложения достаточно для
|
|
||||||
минимального контрольного варианта, но фиксированный `pval` остается новым
|
|
||||||
порогом. Официальная реализация задает его для своего способа нарезки; пары
|
|
||||||
`окно x локальный говорящий` sherpa устроены иначе. Вариант отделяет `N` от
|
|
||||||
порога AHC, но сохраняет калибруемый параметр.
|
|
||||||
|
|
||||||
Если брать реализацию 3D-Speaker как зависимость, она импортирует NumPy, SciPy,
|
|
||||||
scikit-learn, fastcluster, UMAP и HDBSCAN, причем отсутствие UMAP или HDBSCAN
|
|
||||||
роняет уже импорт модуля. Корневой `requirements.txt` при этом не перечисляет
|
|
||||||
fastcluster, UMAP и HDBSCAN
|
|
||||||
([исходник](https://github.com/modelscope/3D-Speaker/blob/065629c313eaf1a01c65c640c46d77e61e9607b4/speakerlab/process/cluster.py#L3-L18),
|
|
||||||
[requirements](https://github.com/modelscope/3D-Speaker/blob/065629c313eaf1a01c65c640c46d77e61e9607b4/requirements.txt)).
|
|
||||||
Для production это несоразмерно счетчику.
|
|
||||||
|
|
||||||
### 3. NME-SC
|
|
||||||
|
|
||||||
NME-SC создан именно для совместной оценки числа кластеров и параметра
|
|
||||||
прореживания без отдельной выборки для настройки. Для каждого числа соседей
|
|
||||||
`p` алгоритм бинаризует матрицу косинусного сходства, симметризует ее, строит
|
|
||||||
ненормированный лапласиан и считает разрывы собственных значений. Нормированный
|
|
||||||
разрыв равен максимальному разрыву, деленному на наибольшее собственное
|
|
||||||
значение; выбирается `p`, минимизирующий отношение доли соседей `p/M` к
|
|
||||||
нормированному разрыву, а `N` задает позиция максимального разрыва
|
|
||||||
([статья авторов, раздел III](https://arxiv.org/abs/2003.02405)).
|
|
||||||
Авторы ограничивали поиск `p` диапазоном до четверти числа строк, а число
|
|
||||||
говорящих числом восемь. Результаты статьи получены на x-vector и англоязычных
|
|
||||||
корпусах, поэтому перенос на наши эмбеддинги еще предстоит проверить.
|
|
||||||
|
|
||||||
Поддерживаемая реализация NeMo разделяет оценку и назначение меток. `NMESC`
|
|
||||||
принимает готовую матрицу сходства и возвращает `est_num_of_spk` вместе с
|
|
||||||
выбранным `p`; следующий слой отдельно выбирает оцененное или известное число
|
|
||||||
говорящих перед спектральной кластеризацией
|
|
||||||
([`NMESC` и выбор числа кластеров](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/nemo/collections/asr/parts/utils/offline_clustering.py#L909-L1041),
|
|
||||||
[`SpeakerClustering.forward_unit_infer`](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/nemo/collections/asr/parts/utils/offline_clustering.py#L1181-L1259)).
|
|
||||||
Более высокий интерфейс NeMo получает многомасштабные эмбеддинги вместе с
|
|
||||||
временной разметкой и сам строит матрицу сходства
|
|
||||||
([`forward_infer`](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/nemo/collections/asr/parts/utils/offline_clustering.py#L1301-L1403)).
|
|
||||||
|
|
||||||
Для ускорения NME NeMo задает целевой размер матрицы 512 строк, перебирает
|
|
||||||
разреженную сетку из 30 значений `p` и предупреждает, что меньше 20 может
|
|
||||||
ухудшить оценку
|
|
||||||
([класс `NMESC`](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/nemo/collections/asr/parts/utils/offline_clustering.py#L872-L977)).
|
|
||||||
Предел не жесткий: регулярный шаг вычисляется целочисленно, поэтому для
|
|
||||||
некоторых размеров после прореживания остается больше 512 строк. Исходник также
|
|
||||||
предупреждает о риске пропустить говорящего с малой долей речи
|
|
||||||
([`subsampleAffinityMat`](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/nemo/collections/asr/parts/utils/offline_clustering.py#L1043-L1066)).
|
|
||||||
Для коротких записей NeMo добавляет случайные якорные эмбеддинги и повторяет
|
|
||||||
оценку. Такое расширение выходит за пределы минимального ядра статьи
|
|
||||||
([исходник](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/nemo/collections/asr/parts/utils/offline_clustering.py#L536-L595)).
|
|
||||||
|
|
||||||
Полная NME-SC после оценки `N` еще раз раскладывает лапласиан, строит
|
|
||||||
спектральные эмбеддинги и запускает k-means. Для этого тикета такая замена
|
|
||||||
итогового кластеризатора не нужна: локальные тесты уже показывают полезный путь
|
|
||||||
`FastClustering` с известным числом говорящих. Нужна только функция
|
|
||||||
`embeddings -> N`.
|
|
||||||
|
|
||||||
## Рекомендуемая граница ответственности
|
|
||||||
|
|
||||||
Здесь речь не об отдельном счетчике, а о способе соединить оценку `N` со
|
|
||||||
штатным кластеризатором:
|
|
||||||
|
|
||||||
```text
|
|
||||||
segmentation -> embeddings +-> счетчик NME -> N
|
|
||||||
+-> FastClustering(features, num_clusters=N)
|
|
||||||
-> штатная финализация sherpa
|
|
||||||
```
|
|
||||||
|
|
||||||
Плюсы:
|
|
||||||
|
|
||||||
- порог метода полной связи больше не определяет число голосовых кластеров;
|
|
||||||
- CAMPPlus и TitaNet используют один модельный проход;
|
|
||||||
- итоговое назначение меток остается в уже принятом `FastClustering`;
|
|
||||||
- на одной матрице можно независимо сравнить несколько счетчиков.
|
|
||||||
|
|
||||||
Минус один, но архитектурный: без нового промежуточного интерфейса текущий
|
|
||||||
`process()` не позволяет вставить счетчик между вычислением матрицы и
|
|
||||||
кластеризацией.
|
|
||||||
|
|
||||||
## Зависимости, лицензии и цена
|
|
||||||
|
|
||||||
Штатная установка NeMo требует среди прочего NumPy, scikit-learn и PyTorch
|
|
||||||
([`pyproject.toml`](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/pyproject.toml)).
|
|
||||||
|
|
||||||
| Вариант | Зависимости прототипа | Работа после эмбеддингов | Ограничение |
|
|
||||||
|---|---|---|---|
|
|
||||||
| Порог `FastClustering` | новых нет | существующие попарные расстояния и AHC | порог зависит от модели и материала |
|
|
||||||
| Счетчик eigengap | NumPy; официальный модуль 3D-Speaker целиком не импортируется | матрица `M x M`, одно собственное разложение | фиксированное прореживание остается параметром |
|
|
||||||
| Полная NME-SC NeMo | `nemo-toolkit`, PyTorch, scikit-learn и другие зависимости NeMo | несколько разложений, затем спектральные эмбеддинги и k-means | меняет итоговый кластеризатор и слишком тяжела для счетчика |
|
|
||||||
| Счетчик NME + `FastClustering` | NumPy | несколько разложений, затем штатный AHC по известному `N` | нужен внутренний интерфейс sherpa и проверка переноса |
|
|
||||||
|
|
||||||
`FastClustering` уже хранит `M(M-1)/2` расстояний и строит AHC. Счетчик eigengap
|
|
||||||
добавляет плотную матрицу сходства и одно собственное разложение. NME
|
|
||||||
повторяет анализ для набора `p`. Полная спектральная кластеризация также хранит
|
|
||||||
собственные векторы и запускает k-means, поэтому для одной оценки `N` избыточна.
|
|
||||||
В проектных замерах основной расход пока приходится на извлечение эмбеддингов,
|
|
||||||
а не на кластеризацию
|
|
||||||
([профиль стоимости](2026-08-14-diarization-cpu-cost.md),
|
|
||||||
[CPU-тест](../benchmarks/2026-08-30-lightweight-diarization-cpu.md)); это не
|
|
||||||
заменяет отдельного замера NME на фактическом числе строк.
|
|
||||||
|
|
||||||
NumPy сейчас есть только в [lock-файле проекта](../../uv.lock), а не в прямых
|
|
||||||
зависимостях `local-transcriber`. Для стенда версию 2.4.3 нужно закрепить явно.
|
|
||||||
Если счетчик дойдет до поставляемого кода, NumPy потребуется объявить прямой
|
|
||||||
зависимостью времени выполнения.
|
|
||||||
|
|
||||||
sherpa-onnx, 3D-Speaker и NeMo Speech распространяют код под Apache-2.0
|
|
||||||
([sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/LICENSE),
|
|
||||||
[3D-Speaker](https://github.com/modelscope/3D-Speaker/blob/065629c313eaf1a01c65c640c46d77e61e9607b4/LICENSE),
|
|
||||||
[NeMo Speech](https://github.com/NVIDIA-NeMo/Speech/blob/d47d7a3c306790637aff39c2d3f9eceb83ca8879/LICENSE)).
|
|
||||||
Авторская реализация NME-SC опубликована под MIT и направляет пользователей к
|
|
||||||
поддерживаемой версии NeMo
|
|
||||||
([репозиторий авторов](https://github.com/tango4j/Auto-Tuning-Spectral-Clustering/tree/ae97f9be9c33b554205c7192c5137ce8f456c1b7)).
|
|
||||||
Файл кластеризации NeMo дополнительно сохраняет BSD-уведомление scikit-learn,
|
|
||||||
NumPy 2.4.3 использует BSD-3-Clause
|
|
||||||
([лицензия](https://github.com/numpy/numpy/blob/v2.4.3/LICENSE.txt)).
|
|
||||||
Лицензионного запрета на прототип нет. Прямое заимствование кода потребует
|
|
||||||
сохранить его уведомления; реализация по формулам статьи должна явно назвать
|
|
||||||
происхождение алгоритма.
|
|
||||||
|
|
||||||
## Ограничения вывода
|
|
||||||
|
|
||||||
- NME-SC проверялась авторами на другой нарезке и эмбеддингах x-vector. Строки
|
|
||||||
sherpa представляют локальных говорящих в перекрывающихся окнах Pyannote после
|
|
||||||
исключения пересечений речи;
|
|
||||||
переносимость нельзя считать доказанной.
|
|
||||||
- Правильное `N` не гарантирует качественную разметку говорящих. Для TitaNet в
|
|
||||||
режиме известного числа кластеров уже наблюдался малый кластер, поэтому нужны
|
|
||||||
прежние прокси-метрики и слуховая проверка.
|
|
||||||
- У sherpa есть отдельный путь для единственного окна, который обходится без
|
|
||||||
общей матрицы эмбеддингов. Стенд должен сохранить этот частный случай, а также
|
|
||||||
текущую обработку пустого результата и нечисловых эмбеддингов.
|
|
||||||
- Python-модуль `FastClustering` нормирует переданную матрицу на месте;
|
|
||||||
сравниваемым вариантам нужны отдельные C-contiguous копии.
|
|
||||||
- Оцененное `N` нужно проверять до вызова `FastClustering`:
|
|
||||||
`1 <= N <= min(8, M)`, где `M` обозначает число валидных строк. Некорректное
|
|
||||||
значение считается ошибкой ячейки, а не молча ограничивается.
|
|
||||||
- Прореживание строк может потерять голосовой кластер с малой долей речи.
|
|
||||||
Поэтому полный набор строк задает основной результат, а сокращенная матрица
|
|
||||||
годится только для отдельной проверки чувствительности.
|
|
||||||
- `numpy.linalg.eigvalsh` использует BLAS/LAPACK. Без закрепленного числа
|
|
||||||
потоков его процессорное и фактическое время нельзя сравнивать между
|
|
||||||
машинами.
|
|
||||||
|
|
||||||
## Точная граница будущего прототипа
|
|
||||||
|
|
||||||
Production-код, CLI, модели по умолчанию и ADR не менять. Сделать отдельный
|
|
||||||
исследовательский стенд на sherpa-onnx 1.13.5 и сохранить точный хеш коммита
|
|
||||||
экспериментальной сборки рядом с результатом.
|
|
||||||
|
|
||||||
### Внутренний интерфейс sherpa
|
|
||||||
|
|
||||||
В экспериментальной ветке sherpa разделить внутренний `Process` в одном месте:
|
|
||||||
после удаления нечисловых строк и синхронной фильтрации пар
|
|
||||||
`окно x локальный говорящий`, до `clustering_->Cluster`.
|
|
||||||
|
|
||||||
- `Prepare(audio)` либо возвращает готовый результат существующего частного
|
|
||||||
пути, либо владеющий объект `PreparedDiarization`. Объект хранит матрицу
|
|
||||||
`float32`, выровненные пары, локальные покадровые метки, число говорящих по
|
|
||||||
кадрам и длину аудио, необходимые текущей финализации.
|
|
||||||
- Экспериментальный Python-модуль отдает C-contiguous копию матрицы и
|
|
||||||
непрозрачный дескриптор подготовленного состояния. Дескриптор живет только в
|
|
||||||
памяти процесса.
|
|
||||||
- `Finalize(handle, row_labels)` проверяет число меток и выполняет существующие
|
|
||||||
`ConvertChunkSpeakerToCluster`, `ReLabel`, `FinalizeLabels` и `ComputeResult`.
|
|
||||||
Так все счетчики используют одну подготовку и одну штатную финализацию.
|
|
||||||
- Пустая речь, одно окно, отсутствие валидных эмбеддингов и одна валидная строка
|
|
||||||
сохраняют нынешнее поведение. Аудио, текст, матрица и дескриптор не
|
|
||||||
сериализуются.
|
|
||||||
|
|
||||||
### Закрепленные входы и счетчики
|
|
||||||
|
|
||||||
Использовать только модель Pyannote Segmentation 3.0 FP32 `model.onnx`
|
|
||||||
(`220ad67ca923bef2fa91f2390c786097bf305bceb5e261d4af67b38e938e1079`) и две
|
|
||||||
модели эмбеддингов из предыдущих измерений:
|
|
||||||
|
|
||||||
- CAMPPlus zh/en
|
|
||||||
(`aa3cfc16963a10586a9393f5035d6d6b57e98d358b347f80c2a30bf4f00ceba2`);
|
|
||||||
- TitaNet small
|
|
||||||
(`ad4a1802485d8b34c722d2a9d04249662f2ece5d28a7a039063ca22f515a789e`).
|
|
||||||
|
|
||||||
Полные SHA-256 и происхождение уже записаны в
|
|
||||||
[калибровке CAMPPlus](../benchmarks/2026-08-14-diarization-calibration.md#модели)
|
|
||||||
и [исследовании TitaNet](2026-08-29-lightweight-diarization-models.md#проверенные-sha-256).
|
|
||||||
INT8-сегментация в эту матрицу не входит: она меняет локальные покадровые метки
|
|
||||||
и уже отклонена по результатам отдельного CPU-теста.
|
|
||||||
|
|
||||||
На каждой исходной матрице сравнить:
|
|
||||||
|
|
||||||
1. диагностический контроль `FastClustering`: для CAMPPlus пороги `1,05` и
|
|
||||||
`1,10`; для TitaNet использовать наименее плохую, но отклоненную точку
|
|
||||||
`1,10`;
|
|
||||||
2. счетчик eigengap по схеме 3D-Speaker с `pval=0.012`, `min_pnum=6`, без
|
|
||||||
спектральных эмбеддингов и k-means;
|
|
||||||
3. счетчик NME по контракту ниже.
|
|
||||||
|
|
||||||
Контрольные пороги не становятся допустимыми настройками. Они только
|
|
||||||
воспроизводят уже наблюдавшийся переход между лишними кластерами и склейкой.
|
|
||||||
|
|
||||||
### Контракт счетчика NME
|
|
||||||
|
|
||||||
Контракт следует формулам статьи и варианту Sparse-Search-20 из
|
|
||||||
[кода авторов](https://github.com/tango4j/Auto-Tuning-Spectral-Clustering/blob/ae97f9be9c33b554205c7192c5137ce8f456c1b7/spectral_opt.py#L463-L495):
|
|
||||||
|
|
||||||
| Параметр | Значение |
|
|
||||||
|---|---|
|
|
||||||
| Вход | все `M` валидных строк; строки конечны и имеют ненулевую норму |
|
|
||||||
| Матрица сходства | нормировать строки на копии, затем `A = E @ E.T` |
|
|
||||||
| Диапазон `p` | `1..floor(M/4)`; при `M < 6` ячейка счетчика помечается пропуском, а диагностический `FastClustering` выполняется независимо |
|
|
||||||
| Разреженный поиск | все значения, если верхняя граница не больше 20; иначе 20 целых точек `linspace` с обоими концами и удалением дублей |
|
|
||||||
| Бинаризация | сохранить top-`p` в каждой строке, включая диагональ; равные значения разрешать по меньшему индексу столбца |
|
|
||||||
| Граф | `(A_p + A_p.T) / 2`, затем обнулить диагональ |
|
|
||||||
| Лапласиан | ненормированный `L = D - A`, `D_ii = sum_j(abs(A_ij))` |
|
|
||||||
| Собственные значения | `numpy.linalg.eigvalsh`, по возрастанию; `epsilon = 1e-10` |
|
|
||||||
| Оценка `N` | `N = 1 + argmax(gaps[:min(8, M - 1)])`; при равенстве брать наименьший индекс |
|
|
||||||
| Выбор `p` | `argmin` по `(p/M) / (max_gap / (lambda_max + epsilon) + epsilon)`; при равенстве брать наименьшее `p` |
|
|
||||||
| Связность | если выбранный граф несвязен, взять первое большее `p` из той же сетки, которое делает его связным; отсутствие такого `p` считать ошибкой ячейки |
|
|
||||||
|
|
||||||
После коррекции связности использовать `N`, рассчитанное для итогового `p`.
|
|
||||||
Статья и код авторов не задают стабильное разрешение равных значений или
|
|
||||||
поведение при слишком малой матрице; таблица фиксирует эти два локальных
|
|
||||||
правила воспроизводимости. Основной расчет идет по всем строкам, как в
|
|
||||||
авторской реализации.
|
|
||||||
|
|
||||||
До прогона на рабочих записях закрепить две синтетические фикстуры с
|
|
||||||
блочными матрицами сходства и ожидаемыми `N=2` и `N=3`. Они проверяют
|
|
||||||
сортировку и разрешение равных значений в `argmax` и `argmin`.
|
|
||||||
Якорные эмбеддинги, Torch, спектральные эмбеддинги и k-means в этот счетчик не
|
|
||||||
входят.
|
|
||||||
|
|
||||||
Основной результат считать на всех строках. Если `M > 512`, дополнительно
|
|
||||||
повторить NME с регулярным прореживанием NeMo, записать `M` до и после и
|
|
||||||
сравнить `N`. Сокращенная матрица не заменяет полный результат даже при
|
|
||||||
совпадении.
|
|
||||||
|
|
||||||
### Проверка и граница решения
|
|
||||||
|
|
||||||
Для каждой модели один раз подготовить матрицу на тех же трех пятиминутных
|
|
||||||
[контрольных записях](../benchmarks/2026-08-30-lightweight-diarization-cpu.md)
|
|
||||||
и на ее независимых копиях выполнить все счетчики. Критерии качества:
|
|
||||||
|
|
||||||
- оцененное число голосовых кластеров точно равно `3 / 2 / 2` на трех
|
|
||||||
контрольных записях для каждой модели отдельно; содержательные кластеры не
|
|
||||||
компенсируют ошибку счета;
|
|
||||||
- метки строк и финальный результат для оцененного `N` совпадают с отдельным
|
|
||||||
вызовом известного `N` на той же матрице;
|
|
||||||
- повторный счет на той же матрице дает те же `p`, `N` и метки;
|
|
||||||
- сохранены прежняя метрика `mapped speaker purity`, число слов без говорящего и
|
|
||||||
диагностика малых кластеров; прогон CAMPPlus и TitaNet заканчивается слуховой
|
|
||||||
проверкой.
|
|
||||||
|
|
||||||
Для каждого счетчика отдельно записать фактическое и процессорное время,
|
|
||||||
`peak RSS`, форму матрицы, конфигурацию NumPy/BLAS и эффективное число потоков.
|
|
||||||
Процесс запускать с одним потоком BLAS/LAPACK; способ ограничения и вывод
|
|
||||||
`numpy.show_config()` входят в результат.
|
|
||||||
|
|
||||||
Числовой гейт времени и памяти эта записка не задает: его определит
|
|
||||||
[Зафиксировать аппаратную матрицу и бюджет памяти](https://git.dementev.space/ddmitry/local-transcriber/issues/35),
|
|
||||||
а общую матрицу соберет
|
|
||||||
[Собрать минимальную матрицу следующего CPU-эксперимента](https://git.dementev.space/ddmitry/local-transcriber/issues/34).
|
|
||||||
До этих решений время и память служат измерениями, но не основанием объявить
|
|
||||||
счетчик принятым.
|
|
||||||
|
|
||||||
Если счетчики eigengap и NME проходят критерии качества, в следующую матрицу
|
|
||||||
идет более дешевый. Если проходит только NME, отдельно решается перенос чистой
|
|
||||||
NumPy-реализации в production. Если оба ошибаются, CAMPPlus и TitaNet остаются
|
|
||||||
кандидатами только для явно заданного числа говорящих.
|
|
||||||
|
|
||||||
Прототип проверяет переносимость оценки `N` на внутренние строки sherpa и цену
|
|
||||||
счетчика. Он не выбирает новую модель по умолчанию, не заменяет
|
|
||||||
`FastClustering` и не расширяет продуктовый контракт.
|
|
||||||
@@ -7,17 +7,15 @@
|
|||||||
## Вопрос и ответ
|
## Вопрос и ответ
|
||||||
|
|
||||||
Первый опубликованный релиз, в котором `window_shift_ratio` доступен из
|
Первый опубликованный релиз, в котором `window_shift_ratio` доступен из
|
||||||
готового Python wheel для offline speaker diarization: **sherpa-onnx 1.13.6**
|
готового Python wheel для offline speaker diarization, - **sherpa-onnx 1.13.6**
|
||||||
от 18 августа 2026 года. В нем параметр входит в публичный Python-конструктор
|
от 18 августа 2026 года. В нем параметр входит в публичный Python-конструктор
|
||||||
`OfflineSpeakerSegmentationPyannoteModelConfig` и доступен как изменяемое поле.
|
`OfflineSpeakerSegmentationPyannoteModelConfig` и доступен как изменяемое поле.
|
||||||
Это подтверждают [binding тега
|
Это подтверждают [binding тега
|
||||||
v1.13.6](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L50-L59),
|
v1.13.6](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L45-L54),
|
||||||
[пример Python API](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/python-api-examples/offline-speaker-diarization.py#L62-L80)
|
[пример Python API](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/python-api-examples/offline-speaker-diarization.py#L62-L80)
|
||||||
и [состав релиза](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.6).
|
и [состав релиза](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.6).
|
||||||
Готовые wheels появились на PyPI в тот же день
|
Готовые wheels появились на PyPI в тот же день
|
||||||
([метаданные 1.13.6](https://pypi.org/pypi/sherpa-onnx/1.13.6/json)). На дату
|
([метаданные 1.13.6](https://pypi.org/pypi/sherpa-onnx/1.13.6/json)).
|
||||||
исследования 1.13.6 также остается [текущей версией пакета на
|
|
||||||
PyPI](https://pypi.org/pypi/sherpa-onnx/json).
|
|
||||||
|
|
||||||
С 1.13.5 можно перейти **обычным обновлением до 1.13.6**. Частный binding,
|
С 1.13.5 можно перейти **обычным обновлением до 1.13.6**. Частный binding,
|
||||||
backport и новый запрос upstream не нужны: нужный follow-up уже принят и
|
backport и новый запрос upstream не нужны: нужный follow-up уже принят и
|
||||||
@@ -29,12 +27,12 @@ backport и новый запрос upstream не нужны: нужный follo
|
|||||||
|---|---|
|
|---|---|
|
||||||
| До 23.07.2026 | Шаг был зашит в C++ как 10% окна. Это исходная проблема, описанная upstream в [PR #3769](https://github.com/k2-fsa/sherpa-onnx/pull/3769). |
|
| До 23.07.2026 | Шаг был зашит в C++ как 10% окна. Это исходная проблема, описанная upstream в [PR #3769](https://github.com/k2-fsa/sherpa-onnx/pull/3769). |
|
||||||
| 23.07.2026, commit [`797e6e9`](https://github.com/k2-fsa/sherpa-onnx/commit/797e6e9) | `window_shift_ratio` добавлен в C++-ядро и CLI. Допустимый диапазон: `(0, 1]`, значение по умолчанию: `0,1`. C API и языковые binding намеренно оставлены для follow-up ([PR #3769](https://github.com/k2-fsa/sherpa-onnx/pull/3769)). |
|
| 23.07.2026, commit [`797e6e9`](https://github.com/k2-fsa/sherpa-onnx/commit/797e6e9) | `window_shift_ratio` добавлен в C++-ядро и CLI. Допустимый диапазон: `(0, 1]`, значение по умолчанию: `0,1`. C API и языковые binding намеренно оставлены для follow-up ([PR #3769](https://github.com/k2-fsa/sherpa-onnx/pull/3769)). |
|
||||||
| 11.08.2026, v1.13.5 | Первый релиз C++-ядра и CLI с параметром ([release notes](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.5)). Python binding этой версии принимает только `model` и не экспортирует поле ([исходник v1.13.5](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L50-L57)). |
|
| 11.08.2026, v1.13.5 | Первый релиз C++-ядра и CLI с параметром ([release notes](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.5)). Python binding этой версии принимает только `model` и не экспортирует поле ([исходник v1.13.5](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.5/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L45-L52)). |
|
||||||
| 13.08.2026, commit [`3e40933`](https://github.com/k2-fsa/sherpa-onnx/commit/3e409338959097c6518998c9b72757db257f5f6f) | Параметр проведен через публичные C/CXX API и языковые binding, включая Python; PR #3870 принят upstream ([описание и мотивировка](https://github.com/k2-fsa/sherpa-onnx/pull/3870)). |
|
| 13.08.2026, commit [`3e40933`](https://github.com/k2-fsa/sherpa-onnx/commit/3e409338959097c6518998c9b72757db257f5f6f) | Параметр проведен через публичные C/CXX API и языковые binding, включая Python; PR #3870 принят upstream ([описание и мотивировка](https://github.com/k2-fsa/sherpa-onnx/pull/3870)). |
|
||||||
| 18.08.2026, v1.13.6 | Первый релиз с публичным Python API. На PyPI есть cp313 и cp314 wheels без отметки yanked для всех платформ проекта ([release](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.6), [PyPI JSON](https://pypi.org/pypi/sherpa-onnx/1.13.6/json)). |
|
| 18.08.2026, v1.13.6 | Первый релиз с публичным Python API. На PyPI есть cp313 и cp314 wheels без отметки yanked для всех платформ проекта ([release](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.6), [PyPI JSON](https://pypi.org/pypi/sherpa-onnx/1.13.6/json)). |
|
||||||
|
|
||||||
Граница проходит между двумя релизами: **1.13.5** дает C++-ядро и CLI, а в
|
Граница проходит между двумя релизами: **1.13.5** дает C++-ядро и CLI,
|
||||||
**1.13.6** доступны C, CXX wrapper, Python и остальные binding.
|
**1.13.6** - C, CXX wrapper, Python и остальные binding.
|
||||||
|
|
||||||
## Публичный контракт 1.13.6
|
## Публичный контракт 1.13.6
|
||||||
|
|
||||||
@@ -47,17 +45,17 @@ pyannote = sherpa_onnx.OfflineSpeakerSegmentationPyannoteModelConfig(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Конструктор имеет сигнатуру `(model, window_shift_ratio=0.1)`. Объект
|
Конструктор имеет сигнатуру `(model, window_shift_ratio=0.1)`, а объект - поле
|
||||||
предоставляет изменяемое поле `.window_shift_ratio`. Прежний вызов только с
|
`.window_shift_ratio`. Прежний вызов только с `model` остается допустимым и
|
||||||
`model` остается допустимым и сохраняет прежнее поведение
|
сохраняет прежнее поведение
|
||||||
([binding](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L50-L59)).
|
([binding](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/python/csrc/offline-speaker-diarization.cc#L45-L54)).
|
||||||
Нативная валидация принимает только `(0, 1]`; ноль в Python не включает default
|
Нативная валидация принимает только `(0, 1]`; ноль в Python не включает default
|
||||||
([конфигурация ядра](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-segmentation-pyannote-model-config.cc#L24-L39)).
|
([конфигурация ядра](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-segmentation-pyannote-model-config.cc)).
|
||||||
|
|
||||||
Параметр читается при создании segmentation-модели. Метод
|
Параметр читается при создании segmentation-модели. Метод
|
||||||
`OfflineSpeakerDiarization.set_config()` обновляет только clustering, поэтому
|
`OfflineSpeakerDiarization.set_config()` обновляет только clustering, поэтому
|
||||||
для каждой точки сетки нужен новый diarizer
|
для каждой точки сетки нужен новый diarizer
|
||||||
([реализация](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L87-L94)).
|
([реализация](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L79-L86)).
|
||||||
|
|
||||||
### C и C++
|
### C и C++
|
||||||
|
|
||||||
@@ -66,10 +64,10 @@ pyannote = sherpa_onnx.OfflineSpeakerSegmentationPyannoteModelConfig(
|
|||||||
Значение должно лежать в `(0, 1]`; только C-конвертер трактует `<= 0` как
|
Значение должно лежать в `(0, 1]`; только C-конвертер трактует `<= 0` как
|
||||||
незаданное и подставляет `0,1`, чтобы не сломать вызывающий код с
|
незаданное и подставляет `0,1`, чтобы не сломать вызывающий код с
|
||||||
zero-initialized struct
|
zero-initialized struct
|
||||||
([C header](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/c-api/c-api.h#L3839-L3846),
|
([C header](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/c-api/c-api.h#L3597-L3611),
|
||||||
[конвертер](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/c-api/c-api.cc#L3145-L3156)).
|
[конвертер](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/c-api/c-api.cc#L2912-L2922)).
|
||||||
- Публичный CXX wrapper получил поле `float window_shift_ratio = 0.1f`
|
- Публичный CXX wrapper получил поле `float window_shift_ratio = 0.1f`
|
||||||
([cxx-api.h](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/c-api/cxx-api.h#L1911-L1917)).
|
([cxx-api.h](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/c-api/cxx-api.h#L1770-L1790)).
|
||||||
- Внутренний C++-контракт ядра существовал уже в 1.13.5, но не давал
|
- Внутренний C++-контракт ядра существовал уже в 1.13.5, но не давал
|
||||||
приложению на Python поддерживаемого пути. Подменять им binding больше нет
|
приложению на Python поддерживаемого пути. Подменять им binding больше нет
|
||||||
смысла.
|
смысла.
|
||||||
@@ -90,19 +88,13 @@ pyannote = sherpa_onnx.OfflineSpeakerSegmentationPyannoteModelConfig(
|
|||||||
| CPython 3.13/3.14, macOS x86-64 | `macosx_10_15_x86_64` или `universal2` | те же platform tags | Готовый комплект есть; macOS в проекте не проходил ручную приемку |
|
| CPython 3.13/3.14, macOS x86-64 | `macosx_10_15_x86_64` или `universal2` | те же platform tags | Готовый комплект есть; macOS в проекте не проходил ручную приемку |
|
||||||
| CPython 3.13/3.14, macOS ARM64 | `macosx_11_0_arm64` или `universal2` | те же platform tags | Готовый комплект есть; macOS в проекте не проходил ручную приемку |
|
| CPython 3.13/3.14, macOS ARM64 | `macosx_11_0_arm64` или `universal2` | те же platform tags | Готовый комплект есть; macOS в проекте не проходил ручную приемку |
|
||||||
|
|
||||||
Linux-поставка рассчитана на glibc 2.17 и новее: `musllinux` wheels в релизе
|
Источник файлов основной поставки - [PyPI 1.13.6
|
||||||
нет. Поэтому таблица не подтверждает готовую бинарную установку на Alpine или
|
JSON](https://pypi.org/pypi/sherpa-onnx/1.13.6/json), нативной части -
|
||||||
другом Linux с musl.
|
|
||||||
|
|
||||||
Источник файлов основной поставки: [PyPI 1.13.6
|
|
||||||
JSON](https://pypi.org/pypi/sherpa-onnx/1.13.6/json), нативной части:
|
|
||||||
[PyPI core 1.13.6 JSON](https://pypi.org/pypi/sherpa-onnx-core/1.13.6/json).
|
[PyPI core 1.13.6 JSON](https://pypi.org/pypi/sherpa-onnx-core/1.13.6/json).
|
||||||
Метаданные основного пакета объявляют `Requires-Python >=3.7` и единственную
|
Метаданные основного пакета объявляют `Requires-Python >=3.7` и единственную
|
||||||
зависимость `sherpa-onnx-core==1.13.6`; core дополнительных `Requires-Dist` не
|
зависимость `sherpa-onnx-core==1.13.6`; core дополнительных `Requires-Dist` не
|
||||||
объявляет. Универсального `abi3` wheel нет: при сохранении открытой границы
|
объявляет. Универсального `abi3` wheel нет: при сохранении открытой границы
|
||||||
Python `>=3.13` готовая бинарная установка на будущем CPython 3.15 потребует
|
Python `>=3.13` будущий CPython 3.15 потребует нового upstream wheel.
|
||||||
нового upstream wheel. Исходный архив опубликован, но локальная нативная сборка
|
|
||||||
для 3.15 в это исследование не входила.
|
|
||||||
|
|
||||||
Для обновления проекта нужно синхронно поменять три места: пин
|
Для обновления проекта нужно синхронно поменять три места: пин
|
||||||
`sherpa-onnx==1.13.6`, uv override на `sherpa-onnx-core==1.13.6` и lock-файл.
|
`sherpa-onnx==1.13.6`, uv override на `sherpa-onnx-core==1.13.6` и lock-файл.
|
||||||
@@ -126,11 +118,9 @@ Python-зависимостей между 1.13.5 и 1.13.6 нет
|
|||||||
- Остальные изменения 1.13.6 относятся к сборке Android/Java, SPM,
|
- Остальные изменения 1.13.6 относятся к сборке Android/Java, SPM,
|
||||||
Flutter/Dart и примерам VAD+ASR. Релиз не заявляет удаления Python API или
|
Flutter/Dart и примерам VAD+ASR. Релиз не заявляет удаления Python API или
|
||||||
смены формата моделей ([release notes](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.6)).
|
смены формата моделей ([release notes](https://github.com/k2-fsa/sherpa-onnx/releases/tag/v1.13.6)).
|
||||||
- Временное окружение CPython 3.13.14 на Linux x86-64 установило согласованную
|
- Наличие wheel не заменяет smoke test нативной загрузки. Для проекта нужны
|
||||||
пару `sherpa-onnx==1.13.6` и `sherpa-onnx-core==1.13.6`. Импорт и создание
|
хотя бы Python 3.13 на Linux x86-64 и Windows x86-64. Для macOS подтверждена
|
||||||
конфигураций со всеми пятью значениями сетки прошли. Для Windows и macOS пока
|
только upstream-поставка.
|
||||||
подтверждена только upstream-поставка; при обновлении проекта нужен отдельный
|
|
||||||
smoke test Windows x86-64 и повтор Linux-проверки из lock-файла.
|
|
||||||
|
|
||||||
## Что именно меняет параметр
|
## Что именно меняет параметр
|
||||||
|
|
||||||
@@ -138,8 +128,8 @@ Python-зависимостей между 1.13.5 и 1.13.6 нет
|
|||||||
и при debug печатает фактическое значение. Затем запись режется на окна по этому
|
и при debug печатает фактическое значение. Затем запись режется на окна по этому
|
||||||
шагу; для записи длиннее одного окна число segmentation-запусков равно
|
шагу; для записи длиннее одного окна число segmentation-запусков равно
|
||||||
`floor((n - W) / S) + 1` плюс одно дополненное нулями окно, если есть остаток
|
`floor((n - W) / S) + 1` плюс одно дополненное нулями окно, если есть остаток
|
||||||
([расчет шага](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-segmentation-pyannote-model.cc#L92-L123),
|
([расчет шага](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-segmentation-pyannote-model.cc#L85-L108),
|
||||||
[цикл окон](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L278-L337)).
|
[цикл окон](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L256-L312)).
|
||||||
|
|
||||||
Для Pyannote Segmentation 3.0 с окном `160000` сэмплов при 16 кГц сетка дает:
|
Для Pyannote Segmentation 3.0 с окном `160000` сэмплов при 16 кГц сетка дает:
|
||||||
|
|
||||||
@@ -154,8 +144,8 @@ Python-зависимостей между 1.13.5 и 1.13.6 нет
|
|||||||
Это не прогноз полного ускорения. После segmentation движок создает отдельное
|
Это не прогноз полного ускорения. После segmentation движок создает отдельное
|
||||||
задание embedding для каждой достаточно длинной пары `(окно, локальный
|
задание embedding для каждой достаточно длинной пары `(окно, локальный
|
||||||
говорящий)`. Их число зависит от речи и перекрытий
|
говорящий)`. Их число зависит от речи и перекрытий
|
||||||
([формирование пар](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L408-L484),
|
([формирование пар и embedding](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L376-L445),
|
||||||
[цикл embedding](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L510-L564)).
|
[цикл embedding](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L469-L528)).
|
||||||
Upstream получил на одном английском файле 1,48× при `0,15` и 2,00× при `0,20`,
|
Upstream получил на одном английском файле 1,48× при `0,15` и 2,00× при `0,20`,
|
||||||
но сам ограничил вывод этим материалом; переносить коэффициенты на русские
|
но сам ограничил вывод этим материалом; переносить коэффициенты на русские
|
||||||
созвоны нельзя ([измерение в PR #3769](https://github.com/k2-fsa/sherpa-onnx/pull/3769)).
|
созвоны нельзя ([измерение в PR #3769](https://github.com/k2-fsa/sherpa-onnx/pull/3769)).
|
||||||
@@ -181,7 +171,7 @@ Upstream получил на одном английском файле 1,48×
|
|||||||
- финальный `num_total_chunks` progress callback как число embedding-заданий.
|
- финальный `num_total_chunks` progress callback как число embedding-заданий.
|
||||||
Название callback вводит в заблуждение: реализация вызывает его внутри
|
Название callback вводит в заблуждение: реализация вызывает его внутри
|
||||||
цикла по `(окно, локальный говорящий)`. В segmentation-цикле вызова нет
|
цикла по `(окно, локальный говорящий)`. В segmentation-цикле вызова нет
|
||||||
([исходник](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L517-L564));
|
([исходник](https://github.com/k2-fsa/sherpa-onnx/blob/v1.13.6/sherpa-onnx/csrc/offline-speaker-diarization-pyannote-impl.h#L469-L520));
|
||||||
- время segmentation, embedding, clustering, total и RTF из штатного
|
- время segmentation, embedding, clustering, total и RTF из штатного
|
||||||
debug-профиля, а также wall time, CPU и peak RSS процесса.
|
debug-профиля, а также wall time, CPU и peak RSS процесса.
|
||||||
5. Чтобы не принять ускорение ценой поломки результата, сохранить прежние
|
5. Чтобы не принять ускорение ценой поломки результата, сохранить прежние
|
||||||
@@ -191,8 +181,8 @@ Upstream получил на одном английском файле 1,48×
|
|||||||
неизменность распознанного текста. Для кандидата после сетки нужна отдельная
|
неизменность распознанного текста. Для кандидата после сетки нужна отдельная
|
||||||
слуховая проверка коротких ответов, смен говорящего и перекрывающейся речи.
|
слуховая проверка коротких ответов, смен говорящего и перекрывающейся речи.
|
||||||
|
|
||||||
Не входят в прототип: выбор нового production default, пользовательский
|
Не входят в прототип: выбор нового production default, пользовательский CLI-
|
||||||
параметр CLI, повторная настройка clustering, сравнение embedding-моделей и
|
параметр, повторная настройка clustering, сравнение embedding-моделей и
|
||||||
параллельный запуск ASR. Эти решения возможны только после сетки. Ускорение
|
параллельный запуск ASR. Эти решения возможны только после сетки. Ускорение
|
||||||
должно сопровождаться ожидаемым снижением числа segmentation-окон и
|
должно сопровождаться ожидаемым снижением числа segmentation-окон и
|
||||||
embedding-заданий. По одному wall time нельзя связать ускорение с шагом окна.
|
embedding-заданий. По одному wall time нельзя связать ускорение с шагом окна.
|
||||||
@@ -200,10 +190,7 @@ embedding-заданий. По одному wall time нельзя связат
|
|||||||
## Рекомендация
|
## Рекомендация
|
||||||
|
|
||||||
Выбрать **обычное обновление до 1.13.6** и сначала проверить его в отдельном
|
Выбрать **обычное обновление до 1.13.6** и сначала проверить его в отдельном
|
||||||
прототипе. Обновление дает проекту штатную ручку для сокращения основной
|
прототипе. Малый backport дублировал бы уже выпущенный upstream-код и потребовал
|
||||||
CPU-работы; свежесть зависимости сама по себе здесь не аргумент. Малый backport
|
бы собственного нативного wheel. Запрос upstream уже фактически выполнен PR
|
||||||
дублировал бы уже выпущенный upstream-код и потребовал бы собственного нативного
|
#3870. Production default оставить `0,1`, пока сетка не подтвердит одновременно
|
||||||
wheel.
|
снижение работы, приемлемую скорость и сохранение качества разметки говорящих.
|
||||||
Запрос upstream уже фактически выполнен PR #3870. Production default оставить
|
|
||||||
`0,1`, пока сетка не подтвердит одновременно снижение работы, приемлемую
|
|
||||||
скорость и сохранение качества разметки говорящих.
|
|
||||||
Reference in new issue
Block a user