DimasMP3 commited on
Commit ·
3794fed
1
Parent(s): 43e383f
feat: add batch prediction functionality and warm-up inference to improve performance
Browse files- app.py +9 -2
- inference.py +48 -4
app.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
from inference import predict
|
| 3 |
|
| 4 |
# Tema dan tata letak diatur di sini
|
| 5 |
-
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
| 6 |
gr.Markdown(
|
| 7 |
"""
|
| 8 |
# 🤖 Deteksi Bentuk Wajah (Indonesia)
|
|
@@ -30,6 +30,13 @@ with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
|
| 30 |
# Hubungkan tombol "Prediksi" dengan fungsi predict yang sudah diimpor
|
| 31 |
submit_btn.click(fn=predict, inputs=image_input, outputs=label_output)
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
# Luncurkan aplikasi jika file ini dijalankan secara langsung
|
| 34 |
if __name__ == "__main__":
|
| 35 |
iface.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
from inference import predict, predict_batch
|
| 3 |
|
| 4 |
# Tema dan tata letak diatur di sini
|
| 5 |
+
with gr.Blocks(theme=gr.themes.Soft(), css=None, analytics_enabled=False) as iface:
|
| 6 |
gr.Markdown(
|
| 7 |
"""
|
| 8 |
# 🤖 Deteksi Bentuk Wajah (Indonesia)
|
|
|
|
| 30 |
# Hubungkan tombol "Prediksi" dengan fungsi predict yang sudah diimpor
|
| 31 |
submit_btn.click(fn=predict, inputs=image_input, outputs=label_output)
|
| 32 |
|
| 33 |
+
# Endpoint batch (opsional) untuk klien JSON; menjaga compat endpoint default tetap single
|
| 34 |
+
with gr.Accordion("Batch (opsional)", open=False):
|
| 35 |
+
files_in = gr.Files(type="file", label="Unggah beberapa gambar")
|
| 36 |
+
json_out = gr.JSON(label="Best of batch (JSON)")
|
| 37 |
+
batch_btn = gr.Button("Prediksi Batch")
|
| 38 |
+
batch_btn.click(fn=predict_batch, inputs=files_in, outputs=json_out, api_name="predict_batch")
|
| 39 |
+
|
| 40 |
# Luncurkan aplikasi jika file ini dijalankan secara langsung
|
| 41 |
if __name__ == "__main__":
|
| 42 |
iface.launch()
|
inference.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
import numpy as np
|
| 2 |
from PIL import Image
|
| 3 |
import tensorflow as tf
|
| 4 |
-
from typing import List, Dict
|
|
|
|
| 5 |
import os
|
| 6 |
|
| 7 |
# --- 1. Konfigurasi Model ---
|
|
@@ -49,6 +50,13 @@ class FaceShapeModel:
|
|
| 49 |
self.model = rebuilt_model
|
| 50 |
print("Model berhasil dibangun ulang dan bobot telah dimuat.")
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
except Exception as e:
|
| 53 |
print(f"Error saat memuat model di inference.py: {e}")
|
| 54 |
self.model = None
|
|
@@ -57,10 +65,16 @@ class FaceShapeModel:
|
|
| 57 |
"""Melakukan prediksi pada satu gambar."""
|
| 58 |
if not self.model:
|
| 59 |
return {"Error": "Model tidak dapat dimuat."}
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
| 61 |
processed_input = preprocess_image(image)
|
| 62 |
-
preds = self.model.predict(processed_input)[0]
|
| 63 |
-
confidences = {label: float(score) for label, score in zip(LABELS, preds)}
|
|
|
|
|
|
|
|
|
|
| 64 |
return confidences
|
| 65 |
|
| 66 |
# Buat instance dari model kita
|
|
@@ -72,3 +86,33 @@ def predict(image_pil: Image.Image) -> Dict[str, float]:
|
|
| 72 |
return model_instance.predict_image(image_pil)
|
| 73 |
return {"Error": "Instance model tidak tersedia."}
|
| 74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import numpy as np
|
| 2 |
from PIL import Image
|
| 3 |
import tensorflow as tf
|
| 4 |
+
from typing import List, Dict, Tuple
|
| 5 |
+
import time
|
| 6 |
import os
|
| 7 |
|
| 8 |
# --- 1. Konfigurasi Model ---
|
|
|
|
| 50 |
self.model = rebuilt_model
|
| 51 |
print("Model berhasil dibangun ulang dan bobot telah dimuat.")
|
| 52 |
|
| 53 |
+
# Warm-up: satu inference dummy agar jalur model siap (mengurangi cold-start)
|
| 54 |
+
try:
|
| 55 |
+
_ = self.model(tf.zeros((1, IMG_SIZE, IMG_SIZE, 3)))
|
| 56 |
+
print("Warm-up inference completed.")
|
| 57 |
+
except Exception as werr:
|
| 58 |
+
print(f"Warm-up failed (non-fatal): {werr}")
|
| 59 |
+
|
| 60 |
except Exception as e:
|
| 61 |
print(f"Error saat memuat model di inference.py: {e}")
|
| 62 |
self.model = None
|
|
|
|
| 65 |
"""Melakukan prediksi pada satu gambar."""
|
| 66 |
if not self.model:
|
| 67 |
return {"Error": "Model tidak dapat dimuat."}
|
| 68 |
+
if image is None:
|
| 69 |
+
return {"Error": "Gambar tidak tersedia."}
|
| 70 |
+
|
| 71 |
+
t0 = time.perf_counter()
|
| 72 |
processed_input = preprocess_image(image)
|
| 73 |
+
preds = self.model.predict(processed_input, verbose=0)[0]
|
| 74 |
+
confidences: Dict[str, float] = {label: float(score) for label, score in zip(LABELS, preds)}
|
| 75 |
+
# Logging ringan
|
| 76 |
+
dt = (time.perf_counter() - t0) * 1000
|
| 77 |
+
print(f"predict_image: took {dt:.1f} ms")
|
| 78 |
return confidences
|
| 79 |
|
| 80 |
# Buat instance dari model kita
|
|
|
|
| 86 |
return model_instance.predict_image(image_pil)
|
| 87 |
return {"Error": "Instance model tidak tersedia."}
|
| 88 |
|
| 89 |
+
|
| 90 |
+
# --- Batch prediction opsional ---
|
| 91 |
+
def _dict_to_payload(d: Dict[str, float]) -> Dict[str, object]:
|
| 92 |
+
# Konversi dict label->score menjadi payload {label, confidences:[{label,confidence}]}
|
| 93 |
+
if not d or any(k == "Error" for k in d.keys()):
|
| 94 |
+
return {"label": "", "confidences": []}
|
| 95 |
+
items: List[Tuple[str, float]] = sorted(d.items(), key=lambda kv: kv[1], reverse=True)
|
| 96 |
+
top = items[0][0] if items else ""
|
| 97 |
+
confs = [{"label": k, "confidence": float(v)} for k, v in items]
|
| 98 |
+
return {"label": top, "confidences": confs}
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def predict_batch(images: List[Image.Image]) -> Dict[str, object]:
|
| 102 |
+
"""Prediksi batch: pilih hasil terbaik, kembalikan payload seragam untuk klien JSON."""
|
| 103 |
+
if not model_instance:
|
| 104 |
+
return {"error": "Instance model tidak tersedia."}
|
| 105 |
+
if not images:
|
| 106 |
+
return {"error": "Tidak ada gambar."}
|
| 107 |
+
|
| 108 |
+
best_payload: Dict[str, object] = {"label": "", "confidences": []}
|
| 109 |
+
best_conf = -1.0
|
| 110 |
+
for img in images:
|
| 111 |
+
d = model_instance.predict_image(img)
|
| 112 |
+
payload = _dict_to_payload(d)
|
| 113 |
+
conf = float(payload.get("confidences", [{"confidence": 0.0}])[0]["confidence"]) if payload.get("confidences") else 0.0
|
| 114 |
+
if conf > best_conf:
|
| 115 |
+
best_conf = conf
|
| 116 |
+
best_payload = payload
|
| 117 |
+
return best_payload
|
| 118 |
+
|