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

Большинство проектов в области компьютерного зрения, которые заходят в тупик, изначально не имели чёткого вопроса. «Выявлять небезопасное поведение» звучит как требование, но разметчик не может это разметить, модель не может этому научиться, а понять, работает ли решение, никто не сможет. Спецификация из трёх строк решает эту проблему за один день и определяет, какую задачу ты будешь обучать, что размечать и какую метрику представлять.
Практические занятия
Почему спецификация нужна в первую очередь#

Каждый этап проекта компьютерного зрения наследует целевой ориентир от нулевого этапа. Расплывчатый вопрос приводит к расплывчатому датасету, разногласиям между разметчиками и метрике, измеряющей не то, что нужно. И никакое обучение на втором этапе этого не исправит.
Три строки#
Спецификация задачи состоит из трёх предложений, по одному в каждой строке. Каждое отвечает на вопрос, который задаст участник проекта:
| Строка | На какой вопрос отвечает | Слабо | Хорошо |
|---|---|---|---|
| Входные данные | Что видит модель? | «Видеозапись с камеры» | «Кадры 1920×1080 с частотой 5 кадров/с с 12 потолочных камер» |
| Результат | Какие данные возвращаются? | «Выявлять проблемы с безопасностью» | «Рамки для forklift и person, каждая с оценкой уверенности» |
| Решение | Кто действует и когда? | «Повысить безопасность» | «Отправить уведомление начальнику смены, если погрузчик находится ближе 2 м к человеку в течение 2 с» |
Особенно внимательно формулируй строку с результатом, потому что тип данных определяет задачу:

