Ultralytics YOLO27: 대기자 명단에 등록하세요.
Get Started
프로덕션 환경의 Ultralytics YOLO·스케일 및 관찰·레슨 9/9
강의중급

비용 및 지연 시간 조정

전체 파이프라인의 프로파일을 측정하고, 지연 시간과 비용에 실제 영향을 주는 조치를 순서대로 적용한 뒤 예산에 도달하면 중단합니다.

팀은 모델에서 몇 밀리초를 줄이는 데 몇 주를 쓰지만, 실제 병목은 데이터베이스 쓰기나 RTSP 디코더인 경우가 많습니다. 주요 조치는 몇 가지뿐이며 효과도 잘 측정할 수 있습니다. YOLO26s는 CPU에서 YOLO26n보다 mAP 포인트가 7.7 높지만 실행 시간은 2.2배 더 걸리고, 프레임을 하나씩 건너뛰며 검출하면 작업량이 절반으로 줄어듭니다. 이 강의에서는 먼저 프로파일을 측정하고, 효과가 가장 크면서 요구 사항에 맞는 조치를 적용한 다음, 미리 정한 예산에 도달하면 중단하는 방법을 다룹니다.

학습 성과

파이프라인의 처음부터 끝까지 프로파일을 측정하고, 조치 하나를 적용해 적용 전후의 지연 시간과 mAP를 확인한 뒤 중단한 이유를 설명합니다.

속성 과정
이미 사용법을 알고 계신다면, 간단히 요약해 드리겠습니다.
  1. 먼저 프로파일을 측정합니다. r.speed 단계와 자체 디코딩 및 싱크 시간을 확인합니다.

  2. 조치는 다음 순서로 적용합니다. 더 작은 모델 → 더 작은 imgsz → 더 빠른 런타임 및 FP16 → INT8 → vid_stride.

  3. 변경할 때마다 대상 하드웨어와 동일한 검증 세트에서 지연 시간과 mAP를 적용 전후로 측정합니다.

  4. 먼저 예산(일수, 허용 가능한 mAP 손실, 목표 FPS)을 정하고, 예산을 다 쓰면 대신 데이터를 개선합니다.

실습형

먼저 전체 파이프라인 측정하기#

모든 Results 객체는 자체 실행 시간을 단계별로 나눕니다:

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
totals = {"preprocess": 0.0, "inference": 0.0, "postprocess": 0.0}
for n, r in enumerate(model.predict(video, stream=True, verbose=False), 1):
    for stage in totals:
        totals[stage] += r.speed[stage]
print({stage: round(ms / n, 1) for stage, ms in totals.items()}, "ms per frame")
{'preprocess': 0.7, 'inference': 36.1, 'postprocess': 0.7} ms per frame

여기서는 추론이 모델 실행 시간의 95%를 차지하므로 모델 관련 조치가 효과가 있습니다. 디코딩과 자체 싱크도 같은 방식으로 시간을 측정한 다음(time.perf_counter()을 각각 감싸서 측정), 결과에 따라 다음 규칙을 적용합니다:

측정 결과먼저 해결할 항목
RTSP 디코딩 60ms, 추론 5ms하드웨어 디코딩, 스트림 해상도 낮추기 또는 디코더 스레드 추가
추론 30ms, Python 후처리 100msNumPy로 벡터화하거나 작업자 프로세스로 옮기기
추론 5ms, 데이터베이스 쓰기 200ms쓰기를 배치 처리하거나 비동기로 전환하기
추론이 주요 병목인 경우아래 조치별 성능 순위표

조치별 성능 순위표#

위에서부터 시도하고 예산을 충족하면 중단합니다:

조정 항목일반적인 속도 향상일반적인 정확도 비용
더 작은 모델(m → s, s → n)1.5–2.5×COCO에서 4.5~7.7 mAP 포인트, 범위가 좁은 작업에서는 그보다 적음
더 작은 imgsz(640 → 480)약 1.8배작고 멀리 있는 객체부터 영향받음
더 빠른 런타임, FP16GPU에서 약 1.5배대개 없음. 동등한 성능인지 확인합니다
캘리브레이션을 적용한 INT8GPU에서 추가로 1.2배, CPU에서 최대 2.9배YOLO26n은 1포인트, 대형 모델은 4~6포인트 손실. QAT로 대부분 복구 가능
프레임 수 줄이기(vid_stride=N)최대 N배빠르게 움직이는 객체와 짧은 이벤트를 놓칠 수 있음
배치 크기 늘리기(서버)처리량은 증가하지만 지연 시간은 줄어들지 않음없음

