MAATRIX / Блог / Как установить и настроить faster-whisper на VPS

Как установить и настроить faster-whisper на VPS

Как установить и настроить faster-whisper на VPS

MAATRIX

Свой сервер распознавания речи заводят ради приватности и отсутствия платы за минуты, а упираются в обычный VPS без видеокарты, где оригинальный Whisper тянет за собой PyTorch на несколько гигабайт. faster-whisper снимает эту проблему: движок CTranslate2 считает на процессоре и без Torch. Ниже установка по шагам — что ставится, какую модель брать, как настроить compute_type и как превратить скрипт в сервис.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что ставится вместе с faster-whisper и чего в нём нет

faster-whisper — не обёртка над оригинальным Whisper, а другая реализация той же модели поверх CTranslate2, движка инференса из проекта OpenNMT. Для VPS это главное: PyTorch не нужен вообще. Актуальный релиз — 1.2.1 от 31 октября 2025 года, минимальный Python — 3.9, а зависимостей всего шесть.

ПакетОграничение версииЗа что отвечает
ctranslate2>=4.0,<5движок инференса
av (PyAV)>=11декодирование аудио
onnxruntime>=1.14,<2модель Silero VAD
tokenizers>=0.13,<1токенизатор
huggingface-hub>=0.21скачивание весов
tqdmиндикатор прогресса

Свежая сборка движка — ctranslate2 4.8.1 (июль 2026), колёса собраны под тег manylinux_2_28: на Ubuntu 22.04/24.04 и Debian 11/12 встаёт готовый бинарник примерно на 39 МБ, для aarch64 есть своё. Ни torch, ни CUDA-довесков в списке нет — а один PyTorch занимает несколько гигабайт диска.

FFmpeg как программу ставить не нужно — это прямо оговорено в README: аудио декодирует PyAV, которая несёт библиотеки FFmpeg внутри пакета. Консольный ffmpeg всё равно пригодится, чтобы вырезать дорожку из видео, но это инструмент подготовки, а не зависимость.

Ещё про процессор: CPU-бэкенд CTranslate2 рассчитывает на AVX2, и без него загрузка модели обрывается на Illegal instruction (core dumped) без объяснений. Проверка — grep -m1 -o avx2 /proc/cpuinfo.

Установка на VPS: venv, PEP 668 и проверка окружения

Ubuntu 24.04 приезжает с Python 3.12, Debian 12 — с 3.11; хватает обоих.

apt update && apt install -y python3-venv python3-pip

Команда pip install faster-whisper от рута на свежей Ubuntu не сработает — это не поломка, а политика PEP 668:

error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install python3-xyz...

Флаг --break-system-packages существует и делает ровно то, что написано в названии. Правильный путь — отдельное окружение под своего пользователя:

useradd -r -m -d /opt/whisper -s /usr/sbin/nologin whisper
sudo -u whisper python3 -m venv /opt/whisper/venv
sudo -u whisper /opt/whisper/venv/bin/pip install --no-cache-dir "faster-whisper==1.2.1"

Версию фиксируем сразу: библиотека разрешает любой ctranslate2 из ветки 4.x, и движок обновится молча при пересборке окружения. --no-cache-dir тоже не для красоты: на машине с 1 ГБ памяти сам pip умеет получить Killed на распаковке колёс, подтверждение в journalctl -k | grep -i oom.

/opt/whisper/venv/bin/pip show faster-whisper | head -3
/opt/whisper/venv/bin/python -c "import ctranslate2; print(ctranslate2.__version__)"
/opt/whisper/venv/bin/python -c "import ctranslate2; print(ctranslate2.get_supported_compute_types('cpu'))"

Третья команда — самая полезная: она говорит, какие типы вычислений реально потянет ваш процессор, и возвращает множество вроде {'int8', 'int8_float32', 'int16', 'float32'}. Обратите внимание, чего в нём нет: float16.

Заодно посмотрите nproc, free -m и df -h / — swap здесь не решение: модель, считающая из подкачки, а не из RAM, замедляется на порядок.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Whisper

Модели: имена, вес и куда всё скачивается

WhisperModel("small") — не путь к файлу, а короткое имя, которое библиотека разворачивает в репозиторий на Hugging Face и качает при первом обращении. Веса лежат в формате CTranslate2, файлы от openai-whisper сюда не подходят.

