Почему 15 миллионов карточек в день — это не шутки
Wildberries, как и любой крупный маркетплейс, сталкивается с диким потоком новых товаров. Каждый продавец загружает фотографии, и каждую из них нужно проверить. Запрещенный контент, низкое качество, водяные знаки, AI-генерация — список проверок растет. 15 миллионов карточек в день. Это примерно 174 карточки в секунду. И каждая карточка — это 5-10 изображений. Представьте этот водопад пикселей.
Если вы думаете, что можно взять ResNet из torchvision, обернуть его в Flask и запустить на мощном инстансе — готовьтесь к счету за облако в миллионы рублей и к задержкам модерации в несколько часов. Так не работает.
Ключ к выживанию: унификация и супер-оптимизация
Проблема в разнородности. Одна модель ищет оружие, другая — насилие, третья определяет, сгенерировано ли изображение нейросетью (и да, для этого у Wildberries есть отдельный детектор, о котором мы уже писали). Запускать каждую на отдельном GPU — расточительно. Запускать последовательно — медленно. Решение — единый конвейер.
1Общий бэкбон, легкие головы
Вместо десятка независимых моделей используется одна мощная модель-бэкбон для извлечения фич (например, EfficientNet-V2 или ConvNeXt, актуальные на 2026 год). Ее вывод — богатое семантическое представление изображения. На эту «основу» навешиваются легкие «головы» — маленькие нейросети, каждая для своей задачи: классификация, детекция объектов, сегментация. Это экономит 70-80% вычислительных ресурсов.
2TensorRT: когда каждую миллисекунду считают
Обученная в PyTorch модель — это еще не продакшен-модель. Это черновик. Продакшен-модель — это план (plan) в TensorRT. NVIDIA TensorRT (на 2026 год актуальна версия 10.x) — компилятор и рантайм для глубокого обучения. Он делает три вещи: статически оптимизирует вычисления графа, квантует веса (например, в FP16 или INT8) и привязывает исполнение к конкретному GPU. Результат — ускорение инференса в 2-5 раз без потери точности.
# Примерная команда конвертации ONNX модели в TensorRT план (актуально на 2026)
trtexec --onnx=moderation_backbone.onnx \
--saveEngine=backbone_fp16.plan \
--precision=FP16 \
--workspace=4096 \
--builderOptimizationLevel=5Звучит просто, но квантование INT8 — это отдельная магия с калибровкой. Нужно пропустить через модель репрезентативную выборку данных, чтобы собрать статистику активаций. Ошибка здесь — и точность рухнет.
3Triton Inference Server: дирижер оркестра моделей
У вас есть бэкбон в TensorRT и несколько голов. Как их соединить? Как масштабировать? Писать свой сервер — гиблое дело. Ответ — Triton Inference Server. Он стал стандартом де-факто к 2026 году. Вы загружаете в него свои модели (TensorRT, PyTorch, ONNX — что угодно), описываете конвейер (pipeline) в простом конфиге, и Triton делает все остальное: батчинг, очередь запросов, исполнение на CPU/GPU, мониторинг.
// Упрощенный пример конфига ансамбля моделей для Triton (model.json)
{
"name": "moderation_pipeline",
"platform": "ensemble",
"max_batch_size": 128,
"input": [{ "name": "IMAGE", "data_type": "TYPE_UINT8", "dims": [ -1, -1, 3 ] }],
"output": [
{ "name": "VIOLENCE_SCORE", "data_type": "TYPE_FP32", "dims": [1] },
{ "name": "AI_GENERATED_SCORE", "data_type": "TYPE_FP32", "dims": [1] }
],
"ensemble_scheduling": {
"step": [
{ "model_name": "image_preprocessor", "model_version": -1, "input_map": { "RAW_IMAGE": "IMAGE" }, "output_map": { "PREPROCESSED_IMAGE": "NORMALIZED_IMAGE" } },
{ "model_name": "common_backbone", "model_version": -1, "input_map": { "INPUT": "NORMALIZED_IMAGE" }, "output_map": { "FEATURES": "EMBEDDING" } },
{ "model_name": "violence_head", "model_version": -1, "input_map": { "INPUT": "EMBEDDING" }, "output_map": { "OUTPUT": "VIOLENCE_SCORE" } },
{ "model_name": "ai_detector_head", "model_version": -1, "input_map": { "INPUT": "EMBEDDING" }, "output_map": { "OUTPUT": "AI_GENERATED_SCORE" } }
]
}
}Triton сам решит, какие модели разместить на одном GPU для минимизации передачи данных, а какие распараллелить. Он динамически собирает батчи из входящих запросов от разных клиентов. Это критически важно для утилизации GPU под 100%.
Пайплайн от загрузки до вердикта
Как выглядит полный путь изображения?
- Прием: Изображение попадает в очередь (Kafka или SQS). Важно: сырые байты, а не ссылки.
- Предобработка (DALI): Выделенный пул воркеров на GPU декодирует JPEG/PNG, меняет размер, нормализует. Результат — тензор в GPU памяти. Ни одного байта не уходит на CPU.
- Инференс (Triton + TensorRT): Тензор отправляется в Triton. Тот прогоняет его через ансамбль моделей. Задержка на этом этапе для сложного ансамбля — 15-50 мс на современном GPU (на 2026 год).
- Постинференс: Логика агрегации результатов. Если три из пяти моделей сказали «запрет», карточка отправляется на ручную проверку. Все решения и сырые скоринги пишутся в лог для последующего переобучения моделей.
| Этап | Инструмент | Цель | Примерная задержка (2026) |
|---|---|---|---|
| Декодирование/Ресайз | NVIDIA DALI | Перенос нагрузки на GPU | 3-5 мс |
| Извлечение признаков | TensorRT (бэкбон) | Максимальное ускорение | 10-20 мс |
| Специализированные проверки | Triton (легкие головы) | Параллельное исполнение | 2-5 мс на голову |
| Принятие решения | Кастомная логика (Go) | Агрегация порогов | < 1 мс |
Где спрятаны грабли? Нюансы, о которых молчат
- Версионирование моделей — ад. Выкатываешь новую версию бэкбона. Все головы должны быть перекалиброваны под новые фичи. В Triton это решается модель-энсамблем, но откатить один компонент без остальных — та еще задача. Нужен четкий CI/CD для моделей.
- Коварство батчинга. Triton собирает батчи динамически, ждет configurable timeout. Если трафик неравномерный, можно получить большие задержки в ожидании сборки батча. Настраивай `max_queue_delay_microseconds` в зависимости от SLA.
- Логирование для переобучения. Сохранять нужно не только итоговый скор, но и сырые логиты, и даже эмбеддинги бэкбона. Это терабайты данных в день. Но без этого не улучшить модель. Поможет архитектура, подобная той, что описана в статье про локальный RAG для миллионов документов — только для векторов признаков.
- А если нет GPU? Для менее требовательных задач или edge-сценариев можно посмотреть на такие решения, как Reka Edge 7B. Но для 15 млн карточек в день — только GPU, и много.
Главный секрет — не в самой крутой модели, а в бесшовной интеграции всех компонентов. DALI должен передавать данные в Triton без копирования в CPU память. Модели в Triton должны использовать общий пул GPU памяти. Мониторинг должен показывать не только «модель жива», но и перцентили задержки на каждом этапе для каждого типа товара.
Что дальше? Эволюция пайплайна
Система не статична. Тренд 2025-2026 годов — внедрение небольших мультимодальных моделей для кросс-проверки. Текст в описании товара противоречит изображению? Модель это заметит. Это следующий уровень модерации, где автоматическое понимание документов (ADE) встречается с компьютерным зрением.
Архитектура, построенная на DALI + TensorRT + Triton, — это фундамент. Она позволяет заменять модели-бэкбоны на более новые (ждем EfficientNet-V3 к 2027?), добавлять новые «головы» для свежих угроз без перестройки всей системы. Это и есть инженерная красота: когда система переживает десяток поколений моделей.
Если вы проектируете похожий пайплайн, начните не с моделей, а с логирования и воспроизводимости данных. Самый дорогой GPU-кластер бесполезен, если вы не можете понять, почему модель сегодня сломалась на кроссовках конкретного бренда. Соберите сначала петлю обратной связи. Все остальное — инструменты.