Инференс модели
Подробное описание настройки и запуска инференса модели на платформе Qubu с использованием Python и BentoML.
Инференс модели
Инференс позволяет запустить обученную модель как сервис для получения предсказаний.
На платформе Qubu инференс реализован с использованием Python и фреймворка BentoML ≥ 1.2, который отвечает за развёртывание сервера, маршруты и документацию API.
Разработчик модели настраивает код, интерфейс и ресурсы, после чего получает готовый endpoint и веб‑форму для тестирования.
Что такое BentoML?
BentoML — это фреймворк для развертывания ML-моделей, который автоматически создаёт REST API, документацию и сервер для вашей модели. В версии 1.2+ сервис описывается как Python-класс с декораторами @bentoml.service и @bentoml.api.
BentoML v1.2+ — новый API
Qubu использует BentoML v1.2+ с class-based API. Старый функциональный стиль (svc = bentoml.Service(...), @svc.api(input=JSON(), output=JSON()), from bentoml.io import JSON) не поддерживается и вызовет ошибку AttributeError: 'Service' object has no attribute 'api'.
1. Конфигурация развертывания
Выбор провайдера и машины
Перед началом необходимо выбрать:
- Провайдер (например, RunPod)
- Тип машины — GPU или CPU (Tesla V100, A100 и др.)
Настройка параметров
Укажите параметры окружения:
- Источник сервиса —
Написать код(редакторservice.py) илиЗагрузить .bento(готовый архив) - Переменные окружения — пути к файлам, ключи API и другие параметры
- Зависимости — содержимое
requirements.txtдля pip - Настройки доступа — Public, Private или Qubu users
Мониторинг состояния
После развертывания станут доступны:
- Обновить статус — проверка текущего состояния
- Пауза / Остановить Endpoint — приостановка или полная остановка контейнера
- Перезапустить сервис — применение изменений кода без пересоздания контейнера
Источник сервиса: «Написать код» или «Загрузить .bento»
Qubu поддерживает два способа задать BentoML‑сервис:
- Написать код: вы редактируете
service.pyпрямо в интерфейсе. Платформа установитrequirements.txtи запуститbentoml serve service:Service. - Загрузить .bento: вы загружаете уже собранный Bento‑архив. Платформа скачает архив, выполнит
bentoml importи запуститbentoml serve <bento_tag>.
.bento — что внутри?
.bento — это архив с кодом сервиса и метаданными. Он может содержать модели/веса, но для Qubu обычно правильнее хранить веса отдельно и грузить их в контейнер в /workspace/model (через модельные артефакты), а в сервисе ссылаться на MODEL_PATH=/workspace/model.
Готовность сервиса
Когда сервис успешно запущен, появится строка Endpoint активен с URL. В блоке Состояние контейнера все флаги (health, ready, этап) должны быть зелёными.
2. Код инференса
В блоке Код инференса вы пишете Python‑код, который обрабатывает запросы и вызывает модель.
BentoML 1.2+ использует class-based подход: вы описываете сервис как класс с декоратором @bentoml.service, а каждый endpoint — метод класса с декоратором @bentoml.api.
Структура сервиса
Каждый сервис — это файл service.py с классом Service:
import bentoml
@bentoml.service(name="my_inference")
class Service:
def __init__(self) -> None:
# Загрузка модели и подготовка к инференсу
pass
@bentoml.api
def predict(self, inputs: dict) -> dict:
# Обработка запроса и возврат результата
return {"result": "..."}Обязательные правила
-
В режиме «Написать код»:
- Класс должен называться
Service(именно с большой буквы) - Файл должен называться
service.py - Платформа запускает сервис командой
bentoml serve service:Service
- Класс должен называться
-
В режиме «Загрузить .bento»:
- Входная точка определяется вашим Bento‑архивом (Qubu запускает
bentoml serve <bento_tag>)
- Входная точка определяется вашим Bento‑архивом (Qubu запускает
Базовый пример
import os
import bentoml
from typing import Any, Dict, Optional
@bentoml.service(name="salary_predict_inference")
class Service:
def __init__(self) -> None:
# Загрузка модели при старте (один раз)
import pickle
model_path = os.getenv("MODEL_PATH", "/workspace/model")
with open(f"{model_path}/model.pkl", "rb") as f:
self.model = pickle.load(f)
@bentoml.api
def predict(self, inputs: dict) -> dict:
skills = inputs.get("skills", [])
variable_1 = inputs.get("variable_1")
variable_2 = inputs.get("variable_2")
variable_3 = inputs.get("variable_3")
prediction = self.model.predict(...)
return {"prediction": str(prediction)}Примеры с разными моделями
import os
import pickle
import bentoml
@bentoml.service(name="sklearn_inference")
class Service:
def __init__(self) -> None:
model_path = os.getenv("MODEL_PATH", "/workspace/model")
with open(f"{model_path}/model.pkl", "rb") as f:
self.model = pickle.load(f)
@bentoml.api
def predict(self, inputs: dict) -> dict:
features = inputs.get("features")
prediction = self.model.predict([features])[0]
return {"prediction": float(prediction)}import os
import bentoml
from catboost import CatBoostClassifier
@bentoml.service(name="catboost_inference")
class Service:
def __init__(self) -> None:
model_path = os.getenv("MODEL_PATH", "/workspace/model")
self.model = CatBoostClassifier()
self.model.load_model(f"{model_path}/catboost_model.cbm")
@bentoml.api
def predict(self, inputs: dict) -> dict:
features = inputs.get("features")
prediction = self.model.predict([features])[0]
probability = self.model.predict_proba([features])[0]
return {
"prediction": int(prediction),
"probability": float(probability.max()),
}import os
import bentoml
import torch
@bentoml.service(name="pytorch_inference")
class Service:
def __init__(self) -> None:
model_path = os.getenv("MODEL_PATH", "/workspace/model")
self.model = torch.load(f"{model_path}/model.pt", weights_only=False)
self.model.eval()
@bentoml.api
def predict(self, inputs: dict) -> dict:
features = torch.tensor(inputs.get("features"))
with torch.no_grad():
output = self.model(features)
prediction = output.argmax().item()
return {"prediction": prediction}Этот шаблон используется по умолчанию при создании нового инференса на Qubu:
import os
import logging
from typing import Any, Dict, Optional
import bentoml
from transformers import pipeline
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
MODEL_PATH = os.getenv("MODEL_PATH", "/workspace/model")
TASK = os.getenv("TASK", "text-classification")
@bentoml.service(name="inference")
class Service:
def __init__(self) -> None:
logger.info(f"Load transformers pipeline: task={TASK}, model_path={MODEL_PATH}")
self.pipeline = pipeline(TASK, model=MODEL_PATH, tokenizer=MODEL_PATH)
@bentoml.api
def predict(self, inputs: Any, parameters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Универсальный endpoint. Поддерживает строку или список строк."""
params = parameters or {}
data = inputs if isinstance(inputs, list) else [inputs]
outputs = self.pipeline(data, **params)
return {"results": outputs, "count": len(outputs)}Несколько endpoints
Вы можете определить несколько методов с @bentoml.api в одном сервисе. Для кастомного маршрута используйте параметр route:
import bentoml
@bentoml.service(name="multi_endpoint")
class Service:
def __init__(self) -> None:
self.model = ...
@bentoml.api
def predict(self, inputs: dict) -> dict:
"""Основной endpoint: POST /predict"""
return {"result": self.model.predict(inputs)}
@bentoml.api(route="/classify")
def classify(self, text: str) -> dict:
"""Дополнительный endpoint: POST /classify"""
return {"label": self.model.classify(text)}
@bentoml.api(route="/health")
def health(self) -> dict:
"""Кастомный health-check: POST /health"""
return {"status": "ok"}Обновление сервиса
После изменения кода нажмите Перезапустить сервис, чтобы обновить версию на сервере. При успешном запуске сервис станет доступен по вашему endpoint, а в браузере появится автосгенерированная документация (Swagger / OAS 3.0).
3. Конструктор UI и маршрутов
После написания кода необходимо настроить интерфейс и маршруты — то, как пользователи будут взаимодействовать с эндпоинтами.
Маршруты
Определите пути и методы для вызова вашей модели
Входные поля
Настройте параметры, которые будут передаваться в модель
Выходные данные
Укажите, как результат должен отображаться пользователю
Настройка маршрута
В блоке Конструктор UI/маршрутов создайте новую функцию:
| Поле | Значение |
|---|---|
| Название | Predict |
| Путь | /predict |
| Метод | POST |
| Content‑Type | application/json |
| Auth | qubu (user) |
| Корень JSON (bodyRoot) | inputs |
Важно
Маршрут должен совпадать с именем метода или параметром route декоратора @bentoml.api. Если метод называется predict и route не задан, BentoML создаст endpoint /predict.
Входные поля
Настройте, какие параметры будут передаваться в модель.
Пример конфигурации для задачи прогнозирования зарплаты:
| Ключ | Заголовок | Тип поля | Дополнительно |
|---|---|---|---|
skills | Навыки | Массив (Array) | тип элементов — text |
variable_1 | График | Select | Опции: гибкий, полный день, вахта, удалёнка, посменно |
variable_2 | Опыт работы | Select | Опции: 1–3, 3–6, 6+, без опыта |
variable_3 | Тип занятости | Select | пользовательские опции |
Каждое поле будет отображаться на странице модели как форма для ввода.
Выходные данные
Затем настройте вывод — какой результат должен отобразиться пользователю.
| Поле | Значение |
|---|---|
| Тип | text |
| Заголовок | ЗП такого специалиста |
| JSON Path | prediction |
Это означает, что из JSON‑ответа сервиса будет взято значение поля prediction и выведено в интерфейсе.
Если структура отличается, результат не отобразится корректно.
4. Зависимости и окружение
requirements.txt
Укажите Python-пакеты в поле Requirements (вкладка Env). Платформа установит их через pip install -r requirements.txt при запуске контейнера.
transformers>=4.40
torch>=2.0
numpy<2Автоустановка зависимостей
Если при запуске сервиса Python не может импортировать модуль, платформа автоматически попытается его установить (до 5 попыток). Но лучше указать все зависимости заранее в requirements.txt — это надёжнее и быстрее.
bentofile.yaml (опционально)
Если вам удобнее описывать сборку Bento через bentofile.yaml, вы можете вставить его в поле bentofile.yaml (в секции «Код»).
На деплое Qubu:
- сохраняет YAML как
/workspace/bentofile.yaml - запускает
bentoml build - затем запускает
bentoml serve <bento_tag>для собранного Bento
Ограничение: Qubu не парсит список зависимостей из bentofile.yaml для установки. Убедитесь, что нужные пакеты всё равно указаны в requirements.txt.
Переменные окружения
Задайте переменные в формате KEY=VALUE (каждая на новой строке):
MODEL_PATH=/workspace/model
TASK=text-classification
HF_HOME=/workspace/.cache/huggingfaceСтандартные переменные, доступные по умолчанию:
| Переменная | Описание |
|---|---|
MODEL_PATH | Путь к артефактам модели (задаётся при синхронизации) |
WORKDIR | Рабочая директория (/workspace) |
HOST | Хост сервиса (0.0.0.0) |
PORT | Порт сервиса (8888) |
5. Развёртывание
Запуск развёртывания
Когда код и UI настроены, нажмите Развернуть Endpoint.
Qubu создаст контейнер с BentoML‑сервисом, установит зависимости и поднимет сервер на выбранной машине.
Получение URL
После успешного развертывания появится активная ссылка:
https://qubu.ai/api/models/<model_slug>/inference/proxyПроверка документации
Откройте URL в браузере — вы увидите BentoML‑страницу с:
- Автоматически сгенерированной документацией API (OpenAPI/Swagger)
- Интерактивной формой для тестовых запросов
- Списком доступных эндпоинтов
6. Тестирование и вызовы
После запуска можно отправлять запросы к сервису через веб‑форму или напрямую по API.
Примеры вызова API
curl -X POST "https://qubu.ai/api/models/salary-predict/inference/proxy/predict" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"inputs": {
"skills": ["Python", "ML"],
"variable_1": "remote",
"variable_2": "between1And3",
"variable_3": "fullDay"
}
}'import requests
url = "https://qubu.ai/api/models/salary-predict/inference/proxy/predict"
headers = {
"Authorization": "Bearer <API_KEY>",
"Content-Type": "application/json"
}
data = {
"inputs": {
"skills": ["Python", "ML"],
"variable_1": "remote",
"variable_2": "between1And3",
"variable_3": "fullDay"
}
}
response = requests.post(url, headers=headers, json=data)
print(response.json())const response = await fetch(
'https://qubu.ai/api/models/salary-predict/inference/proxy/predict',
{
method: 'POST',
headers: {
'Authorization': 'Bearer <API_KEY>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
inputs: {
skills: ['Python', 'ML'],
variable_1: 'remote',
variable_2: 'between1And3',
variable_3: 'fullDay'
}
})
}
);
const data = await response.json();
console.log(data);На странице модели доступна встроенная веб-форма для тестирования:
- Откройте страницу вашей модели на Qubu
- Найдите блок Тестирование
- Заполните поля формы
- Нажмите Выполнить
- Результат появится в блоке вывода
Веб-форма автоматически подставляет токен авторизации, если вы вошли в систему.
Пример ответа
{ "prediction": "65000 ₽" }Результат запроса также появится на странице модели в блоке вывода.
7. Взаимосвязь кода и интерфейса
Синхронизация кода и интерфейса
Главный принцип — код и интерфейс должны быть синхронизированы, иначе интерфейс не сможет корректно отобразить результат.
8. Логи и диагностика
Блок Логи (tail) отображает состояние запущенного сервиса в реальном времени и помогает:
- Отследить ошибки
- Проверить успешную инициализацию модели
- Убедиться, что BentoML‑сервис запущен корректно
Доступные источники логов
BentoML
Логи самого сервиса инференса и обработки запросов
Admin API
Логи административного API и управления контейнером
Start Service
Логи запуска и инициализации сервиса
Qubu Agent
Логи агента мониторинга и синхронизации артефактов
Типичные ошибки
9. Доступ к сервису
Режим доступа определяет, кто может выполнять запросы к модели:
Публичный доступ
Модель доступна всем без авторизации. Идеально для:
- Демонстрационных моделей
- Открытых API
- Прототипов и экспериментов
curl -X POST "https://qubu.ai/api/models/model-name/inference/proxy/predict" \
-H "Content-Type: application/json" \
-d '{"inputs": {...}}'Публичные модели могут быть использованы кем угодно. Не размещайте в них чувствительные данные.
Доступ для зарегистрированных пользователей
Модель доступна только авторизованным пользователям Qubu. Требуется API-ключ:
curl -X POST "https://qubu.ai/api/models/model-name/inference/proxy/predict" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"inputs": {...}}'API-ключ можно получить в настройках профиля на платформе Qubu.
Приватный доступ
Модель доступна только владельцу и приглашённым участникам. Максимальная безопасность:
curl -X POST "https://qubu.ai/api/models/model-name/inference/proxy/predict" \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"inputs": {...}}'Функции приватного доступа:
- Управление списком участников
- Индивидуальные разрешения
- Полный контроль над использованием
10. Продвинутые настройки BentoML (resources / traffic / batching)
Qubu не выносит в UI многие “операторские” параметры BentoML (таймауты, concurrency, batching, ограничения ресурсов) — их проще и надёжнее задавать в коде.
Где это обычно настраивается:
- resources / device: в
@bentoml.service(...)и логике загрузки модели (CPU/GPU, пути, кеши) - traffic / timeouts / concurrency: в параметрах
@bentoml.api(...)и/или настройках сервера BentoML - batching: через встроенные механизмы BentoML (если вам это действительно нужно)
Если вы не уверены, нужно ли это — чаще всего достаточно корректно выставить requirements.txt, загрузку модели в __init__ и явные маршруты/схемы входа/выхода.
Итог
1. Выберите источник сервиса
Либо напишите class-based BentoML‑сервис в service.py, либо загрузите готовый .bento архив
2. Настройте UI
Определите входные поля и выходные данные
3. Разверните
Выберите машину и запустите endpoint
4. Используйте
Получите готовый API и веб-форму для тестирования
Ключевые преимущества
Инференс на Qubu объединяет Python‑код и визуальную настройку интерфейса. Вы пишете class-based сервис на BentoML, описываете структуру данных в конструкторе UI, выбираете машину и разворачиваете сервис. После запуска модель доступна через веб‑форму и REST API, а все изменения можно обновить и перезапустить в один клик.