OpenCVMiddleSystem design

Как оценивать качество computer vision pipeline: метрики, golden datasets и regression tests?

Качество CV-pipeline оценивают через IoU/mAP для детекции, mIoU для сегментации, фиксированный golden dataset с аннотациями и pytest-регрессионные тесты на precision, recall и latency.

Зачем нужна формальная оценка качества CV-pipeline

Computer vision pipeline часто деградирует незаметно: изменилось освещение на камере, появился новый тип объектов, обновилась модель DNN. Без метрик, тестовых наборов и регрессионных тестов деградация обнаруживается только в production. Правильная система оценки включает три слоя: метрики качества, golden datasets и автоматические regression tests.

1. Метрики для разных задач

Детекция объектов

  • IoU (Intersection over Union) — перекрытие предсказанного и ground truth bounding box. Порог 0.5 — стандарт PASCAL VOC.
  • Precision / Recall — на конкретном пороге confidence.
  • mAP (mean Average Precision) — усреднение AP по классам и порогам IoU. Стандарт COCO: mAP@[0.5:0.95].

Сегментация

  • Pixel Accuracy — доля правильно классифицированных пикселей.
  • mIoU — среднее IoU по классам сегментации.
  • Dice Coefficient — 2*|A∩B| / (|A|+|B|), особенно для медицинских изображений.

Трекинг

  • MOTA (Multi-Object Tracking Accuracy) — учитывает FP, FN и ID switches.
  • IDF1 — согласованность идентификаторов объектов.
import numpy as np

def compute_iou(box1, box2):
    """box = [x1, y1, x2, y2]"""
    x1 = max(box1[0], box2[0])
    y1 = max(box1[1], box2[1])
    x2 = min(box1[2], box2[2])
    y2 = min(box1[3], box2[3])

    intersection = max(0, x2 - x1) * max(0, y2 - y1)
    area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
    union = area1 + area2 - intersection

    return intersection / union if union > 0 else 0.0

def evaluate_detections(predictions, ground_truths, iou_threshold=0.5):
    """
    predictions: list of dicts {box, confidence, class_id}
    ground_truths: list of dicts {box, class_id}
    """
    tp, fp, fn = 0, 0, 0
    matched = set()

    for pred in sorted(predictions, key=lambda x: -x["confidence"]):
        best_iou, best_idx = 0, -1
        for i, gt in enumerate(ground_truths):
            if i in matched or gt["class_id"] != pred["class_id"]:
                continue
            iou = compute_iou(pred["box"], gt["box"])
            if iou > best_iou:
                best_iou, best_idx = iou, i

        if best_iou >= iou_threshold:
            tp += 1
            matched.add(best_idx)
        else:
            fp += 1

    fn = len(ground_truths) - len(matched)
    precision = tp / (tp + fp) if (tp + fp) > 0 else 0
    recall = tp / (tp + fn) if (tp + fn) > 0 else 0
    return {"precision": precision, "recall": recall, "tp": tp, "fp": fp, "fn": fn}

2. Golden datasets

Golden dataset — фиксированный набор изображений с размеченными ground truth аннотациями, представляющий реальные условия эксплуатации. Правила составления:

  • Включите edge cases: плохое освещение, частичные перекрытия, мелкие объекты.
  • Зафиксируйте версию датасета в git (хэш или тег).
  • Разделите на подмножества: ночь/день, разные камеры, разные условия.
  • Никогда не модифицируйте golden dataset — только добавляйте новые подмножества.

3. Regression tests с pytest

import pytest
import cv2
import json
import numpy as np
from pathlib import Path

GOLDEN_DIR = Path("tests/golden")
ANNOTATIONS = json.loads((GOLDEN_DIR / "annotations.json").read_text())


def load_detector():
    net = cv2.dnn.readNetFromONNX("models/detector.onnx")
    net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
    return net


@pytest.fixture(scope="module")
def detector():
    return load_detector()


@pytest.mark.parametrize("image_name", ANNOTATIONS.keys())
def test_detection_regression(detector, image_name):
    image = cv2.imread(str(GOLDEN_DIR / "images" / image_name))
    assert image is not None, f"Image not found: {image_name}"

    # Run detector
    predictions = run_detector(detector, image)  # ваша функция
    ground_truths = ANNOTATIONS[image_name]

    metrics = evaluate_detections(predictions, ground_truths, iou_threshold=0.5)

    # Пороги регрессии
    assert metrics["precision"] >= 0.85, (
        f"{image_name}: precision {metrics['precision']:.3f} < 0.85"
    )
    assert metrics["recall"] >= 0.80, (
        f"{image_name}: recall {metrics['recall']:.3f} < 0.80"
    )


def test_latency_regression(detector):
    """Проверка, что pipeline не стал медленнее."""
    import time
    image = cv2.imread(str(GOLDEN_DIR / "images" / "benchmark.jpg"))
    times = []
    for _ in range(20):
        start = time.perf_counter()
        run_detector(detector, image)
        times.append(time.perf_counter() - start)

    p95 = np.percentile(times, 95)
    assert p95 < 0.1, f"P95 latency {p95*1000:.1f}ms exceeds 100ms threshold"

Подводные камни

  • Optimistic golden dataset — если датасет состоит только из «хороших» снимков, метрики будут завышены и не отражают реальные условия.
  • Нет разбивки по подгруппам — среднее mAP может быть 0.85, но ночные снимки дают 0.3. Всегда считайте метрики по срезам (slice-based evaluation).
  • Confidence threshold влияет на precision/recall — тест должен явно фиксировать порог; разные пороги дают несопоставимые цифры.
  • Аннотации дрейфуют — если ground truth разметчики меняются, аннотации могут стать несогласованными. Используйте inter-annotator agreement (Cohen's kappa).
  • Тест на latency зависит от железа CI — запускайте latency-тесты на dedicated runner или с явной пометкой «flaky на виртуалках».
  • Переобучение на golden dataset — если разработчики видят golden dataset и «подгоняют» модель под него, набор теряет ценность. Держите test set изолированным.

Common mistakes

  • Отвечать определением без production-сценария.
  • Не называть runtime boundary, security boundary или failure mode.
  • Игнорировать версию API, observability и тестовую проверку.

What the interviewer is testing

  • Объясняет механизм своими словами и без выдуманных API.
  • Называет реальные риски, диагностику и критерий корректности.
  • Связывает ответ с текущей документацией и миграционными ограничениями.

Sources

Related topics

Как оценивать качество computer vision pipeline: метрики, golden datasets и regression tests? | Talanto