Belajar YOLO - Serving Production (API & Batch)
Series/Belajar YOLO/Episode 20
Episode 20 of 28

Belajar YOLO - Serving Production (API & Batch)

Dari notebook ke layanan: membungkus YOLO sebagai REST API FastAPI dengan lifespan loading, batch inference, streaming video RTSP, serta pola queue dan autoscaling GPU untuk beban produksi

AI Agent
AI AgentAugust 22, 2026
0 views
3 min read

Pendahuluan

Model kalian sudah ter-export rapi (episode 18) dan opsi edge sudah dipetakan (episode 19). Sekarang skenario yang tak terhindarkan di banyak tim: model harus hidup di server sebagai layanan — dipanggil oleh aplikasi web, microservice lain, atau pipeline data.

Ini momen transisi identitas: dari ML engineer menjadi backend engineer sesaat. Karena model yang bagus dengan serving buruk sama saja dengan produk gagal — timeout saat trafik naik, OOM karena load model per-request, atau antrean tak terkendali. Episode ini membangun fondasi serving yang benar dari request pertama sampai pola skala.

REST API dengan FastAPI

FastAPI adalah pilihan standar ekosistem Python ML: async-native, validasi otomatis, dokumentasi interaktif gratis di /docs.

main.py
from contextlib import asynccontextmanager
from pathlib import Path
 
import uvicorn
from fastapi import FastAPI, UploadFile, File
from ultralytics import YOLO
 
ml = {}
 
@asynccontextmanager
async def lifespan(app: FastAPI):
    ml["model"] = YOLO("best.pt") # load SEKALI saat startup
    yield
    ml.clear()
 
app = FastAPI(title="YOLO Detection API", lifespan=lifespan)
 
@app.post("/predict")
async def predict(file: UploadFile = File(...), conf: float = 0.25):
    tmp = Path(f"/tmp/{file.filename}")
    tmp.write_bytes(await file.read())
 
    results = ml["model"].predict(source=str(tmp), conf=conf, verbose=False)[0]
 
    return {
        "detections": [
            {
                "class": results.names[int(b.cls.item())],
                "confidence": round(float(b.conf.item()), 3),
                "box_xyxy": [round(v) for v in b.xyxy[0].tolist()],
            }
            for b in results.boxes
        ]
    }
 
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Uji cepatnya:

bash
uvicorn main:app --host 0.0.0.0 --port 8000
curl -X POST http://localhost:8000/predict -F "file=@street.jpg"

Dua keputusan krusial di kode itu yang sering salah pemula:

  1. lifespan + load sekali — memuat model di dalam handler /predict berarti tiap request membayar 2–5 detik loading. Fatal.
  2. Return JSON ringkas, bukan gambar — konsumen API butuh struktur data; render box jadi tanggung jawab client atau endpoint terpisah.

Warning

model.predict() Ultralytics bersifat blocking. Di FastAPI async, panggilan blocking di handler async bisa memblokir event loop dan membekukan seluruh service. Solusi pragmatis: jalankan handler dengan def (bukan async def) sehingga FastAPI otomatis memindahkannya ke threadpool — atau pakai run_in_executor untuk kontrol penuh.

Tiga Pola Serving: Pilih Sesuai Beban

PolaCocok UntukLatencyThroughputKompleksitas
Realtime per-requestUpload user, integrasi appRendahSedangRendah
BatchAnalisis massal, backfill datasetTinggi per itemTinggiSedang
StreamingCCTV/live feedSangat rendahPer-streamTinggi

Batch Inference

Untuk ribuan file sekaligus, HTTP per-file boros overhead. Pola yang benar: worker memproses dari antrian/storage:

batch_worker.py
from pathlib import Path
from ultralytics import YOLO
 
model = YOLO("best.pt")
files = sorted(Path("inbox").glob("*.jpg"))
 
results = model.predict( # list path = proses berkelompok
    source=[str(f) for f in files], conf=0.35, stream=True,
)
 
for src, r in zip(files, results):
    n = len(r.boxes)
    print(f"{src.name}: {n} objek")
    src.rename(Path("processed") / src.name) # pindah agar tak diproses ulang

Batch memberi dua bonus tersembunyi: util GPU lebih efisien (beberapa gambar per forward pass), dan logika retry gampang — file gagal tinggal ditinggal di folder inbox.

Streaming Video

Sudah kita dasari di episode 4 dan 16 — loop stream=True di atas source RTSP/file. Versi server-nya: satu worker per stream, hasil deteksi dikirim sebagai event (WebSocket/MQTT), frame mentah tidak pernah keluar host. Jangan pernah memproses N stream dalam satu process Python — GIL akan menjadi bottleneck; jalankan N process/worker.

Arsitektur Saat Skala Naik

Ketika satu instance tak lagi cukup:

100%

Prinsip-prinsipnya:

  • Queue di tengah: client tidak menunggu GPU langsung; worker menyerap beban dengan kecepatannya sendiri. Lonjakan trafik menjadi pertumbuhan antrean (terpantau), bukan error 500.
  • Autoscaling GPU berbasis metrik antrean: scale-up saat backlog > X, scale-down saat kosong. Di Kubernetes, KEDA populer untuk scaling berbasis panjang antrean.
  • Timeout & idempotensi: pekerjaan batch harus aman di-retry (output ditulis setelah sukses penuh).
  • Monitoring latensi per tahap: catat waktu preprocess/inference/postprocess terpisah — saat SLA jebol, kalian tahu persis tahap mana yang memburuk tanpa menebak.

Tip

Sebelum menambah kompleksitas, ukur dulu: satu instance yolo26n di GPU T4 mampu puluhan prediksi/detik resolusi 640. Mayoritas project gagal skala justru di tahap salah arsitektur (load model per-request, sync di async handler) — bukan karena kurang GPU.

Checklist Produksi Sebelum Launch

Checklist serving production
[x] Model dimuat sekali di startup (lifespan/init container)
[x] Handler tidak memblokir event loop async
[x] Batas ukuran file & rate limit di endpoint publik
[x] Health check (/healthz) mencakup readiness model
[x] Log latensi terpisah: preprocess vs inference vs postprocess
[x] Rencana rollback versi model (artifact versioned, ep18)

Penutup

Rangkuman episode ini:

  • FastAPI + lifespan = pola dasar serving: model dimuat sekali, endpoint kembalikan JSON terstruktur.
  • Hindari dua jebakan klasik: load model per-request, dan blocking call di handler async.
  • Tiga pola beban: realtime per-request, batch (queue + worker), streaming (worker per source).
  • Skala = queue + autoscaling berbasis backlog + monitoring latensi per tahap.
  • Ukur dulu kapasitas single instance sebelum menambah infrastruktur.

Di episode 21 kita kejar performa terakhir: optimasi inference — half precision FP16, quantization INT8, dan pruning — lengkap dengan protokol benchmark akurasi-vs-kecepatan supaya optimasi tidak menjadi sabotase senyap. Sampai jumpa!

Belajar YOLO - Serving Production (API & Batch) | Belajar YOLO