Tantangan Validasi Model Vision pada Pipeline CI

Penerapan continuous integration (CI) untuk model computer vision sering kali terhambat oleh durasi pengujian yang panjang dan ketiadaan verifikasi performa runtime sebelum artefak model masuk ke production registry. Pada arsitektur deteksi objek modern seperti YOLO26, verifikasi bobot (weights) tidak cukup hanya mengandalkan kelulusan syntax test script training. Diperlukan validasi komputasi nyata: verifikasi struktur graf export ONNX, evaluasi drop metrik mAP, serta uji regresi latensi inferensi.

Panduan ini menguraikan arsitektur CI berbasis GitHub Actions untuk mengeksekusi pipeline validasi model YOLO26. Alur kerja mencakup export ONNX terintegrasi, benchmark latensi inference engine, dan penerapan quality gate berbasis ambang batas (thresholds).

Arsitektur Validasi Otomatis

Pipeline validasi bekerja secara deterministik pada setiap pull request yang menyentuh berkas bobot model (.pt) atau konfigurasi inferensi. Alur eksekusi dirancang sebagai berikut:

  1. Restorasi Cache Lingkungan: Menggunakan layer cache untuk Python wheel dan ONNX Runtime backend guna menghemat waktu build runner.
  2. Validasi Graf & Export ONNX: Konversi bobot native PyTorch ke format ONNX opset 17+ dengan post-processing terintegrasi. Hal ini mengeliminasi kebutuhan Non-Maximum Suppression (NMS) manual di layer CPU klien.
  3. Evaluasi Metrik (mAP 50-95): Eksekusi validasi pada subset data test untuk memastikan kuantisasi atau export tidak mendegradasi akurasi deteksi melebihi batas toleransi.
  4. Quality Gate Latensi: Benchmark latensi end-to-end pada inference engine target (ONNX Runtime). Jika latensi P95 melewati threshold yang ditentukan, build ditandai gagal (exit status non-zero).

Script Benchmarking Latensi dan Verifikasi Threshold

Skrip berikut mengevaluasi waktu eksekusi model ONNX secara end-to-end. Benchmark mengisolasi overhead I/O, menerapkan fase warm-up untuk menstabilkan alokasi memori runtime, dan menghitung persentil latensi.

import time
import sys
import numpy as np
import onnxruntime as ort

def benchmark_model(onnx_path, iterations=200, warmup=20, max_latency_p95_ms=15.0):
    session_options = ort.SessionOptions()
    session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
    session = ort.InferenceSession(onnx_path, session_options, providers=["CPUExecutionProvider"])
    
    input_meta = session.get_inputs()[0]
    shape = [dim if isinstance(dim, int) else 1 for dim in input_meta.shape]
    dummy_input = np.random.randn(*shape).astype(np.float32)

    # Fase warm-up
    for _ in range(warmup):
        session.run(None, {input_meta.name: dummy_input})

    # Pengukuran latensi
    latencies = []
    for _ in range(iterations):
        t0 = time.perf_counter()
        session.run(None, {input_meta.name: dummy_input})
        latencies.append((time.perf_counter() - t0) * 1000.0)

    p95_latency = np.percentile(latencies, 95)
    mean_latency = np.mean(latencies)

    print(f"[INFO] Benchmark Selesai: Rata-rata = {mean_latency:.2f} ms | P95 = {p95_latency:.2f} ms")

    if p95_latency > max_latency_p95_ms:
        print(f"[ERROR] Latensi P95 ({p95_latency:.2f} ms) melampaui batas ambang ({max_latency_p95_ms} ms)!")
        sys.exit(1)

    print("[SUCCESS] Kriteria performa terpenuhi.")
    sys.exit(0)

if __name__ == "__main__":
    benchmark_model(sys.argv[1])

Konfigurasi GitHub Actions & Strategi Caching

Dependencies deep learning seperti PyTorch dan ONNX Runtime berukuran besar (ratusan megabyte). Menyimpannya pada cache GitHub Actions menghemat waktu deployment runner hingga 60-70%. Konfigurasi di bawah menjalankan tahapan export, validasi mAP minimal, dan eksekusi quality gate latensi.

name: Model Validation & ONNX Latency Gate

on:
  pull_request:
    paths:
      - 'models/**'
      - '.github/workflows/model-ci.yml'

jobs:
  validate-and-gate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.10'

      - name: Cache Pip Packages
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('requirements-ci.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-

      - name: Install Runtime Dependencies
        run: |
          pip install -r requirements-ci.txt

      - name: Export Model to ONNX
        run: |
          # Export dengan end-to-end decoders terintegrasi
          python -m yolo_framework.export --weights models/yolo26-custom.pt --format onnx --opset 17 --simplify

      - name: Evaluate Accuracy Gate (mAP)
        run: |
          # Evaluasi pada validation subset
          python -m yolo_framework.val --weights models/yolo26-custom.onnx --data data/ci-val-split.yaml --min-map50-95 0.45

      - name: Evaluate Latency Gate
        run: |
          python scripts/ci_benchmark.py models/yolo26-custom.onnx

Trade-offs dan Pertimbangan Produksi

  • Hardware Environment Parity: Runner GitHub Actions standar menggunakan CPU virtual (vCPU shared). Nilai latensi absolut tidak dapat disamakan secara langsung dengan edge device atau GPU target produksi. Gunakan CI self-hosted runner jika memerlukan profiling berbasis hardware khusus seperti TensorRT pada GPU NVIDIA atau NPU embedded.
  • Model Graph Optimization: Export model langsung tanpa --simplify sering kali menyisakan node redundan di graf ONNX yang memicu degradasi latensi sekunder. Selalu validasi struktur graf menggunakan tool seperti ONNX Simplifier sebelum evaluasi latensi.
  • Threshold Degradasi Dinamis: Alih-alih nilai latensi absolut statis, alternatif arsitektur yang lebih toleran terhadap fluktuasi runner vCPU adalah membandingkan latensi PR cabang terhadap latensi baseline branch main (regresi persentase maksimal, misal +5%).