Menjalankan inferensi model AI pada Neural Processing Unit (NPU) seperti arsitektur AMD Ryzen AI menuntut efisiensi daya dan latensi rendah. Namun, transisi runtime dari referensi FP32 di CPU ke execution provider akselerator (seperti DirectML atau Vitis AI) sering memicu silent precision degradation. Masalah ini diperparah oleh keterbatasan hardware runner CI lokal yang rentan terhadap driver watchdog timeout (TDR) saat kompilasi graf ONNX.
Artikel ini menyajikan panduan implementasi verification workflow otomatis untuk mendeteksi regresi numerik toleransi FP16/INT8 sekaligus mengisolasi flaky tests di lingkungan CI lokal.
Penyebab Precision Drift pada NPU Execution Provider
Saat model dieksekusi melalui execution provider hardware-accelerated (misalnya DirectML EP), beberapa transformasi terjadi pada level kompilator:
- Kernel Fusion & Operator Rewriting: Fusi konvolusi, bias, dan aktivasi mengubah urutan evaluasi floating-point, membatalkan sifat asosiatif matematika aritmatika komputer.
- Subnormal Flushes: NPU sering mengaktifkan mode Flush-to-Zero (FTZ) dan Denormals-are-Zero (DAZ) untuk mempertahankan throughput, menyebabkan gradien atau bobot bernilai mendekati nol langsung terpotong.
- Quantization Rounding: Konversi ke FP16 memangkas rentang representasi eksponen dari 8 bit ke 5 bit dan mantisa dari 23 bit ke 10 bit, menghasilkan pembulatan yang terakumulasi di sepanjang layer residual.
Pengujian regresi tidak bisa hanya mengandalkan perbandingan nilai eksak (==). Dibutuhkan metrik berbasis toleransi relatif dan absolut (rtol dan atol), serta Cosine Similarity untuk verifikasi arah vektor output.
Metrik Verifikasi Numerik: rtol, atol, dan Cosine Similarity
Pengujian deterministik di CI membutuhkan formula komparasi numpy: |a - b| <= (atol + rtol * |b|). Untuk inferensi FP16 pada NPU Ryzen AI, nilai toleransi tipikal adalah:
- FP16:
rtol=1e-2,atol=1e-3, Cosine Similarity >= 0.999 - INT8 (Quantized):
rtol=1e-1,atol=5e-2, Cosine Similarity >= 0.990
Jika nilai output melewati batas ini, model dianggap mengalami degradasi fungsional dan pipeline CI harus gagal (fail-fast).
Test Suite Otomatis dengan pytest
Berikut adalah contoh implementasi test suite Python untuk membandingkan output inferensi antara CPUExecutionProvider (FP32 baseline) dan DmlExecutionProvider (DirectML FP16 pada NPU/iGPU). Kode ini mencakup retry logic untuk mengisolasi driver timeout pada kompilasi graf awal.
import time
import pytest
import numpy as np
import onnxruntime as ort
MODEL_PATH = "models/resnet50.onnx"
DEVICE_ID = 0
def create_session(provider: str):
so = ort.SessionOptions()
so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
if provider == "DmlExecutionProvider":
providers = [(provider, {"device_id": DEVICE_ID})]
else:
providers = [provider]
return ort.InferenceSession(MODEL_PATH, sess_options=so, providers=providers)
def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
dot_product = np.dot(a.flatten(), b.flatten())
norm_a = np.linalg.norm(a.flatten())
norm_b = np.linalg.norm(b.flatten())
return float(dot_product / (norm_a * norm_b))
@pytest.fixture(scope="module")
def dummy_input():
np.random.seed(42)
# Sesuaikan shape input dengan arsitektur model
return np.random.randn(1, 3, 224, 224).astype(np.float32)
def run_inference_with_timeout_retry(session, input_data, max_retries=2):
input_name = session.get_inputs()[0].name
for attempt in range(max_retries):
try:
return session.run(None, {input_name: input_data})[0]
except Exception as e:
if "Timeout" in str(e) or "DXGI_ERROR" in str(e):
if attempt < max_retries - 1:
time.sleep(2)
continue
raise e
def test_npu_fp16_numeric_drift(dummy_input):
session_cpu = create_session("CPUExecutionProvider")
session_npu = create_session("DmlExecutionProvider")
# 1. Warm-up run untuk isolasi kompilasi shader/kernel NPU
_ = run_inference_with_timeout_retry(session_npu, dummy_input)
# 2. Execution baseline & target
output_cpu = run_inference_with_timeout_retry(session_cpu, dummy_input)
output_npu = run_inference_with_timeout_retry(session_npu, dummy_input)
# 3. Validasi Cosine Similarity
similarity = cosine_similarity(output_cpu, output_npu)
assert similarity >= 0.999, f"Cosine similarity gagal: {similarity:.5f} < 0.999"
# 4. Validasi Bound Element-wise
# Toleransi FP16: rtol=1e-2, atol=1e-3
np.testing.assert_allclose(
output_npu,
output_cpu,
rtol=1e-2,
atol=1e-3,
err_msg="Presisi NPU keluar dari batas toleransi FP16 relatif terhadap CPU FP32"
)
Mitigasi Flaky Test Akibat Driver Timeout
Runner lokal Windows/Linux yang mengeksekusi pengujian NPU sering menghadapi kendala eksekusi yang bukan disebabkan oleh bug model:
- TDR (Timeout Detection and Recovery): Kompilasi model ONNX yang besar pada DirectX/DirectML dapat memakan waktu beberapa detik. Windows mengira driver hang jika render pipeline terblokir lebih dari rentang TDR standar (2 detik). Solusi: Tingkatkan
TdrDelaydi registry runner CI atau pisahkan fase model pre-compilation dari fase inferensi. - Cold Cache Initialization: Inisialisasi awal NPU EP membutuhkan loading dynamic library driver. Jalankan warmup inference pass sebelum mengukur latensi dan mengevaluasi output.
- Concurrency Locks: NPU tidak didesain untuk multi-process queuing yang agresif di level pytest. Hindari menjalankan pytest dengan flag
-n auto(pytest-xdist) tanpa memetakandevice_idsecara eksplisit ke thread worker terpisah.
Integrasi ke Pipeline CI Lokal
Gunakan shell script pengujian yang mengisolasi variabel lingkungan DirectML sebelum memanggil runner:
# Matikan fallback ke CPU agar degradasi driver gagal secara eksplisit
export ORT_DISABLE_PROVIDER_FALLBACK=1
# Jalankan pengujian numerik dengan isolasi run tunggal
pytest tests/test_numeric_drift.py -v --tb=short
Mengotomatisasi verifikasi numerik ini memastikan setiap pembaruan model, kuantisasi, atau update runtime ONNX tidak menimbulkan silent failure di sisi akurasi sebelum didistribusikan ke perangkat end-user.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!