Эмбеддинги для миллиона документов: своё железо против API
У вас есть корпус документов — условно миллион штук, может быть меньше, может больше — и его нужно превратить в векторы один раз: для RAG, для поиска по смыслу, для дедупликации или кластеризации. Это не постоянный инференс, который крутится месяцами и обслуживает живой трафик, а разовая (или редкая) пакетная задача с чётким концом. Экономика такой задачи считается иначе, чем экономика постоянного сервиса, и если вы прикидываете стоимость по тем же формулам, что и для непрерывного API-трафика, вы почти наверняка ошибётесь — причём в обе стороны.
Содержание
- Почему разовую пакетную задачу нельзя считать как постоянный инференс
- Из чего состоит конвейер обработки миллиона документов
- Как считать стоимость через облачный API эмбеддингов
- Как считать стоимость временной аренды GPU-сервера
- Точка сравнения: когда временная аренда выгоднее API
- Практические нюансы: батчинг, чекпоинты и повторные прогоны
Почему разовую пакетную задачу нельзя считать как постоянный инференс
Когда вы сравниваете «свой инференс против API» для сервиса, который работает месяцами, в расчёт входят амортизация железа на годы, простои между запросами, резервирование мощности под пиковую нагрузку, отказоустойчивость. Для разовой обработки корпуса ничего этого нет — есть конечный объём работы (N документов, M токенов) и вопрос, за какое время и по какой цене его можно прогнать.
Ключевое отличие в том, что при разовой задаче стоимость аренды железа — это не абонентская плата за месяц использования на 5%, а плата за отрезок времени, в течение которого GPU занят вашей задачей почти на 100%. Если облачный API берёт деньги пропорционально объёму без верхнего предела скидки, а аренда сервера — фиксированная почасовая или посуточная ставка независимо от того, насколько плотно вы грузите GPU, то при достаточно большом объёме кривая аренды почти всегда обгоняет кривую API по выгоде. Вопрос не «выгодно ли своё железо вообще», а «за какое время вы успеваете прогнать корпус на арендованной мощности, прежде чем аренда столько же стоит, сколько API».
Второй момент: для постоянного сервиса важна доступность 24/7, отказоустойчивость под живых пользователей. Для батч-задачи важна в первую очередь пропускная способность на ограниченном окне времени и устойчивость к перезапуску — если сервер упал на середине прогона, вы просто продолжаете с чекпоинта, а не теряете обращающихся к вам пользователей.
Из чего состоит конвейер обработки миллиона документов
Прежде чем считать деньги, полезно разложить задачу на шаги — от этого зависит, где именно тратится время и бюджет:
- Извлечение текста. PDF, DOCX, HTML, сканы — на этом этапе часто уходит больше человеко-часов, чем на сам инференс. Для сканов добавляется OCR, который сам по себе может быть GPU-задачей.
- Чанкинг. Документ режется на фрагменты нужного размера (обычно 200-500 токенов с перекрытием), потому что модели эмбеддингов имеют ограничение на длину входа. От размера чанка напрямую зависит итоговое число единиц, которые нужно прогнать через модель — один документ на 5000 токенов может дать 15-20 чанков.
- Токенизация. Быстрая CPU-операция, но при миллионах чанков её тоже стоит распараллелить, иначе она станет узким местом перед GPU.
- Прямой проход через модель эмбеддингов. Собственно вычисление векторов — либо через облачный API, либо на собственном/арендованном GPU.
- Запись в векторную базу. Загрузка векторов в Qdrant, Milvus, pgvector или аналог — тоже небыстрая операция при больших объёмах, особенно если база строит индекс (HNSW) по мере вставки.
Экономику дальше считаем прежде всего для шага 4 — он либо тарифицируется провайдером API, либо занимает GPU-время на арендованном сервере. Но шаги 1-3 и 5 тоже требуют ресурсов и времени, и если вы арендуете сервер только под шаг 4, они всё равно съедят часть бюджета на CPU-инстансе до и после GPU-прогона.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверКак считать стоимость через облачный API эмбеддингов
Облачные провайдеры эмбеддингов обычно тарифицируют по числу входных токенов — цена за миллион токенов. Формула стоимости прямая:
Стоимость_API = (Число_документов × Среднее_число_токенов_на_документ) / 1_000_000 × Цена_за_млн_токенов
Для корпуса в миллион документов при среднем размере документа, скажем, в несколько тысяч токенов, суммарный объём легко доходит до нескольких миллиардов токенов. Точную цену за миллион токенов у конкретного провайдера на конец августа 2026 года нужно смотреть в его актуальном прайсе — она меняется, и называть здесь цифру, которая устареет через месяц, смысла нет. Важнее понимать структуру расходов:
- Цена растёт линейно с объёмом, скидок за масштаб для разовой пакетной загрузки у большинства провайдеров нет (в отличие от корпоративных контрактов на постоянный трафик).
- Rate limits. У API есть ограничение на число запросов и токенов в минуту. Для миллиона документов это означает, что прогон растягивается во времени не потому, что вы платите за время, а потому что провайдер физически не даст залить весь объём за час — вы упрётесь в лимит и получите 429-е ошибки.
- Batch API у некоторых провайдеров дешевле синхронного, но выполняется асинхронно, с задержкой в часы, и тоже имеет свои лимиты на объём одной пачки.
- Сетевые и повторные издержки. Часть запросов будет падать по таймауту или временной ошибке — на объёме в миллионы вызовов это не исключение, а статистически гарантированное событие, и повторные попытки увеличивают реальный расход токенов сверх теоретического минимума.
Расчёт стоимости через API для такой задачи должен закладывать не «идеальную» цену за токен, а цену с учётом retry-логики и времени, которое вы физически потратите, упираясь в rate limit.
Как считать стоимость временной аренды GPU-сервера
Здесь логика другая: вы платите не за объём данных, а за время, в течение которого сервер в вашем распоряжении.
Стоимость_аренды = Время_обработки_в_часах × Цена_за_час_аренды_GPU
Время обработки, в свою очередь, зависит от пропускной способности выбранной модели эмбеддингов на конкретном GPU — а это как раз то, что вы не должны выдумывать заранее, а обязаны измерить на своих реальных данных перед тем, как планировать аренду на конкретный срок. Модели эмбеддингов сильно отличаются по размеру и, соответственно, по скорости:
| Класс модели | Примеры | Размер векторов | Относительная скорость инференса |
|---|---|---|---|
| Малые (small) | bge-small, e5-small, gte-small | 384 | Высокая |
| Средние (base) | bge-base, e5-base, multilingual-e5-base | 768 | Средняя |
| Крупные (large) | bge-large, e5-large-v2, gte-large | 1024 | Ниже средней |
Выбор модели — это компромисс между качеством векторов (крупные модели обычно точнее на бенчмарках вроде MTEB) и скоростью пакетной обработки. Для разовой задачи на миллион документов разница между small и large моделью может означать разницу в разы во времени прогона, а значит — и в стоимости аренды.
Практический процесс для расчёта:
- Разверните модель локально или на небольшом тестовом инстансе с тем же типом GPU, который планируете арендовать.
- Прогоните репрезентативную выборку из вашего реального корпуса (не синтетические тексты — длина и структура документов сильно влияют на throughput) — нескольких тысяч чанков достаточно для оценки.
- Замерьте реальную скорость на своих данных и с той длиной чанков, которую вы выбрали.
- Экстраполируйте на весь объём и добавьте запас 20-30% на накладные расходы — прогрев модели, запись в БД, паузы между батчами.
- Умножьте получившееся время на почасовую ставку аренды нужной конфигурации GPU.
Для развёртывания самой модели на сервере обычно используют библиотеки вроде sentence-transformers, либо более производительные серверные обёртки — например, Hugging Face Text Embeddings Inference (TEI), которая умеет динамический батчинг и эффективнее использует GPU, чем наивный цикл по одному документу за раз. Пример запуска TEI в Docker:
docker run --gpus all -p 8080:80 \
-v $PWD/data:/data \
ghcr.io/huggingface/text-embeddings-inference:latest \
--model-id BAAI/bge-base-en-v1.5 \
--max-batch-tokens 16384
После старта сервис принимает запросы на /embed пакетами, и throughput при правильно подобранном --max-batch-tokens может быть в разы выше, чем при последовательной обработке через sentence-transformers в одном процессе на GPU без батчинга.
Точка сравнения: когда временная аренда выгоднее API
Прямого универсального порога здесь нет — он зависит от цены токена у конкретного API, почасовой ставки аренды и реальной измеренной скорости вашей модели на вашем железе. Но общая логика такая:
- Чем больше объём корпуса, тем сильнее сдвигается баланс в сторону аренды. У API стоимость растёт линейно и без потолка. У аренды стоимость растёт с объёмом только через время обработки, а если вы можете параллелить обработку на нескольких GPU одновременно, время (и, соответственно, итоговая стоимость аренды) сокращается почти пропорционально числу карт — чего у тарифицируемого по токенам API просто нет как рычага экономии.
- Чем меньше и разовее задача, тем выше относительные накладные расходы аренды. Разворачивание сервера, настройка окружения, загрузка весов модели, тестовый прогон — это фиксированные часы, которые при небольшом корпусе (условно, десятки тысяч документов) могут оказаться сравнимы или дороже, чем просто заплатить API за объём и не тратить время инженера на инфраструктуру.
- Модель имеет значение. Если вам достаточно качества небольшой модели эмбеддингов (что для многих задач RAG вполне рабочий вариант), собственный прогон почти всегда дешевле. Если нужно качество топовых проприетарных моделей, недоступных для self-host, сравнение теряет смысл — вопрос не в цене, а в доступности модели.
- Это разовая задача, а не подписка. Частая ошибка — сравнить помесячную ставку выделенного сервера со счётом от API за один прогон. Для батч-задачи вы арендуете сервер на конкретный срок (дни, иногда часы), и считать нужно именно его, а не полную месячную ставку.
Возьмите свои реальные цифры — объём корпуса в токенах, актуальную цену API за миллион токенов, почасовую ставку интересующей вас GPU-конфигурации и измеренную скорость модели — и подставьте в обе формулы выше. Для по-настоящему больших разовых корпусов (сотни миллионов и выше токенов) аренда мощного GPU-сервера на нужный срок в большинстве случаев обходится дешевле, чем оплата того же объёма через API по токенам, особенно если есть возможность распараллелить обработку на несколько карт одновременно и уложить весь прогон в несколько дней аренды.
Практические нюансы: батчинг, чекпоинты и повторные прогоны
Разовая пакетная обработка миллиона документов редко проходит с первого раза идеально гладко, и это нужно закладывать в план работ и в бюджет времени аренды заранее.
Динамический батчинг. Не отправляйте документы в модель по одному — это резко занижает утилизацию GPU. Группируйте чанки в батчи по суммарной длине токенов (а не по фиксированному числу документов), чтобы каждый батч заполнял память GPU равномерно независимо от разброса длины текстов.
Чекпоинты обязательны. На объёме в миллион документов процесс будет идти часы, а иногда и дни. Сохраняйте прогресс — например, ID последнего обработанного документа или номер батча — в отдельный файл или таблицу после каждого N-го батча:
import json
def save_checkpoint(last_processed_id, path="checkpoint.json"):
with open(path, "w") as f:
json.dump({"last_id": last_processed_id}, f)
def load_checkpoint(path="checkpoint.json"):
try:
with open(path) as f:
return json.load(f)["last_id"]
except FileNotFoundError:
return None
Это критично именно для аренды: если сервер по любой причине перезагрузится или процесс упадёт по OOM на 800-тысячном документе, вы не хотите оплачивать повторную аренду и повторный прогон всего миллиона с нуля.
Запускайте долгие прогоны в tmux или screen, либо как systemd-сервис, а не в интерактивной SSH-сессии, которая оборвётся при разрыве соединения:
tmux new -s embeddings
python run_embeddings.py --checkpoint checkpoint.json --batch-tokens 16384
# Ctrl+B, D — отключиться от сессии, процесс продолжит работать
Мониторьте утилизацию GPU, чтобы убедиться, что вы платите за реальную работу, а не простаиваете из-за узкого места на CPU-стороне (токенизация, чтение файлов, запись в БД): watch -n 2 nvidia-smi. Если утилизация стабильно ниже 70-80%, узкое место не в видеокарте, и увеличение батча или числа воркеров-читателей может ускорить прогон без смены железа — а значит, сократить оплаченное время аренды.
Дедупликация до инференса. Если в корпусе есть повторяющиеся или почти идентичные документы (частая ситуация с логами, версиями документов, парсенным контентом), дедупликация на уровне хэша снижает реальный объём работы — и для API, и для аренды — ещё до старта платного процесса.
Запись в векторную базу отдельным потоком. Не блокируйте GPU-инференс ожиданием записи в Qdrant/Milvus — пишите векторы в буфер и сбрасывайте их в базу асинхронно, иначе часть арендованного GPU-времени будет простаивать в ожидании диска и сети.
Подробнее о том, как сам механизм эмбеддингов превращает текст в координаты и почему выбор модели влияет на качество RAG, можно почитать в статье про то, как embedding превращает смысл в координаты и в разборе моделей эмбеддингов для RAG. Если объём корпуса скромнее и GPU не требуется вовсе, стоит сначала оценить вариант из статьи как считать эмбеддинги на CPU без GPU — для не очень больших корпусов это может закрыть задачу вообще без аренды видеокарты. А если после разовой генерации вам понадобится перенести готовые векторы в другую векторную базу, пригодится материал про перенос эмбеддингов при смене векторной базы.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Что выгоднее для миллиона документов — API или своя GPU-аренда?
Однозначного ответа без ваших цифр нет, но чем больше суммарный объём токенов и чем лучше задача параллелится на несколько GPU, тем сильнее экономика склоняется в сторону временной аренды. Для небольших разовых корпусов накладные расходы на развёртывание своего инференса могут перевесить экономию.
Нужно ли арендовать сервер на месяц, если обработка займёт два дня?
Нет — логичнее посуточная или почасовая аренда именно на тот срок, который нужен для прогона плюс запас на тесты и повторы. Считать нужно фактическое время использования, а не полную месячную ставку.
Какую модель эмбеддингов выбрать, чтобы уложиться в разумное время?
Начните с небольшой или средней модели (класса base) — для большинства задач RAG её качества достаточно, а скорость пакетной обработки заметно выше, чем у крупных моделей. Точную скорость всё равно нужно измерить на своих данных перед арендой, а не полагаться на чужие бенчмарки.
Что делать, если во время прогона на арендованном сервере что-то упало?
Именно поэтому чекпоинты обязательны при любой батч-задаче на миллионы документов — без них частичный сбой означает повторную оплату аренды за весь объём, а с чекпоинтом вы перезапускаете процесс с последней сохранённой точки.
Можно ли ускорить обработку, взяв сервер с несколькими GPU?
Да, и для разовой задачи это часто самый прямой способ сократить время аренды — батч-инференс эмбеддингов хорошо параллелится по документам между картами, в отличие от обучения модели.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →