Update validate_onnx_parity.py
Build and Deploy / build (map[dockerfile:apps/api/Dockerfile name:api]) (push) Failing after 3m54s
Build and Deploy / build (map[dockerfile:apps/ml-service/Dockerfile name:ml]) (push) Failing after 41s
Build and Deploy / build (map[dockerfile:apps/web/Dockerfile name:web]) (push) Failing after 21s
Build and Deploy / deploy (push) Skipped
Build and Deploy / build (map[dockerfile:apps/api/Dockerfile name:api]) (push) Failing after 3m54s
Build and Deploy / build (map[dockerfile:apps/ml-service/Dockerfile name:ml]) (push) Failing after 41s
Build and Deploy / build (map[dockerfile:apps/web/Dockerfile name:web]) (push) Failing after 21s
Build and Deploy / deploy (push) Skipped
update code & comment
This commit is contained in:
@@ -10,10 +10,10 @@ import onnxruntime as ort
|
|||||||
import tensorflow as tf
|
import tensorflow as tf
|
||||||
from PIL import Image, UnidentifiedImageError
|
from PIL import Image, UnidentifiedImageError
|
||||||
|
|
||||||
|
# Definisi label kelas sesuai urutan output model klasifikasi
|
||||||
LABELS = ["Bercak Daun", "Daun Sehat", "Karat Daun", "Hawar Daun"]
|
LABELS = ["Bercak Daun", "Daun Sehat", "Karat Daun", "Hawar Daun"]
|
||||||
|
|
||||||
|
# Kelas eksepsi kustom untuk menangani ketidaksesuaian akurasi prediksi
|
||||||
class ParityError(RuntimeError):
|
class ParityError(RuntimeError):
|
||||||
"""Raised when Keras and ONNX predictions do not match."""
|
"""Raised when Keras and ONNX predictions do not match."""
|
||||||
pass
|
pass
|
||||||
@@ -33,16 +33,19 @@ def preprocess_image(image_path, input_size):
|
|||||||
Raises:
|
Raises:
|
||||||
ParityError: If image cannot be loaded or processed.
|
ParityError: If image cannot be loaded or processed.
|
||||||
"""
|
"""
|
||||||
|
# Penanganan error secara aman saat memuat gambar ke format RGB
|
||||||
try:
|
try:
|
||||||
img = Image.open(image_path).convert("RGB")
|
img = Image.open(image_path).convert("RGB")
|
||||||
except (FileNotFoundError, UnidentifiedImageError, OSError) as e:
|
except (FileNotFoundError, UnidentifiedImageError, OSError) as e:
|
||||||
raise ParityError(f"Failed to load image {image_path}: {e}")
|
raise ParityError(f"Failed to load image {image_path}: {e}")
|
||||||
|
|
||||||
|
# Penyesuaian resolusi gambar menggunakan metode interpolasi Bilinear
|
||||||
try:
|
try:
|
||||||
img = img.resize((input_size, input_size), Image.Resampling.BILINEAR)
|
img = img.resize((input_size, input_size), Image.Resampling.BILINEAR)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ParityError(f"Failed to resize image {image_path}: {e}")
|
raise ParityError(f"Failed to resize image {image_path}: {e}")
|
||||||
|
|
||||||
|
# Konversi ke matriks float32 dan penambahan dimensi batch (1, H, W, C)
|
||||||
img_array = np.array(img, dtype=np.float32)
|
img_array = np.array(img, dtype=np.float32)
|
||||||
img_batch = np.expand_dims(img_array, axis=0)
|
img_batch = np.expand_dims(img_array, axis=0)
|
||||||
|
|
||||||
@@ -60,6 +63,7 @@ def predict_keras(model, image_batch):
|
|||||||
Returns:
|
Returns:
|
||||||
Predictions array (1, num_classes).
|
Predictions array (1, num_classes).
|
||||||
"""
|
"""
|
||||||
|
# Eksekusi inferensi pada model TensorFlow/Keras tanpa log proses
|
||||||
predictions = model.predict(image_batch, verbose=0)
|
predictions = model.predict(image_batch, verbose=0)
|
||||||
return predictions
|
return predictions
|
||||||
|
|
||||||
@@ -75,6 +79,7 @@ def predict_onnx(session, image_batch):
|
|||||||
Returns:
|
Returns:
|
||||||
Predictions array (1, num_classes).
|
Predictions array (1, num_classes).
|
||||||
"""
|
"""
|
||||||
|
# Eksekusi inferensi secara dinamis pada model ONNX menggunakan sesi runtime
|
||||||
input_name = session.get_inputs()[0].name
|
input_name = session.get_inputs()[0].name
|
||||||
predictions = session.run(None, {input_name: image_batch})
|
predictions = session.run(None, {input_name: image_batch})
|
||||||
return predictions[0]
|
return predictions[0]
|
||||||
@@ -94,14 +99,18 @@ def validate_image(image_path, keras_model, onnx_session, input_size, atol):
|
|||||||
Raises:
|
Raises:
|
||||||
ParityError: If predictions do not match or image cannot be processed.
|
ParityError: If predictions do not match or image cannot be processed.
|
||||||
"""
|
"""
|
||||||
|
# Menyiapkan tensor gambar untuk pengujian
|
||||||
img_batch = preprocess_image(image_path, input_size)
|
img_batch = preprocess_image(image_path, input_size)
|
||||||
|
|
||||||
|
# Mengekstrak matriks probabilitas dari kedua format model
|
||||||
keras_pred = predict_keras(keras_model, img_batch)
|
keras_pred = predict_keras(keras_model, img_batch)
|
||||||
onnx_pred = predict_onnx(onnx_session, img_batch)
|
onnx_pred = predict_onnx(onnx_session, img_batch)
|
||||||
|
|
||||||
|
# Mendapatkan indeks kelas dengan probabilitas tertinggi (Top-1)
|
||||||
keras_label_idx = np.argmax(keras_pred[0])
|
keras_label_idx = np.argmax(keras_pred[0])
|
||||||
onnx_label_idx = np.argmax(onnx_pred[0])
|
onnx_label_idx = np.argmax(onnx_pred[0])
|
||||||
|
|
||||||
|
# Validasi keselarasan keputusan klasifikasi utama
|
||||||
if keras_label_idx != onnx_label_idx:
|
if keras_label_idx != onnx_label_idx:
|
||||||
keras_label = LABELS[keras_label_idx]
|
keras_label = LABELS[keras_label_idx]
|
||||||
onnx_label = LABELS[onnx_label_idx]
|
onnx_label = LABELS[onnx_label_idx]
|
||||||
@@ -110,6 +119,7 @@ def validate_image(image_path, keras_model, onnx_session, input_size, atol):
|
|||||||
f"Keras={keras_label}, ONNX={onnx_label}"
|
f"Keras={keras_label}, ONNX={onnx_label}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Validasi selisih nilai desimal probabilitas menggunakan toleransi absolut
|
||||||
if not np.allclose(keras_pred, onnx_pred, atol=atol):
|
if not np.allclose(keras_pred, onnx_pred, atol=atol):
|
||||||
max_diff = np.max(np.abs(keras_pred - onnx_pred))
|
max_diff = np.max(np.abs(keras_pred - onnx_pred))
|
||||||
raise ParityError(
|
raise ParityError(
|
||||||
@@ -117,6 +127,7 @@ def validate_image(image_path, keras_model, onnx_session, input_size, atol):
|
|||||||
f"max difference={max_diff:.6e} (atol={atol})"
|
f"max difference={max_diff:.6e} (atol={atol})"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Pencatatan log sistem jika kedua model presisi 100%
|
||||||
label = LABELS[keras_label_idx]
|
label = LABELS[keras_label_idx]
|
||||||
logging.info(f"PASS: {image_path} -> {label}")
|
logging.info(f"PASS: {image_path} -> {label}")
|
||||||
|
|
||||||
@@ -125,6 +136,7 @@ def main():
|
|||||||
"""Validate parity between Keras and ONNX models."""
|
"""Validate parity between Keras and ONNX models."""
|
||||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||||
|
|
||||||
|
# Inisialisasi parser argumen untuk antarmuka CLI (Command Line Interface)
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Validate parity between Keras and ONNX models"
|
description="Validate parity between Keras and ONNX models"
|
||||||
)
|
)
|
||||||
@@ -161,6 +173,7 @@ def main():
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Pengecekan eksistensi berkas model sebelum memuat memori
|
||||||
if not args.keras_model.exists():
|
if not args.keras_model.exists():
|
||||||
msg = f"Keras model not found at {args.keras_model}"
|
msg = f"Keras model not found at {args.keras_model}"
|
||||||
logging.error(msg)
|
logging.error(msg)
|
||||||
@@ -171,15 +184,18 @@ def main():
|
|||||||
logging.error(msg)
|
logging.error(msg)
|
||||||
raise FileNotFoundError(msg)
|
raise FileNotFoundError(msg)
|
||||||
|
|
||||||
|
# Memuat model Keras (tanpa kompilasi agar lebih hemat beban komputasi)
|
||||||
logging.info(f"Loading Keras model from {args.keras_model}...")
|
logging.info(f"Loading Keras model from {args.keras_model}...")
|
||||||
keras_model = tf.keras.models.load_model(args.keras_model, compile=False)
|
keras_model = tf.keras.models.load_model(args.keras_model, compile=False)
|
||||||
|
|
||||||
|
# Memuat sesi ONNX dengan penyedia eksekusi CPU murni
|
||||||
logging.info(f"Loading ONNX model from {args.onnx_model}...")
|
logging.info(f"Loading ONNX model from {args.onnx_model}...")
|
||||||
onnx_session = ort.InferenceSession(
|
onnx_session = ort.InferenceSession(
|
||||||
str(args.onnx_model),
|
str(args.onnx_model),
|
||||||
providers=["CPUExecutionProvider"],
|
providers=["CPUExecutionProvider"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Iterasi pengujian paritas (kesetaraan performa) untuk setiap gambar
|
||||||
logging.info(f"Validating {len(args.images)} image(s)...")
|
logging.info(f"Validating {len(args.images)} image(s)...")
|
||||||
for image_path in args.images:
|
for image_path in args.images:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user