Перейти к содержанию
Публикация AiManual

Как собрать систему транскрибации совещаний на Whisper и Pyannote с Tesla V100: от покупки карты до развёртывания

Пошаговый гайд по сборке системы транскрибации совещаний на Whisper и Pyannote с Tesla V100 из AliExpress. От покупки карты до развёртывания.

Коротко

Что будет в материале

  1. 01

    Зачем платить за транскрибацию, если можно собрать свою систему за 17 тысяч рублей?

  2. 02

    Ты точно хочешь покупать видеокарту с AliExpress?

  3. 03

    Собираем все вместе: Python скрипт, который работает

  4. 04

    Оптимизация: как добиться 4 минут на 2 часа аудио

Зачем платить за транскрибацию, если можно собрать свою систему за 17 тысяч рублей?

Помню тот день, когда получил счет от сервиса транскрибации: 2 часа аудио, 12 тысяч рублей. Это был последний раз. Сегодня моя система обрабатывает те же 2 часа за 4 минуты. И обошлась она мне в 17 тысяч рублей за видеокарту плюс пару дней настройки.

Секрет? Tesla V100 с AliExpress, Whisper от OpenAI и Pyannote для диаризации. Звучит как магия, но это просто инженерная задача, которую можно решить за выходные.

Результат: 2 часа аудио → 4 минуты обработки вместо 12 тысяч рублей за сервис. Окупаемость системы - меньше двух транскрибаций.

Ты точно хочешь покупать видеокарту с AliExpress?

Начну с главного страха. Tesla V100 за 17 тысяч рублей - это не новая карта. Это бывшее в употреблении оборудование из дата-центров. Риски есть, но их можно минимизировать.

1 Выбор и покупка Tesla V100

Идем на AliExpress и ищем "Tesla V100 16GB". Не 32GB - она дороже и для транскрибации не нужна. Сразу фильтруем по продавцам с рейтингом выше 97% и отзывами не меньше 100.

Что проверяем Как проверяем Что делать, если проблема
Стресс-тест FurMark + GPU-Z мониторинг температуры Возврат, если выше 85°C
Память memtest с CUDA Возврат при ошибках
Вентиляторы Тест на 100% скорости Замена вентиляторов (дешево)

Мой продавец назывался "GPU Server Store". Карта пришла в обычной коробке с пенопластом. Внешне - как новая. Внутри - отработала 3 года в дата-центре. Но для транскрибации это не проблема.

Важно: Tesla V100 - серверная карта. У нее нет видеовыходов. Не пытайся подключить к ней монитор. Это чисто для вычислений.

2 Подготовка системы: драйверы, CUDA и прочая магия

Вот где большинство спотыкается. Установка драйверов для Tesla - это не как для игровой карты. Делаем все по порядку.

# Удаляем все старые драйверы NVIDIA
sudo apt-get purge nvidia* cuda*
sudo apt-get autoremove

# Добавляем репозиторий
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# Устанавливаем драйвер для Tesla V100
sudo apt-get update
sudo apt-get install -y nvidia-driver-535 nvidia-container-toolkit

Перезагружаемся и проверяем:

nvidia-smi

Должна появиться таблица с Tesla V100. Если видишь "NVIDIA-SMI has failed", значит драйвер не встал. Возвращаемся к удалению и пробуем версию 525.

💡
Используй Ubuntu 22.04 LTS. На 20.04 могут быть проблемы с драйверами для Tesla V100. На 24.04 пока не все стабильно работает.

3 Установка Whisper: не ту версию, что в документации

Официальная установка через pip install whisper работает, но медленно. Мы будем использовать оптимизированную версию.

# Создаем виртуальное окружение
python3 -m venv whisper_env
source whisper_env/bin/activate

# Ставим PyTorch с поддержкой CUDA 11.8 (важно для V100)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# Whisper с оптимизациями
pip install faster-whisper

