YOLO для промышленного компьютерного зрения: кейс хакатона по бинарной сегментации | AiManual
AiManual Logo Ai / Manual.
16 Янв 2026 Гайд

Как выжать максимум из YOLO для промышленного CV: кейс хакатона с реальными данными

Реальный кейс хакатона: как мы решали задачу бинарной сегментации загрязнений камер на производстве с помощью YOLO. Полное руководство по оптимизации, валидации

Когда реальность бьет по голове: хакатон, где все не так

Мы пришли на промышленный хакатон с блеском в глазах и готовым пайплайном YOLOv8. Задача казалась простой: бинарная сегментация загрязнений на защитных стеклах камер. Два класса - чистый стекло и грязь. Что может быть проще?

Пока не открыли датасет.

Первая ошибка 90% команд: они начинают обучать модель до того, как посмотрят на данные. Мы тоже чуть не наступили на эти грабли.

Проблема, которая ломает стандартные подходы

В производственных условиях камеры смотрят на конвейер 24/7. Защитные стекла покрываются пылью, масляными брызгами, мелкими частицами. Когда загрязнение достигает критического уровня - система должна подать сигнал для чистки.

Звучит логично? Вот только в реальности:

  • Грязь не имеет четких границ
  • Освещение меняется каждые 15 минут (смена дня и ночи, включение прожекторов)
  • Некоторые виды загрязнений полупрозрачные
  • Аннотации сделаны людьми с разной степенью внимательности

Первое открытие: метрики врут

Мы стартовали с mAP@0.5 - стандартной метрики для YOLO. Первая же модель показала 0.89. Отличный результат! Но когда мы запустили инференс на реальных данных...

Система то не видела критических загрязнений, то кричала о чистом стекле. Почему? Потому что mAP@0.5 измеряет пересечение bounding box с ground truth на уровне 50%. А в нашей задаче даже 10% загрязнения - уже проблема.

💡
В промышленном CV метрики должны соответствовать бизнес-логике. Нельзя слепо доверять стандартным показателям.

Наш план атаки: от простого к сложному

1 Анализ данных до первой строчки кода

Мы потратили 3 часа на визуализацию датасета. Не на обучение - на изучение. Открыли каждое изображение, посмотрели распределение классов, вариации освещения, качество аннотаций.

Что обнаружили:

  • Классовый дисбаланс 95:5 (чистые:загрязненные)
  • В 30% случаев аннотации не соответствовали реальным границам загрязнений
  • Ночные снимки имели другой цветовой профиль

2 Кастомные метрики под бизнес-задачу

Вместо mAP@0.5 мы создали две метрики:

  1. False Negative Rate (FNR) - процент пропущенных критических загрязнений
  2. False Positive Rate (FPR) - процент ложных срабатываний на чистых стеклах

В производственных условиях FNR дороже в 10 раз. Пропустить грязь - остановить линию. Ложное срабатывание - отправить техника на проверку.

3 Балансировка данных без oversampling

Oversampling загрязненных образцов - очевидное, но плохое решение. Модель начинает находить загрязнения там, где их нет.

Вместо этого мы:

# Создаем синтетические загрязнения на чистых стеклах
import albumentations as A

def add_contamination(image):
    transform = A.Compose([
        A.RandomFog(p=0.3),          # Туман/дым
        A.RandomRain(p=0.2),         # Капли
        A.RandomShadow(p=0.25),      # Тени
        A.GaussNoise(p=0.25),        # Шум
    ])
    return transform(image=image)['image']

4 Трансферное обучение с умом

Брать предобученную на COCO модель YOLO - все равно что использовать молоток для микрохирургии. COCO знает про людей, машины, животных. Наша задача - найти едва заметные пятна на стекле.

Мы взяли модель, предобученную на датасете с похожими текстурами, и дообучили только последние слои.

Технические лайфхаки, которые сработали

Аугментации, которые имеют смысл

Стандартные аугментации (поворот, масштабирование) бесполезны для камер, закрепленных в одном положении. Вместо них:

