DimasMP3 commited on
Commit
3794fed
·
1 Parent(s): 43e383f

feat: add batch prediction functionality and warm-up inference to improve performance

Browse files
Files changed (2) hide show
  1. app.py +9 -2
  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
+