Имя моделиРепозиторийВес model.bin
tinySystran/faster-whisper-tiny0,08 ГБ
baseSystran/faster-whisper-base0,15 ГБ
smallSystran/faster-whisper-small0,48 ГБ
mediumSystran/faster-whisper-medium1,53 ГБ
large-v3Systran/faster-whisper-large-v33,09 ГБ
turbomobiuslabsgmbh/faster-whisper-large-v3-turbo1,62 ГБ
distil-large-v3Systran/faster-distil-whisper-large-v31,51 ГБ

Опечатка обрывает запуск до всякой сети: ValueError: Invalid model size 'large-v4', expected one of: tiny.en, tiny, base.en, base, small.en, small, .... Полный список выдаёт python -c "from faster_whisper import available_models; print(available_models())".

Две ловушки выбора. Модели с суффиксом .en — только английские, и дистиллированные distil-large-v3 и distil-large-v3.5 тоже. Для русского остаются small, medium, large-v3 и мультиязычный turbo: при вдвое меньшем весе он близок к large-v3 на расшифровке, но хуже переводит.

Скачанное ложится в ~/.cache/huggingface/hub того пользователя, от которого запущен процесс. Отсюда регулярный сюрприз: прогрели модель под рутом, а сервис работает от whisper и качает всё заново к себе. Лечится переменной HF_HOME или аргументом download_root, заданными одинаково везде.

sudo -u whisper HF_HOME=/opt/whisper/hf /opt/whisper/venv/bin/python \
  -c "from faster_whisper import download_model; download_model('small')"
du -sh /opt/whisper/hf

Прогрев при развёртывании решает три задачи разом: виден размер кеша, первый запрос не ждёт скачивания, и вы сразу узнаете, если до huggingface.co не достучаться. Дальше ставьте local_files_only=True, чтобы сетевой сбой не превращался в поход в интернет посреди очереди.

Первый запуск на процессоре: compute_type, потоки и генератор

Минимальный рабочий скрипт выглядит так:

from faster_whisper import WhisperModel

model = WhisperModel("small", device="cpu", compute_type="int8", cpu_threads=4)
segments, info = model.transcribe("call.m4a", language="ru", beam_size=5, vad_filter=True)

print(info.language, info.language_probability, info.duration)
for seg in segments:
    print(f"[{seg.start:.2f} -> {seg.end:.2f}] {seg.text.strip()}")

Три параметра здесь неочевидны, и на них спотыкается настройка.

compute_type. По умолчанию auto. Но скопируйте пример из README, где для видеокарты указан float16, и на CPU прилетит ValueError: Requested float16 compute type, but the target device or backend do not support efficient float16 computation. На процессоре рабочие варианты — int8 и float32. По качеству на речи разница в пределах шума, по памяти — почти двукратная, поэтому на VPS берут int8: веса лежат в fp16, CTranslate2 квантует их при загрузке.

cpu_threads. Умолчание 0 означает «как решит CTranslate2», а движок берёт четыре потока и учитывает OMP_NUM_THREADS. На виртуалке с двумя vCPU это гарантированная толкотня за ядра: ставьте число ядер явно. Соседний num_workers влияет не на скорость одного файла, а на параллельные вызовы transcribe() из разных потоков, и каждый воркер добавляет свою память.

Генератор. segments — не список: расшифровка стартует только при переборе. Отсюда два следствия: time вокруг вызова transcribe() покажет доли секунды и ничего не значит, а try/except вокруг него не поймает ошибку, которая случится при итерации.

Про скорость честно. Разработчики публикуют в README замер на записи в 13 минут: small в int8 с beam_size=5 — 1 минута 42 секунды и около 1477 МБ памяти, в float32 — 2 минуты 37 секунд и 2257 МБ. Сделано это на десктопном Core i7-12700K в восемь потоков, так что переносить цифру на vCPU нельзя: считайте её верхней границей, а свою снимайте через time.

Настройка качества: VAD, язык и борьба с галлюцинациями

Установка заканчивается работающим скриптом, а полезный результат начинается с настройки. Четыре параметра дают почти весь эффект.

vad_filter=True подключает Silero VAD, который выбрасывает куски без речи; в версии 1.2.1 детектор обновили до шестой версии. Умолчание консервативное — вырезается тишина длиннее двух секунд, для звонков порог стоит ужесточить.

segments, info = model.transcribe(
    "call.wav",
    language="ru",
    vad_filter=True,
    vad_parameters=dict(min_silence_duration_ms=500),
    condition_on_previous_text=False,
    initial_prompt="MAATRIX, VPS, NVMe, тариф, Нью-Йорк",
)

Дело не только в скорости: на тишине и фоновой музыке Whisper склонен выдумывать текст, и вставки вроде «Продолжение следует» видел всякий, кто расшифровывал русские записи.

