Оценка разделения больше не роняет задачу, журнал доступен через API
Полный трейс показал настоящую причину сбоя: падало не распознавание, а метрика separation_quality из 0.5.0 - она скармливала модели отпечатков реплики целиком, и на 190-секундной та не выдержала. Теперь берётся кусок из середины реплики, а сбой оценки лишь обнуляет её, не трогая расшифровку. Добавлен /v1/logs: последние записи журнала с фильтром по уровню, чтобы разбирать сбои без копирования консоли вручную. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f2f266ba1c
commit
710fc8b90b
+22
-6
@@ -26,6 +26,9 @@ SAMPLE_RATE = 16000
|
||||
MAX_CHUNK_SEC = 60.0
|
||||
# Ниже этого делить бессмысленно: явно не длина виновата.
|
||||
MIN_SPLIT_SEC = 2.0
|
||||
# Модель отпечатков рассчитана на короткий фрагмент и на длинном падает.
|
||||
# Для оценки голоса секунд более чем достаточно.
|
||||
EMBED_SEC = 8.0
|
||||
# Ниже этого значения голоса практически неразличимы и разметка по говорящим
|
||||
# случайна. На записях с одним микрофоном в комнате так бывает часто.
|
||||
RELIABLE_SEPARATION = 0.35
|
||||
@@ -169,15 +172,23 @@ class Pipeline:
|
||||
self._embedder = sherpa_onnx.SpeakerEmbeddingExtractor(
|
||||
sherpa_onnx.SpeakerEmbeddingExtractorConfig(
|
||||
model=str(self.models_dir / EMB_MODEL_REL), num_threads=self.threads))
|
||||
vectors = []
|
||||
vectors, labels = [], []
|
||||
for seg in usable:
|
||||
# Берём кусок из середины реплики: там речь устойчивее, чем на краях,
|
||||
# а длинный фрагмент модель отпечатков просто не переваривает.
|
||||
middle = (seg.start + seg.end) / 2
|
||||
half = min(EMBED_SEC, seg.end - seg.start) / 2
|
||||
piece = samples[int((middle - half) * SAMPLE_RATE):int((middle + half) * SAMPLE_RATE)]
|
||||
if len(piece) < SAMPLE_RATE // 2:
|
||||
continue
|
||||
stream = self._embedder.create_stream()
|
||||
stream.accept_waveform(SAMPLE_RATE,
|
||||
samples[int(seg.start * SAMPLE_RATE):int(seg.end * SAMPLE_RATE)])
|
||||
stream.accept_waveform(SAMPLE_RATE, piece)
|
||||
stream.input_finished()
|
||||
vectors.append(np.array(self._embedder.compute(stream)))
|
||||
return separation_quality(np.array(vectors),
|
||||
np.array([s.speaker for s in usable]))
|
||||
labels.append(seg.speaker)
|
||||
if len(vectors) < 4:
|
||||
return 0.0
|
||||
return separation_quality(np.array(vectors), np.array(labels))
|
||||
|
||||
def _recognize_safely(self, audio: np.ndarray, depth: int = 0) -> str:
|
||||
"""Распознаёт кусок, при ошибке деля его пополам.
|
||||
@@ -214,7 +225,12 @@ class Pipeline:
|
||||
t0 = time.time()
|
||||
raw = self._diarizer(num_speakers).process(samples).sort_by_start_time()
|
||||
segments = [Segment(start=s.start, end=s.end, speaker=s.speaker) for s in raw]
|
||||
quality = self._separation_quality(samples, segments)
|
||||
try:
|
||||
quality = self._separation_quality(samples, segments)
|
||||
except Exception as exc: # noqa: BLE001 - оценка вспомогательная
|
||||
# Метрика не должна ронять задачу: без неё расшифровка всё равно нужна.
|
||||
log.warning("не удалось оценить разделение говорящих: %s", exc)
|
||||
quality = 0.0
|
||||
t_diar = time.time() - t0
|
||||
|
||||
t0 = time.time()
|
||||
|
||||
Reference in New Issue
Block a user