수치는 YOLO26, TensorRT, OpenVINO 벤치마크 표와 아래 측정값에서 가져왔습니다. 실제 결과는 다를 수 있으므로 직접 측정하세요.

더 작은 모델#

COCO에서 측정한 YOLO26 벤치마크 표:

모델mAP50-95CPU ONNX(ms)T4 TensorRT10(ms)
YOLO26n40.938.91.7
YOLO26s48.687.22.5
YOLO26m53.1220.04.7

CPU에서는 모델을 한 단계씩 줄일 때마다 2.2~2.5배 빨라집니다. COCO의 정확도 차이는 최악의 경우입니다. 대형 객체를 다루는 3클래스 창고 검출기는 80클래스 일반 검출기보다 m에서 n으로 줄였을 때 성능 저하가 훨씬 적은 경우가 많으므로, 자체 검증 세트에서 더 작은 모델을 반드시 테스트해야 합니다.

더 작은 입력 크기#

연산량은 픽셀 수에 대략 비례하므로 imgsz=480은 640 크기의 약 56%에 해당하는 연산만 필요합니다. 테스트용 노트북의 CPU에서 ONNX Runtime, 배치 크기 1로 측정한 결과입니다(bus.jpg에서 25회 실행한 중앙값):

모델640480320
YOLO26n21.3 ms12.1 ms7.1 ms
YOLO26s55.0 ms24.2 ms12.9 ms

판단 기준: 새 크기에서 가장 작은 주요 객체가 여전히 약 10픽셀 이상이면 시도해 보세요. 객체가 작거나 멀리 있는 경우(드론, 카메라에서 멀리 떨어진 선반)에는 해상도를 유지하고 대신 모델 크기를 줄입니다.

프레임 수 줄이기#

추론이 프레임 속도의 병목이고 장면이 천천히 움직인다면 vid_stride=N으로 N번째 프레임마다 검출을 실행하고 트래커로 그 사이를 연결합니다:

import time

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
video = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
model.predict(video, vid_stride=30, verbose=False)  # warm up so the first timing is fair

for stride in (1, 2, 3):
    start, ids, frames = time.perf_counter(), set(), 0
    for r in model.track(video, stream=True, vid_stride=stride, tracker="bytetrack.yaml", classes=[0], verbose=False):
        frames += 1
        if r.boxes.is_track:
            ids.update(r.boxes.id.int().tolist())
    print(f"vid_stride={stride}: {frames} frames in {time.perf_counter() - start:.2f} s, {len(ids)} person IDs")
vid_stride=1: 62 frames in 2.66 s, 32 person IDs
vid_stride=2: 31 frames in 1.19 s, 30 person IDs
vid_stride=3: 20 frames in 0.95 s, 28 person IDs

프레임 수를 절반으로 줄이면 실행 시간도 대략 절반이 되며, 트래커는 ID 32개 중 30개를 유지했습니다. vid_stride=3에서는 ID 4개를 놓쳤으며, 이 보행 속도의 장면에서는 여기서 중단해야 한다는 신호입니다.

클라우드 비용 절감하기#

Ultralytics Platform 배포 개요: 24시간 동안 HTTP 요청 59건, 오류율 0%, P95 지연 시간 226ms, us-west2의 vCPU 1개 및 4GiB 엔드포인트(시간당 $0.17)

비용은 인스턴스 사용 시간을 유용한 작업량으로 나눈 값입니다. 이 카드와 같은 화면에서 필요한 수치 두 가지를 확인할 수 있습니다. 시간당 비용(여기서는 vCPU 1개와 4GiB에 $0.17)과 처리 작업량(대부분 유휴 상태인 엔드포인트가 P95 226ms로 24시간 동안 요청 59건 처리)입니다. 그런 다음:

  • 사용률을 높입니다. GPU 사용률이 30%라면 비용의 70%가 낭비됩니다. 스트림을 배치 처리하거나(이전 강의 참조) Triton Inference Server를 통해 서비스를 제공합니다.
  • 적정 규모를 사용합니다. 대형 GPU가 필요하다고 가정하기 전에 더 작은 GPU 또는 OpenVINO를 사용하는 CPU에서 벤치마크를 실행합니다.
  • 배치 작업에는 스팟 또는 선점형 용량을 사용합니다. 보관된 동영상을 다시 처리하는 작업은 중단을 감당할 수 있지만, 라이브 스트림은 그렇지 않습니다.