language="ru" экономит проход определения языка и защищает от классической ошибки: язык определяется по первым тридцати секундам, и запись с англоязычной заставкой уедет в английский целиком. info.language_probability ниже 0,7 — повод посмотреть файл руками.

condition_on_previous_text=False лечит зацикливание: одна фраза повторяется десятки раз, тайм-коды идут дальше, текст стоит на месте. Модель подаёт себе на вход собственный предыдущий вывод и на однообразном аудио сваливается в петлю.

initial_prompt и близкий к нему hotwords подсказывают написание имён и терминов — пара десятков слов заметно уменьшает объём правок.

Тайм-коды для субтитров берутся прямо из сегментов; word_timestamps=True даст разбивку по словам, но за это платят временем обработки.

def ts(t):
    h, r = divmod(t, 3600)
    m, sec = divmod(r, 60)
    return f"{int(h):02}:{int(m):02}:{int(sec):02},{int(sec % 1 * 1000):03}"

with open("out.srt", "w", encoding="utf-8") as f:
    for i, seg in enumerate(segments, 1):
        f.write(f"{i}\n{ts(seg.start)} --> {ts(seg.end)}\n{seg.text.strip()}\n\n")

Не хочется писать код — поставьте в то же окружение whisper-ctranslate2: whisper-ctranslate2 call.m4a --model small --language ru --compute_type int8 --output_format srt.

Отдельно про пакетный режим. BatchedInferencePipeline с параметром batch_size ускоряет обработку в разы, VAD в нём включён по умолчанию. Плата — память: в тех же замерах README пакет из восьми поднял расход с 1477 МБ до 3608 МБ, на 4 ГБ это прямая дорога к OOM. И помните, чего faster-whisper не делает вовсе: он не разделяет говорящих, для диаризации нужен WhisperX или pyannote.

Из скрипта в сервис: systemd, HTTP-обёртка и Nginx

Загрузка модели занимает секунды и весь свой объём в памяти, поэтому её поднимают один раз при старте, а запросы ставят в очередь: два параллельных распознавания на двух ядрах ничего не ускорят, зато удвоят память.

# /opt/whisper/app.py
import asyncio, os, tempfile
from fastapi import FastAPI, File, Header, HTTPException, UploadFile
from faster_whisper import WhisperModel

MODEL = WhisperModel("small", device="cpu", compute_type="int8", cpu_threads=4)
LOCK = asyncio.Semaphore(1)
TOKEN = os.environ["WHISPER_TOKEN"]
app = FastAPI()

def run(path):
    segments, info = MODEL.transcribe(path, language="ru", vad_filter=True)
    return info.language, info.duration, " ".join(s.text.strip() for s in segments)

@app.post("/transcribe")
async def transcribe(file: UploadFile = File(...), authorization: str = Header("")):
    if authorization != f"Bearer {TOKEN}":
        raise HTTPException(401, "bad token")
    with tempfile.NamedTemporaryFile(suffix=".audio", delete=False) as tmp:
        tmp.write(await file.read())
        path = tmp.name
    try:
        async with LOCK:
            lang, dur, text = await asyncio.to_thread(run, path)
    finally:
        os.unlink(path)
    return {"language": lang, "duration": dur, "text": text}

Обратите внимание на функцию run: перебор генератора идёт внутри неё, то есть в рабочем потоке. Вынесете сборку текста наружу — расшифровка выполнится в event loop и заблокирует сервис целиком.

[Unit]
Description=faster-whisper API
After=network-online.target

[Service]
User=whisper
WorkingDirectory=/opt/whisper
Environment=HF_HOME=/opt/whisper/hf
Environment=OMP_NUM_THREADS=4
EnvironmentFile=/etc/whisper/whisper.env
ExecStart=/opt/whisper/venv/bin/uvicorn app:app --host 127.0.0.1 --port 8001
Restart=on-failure
RestartSec=5
TimeoutStartSec=300

[Install]
WantedBy=multi-user.target

TimeoutStartSec=300 здесь не паранойя: если модели нет в кеше, старт займёт столько, сколько качается файл. Наружу сервис выставляют через Nginx, и там два умолчания ломают всё:

location / {
    proxy_pass http://127.0.0.1:8001;
    client_max_body_size 512m;
    proxy_read_timeout 1800s;
    proxy_send_timeout 1800s;
}

