Тестирование Систем Компьютерного Зрения

Компьютерное зрение управляет автономными транспортными средствами, медицинской диагностикой, системами безопасности и QA в производстве. В отличие от традиционного программного обеспечения, CV модели имеют дело с неоднозначностью, визуальной вариативностью и сложностью реального мира.

Тестирование CV систем требует оценки точности при разнообразных условиях, состязательной робастности, справедливости между демографиями и ограничений производительности реального времени.

Основные Стратегии Тестирования

1. Метрики Точности

from sklearn.metrics import accuracy_score, precision_recall_fscore_support
import numpy as np

class ОценщикМоделиCV:
    def __init__(self, модель):
        self.модель = модель

    def оценить_классификацию(self, тестовые_изображения, истинные_метки):
        """Оценить модель классификации"""
        предсказания = self.модель.predict(тестовые_изображения)
        предсказанные_метки = np.argmax(предсказания, axis=1)

        # Общая точность
        точность = accuracy_score(истинные_метки, предсказанные_метки)

        # Метрики по классам
        точность_класс, полнота, f1, поддержка = precision_recall_fscore_support(
            истинные_метки,
            предсказанные_метки,
            average=None
        )

        return {
            'точность': точность,
            'метрики_по_классам': {
                self.модель.имена_классов[i]: {
                    'точность': точность_класс[i],
                    'полнота': полнота[i],
                    'f1_score': f1[i],
                    'поддержка': поддержка[i]
                }
                for i in range(len(self.модель.имена_классов))
            }
        }

2. Валидация Датасета

import cv2
from collections import Counter

class ВалидаторДатасета:
    def проверить_баланс_классов(self):
        """Обнаружить дисбаланс классов"""
        подсчеты_меток = Counter(self.dataset.метки)
        всего = len(self.dataset.метки)

        отчет_дисбаланса = {}
        for имя_класса, подсчет in подсчеты_меток.items():
            процент = (подсчет / всего) * 100
            отчет_дисбаланса[имя_класса] = {
                'подсчет': подсчет,
                'процент': процент,
                'дисбалансирован': процент < 5 or процент > 50
            }

        return отчет_дисбаланса

    def анализировать_качество_изображения(self):
        """Проверить низкокачественные изображения"""
        проблемы_качества = []

        for путь_изобр in self.dataset.пути_изображений:
            изобр = cv2.imread(путь_изобр)

            # Проверить разрешение
            высота, ширина = изобр.shape[:2]
            if высота < 224 or ширина < 224:
                проблемы_качества.append({
                    'изображение': путь_изобр,
                    'проблема': 'низкое_разрешение',
                    'разрешение': f"{ширина}x{высота}"
                })

            # Проверить яркость
            серое = cv2.cvtColor(изобр, cv2.COLOR_BGR2GRAY)
            яркость = np.mean(серое)
            if яркость < 30 or яркость > 225:
                проблемы_качества.append({
                    'изображение': путь_изобр,
                    'проблема': 'плохая_яркость',
                    'яркость': яркость
                })

        return проблемы_качества

3. Состязательное Тестирование

import tensorflow as tf

class СостязательныйТестер:
    def __init__(self, модель):
        self.модель = модель

    def атака_fgsm(self, изображение, истинная_метка, epsilon=0.01):
        """Атака Fast Gradient Sign Method"""
        тензор_изображения = tf.convert_to_tensor(изображение[np.newaxis, ...])

        with tf.GradientTape() as tape:
            tape.watch(тензор_изображения)
            предсказание = self.модель(тензор_изображения)
            потеря = tf.keras.losses.sparse_categorical_crossentropy(
                [истинная_метка], предсказание
            )

        градиент = tape.gradient(потеря, тензор_изображения)
        знаковый_град = tf.sign(градиент)

        # Создать состязательное изображение
        состязательное_изображение = изображение + epsilon * знаковый_град.numpy()[0]
        состязательное_изображение = np.clip(состязательное_изображение, 0, 1)

        # Тестировать успешность атаки
        сост_предск = self.модель.predict(состязательное_изображение[np.newaxis, ...])
        сост_метка = np.argmax(сост_предск)

        return {
            'оригинальная_метка': истинная_метка,
            'состязательная_метка': сост_метка,
            'атака_успешна': сост_метка != истинная_метка,
            'состязательное_изображение': состязательное_изображение
        }

    def тестировать_робастность(self, тестовый_набор, значения_epsilon=[0.01, 0.05, 0.1]):
        """Тестировать робастность между силами атаки"""
        результаты = {eps: {'успехи': 0, 'всего': 0} for eps in значения_epsilon}

        for изображение, метка in тестовый_набор:
            for epsilon in значения_epsilon:
                результат = self.атака_fgsm(изображение, метка, epsilon)
                результаты[epsilon]['всего'] += 1
                if результат['атака_успешна']:
                    результаты[epsilon]['успехи'] += 1

        # Рассчитать оценки робастности
        оценки_робастности = {
            eps: 1 - (данные['успехи'] / данные['всего'])
            for eps, данные in результаты.items()
        }

        return оценки_робастности

