chore(diarization): добавлена обвязка замеров в .scratch
- Зачем:
- тикеты карты #10-#13 опираются на измерительную обвязку, которая до сих пор
жила во временном каталоге сессии и исчезла бы вместе с ним.
- Что:
- перенесены четыре скрипта разведки: ASR, один прогон диаризации, свип порога
кластеризации и подсчёт чистоты ASR-сегментов.
- общая часть вынесена в common.py: пути от корня репозитория вместо
захардкоженных, конфигурация диаризатора, проверка наличия моделей.
- починен замер пиковой памяти: нужен экспорт K32GetProcessMemoryInfo из
kernel32 и явные argtypes, иначе дескриптор процесса уезжает 32-битным.
- модели и выход замеров исключены из истории локальным .gitignore.
- Проверка:
- export PYTHONIOENCODING=utf-8
- uv run --with sherpa-onnx python .scratch/diarization/bench_diar.py "<запись>" 8 0.9
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
87030e9718
commit
1cb6a36c92
@@ -0,0 +1,62 @@
|
||||
"""Свип порога кластеризации и явного числа говорящих.
|
||||
|
||||
uv run --with sherpa-onnx python .scratch/diarization/bench_sweep.py <файл> [потоки]
|
||||
|
||||
Каждая конфигурация — полный прогон сегментации и эмбеддингов (около 2,5 минут
|
||||
на 26-минутную запись), поэтому свип имеет смысл вести на коротком фрагменте, а
|
||||
полные записи оставить для проверки финального кандидата.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
from common import DEFAULT_THREADS, SAMPLE_RATE, load_audio, make_diarizer
|
||||
|
||||
# подпись, num_clusters, threshold
|
||||
CONFIGS = [
|
||||
("авто, порог 0.5", -1, 0.5),
|
||||
("авто, порог 0.7", -1, 0.7),
|
||||
("авто, порог 0.9", -1, 0.9),
|
||||
("явно k=5", 5, 0.5),
|
||||
]
|
||||
|
||||
# говорящий с речью короче порога считается остаточным кластером, не участником
|
||||
MIN_SPEAKER_S = 30.0
|
||||
|
||||
|
||||
def main(audio_path: str, threads: int) -> None:
|
||||
samples = load_audio(audio_path)
|
||||
duration = len(samples) / SAMPLE_RATE
|
||||
print(f"файл: {audio_path}")
|
||||
print(f"длительность: {duration / 60:.1f} мин, потоков: {threads}\n")
|
||||
|
||||
for label, num_clusters, threshold in CONFIGS:
|
||||
diarizer = make_diarizer(
|
||||
threshold=threshold, num_clusters=num_clusters, threads=threads
|
||||
)
|
||||
t0 = time.perf_counter()
|
||||
segments = diarizer.process(samples).sort_by_start_time()
|
||||
elapsed = time.perf_counter() - t0
|
||||
|
||||
totals: dict[int, float] = {}
|
||||
for seg in segments:
|
||||
totals[seg.speaker] = totals.get(seg.speaker, 0.0) + (seg.end - seg.start)
|
||||
real = [spk for spk, t in totals.items() if t >= MIN_SPEAKER_S]
|
||||
top = sorted(totals.values(), reverse=True)[:8]
|
||||
|
||||
print(f"--- {label}")
|
||||
print(
|
||||
f" {elapsed:.0f} с ({duration / elapsed:.1f}x RTF), "
|
||||
f"говорящих: {len(totals)}, из них >= {MIN_SPEAKER_S:.0f} с речи: {len(real)}, "
|
||||
f"интервалов: {len(segments)}"
|
||||
)
|
||||
print(" топ по времени (мин): " + ", ".join(f"{t / 60:.1f}" for t in top))
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
raise SystemExit(__doc__)
|
||||
main(sys.argv[1], int(sys.argv[2]) if len(sys.argv) > 2 else DEFAULT_THREADS)
|
||||
Reference in New Issue
Block a user