Одна метка для всего изображения означает классификацию; рамки — детекцию; контуры — сегментацию; суставы — позу; повёрнутые рамки — OBB; класс или расстояние для каждого пикселя — семантическую сегментацию или глубину. В следующем уроке рассматриваются все семь задач.
Пример: отделяем модель от правила#
«Мы хотим знать, когда погрузчики делают что-то опасное на складе».
Это описание проекта, а не задачи. Оно объединяет две работы: поиск погрузчиков и людей — задача, связанная с пикселями, — и определение того, что считать опасным, — вопрос правил. Раздели их:
Моделью является только детектор. Ромб — это обычный код. Попробуй разделить их прямо сейчас: на bus.jpg нет погрузчика, но автобус и четыре человека подойдут для той же проверки. Первое правило звучит так: «отправлять оповещение, если рамки человека и транспортного средства перекрываются более чем на 30 %»:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
result = model("https://ultralytics.com/images/bus.jpg", verbose=False)[0]
# The model's job ends here: a class, a box, and a confidence per object.
people = [b.xyxy[0].tolist() for b in result.boxes if result.names[int(b.cls)] == "person"]
vehicles = [b.xyxy[0].tolist() for b in result.boxes if result.names[int(b.cls)] == "bus"]
def share_inside(a, b):
"""Fraction of box a's area that lies inside box b."""
w = max(0, min(a[2], b[2]) - max(a[0], b[0]))
h = max(0, min(a[3], b[3]) - max(a[1], b[1]))
return w * h / ((a[2] - a[0]) * (a[3] - a[1]))
# The decision rule is plain code: change the policy without retraining.
ALERT_SHARE = 0.30
for person in people:
share = max(share_inside(person, v) for v in vehicles)
print(f"person at x={person[0]:3.0f}: {share:4.0%} inside the bus box -> {'ALERT' if share > ALERT_SHARE else 'ok'}")person at x= 49: 70% inside the bus box -> ALERT
person at x=223: 76% inside the bus box -> ALERT
person at x=670: 70% inside the bus box -> ALERT
person at x= 0: 62% inside the bus box -> ALERTЧетыре оповещения — и все ложные: люди идут по тротуару перед припаркованным автобусом. Детектор правильно определил все рамки; ошибочно правило, ведь перекрытие на 2D-изображении не означает, что два объекта находятся близко друг к другу в реальном мире. Поскольку правило — это код, исправлять нужно код. Сравни положение ног каждого человека на земле с линией колёс автобуса:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
result = model("https://ultralytics.com/images/bus.jpg", verbose=False)[0]
people = [b.xyxy[0].tolist() for b in result.boxes if result.names[int(b.cls)] == "person"]
vehicles = [b.xyxy[0].tolist() for b in result.boxes if result.names[int(b.cls)] == "bus"]
def same_ground_line(person, vehicle, tolerance=40):
"""True when the person's feet are level with the vehicle's wheels and inside its width."""
feet_x, feet_y = (person[0] + person[2]) / 2, person[3]
return vehicle[0] < feet_x < vehicle[2] and abs(feet_y - vehicle[3]) < tolerance
for person in people:
alert = any(same_ground_line(person, v) for v in vehicles)
print(f"person at x={person[0]:3.0f}: feet y={person[3]:.0f}, bus wheels y={vehicles[0][3]:.0f} -> {'ALERT' if alert else 'ok'}")person at x= 49: feet y=902, bus wheels y=751 -> ok
person at x=223: feet y=861, bus wheels y=751 -> ok
person at x=670: feet y=880, bus wheels y=751 -> ok
person at x= 0: feet y=871, bus wheels y=751 -> okНоги каждого человека находятся на 110–150 пикселей ниже линии колёс, значит, люди стоят ближе к камере, чем автобус. Переобучение и повторная разметка не нужны: команда по безопасности дорабатывает правило, а детектор остаётся без изменений. В рабочей версии положение ног можно было бы переводить в координаты пола с помощью калиброванной камеры, а двухсекундное условие непрерывности реализовать через отслеживание объектов — всё это по-прежнему код поверх тех же рамок.
Субъективные слова — «небезопасный», «дефектный», «подозрительный», «VIP» — должны использоваться в коде, который обрабатывает вывод модели. Модель отвечает на вопросы о пикселях. Если два эксперта поспорят о метке, этот спор должен быть разрешён правилом, которое можно версионировать, тестировать и менять.
Подводные камни#
Симптом: в спецификации написано «обнаруживать проблемы с безопасностью» или «понимать сцену», и разметчики спрашивают, что именно нужно рисовать. Решение: опиши выход как тип данных с именованными классами: «рамки для forklift и person».
Симптом: два разметчика по-разному размечают один и тот же кадр для класса вроде suspicious_behavior или dangerous. Решение: замени его на видимое свидетельство — человека в закрытой зоне, дверь, которую удерживают открытой, — а оценочное суждение перенеси в строку решения.
Симптом: «опиши происходящее» (создание описаний) или «предскажи, заденет ли погрузчик человека» (прогнозирование). Решение: сначала найди объекты, затем добавь поверх этого правила трекинга и движения. Создай детектор до разработки предсказаний.
От спецификации к критериям успеха#
Спецификация определяет, что делает модель, но не то, какой результат достаточно хорош и во сколько обходится пропуск по сравнению с ложным срабатыванием. В материале «Начни с бизнес-цели» из курса «Создание высокопроизводительных датасетов YOLO» спецификация превращается в измеримые целевые показатели. Каждый следующий урок этого курса опирается на три строки твоей спецификации.
Показать решение
Спецификация для камеры, направленной на полку:
- Входные данные: один неподвижный кадр 1920×1080 каждые 60 секунд с камеры, направленной на один проход.
- Результат: рамки для
productиempty_facing(место на полке без товара), каждая с оценкой уверенности. - Решение: создать задачу на пополнение запасов, если на одной полке в течение пяти последовательных кадров в часы работы магазина сохраняются три или более рамки
empty_facing.
Модель не может увидеть слова задача на пополнение запасов, одна полка, сохраняются в течение пяти кадров и часы работы магазина. Правило будет таким:
if count(empty_facing on shelf S) >= 3 in each of the last 5 stills and store_is_open(now):
create_restock_task(shelf=S)Магазин может изменить «3», «5» или часы работы, не размечая ни одного изображения.