주의할 점#

병목이 아닌 모델을 최적화하기

증상: 2주간 조정해 10ms를 줄였지만 시스템은 여전히 느립니다. 해결 방법: 먼저 전체 파이프라인을 프로파일링하고 무엇이든 가장 큰 비중을 차지하는 단계를 개선합니다.

추론 후 프레임 건너뛰기

증상: 결과 루프 안의 if i % 2: continue은 아무것도 절약하지 못합니다. 해결 방법: 모델은 이미 해당 프레임을 처리했으므로 vid_stride을 사용해 소스 단계에서 프레임을 건너뜁니다.

지연 시간을 줄이기 위한 배칭

증상: 처리량은 증가했지만 각 프레임의 도착이 늦어집니다. 해결 방법: 배치 크기를 늘리면 프레임당 지연 시간이 아니라 비용 대비 처리량이 증가합니다. 여러 스트림이나 오프라인 작업에 배칭을 사용하고, 단일 실시간 카메라에는 사용하지 마세요.

데이터 개선의 효과

모델 개선 수단을 모두 사용했다면 다음 선택지는 더 나은 데이터입니다. 프로덕션 환경을 반영하는 데이터셋을 사용하면 더 작은 모델도 더 큰 모델이 필요로 했던 정확도에 도달할 수 있으므로, 정확도 손실 없이 비용을 줄일 수 있습니다.

사용해 보기

최적화 예산(일수, 허용 가능한 최대 mAP 손실, 대상 하드웨어에서의 목표 FPS)을 작성합니다. r.speed에 자체 디코딩 및 싱크 타이밍을 더해 파이프라인을 프로파일링하고, 병목에 적합한 가장 효과적인 방법을 선택해 적용한 다음, 동일한 하드웨어와 val 세트에서 적용 전후의 지연 시간과 mAP50-95를 기록합니다. 예산, 프로파일, 적용 전후 비교표, 작업을 중단한 지점을 제출합니다.

커밋
git add -A && git commit -m "perf(production): tuned model + runtime for target latency budget"
완료 조건
다음 조건을 모두 충족하면 레슨을 완료한 것입니다.
  • 수치를 바탕으로 종단 간 프레임 처리 시간에서 어느 단계가 가장 큰 비중을 차지하는지 파악했습니다.

  • 동일한 하드웨어와 val 세트에서 한 가지 방법을 적용하고 적용 전후의 지연 시간과 mAP를 기록했습니다.

  • 작성한 예산에 도달했을 때 작업을 중단했으며, 다음에 시도할 작업을 설명할 수 있습니다.

정답 보기
from ultralytics import YOLO

if __name__ == "__main__":  # validation starts worker processes on macOS and Windows
    data, img = "coco8.yaml", "https://ultralytics.com/images/bus.jpg"  # use your val YAML and a production frame
    for weights, imgsz in (("yolo26s.pt", 640), ("yolo26n.pt", 640), ("yolo26n.pt", 480)):
        model = YOLO(weights)
        runs = [model(img, imgsz=imgsz, verbose=False)[0].speed["inference"] for _ in range(20)][5:]
        m = model.val(data=data, imgsz=imgsz, batch=1, plots=False, verbose=False).box.map
        print(f"{weights} imgsz={imgsz}: {sorted(runs)[len(runs) // 2]:.1f} ms, mAP50-95 {m:.3f}")

각 행은 워밍업을 위해 처음 5회 실행을 제외하고 나머지 실행 결과의 중앙값을 보고합니다. 지연 시간 예산을 충족하고 mAP 손실이 예산 이내인 첫 번째 행을 선택합니다. 예산 설정 예시: "이틀, 최대 1 mAP 포인트 손실, 매장 서버에서 20 FPS." yolo26s의 640 설정이 프레임당 50 ms를 넘고, yolo26n의 640 설정이 val 세트에서 1포인트 이내의 손실로 목표를 충족한다면 여기서 멈추고, 둘째 날을 480 설정에 쓰지 마세요.

다음 단계

과정을 완료했습니다. 시험에 응시해 수료증을 받고, 이 워크플로를 관리형 종단 간 시스템으로 실행하는 Ultralytics Platform으로 구축하기를 계속 진행하세요.