Без первой строки часовая запись отвалится с 413 Request Entity Too Large — по умолчанию Nginx принимает 1 МБ. Без второй расшифровка оборвётся на шестидесятой секунде: клиент получит 504 Gateway Time-out, а в /var/log/nginx/error.log появится upstream timed out (110: Connection timed out) while reading response header from upstream. Обиднее всего то, что сервер продолжает считать в пустоту, а клиент по таймауту шлёт файл заново. Для длинных записей правильнее возвращать идентификатор задачи и отдавать результат отдельным запросом.

Порт 8001 наружу не выставляем: ufw allow 22/tcp, ufw allow 'Nginx Full' — и всё. Открытый эндпоинт транскрибации — это бесплатный процессор для посторонних, который легко нагрузить одним большим файлом.

Писать своё не обязательно. Есть speaches — сервер с OpenAI-совместимым API поверх faster-whisper и выгрузкой простаивающих моделей. Есть контейнер onerahmet/openai-whisper-asr-webservice: порт 9000, переменные ASR_ENGINE=faster_whisper и ASR_MODEL=small, на выходе srt, vtt и json. Кеш пробрасывайте томом (-v $PWD/cache:/root/.cache/), иначе каждый перезапуск качает веса заново.

Какой сервер под faster-whisper брать в MAATRIX

Нагрузка тут процессорная и рваная: между запросами тишина, во время расшифровки ядра заняты полностью. Диск нужен под веса и временные файлы, сеть — только на приём аудио.

СценарийvCPURAMДиск
Разовые файлы, small в int824 ГБ40 ГБ NVMe
Регулярный поток, medium или turbo48 ГБ80 ГБ NVMe
large-v3, очередь, пакетный режим816 ГБ160 ГБ NVMe

Честный минимум — 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. Хватает для small в int8: у разработчиков связка укладывается в полтора гигабайта, остаётся запас на систему и буфер файла. Тариф на 2 ГБ выглядит соблазнительно и работать не будет — модель загрузится, а первый длинный файл принесёт Killed и запись oom-kill в journalctl -k.

Комфортный вариант — 4 vCPU, 8 ГБ RAM, 80 ГБ NVMe. Помещается turbo или medium, включается пакетный режим, очередь перестаёт быть узким местом. CPU-бэкенд CTranslate2 масштабируется по числу физических ядер, так что удвоение vCPU — самый прямой способ ускориться без смены модели.

Про large-v3 скажем прямо: на процессоре это разговор о работоспособности, а не о скорости. В модели 1550 миллионов параметров против 244 миллионов у small, и час записи будет считаться часами. Нужно такое качество на потоке — нужна видеокарта. Расчёт по памяти есть в материале про то, сколько RAM нужно для Whisper по моделям, а просадки скорости — в статье Whisper медленно распознаёт речь.

Локация — Лондон (UK). Причина прикладная: веса едут с huggingface.co, а с российских адресов этот хост отвечает через раз — первый же WhisperModel("small") зависает на скачивании. С британской площадки модели качаются напрямую, плюс низкий пинг до Европы и периметр GDPR для записей переговоров. Расшифровываете звонки российских клиентов под 152-ФЗ — берите Россию и прогревайте кеш заранее, переносом каталога HF_HOME.

Повторять всё это руками не обязательно: Whisper есть в каталоге приложений apps.maatrix.io. Он ставится автоматически при заказе сервера, работает на Ubuntu и Debian, а доступы появляются в личном кабинете, в разделе «Доступ». Оплата картами российских банков, по СБП, криптовалютой или токеном MAAT. Типовые сбои после запуска разобраны отдельно: faster-whisper на сервере: частые ошибки и решения.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Whisper

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Нужно ли ставить ffmpeg перед установкой faster-whisper?

Для расшифровки нет: аудио декодирует PyAV, которая несёт библиотеки FFmpeg внутри пакета. Консольный ffmpeg полезен для подготовки: ffmpeg -i call.mp4 -vn -ac 1 -ar 16000 -c:a pcm_s16le call.wav.

Первый запуск висит несколько минут и ничего не пишет. Это нормально?

Да, если модели нет в кеше: библиотека молча качает веса, а large-v3 — это 3,09 ГБ. Проверьте du -sh ~/.cache/huggingface/hub. Правильный порядок — прогреть модель при развёртывании через download_model() и задать всем процессам одинаковый HF_HOME.

Как зафиксировать версии, чтобы обновление не сломало сервер?

Пропишите обе строки явно — faster-whisper==1.2.1 и ctranslate2==4.8.1 — в requirements.txt. Библиотека допускает любой ctranslate2 из ветки 4.x, поэтому движок может обновиться при пересборке окружения. Признак несовместимости — RuntimeError: Unsupported model binary version при загрузке модели из старого кеша.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.