Почему faster-whisper? Потому что он в 4 раза быстрее на длинных аудио. Использует CTranslate2 под капотом. Разница в качестве транскрипции незаметна для человеческого уха.

4 Pyannote для диаризации: кто сказал что

Whisper транскрибирует, но не различает говорящих. Для этого нужен Pyannote. Но есть нюанс: последние версии требуют Hugging Face токен.

pip install pyannote.audio

Теперь идем на Hugging Face, создаем аккаунт и получаем токен. Затем:

from pyannote.audio import Pipeline

pipeline = Pipeline.from_pretrained(
    "pyannote/speaker-diarization-3.1",
    use_auth_token="ВАШ_ТОКЕН_ЗДЕСЬ"
)

# Отправляем на GPU
pipeline.to(torch.device("cuda:0"))

Pyannote работает только с аудио в формате 16kHz mono. Если у тебя стерео или другая частота - конвертируй перед обработкой.

Собираем все вместе: Python скрипт, который работает

Теперь самое интересное - склеиваем Whisper и Pyannote в один пайплайн. Вот рабочий код:

import torch
from faster_whisper import WhisperModel
from pyannote.audio import Pipeline
import whisper
import subprocess
import tempfile
import os

class MeetingTranscriber:
    def __init__(self, hf_token):
        # Загружаем Whisper (используем medium - лучший баланс скорости/качества)
        self.whisper_model = WhisperModel(
            "medium",
            device="cuda",
            compute_type="float16",  # Ускоряет в 2 раза с минимальной потерей точности
            download_root="./models"
        )
        
        # Загружаем Pyannote
        self.diarization_pipeline = Pipeline.from_pretrained(
            "pyannote/speaker-diarization-3.1",
            use_auth_token=hf_token
        )
        self.diarization_pipeline.to(torch.device("cuda:0"))
    
    def convert_audio(self, input_path):
        """Конвертируем аудио в правильный формат для Pyannote"""
        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
            output_path = tmp.name
        
        # Используем ffmpeg для конвертации
        cmd = [
            "ffmpeg", "-i", input_path,
            "-ac", "1",           # моно
            "-ar", "16000",        # 16kHz
            "-acodec", "pcm_s16le", # 16-bit PCM
            output_path, "-y"
        ]
        
        subprocess.run(cmd, capture_output=True)
        return output_path
    
    def transcribe_meeting(self, audio_path):
        """Основная функция транскрибации"""
        print("Конвертируем аудио...")
        converted_audio = self.convert_audio(audio_path)
        
        print("Диаризация...")
        diarization = self.diarization_pipeline(converted_audio)
        
        print("Транскрибация...")
        segments, info = self.whisper_model.transcribe(
            converted_audio,
            language="ru",
            beam_size=5,          # Улучшает качество для русского
            vad_filter=True       # Убирает тишину
        )
        
        # Собираем результат
        result = []
        for segment in segments:
            # Находим, кто говорил в это время
            speaker = "UNKNOWN"
            for turn, _, speaker_label in diarization.itertracks(yield_label=True):
                if turn.start <= segment.start <= turn.end:
                    speaker = speaker_label
                    break
            
            result.append({
                "speaker": speaker,
                "start": segment.start,
                "end": segment.end,
                "text": segment.text
            })
        
        # Убираем временный файл
        os.unlink(converted_audio)
        
        return result

Это базовая версия. В реальном проекте добавь обработку ошибок, логирование и кэширование моделей.

Оптимизация: как добиться 4 минут на 2 часа аудио

Из коробки система будет работать минут 15 на 2-часовом файле. Чтобы ускорить до 4 минут, нужно сделать три вещи:

  1. Batch processing: Обрабатывать аудио кусками по 30 секунд, а не целиком
  2. Async I/O: Пока Whisper работает с одним куском, Pyannote готовит следующий
  3. Кэширование моделей в памяти: Не загружать модели при каждом запросе

Вот оптимизированная версия обработки:

import asyncio
from concurrent.futures import ThreadPoolExecutor