Аугментация Эффект Когда использовать
ColorJitter (только brightness) Имитация изменения освещения Всегда
GaussianBlur Дефокусировка При плохой фокусировке камер
CutOut на границах Учет частичного загрязнения Когда загрязнения локальные

Постобработка предсказаний

YOLO выдает bounding boxes. Нам нужны маски загрязнений. Решение:

def boxes_to_contamination_mask(boxes, image_shape):
    """Преобразуем bounding boxes в карту загрязнений"""
    mask = np.zeros(image_shape[:2], dtype=np.uint8)
    
    for box in boxes:
        x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
        confidence = box.conf[0].cpu().numpy()
        
        # Учитываем уверенность модели
        intensity = int(confidence * 255)
        cv2.rectangle(mask, 
                      (int(x1), int(y1)), 
                      (int(x2), int(y2)), 
                      intensity, 
                      thickness=cv2.FILLED)
    
    # Морфологические операции для сглаживания
    kernel = np.ones((5,5), np.uint8)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
    
    return mask

Валидация, которая не врет

Мы разделили данные на три части:

  1. Тренировочные - 60%
  2. Валидационные - 20% - для подбора гиперпараметров
  3. Тестовые - 20% - финальная проверка, которую смотрели только один раз

Самая частая ошибка в хакатонах - использовать тестовый сет для валидации во время обучения. Так модель "подстраивается" под тест, и результаты становятся бессмысленными.

Зафиксируйте тестовый сет до начала экспериментов. Не подглядывайте в него до финальной оценки.

Развертывание на грани: оптимизация для Raspberry Pi

Наш победный трюк: мы не просто обучили точную модель. Мы сделали ее достаточно легкой для работы на Raspberry Pi 4 на производстве.

Как:

# Квантование модели для ускорения инференса
model.export(format='onnx', 
             dynamic=False, 
             simplify=True, 
             opset=12)

# Затем в отдельном скрипте
import onnxruntime as ort
import onnx
from onnxruntime.quantization import quantize_dynamic, QuantType

# Динамическое квантование
quantized_model = quantize_dynamic(
    'model.onnx',
    'model_quantized.onnx',
    weight_type=QuantType.QUInt8
)

Результат: скорость инференса выросла в 3 раза при падении точности всего на 2%.

Ошибки, которые совершили другие команды

  • Слепая гонка за mAP - лучшая по метрике модель оказалась бесполезной на реальных данных
  • Игнорирование бизнес-логики - команды не спрашивали, какая ошибка дороже: пропустить загрязнение или вызвать ложную тревогу
  • Переобучение на артефактах - модели учились распознавать не загрязнения, а артефакты съемки
  • Отсутствие постобработки - raw predictions от YOLO давали слишком много шума

Что в итоге победило

Наша система:

  • Обнаруживала 98% критических загрязнений (FNR = 2%)
  • Ложные срабатывания - менее 5% (FPR = 5%)
  • Работала на Raspberry Pi 4 в реальном времени
  • Имела механизм калибровки чувствительности под каждый цех

Но главное - мы создали не просто модель, а систему, которая понимала контекст задачи. Именно это принесло нам победу.

Чему научились

Промышленное компьютерное зрение - это не про точность на валидационном сете. Это про:

  1. Понимание бизнес-контекста - каждая ошибка имеет свою цену
  2. Адаптацию под железо - самая точная модель бесполезна, если не влезает в память
  3. Верификацию на реальных данных - до того, как отдавать заказчику
  4. Системность мышления - от сбора данных до постобработки

Если вы думаете о переходе от прототипа к продакшену, обязательно прочитайте про технический долг в AI-разработке. Наш опыт показал: ошибки на этапе проектирования обходятся в 10 раз дороже.

И помните: в промышленном CV нет "просто обучить модель". Есть "создать систему, которая работает в условиях цеха". Где пыль, вибрация, перепады температуры и сменный персонал, который может случайно задеть камеру.

Следующий шаг для нас - внедрение active learning. Система будет сама выбирать, какие кадры нужно переразметить для улучшения модели. Но это уже другая история...