Почему ваш STT падает на длинных аудио и как это исправить
Вы загружаете часовую запись совещания в Whisper или Parakeet Multitalk и получаете мешанину из слов. Или модель просто выдает ошибку памяти. Знакомо? Это не баг, это фича всех современных STT-моделей. У них есть ограничение контекста — обычно от 10 до 30 секунд.
Просто резать файл на равные 30-секундные куски — самый быстрый путь к катастрофе. Слова обрываются на полуслове, контекст теряется, имена собственные превращаются в абракадабру. Точность падает на 15-40% в зависимости от аудио.
Как НЕ делать: никогда не используйте np.array_split() для аудио. Вы получите чанки, которые режут слова пополам. Модель не поймет «ин-вести-ции» и превратит это в «инвестиции» (что, впрочем, иногда и правильно).
Метод 1: Silero VAD — режем по тишине
Детектор речевой активности — ваш лучший друг. Он находит моменты, когда люди действительно говорят, и режет там. Silero VAD (актуальная версия 4.0 на март 2026) работает локально, быстро и на удивление точно даже в шумных условиях.
import torch
import numpy as np
from silero_vad import load_silero_vad, get_speech_timestamps
def chunk_by_silero_vad(audio_numpy, sample_rate=16000):
"""Разбивает аудио по речевым сегментам."""
model = load_silero_vad() # Загружаем последнюю версию
timestamps = get_speech_timestamps(
audio_numpy,
model,
sampling_rate=sample_rate,
threshold=0.5, # Чувствительность
min_speech_duration_ms=300,
min_silence_duration_ms=500
)
chunks = []
for ts in timestamps:
start = int(ts['start'] / sample_rate * 1000) # в мс
end = int(ts['end'] / sample_rate * 1000)
chunk = audio_numpy[ts['start']:ts['end']]
chunks.append((chunk, start, end))
return chunks
Почему это работает? Потому что естественная речь состоит из пауз. Даже самый болтливый менеджер делает вдох. VAD ловит эти моменты и создает чанки, которые соответствуют речевым фразам, а не произвольным временным интервалам.
1 Настройка порога чувствительности
Самая частая ошибка — оставить threshold=0.5 для всех сценариев. В шумном кафе повышайте до 0.7. Для студийной записи можно 0.3. Проверяйте визуально:
# Визуализация чанков
import matplotlib.pyplot as plt
def plot_chunks(audio, chunks):
plt.figure(figsize=(12, 4))
plt.plot(audio, alpha=0.5)
for chunk, start, end in chunks:
plt.axvspan(start, end, alpha=0.3, color='red')
plt.show()
Метод 2: Диаризация — когда говорят несколько человек
Если в аудио несколько голосов, VAD не поможет. Он создаст один длинный чанк, где два человека перебивают друг друга. Нужна диаризация — разделение по говорящим. Здесь все сложно. Классический pyannote.audio (который в 2026 году сломан на перекрывающейся речи), но есть альтернативы.
# Используем SpeechBrain для диаризации (актуально на 2026)
from speechbrain.inference import SpeakerRecognition
from speechbrain.inference import SepformerSeparation as Separator
import torchaudio
def diarize_chunks(audio_path):
# Загружаем модель разделения речи
separator = Separator.from_hparams(
source="speechbrain/sepformer-wsj02mix",
savedir="tmp"
)
# Разделяем каналы если есть
est_sources = separator.separate_file(audio_path)
# Дальше кластеризация по голосам
# (Это упрощенный пример, реальный код на 30 строк дольше)
return separated_chunks
Диаризация жрет ресурсы как не в себя. На час аудио может уйти 30 минут на GPU. Но для расшифровки интервью или судебных заседаний — это единственный способ.
Метод 3: Overlap chunking — перекрываемся для контекста
Самый элегантный метод. Мы режем аудио на чанки с перекрытием (overlap). Например, чанк 30 секунд, overlap 5 секунд. Почему? Потому что контекст. Слово, которое попало на стык, будет обработано дважды, и модель сможет использовать контекст из предыдущего чанка.
def overlap_chunk(audio_numpy, sample_rate, chunk_duration_sec=30, overlap_sec=5):
"""Создает перекрывающиеся чанки."""
chunk_samples = chunk_duration_sec * sample_rate
overlap_samples = overlap_sec * sample_rate
step_samples = chunk_samples - overlap_samples
chunks = []
for start in range(0, len(audio_numpy), step_samples):
end = start + chunk_samples
chunk = audio_numpy[start:end]
if len(chunk) < chunk_samples * 0.5: # Пропускаем слишком маленькие обрезки
continue
chunks.append({
'audio': chunk,
'start_ms': int(start / sample_rate * 1000),
'end_ms': int(min(end, len(audio_numpy)) / sample_rate * 1000)
})
return chunks
После транскрипции нужно склеить результаты, убрав дублирующиеся перекрывающиеся части. Это отдельная задача, но решаемая.
| Метод | Точность | Скорость | Когда использовать |
|---|---|---|---|
| Silero VAD | Высокая (если паузы есть) | Быстрая | Монологи, лекции, подкасты |
| Диаризация | Средняя (зависит от модели) | Очень медленная | Интервью, совещания, диалоги |
| Overlap chunking | Высокая | Быстрая | Любое длинное аудио, где важна связность |
Собираем пайплайн: от аудио до текста
Теперь соединим все вместе. Идеальный пайплайн для 2026 года выглядит так:
1 Предобработка аудио
Приводим все к 16 кГц, моно. Убираем шум простым фильтром, если нужно.
import librosa
def preprocess_audio(file_path):
audio, sr = librosa.load(file_path, sr=16000, mono=True)
# Простой noise reduction (спектральное вычитание)
from scipy import signal
# ... код шумоподавления
return audio, sr
2 Адаптивный выбор метода чанкинга
Определяем, есть ли несколько голосов. Если да — диаризация + overlap. Если нет — VAD.
def detect_speaker_count(audio, sr):
"""Простая оценка количества говорящих через спектральные особенности."""
# Используем кепстральный анализ
# Если находим резкие изменения в характеристиках — несколько голосов
return count
3 Транскрипция чанков
Подаем каждый чанк в STT-модель. Лучше использовать локальные модели, если конфиденциальность важна. Как выбрать? Читайте полный гайд по выбору STT-модели в 2025 (актуально и для 2026).
from transformers import pipeline
# Используем актуальную на 2026 модель, например, Whisper-large-v4
stt = pipeline("automatic-speech-recognition",
model="openai/whisper-large-v4",
device="cuda")
def transcribe_chunks(chunks):
transcripts = []
for chunk in chunks:
result = stt(chunk['audio'])
transcripts.append({
'text': result['text'],
'start': chunk['start_ms'],
'end': chunk['end_ms']
})
return transcripts
4 Склейка и постобработка
Убираем дубли из overlap, соединяем тексты, добавляем пунктуацию (если модель не сделала).
Что сломается и как избежать
- Тишина в студии. VAD может не найти пауз и создать один чанк на 2 часа. Решение: принудительно резать каждые 60 секунд, если сегмент речи длиннее порога.
- Фоновый шум. Гул кондиционера принимается за речь. Повышайте порог VAD до 0.6-0.7.
- Перекрывающаяся речь. Два человека говорят одновременно. Диаризация падает. Здесь поможет только специальная обработка перекрытий.
- Разные языки в одном аудио. STT-модель нужно переключать. Детектируйте язык каждые 10 секунд.
Частые вопросы
Какой overlap оптимальный?
5-10% от длины чанка. Для 30-секундных чанков overlap 2-3 секунды. Меньше — рискуете потерять контекст, больше — удваиваете работу.
Можно ли комбинировать VAD и overlap?
Да, и это даст лучший результат. Сначала режем по VAD, затем если чанк длиннее 30 секунд, применяем overlap внутри него.
Какая STT-модель лучше для чанкованного аудио?
Модели с большим контекстом (Whisper, Parakeet) справляются лучше. Но если вы работаете с медицинскими или юридическими текстами, смотрите специализированные сравнения.
Что будет дальше?
К 2027 году STT-модели научатся обрабатывать контекст в несколько часов без чанкинга. Но пока мы застряли в эпохе патчей. Мой совет: не экономьте на overlap. Лучше потратить лишние 10% вычислений, чем потом вручную склеивать обрывки фраз. И проверяйте чанки визуально — иногда простой график спасет вас от часов отладки.
Если делаете автосекретаря, посмотрите как собрать систему с субсекундной задержкой. Там чанкинг нужен, но совсем другой.