Computer Vision at scale: lessons from production
How to process 100K+ images per day: architecture, optimization, and pitfalls of production CV systems.
Постановка задачи
Клиент — крупный e-commerce ритейлер с каталогом из 2+ млн товаров. Задача: автоматическая проверка качества фотографий товаров (наличие водяных знаков, правильная ориентация, соответствие категории).
Нагрузка: 100K+ новых изображений в день, пиковые нагрузки до 5000 изображений/час. Требование к latency: < 200ms на изображение.
Архитектура решения
1. Upload Pipeline
Изображения загружаются в S3-совместимое хранилище. Lambda-функция (или аналог) ставит задачу в очередь (SQS/RabbitMQ).
2. Preprocessing Workers
Автомасштабируемый пул воркеров забирает задачи из очереди. Предобработка: resize, нормализация, конвертация форматов.
3. Inference Cluster
GPU-ноды с батчевой обработкой. Модели: YOLOv8 для детекции, EfficientNet для классификации, кастомные модели для специфических проверок.
4. Post-processing & Storage
Агрегация результатов, сохранение в PostgreSQL + кэш в Redis. Webhook-уведомления для внешних систем.
Ключевые оптимизации
1. Batch Processing
Самый эффективный способ увеличить throughput — батчевая обработка. Вместо обработки изображений по одному собирайте батчи:
# Плохо: по одному
for image in images:
result = model.predict(image) # 50ms
# Хорошо: батчами
batch_size = 32
for i in range(0, len(images), batch_size):
batch = images[i:i + batch_size]
results = model.predict(batch) # 60ms на весь батчРезультат: увеличение throughput с 20 до 500 изображений/сек на одном GPU.
2. Model Quantization
FP32 → INT8 снижает размер модели в 4 раза и ускоряет инференс в 2-3 раза с потерей точности < 1%.
# ONNX Runtime с quantization import onnx from onnxruntime.quantization import quantize_dynamic model_fp32 = 'model.onnx' model_quantized = 'model_int8.onnx' quantize_dynamic(model_fp32, model_quantized)
3. TensorRT / ONNX Runtime
Для NVIDIA GPU используйте TensorRT. Для универсального развертывания — ONNX Runtime с оптимизациями графа.
| Фреймворк | Latency (batch=32) | Условия |
|---|---|---|
| PyTorch (baseline) | 120ms | — |
| ONNX Runtime | 85ms | Graph optimization |
| TensorRT | 45ms | FP16, NVIDIA GPU |
| TensorRT + INT8 | 32ms | Calibration required |
4. Caching Strategy
30-40% изображений — дубликаты или очень похожие фото. Используйте perceptual hashing (pHash) для дедупликации и кэширования результатов.
import imagehash
from PIL import Image
def get_image_hash(image_path):
img = Image.open(image_path)
return str(imagehash.phash(img))
# Проверка кэша перед инференсом
image_hash = get_image_hash(image_path)
if cached_result := redis.get(f"cv:{image_hash}"):
return json.loads(cached_result) # < 1msАвтомасштабирование
Нагрузка CV-систем неравномерна: пики при загрузке новых коллекций, ночные batch-обработки. Настройте HPA (Horizontal Pod Autoscaler) с кастомными метриками:
# HPA configuration
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: cv-inference-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: cv-inference
minReplicas: 2
maxReplicas: 20
metrics:
- type: External
external:
metric:
name: queue_messages_ready
target:
type: AverageValue
averageValue: "50" # 50 сообщений на подПодводные камни
1. Memory Leaks в долгоживущих процессах
PyTorch и TensorFlow кэшируют CUDA memory. При длительной работе без перезагрузки процесс может занять всю доступную память. Решение: graceful restart каждые N запросов или по таймеру.
2. Размер входных изображений
Пользователи загружают 50MP фото с камер. Обязательно ограничивайте размер на входе (max 1920x1080) и ресайзите до подачи в модель.
3. Форматы и цветовые пространства
CMYK, grayscale, EXIF-ориентация — все это ломает предобученные модели, ожидающие RGB. Нормализуйте формат на входе:
def normalize_image(image_path):
img = Image.open(image_path)
# EXIF orientation
img = ImageOps.exif_transpose(img)
# Convert to RGB
if img.mode != 'RGB':
img = img.convert('RGB')
# Resize with aspect ratio preservation
img.thumbnail((1920, 1920))
return img4. Timeout и retries
CV-модели могут "зависнуть" на поврежденных изображениях. Всегда используйте timeouts и circuit breaker pattern.
Мониторинг
Ключевые метрики для CV-системы:
- Throughput: изображений/сек
- Latency: p50, p95, p99 (разбивка по этапам)
- GPU utilization: % использования VRAM и вычислений
- Queue depth: количество необработанных задач
- Error rate: % ошибок по типам (OOM, timeout, corrupt image)
- Model drift: распределение confidence scores со временем
Итоговые цифры
| Метрика | Целевое | Достигнутое |
|---|---|---|
| Throughput | 100K/день | 350K/день (пик 500K) |
| Latency (p95) | < 200ms | 145ms |
| GPU utilization | — | 75% среднее |
| Cache hit rate | — | 35% |
| Стоимость обработки | — | $0.0012/изображение |