4. Тестирование Аугментации

import albumentations as A

class ТестерАугментации:
    def тестировать_с_аугментациями(self, изображение, истинная_метка):
        """Тестировать консистентность модели под аугментациями"""
        аугментации = [
            ('поворот', A.Rotate(limit=15, p=1)),
            ('яркость', A.RandomBrightness(limit=0.2, p=1)),
            ('размытие', A.Blur(blur_limit=3, p=1)),
            ('шум', A.GaussNoise(var_limit=(10, 50), p=1)),
            ('флип', A.HorizontalFlip(p=1))
        ]

        оригинальное_предсказание = self.модель.predict(изображение[np.newaxis, ...])[0]
        оригинальный_класс = np.argmax(оригинальное_предсказание)

        результаты = {}

        for имя_aug, аугментация in аугментации:
            аугментированное = аугментация(image=изображение)['image']
            aug_предск = self.модель.predict(аугментированное[np.newaxis, ...])[0]
            aug_класс = np.argmax(aug_предск)

            результаты[имя_aug] = {
                'предсказание_изменилось': aug_класс != оригинальный_класс,
                'всё_ещё_правильно': aug_класс == истинная_метка
            }

        # Рассчитать оценку инвариантности
        оценка_инвариантности = sum(
            1 for р in результаты.values() if not р['предсказание_изменилось']
        ) / len(результаты)

        return {
            'результаты_аугментации': результаты,
            'оценка_инвариантности': оценка_инвариантности
        }

Тестирование Производительности

import time

class ТестерПроизводительности:
    def бенчмарк_инференса(self, тестовые_изображения, размеры_батчей=[1, 8, 32]):
        """Бенчмарк скорости инференса"""
        результаты = {}

        for размер_батча in размеры_батчей:
            латентности = []

            for i in range(0, len(тестовые_изображения), размер_батча):
                батч = тестовые_изображения[i:i+размер_батча]

                начало = time.time()
                _ = self.модель.predict(батч)
                конец = time.time()

                латентность_мс = (конец - начало) * 1000 / len(батч)
                латентности.append(латентность_мс)

            результаты[f'batch_{размер_батча}'] = {
                'средняя_латентность_мс': np.mean(латентности),
                'латентность_p95_мс': np.percentile(латентности, 95),
                'пропускная_способность_fps': 1000 / np.mean(латентности)
            }

        return результаты

Лучшие Практики

ПрактикаОписание
Разнообразный Тестовый НаборВключать разное освещение, углы, фоны
Сбор Крайних СлучаевОкклюзии, экстремальные углы, плохое освещение
Кросс-Датасет ВалидацияТестировать на данных из разных источников
Состязательное УкреплениеВключать состязательные примеры в обучение
Непрерывная ОценкаМониторить производственный дрейф производительности
Тестирование СправедливостиТестировать между демографиями (тона кожи, возрасты)

Заключение

Тестирование компьютерного зрения выходит за пределы метрик точности—требуя тестирования робастности, валидации датасетов, состязательных защит и оценки справедливости. По мере развертывания CV систем в критически важных для безопасности приложениях, строгое тестирование становится критичным.