문제 정의하기
막연한 제품 아이디어를 세 줄짜리 작업 명세로 구체화하고, 재학습 없이 변경할 수 있도록 비즈니스 규칙을 코드에 둡니다.

중단되는 컴퓨터 비전 프로젝트 대부분은 명확한 질문에서 시작하지 않았습니다. “위험한 행동 감지”는 요구사항처럼 들리지만, 주석 작업자는 라벨을 지정할 수 없고, 모델은 이를 학습할 수 없으며, 누구도 제대로 작동하는지 판단할 수 없습니다. 세 줄짜리 명세를 작성하면 반나절 만에 이 문제를 해결할 수 있으며, 학습할 작업, 라벨링할 대상, 보고할 지표도 결정할 수 있습니다.
실습형
명세를 먼저 작성해야 하는 이유#

컴퓨터 비전 프로젝트의 모든 단계는 0단계에서 정한 목표를 이어받습니다. 모호한 질문은 모호한 데이터셋과 의견이 일치하지 않는 주석 작업자, 잘못된 대상을 측정하는 지표를 낳으며, 2단계에서 아무리 학습해도 이를 바로잡을 수 없습니다.
세 줄#
작업 명세는 세 문장으로 구성되며 한 줄에 하나씩 작성합니다. 각 문장은 프로젝트에 참여하는 서로 다른 담당자가 할 질문에 답합니다.
| 행 | 답하는 질문 | 미흡한 예 | 좋은 예 |
|---|---|---|---|
| 입력 | 모델은 무엇을 보나요? | “카메라 영상” | “천장 카메라 12대에서 초당 5프레임으로 촬영한 1920×1080 프레임” |
| 출력 | 어떤 데이터가 반환되나요? | “안전 문제 감지” | “forklift 및 person의 박스와 각각의 신뢰도 점수” |
| 결정 | 누가 언제 조치하나요? | “안전 개선” | “지게차와 사람이 2초 동안 2m 이내에 있으면 교대 근무 감독자에게 알림” |
출력 행은 작업을 결정하는 데이터 유형을 지정하므로 가장 세심하게 작성해야 합니다.

이미지 전체에 하나의 라벨을 지정하면 분류, 박스를 사용하면 탐지, 윤곽선을 사용하면 분할, 관절을 사용하면 포즈, 회전 박스를 사용하면 OBB, 픽셀별 클래스나 거리를 사용하면 시맨틱 분할 또는 깊이입니다. 다음 강의에서 7가지 작업을 모두 살펴봅니다.
예제: 모델과 규칙 분리하기#
“창고 바닥에서 지게차가 위험한 행동을 하는 시점을 알고 싶습니다.”
이 문장은 작업이 아니라 프로젝트를 설명합니다. 두 가지 역할을 하나로 섞고 있습니다. 지게차와 사람을 찾는 것은 픽셀에 관한 일이고, 위험한 행동의 기준을 정하는 것은 정책에 관한 일입니다. 다음처럼 분리합니다.
탐지기만 모델입니다. 마름모는 일반 코드입니다. 지금 바로 이 분리 방식을 시험해 볼 수 있습니다. 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픽셀 아래에 있으므로 버스보다 카메라에 더 가까이 서 있습니다. 재학습도 재라벨링도 필요하지 않습니다. 탐지기는 그대로 두고 안전팀이 규칙을 반복 개선합니다. 프로덕션 버전에서는 보정된 카메라를 사용해 발 위치를 바닥 좌표로 변환하고 객체 추적을 통해 2초 지속 조건을 추가할 수 있습니다. 이 경우에도 같은 박스 위에 코드만 추가하면 됩니다.
주관적인 표현인 "안전하지 않음", "결함 있음", "의심스러움", "VIP"는 모델 출력을 읽는 코드에 두어야 합니다. 모델은 픽셀에 관한 질문에 답합니다. 두 전문가가 라벨을 두고 의견이 다를 수 있다면, 버전 관리와 테스트가 가능하고 변경할 수 있는 규칙에 그 판단을 두어야 합니다.
주의할 점#
증상: 사양에 "안전 문제 감지" 또는 "장면 이해"라고 적혀 있어 어노테이터가 무엇을 그려야 하는지 묻습니다. 해결: 출력을 이름이 지정된 클래스를 포함하는 데이터 유형으로 다시 작성합니다. 예: "forklift 및 person의 박스".
증상: suspicious_behavior 또는 dangerous 같은 클래스를 같은 프레임에서 두 어노테이터가 서로 다르게 라벨링합니다. 해결: 제한 구역 안에 있는 사람이나 열린 상태로 고정된 문처럼 눈에 보이는 증거로 대체하고, 판단은 의사결정 규칙으로 옮깁니다.
증상: "무슨 일이 일어나고 있는지 설명"(캡셔닝) 또는 "지게차가 누군가를 칠지 예측"(예측)합니다. 해결: 먼저 객체의 위치를 찾은 다음, 그 위에 트래킹 및 동작 규칙을 추가합니다. 예측에 앞서 디텍터를 구축합니다.
명세에서 성공 기준으로#
명세는 모델이 무엇을 하는지 정의하지만, 어느 정도면 충분한지 또는 미탐이 오탐보다 얼마나 더 큰 비용을 초래하는지는 다루지 않습니다. Building High-Performance YOLO Datasets의 비즈니스 목표부터 시작하기에서는 명세를 측정 가능한 목표로 구체화합니다. 이 과정의 이후 모든 강의는 여러분이 작성한 세 줄을 참조합니다.
정답 보기
선반 카메라의 명세 예시:
- 입력: 한 통로를 향해 고정된 카메라에서 60초마다 촬영하는 1920×1080 정지 이미지입니다.
- 출력:
product및empty_facing(제품이 없는 선반 칸)의 바운딩 박스와 각각의 신뢰도 점수입니다. - 결정: 매장 영업 시간 중 동일한 선반에서
empty_facing박스가 3개 이상 연속 5장의 정지 이미지에 나타나면 재고 보충 작업을 생성합니다.
모델이 볼 수 없는 단어는 재고 보충 작업, 동일한 선반, 연속 5장의 이미지에 나타남, 매장 영업 시간입니다. 규칙으로 작성하면 다음과 같습니다.
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” 또는 영업 시간을 변경할 수 있습니다.