COCODEDE04 commited on
Commit
0fb25af
·
verified ·
1 Parent(s): 01bd87c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -33
app.py CHANGED
@@ -1,63 +1,96 @@
 
 
 
1
  import gradio as gr
2
  import tensorflow as tf
3
- import numpy as np
4
- import json
5
 
6
- # ========== 1. Load model and stats once at startup ==========
7
- MODEL_PATH = "best_model_converted.keras"
8
- STATS_PATH = "Means & Std for Excel.json"
 
 
9
 
10
  print("Loading model and stats...")
11
- model = tf.keras.models.load_model(MODEL_PATH)
 
12
  with open(STATS_PATH, "r") as f:
13
  stats = json.load(f)
14
 
15
- # Feature order inferred from the JSON keys (keep stable)
16
  FEATURES = list(stats.keys())
17
- CLASSES = ["Top", "Mid-Top", "Mid", "Mid-Low", "Low"]
 
 
 
 
 
 
 
 
18
 
19
- # ========== 2. Define prediction function ==========
20
- def predict_ratios(input_dict):
21
  """
22
- input_dict: dictionary of {indicator_name: value}
23
- returns: dict with probabilities and predicted class
24
  """
25
- # Build z-score vector in model’s feature order
 
 
26
  zscores = []
27
- zscore_dict = {}
28
-
29
- for feat in FEATURES:
30
- val = float(input_dict.get(feat, 0))
31
- mean = stats[feat]["mean"]
32
- std = stats[feat]["std"]
33
- z = 0.0 if std == 0 else (val - mean) / std
34
  zscores.append(z)
35
- zscore_dict[feat] = z
36
 
37
  X = np.array([zscores], dtype=np.float32)
38
-
39
- # Predict
40
  probs = model.predict(X, verbose=0)[0]
41
  pred_idx = int(np.argmax(probs))
42
  pred_state = CLASSES[pred_idx]
43
 
44
  return {
45
- "predicted_state": pred_state,
 
 
46
  "probabilities": {CLASSES[i]: float(probs[i]) for i in range(len(CLASSES))},
47
- "z_scores": zscore_dict
48
  }
49
 
50
- # ========== 3. Gradio interface ==========
51
- # Build input components automatically for manual testing in the browser.
52
- inputs = [gr.Number(label=f) for f in FEATURES]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
 
54
  iface = gr.Interface(
55
- fn=lambda *vals: predict_ratios({f: v for f, v in zip(FEATURES, vals)}),
56
- inputs=inputs,
57
  outputs="json",
58
  title="Static Fingerprint Model API",
59
- description="Enter ratios below or call via POST /run/predict"
 
 
 
60
  )
61
 
62
- # Launch app
63
- iface.launch()
 
1
+ import os
2
+ import json
3
+ import numpy as np
4
  import gradio as gr
5
  import tensorflow as tf
 
 
6
 
7
+ # ---------- CONFIG ----------
8
+ MODEL_PATH = "best_model.h5" # <- you converted & uploaded this
9
+ STATS_PATH = "Means & Std for Excel.json" # <- keep exact filename
10
+ CLASSES = ["Top", "Mid-Top", "Mid", "Mid-Low", "Low"] # your group order
11
+ # ----------------------------
12
 
13
  print("Loading model and stats...")
14
+ model = tf.keras.models.load_model(MODEL_PATH, compile=False)
15
+
16
  with open(STATS_PATH, "r") as f:
17
  stats = json.load(f)
18
 
19
+ # Feature order: use the order from JSON to remain consistent
20
  FEATURES = list(stats.keys())
21
+ print("Feature order:", FEATURES)
22
+
23
+ def _zscore(val: float, mean: float, sd: float) -> float:
24
+ # Robust z-score (avoid division by zero)
25
+ try:
26
+ v = float(val)
27
+ except (TypeError, ValueError):
28
+ v = 0.0
29
+ return 0.0 if (sd is None or sd == 0) else (v - mean) / sd
30
 
31
+ def predict_core(ratios: dict):
 
32
  """
33
+ ratios: dict mapping feature name -> raw numeric ratio.
34
+ Returns a dict with predicted_state, probabilities, z_scores, missing.
35
  """
36
+ # Validate presence
37
+ missing = [f for f in FEATURES if f not in ratios]
38
+ # Build z-score vector in the exact FEATURE order
39
  zscores = []
40
+ zscores_dict = {}
41
+ for f in FEATURES:
42
+ mean = stats[f]["mean"]
43
+ sd = stats[f]["std"]
44
+ val = ratios.get(f, 0.0)
45
+ z = _zscore(val, mean, sd)
 
46
  zscores.append(z)
47
+ zscores_dict[f] = z
48
 
49
  X = np.array([zscores], dtype=np.float32)
 
 
50
  probs = model.predict(X, verbose=0)[0]
51
  pred_idx = int(np.argmax(probs))
52
  pred_state = CLASSES[pred_idx]
53
 
54
  return {
55
+ "input_ok": len(missing) == 0,
56
+ "missing": missing,
57
+ "z_scores": zscores_dict,
58
  "probabilities": {CLASSES[i]: float(probs[i]) for i in range(len(CLASSES))},
59
+ "predicted_state": pred_state
60
  }
61
 
62
+ # ---------- Gradio adapter ----------
63
+ def predict_from_json(payload, x_api_key: str = ""):
64
+ """
65
+ Gradio will pass the JSON from the UI or /run/predict.
66
+ We accept either:
67
+ { ...feature: value... }
68
+ or [ { ...feature: value... } ] (unwrap common API client shape)
69
+ """
70
+ # Optional simple API key
71
+ if API_KEY and x_api_key.strip() != API_KEY:
72
+ return {"error": "Unauthorized: missing or invalid X-API-Key header"}
73
+
74
+ # Unwrap if list-of-one was sent
75
+ if isinstance(payload, list) and len(payload) == 1 and isinstance(payload[0], dict):
76
+ payload = payload[0]
77
+
78
+ if not isinstance(payload, dict):
79
+ return {"error": "Invalid payload: expected a JSON object mapping feature -> value."}
80
+
81
+ return predict_core(payload)
82
 
83
+ # Minimal UI: a single JSON box for quick manual tests.
84
  iface = gr.Interface(
85
+ fn=predict_from_json,
86
+ inputs=gr.JSON(label="ratios JSON (dict of feature -> value)"),
87
  outputs="json",
88
  title="Static Fingerprint Model API",
89
+ description=(
90
+ "POST JSON to /run/predict with a dict of your 21 ratios. "
91
+ "Server normalises using saved means/stds and returns probabilities + state."
92
+ )
93
  )
94
 
95
+ if __name__ == "__main__":
96
+ iface.launch()