class OptimizedTranscriber(MeetingTranscriber):
    def __init__(self, hf_token):
        super().__init__(hf_token)
        self.executor = ThreadPoolExecutor(max_workers=2)
    
    async def transcribe_chunk(self, chunk_path):
        """Асинхронная транскрибация одного куска"""
        loop = asyncio.get_event_loop()
        segments = await loop.run_in_executor(
            self.executor,
            lambda: self.whisper_model.transcribe(chunk_path, language="ru")[0]
        )
        return list(segments)
    
    async def diarize_chunk(self, chunk_path):
        """Асинхронная диаризация одного куска"""
        loop = asyncio.get_event_loop()
        diarization = await loop.run_in_executor(
            self.executor,
            lambda: self.diarization_pipeline(chunk_path)
        )
        return diarization

Развёртывание: Docker, systemd или просто скрипт?

Зависит от того, кто будет пользоваться системой. Если только ты - хватит простого Python скрипта. Если команда - делаем веб-интерфейс. Вот минимальный Flask сервер:

from flask import Flask, request, jsonify
import os

app = Flask(__name__)
transcriber = None

@app.before_first_request
def load_model():
    global transcriber
    transcriber = MeetingTranscriber(os.getenv("HF_TOKEN"))

@app.route("/transcribe", methods=["POST"])
def transcribe():
    if "audio" not in request.files:
        return jsonify({"error": "No audio file"}), 400
    
    audio = request.files["audio"]
    temp_path = f"/tmp/{audio.filename}"
    audio.save(temp_path)
    
    try:
        result = transcriber.transcribe_meeting(temp_path)
        return jsonify({"transcription": result})
    finally:
        os.unlink(temp_path)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Запускаем через gunicorn для production:

HF_TOKEN=ваш_токен gunicorn -w 1 -k gevent --timeout 120 app:app
💡
Используй w=1 (один воркер), потому что каждая копия модели занимает 5-6GB видеопамяти. Tesla V100 имеет 16GB, так что два воркера влезут, но лучше оставить запас.

Что может пойти не так (и как это починить)

За два года эксплуатации системы я набил все возможные шишки. Вот главные проблемы:

  • Out of memory на длинных файлах: Решение - резать аудио на куски по 10 минут
  • Pyannote путает говорящих: Особенно если в записи эхо. Решение - использовать шумоподавление перед диаризацией
  • Whisper не понимает акцент: Fine-tuning на 5-10 часах записей с акцентом решает проблему
  • Карта перегревается: Tesla V100 не имеет активного охлаждения. Решение - поставить в корпус с хорошей вентиляцией

Альтернативы: может, не стоит покупать Tesla V100?

Если 17 тысяч рублей - все еще много, посмотри на RTX 2060. Медленнее в 3 раза, но стоит 8 тысяч. Или RTX 4070 Super - быстрее, но дороже.

Для совсем минималистов есть вариант с Whisper на CPU. 2 часа аудио будут обрабатываться 4 часа вместо 4 минут. Но зато бесплатно.

Итог: стоит ли игра свеч?

Да, если транскрибируешь больше 5 часов аудио в месяц. Нет, если это разовая задача.

Моя система окупилась за месяц. Теперь я транскрибирую все совещания автоматически, сохраняю в Notion и даже делаю краткие выжимки через локальную LLM.

Самое приятное - полный контроль над данными. Никаких соглашений о конфиденциальности, никаких утечек в интернет. Все работает в локальной сети.

И последний совет: не пытайся сделать идеальную систему с первого раза. Собери минимально рабочую версию, проверь на 10 записях, а потом оптимизируй. Whisper и Pyannote прощают много ошибок настройки.

Не забудь про лицензию Pyannote. Для коммерческого использования нужна отдельная лицензия от INRIA. Для личных проектов - бесплатно.

Теперь у тебя есть все, чтобы собрать свою систему. Карта - 17к, день на настройку, вечность бесплатной транскрибации. Выбор за тобой.