Sara-Adjo commited on
Commit
96d6137
ยท
verified ยท
1 Parent(s): dec4a9c

Update predict.py

Browse files
Files changed (1) hide show
  1. predict.py +119 -76
predict.py CHANGED
@@ -1,43 +1,36 @@
 
 
 
 
 
 
 
1
  import argparse
2
  import numpy as np
3
  from pathlib import Path
4
  from PIL import Image
5
- import torch
6
- from torchvision import transforms
7
- from model_pytorch import SaraCNN
8
- import tensorflow as tf
9
-
10
 
11
  CLASS_NAMES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
12
-
13
- IMAGE_SIZE = (150, 150)
14
- MEAN = [0.485, 0.456, 0.406]
15
- STD = [0.229, 0.224, 0.225]
16
 
17
 
 
 
 
18
 
 
 
 
 
19
 
20
- # PyTorch Prediction
21
- def predict_pytorch(image_path: str,
22
- model_path: str = "sara_model.pth") -> dict:
23
- """
24
- Load the PyTorch checkpoint and return class probabilities.
25
-
26
- Args:
27
- image_path : path to the input image
28
- model_path : path to the saved .pth file
29
-
30
- Returns:
31
- dict with keys: predicted_class, confidence, all_probabilities
32
- """
33
-
34
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
35
 
36
- checkpoint = torch.load(model_path, map_location=device)
37
  class_names = checkpoint.get("class_names", CLASS_NAMES)
38
- num_classes = len(class_names)
39
 
40
- model = SaraCNN(num_classes=num_classes)
41
  model.load_state_dict(checkpoint["model_state"])
42
  model.to(device)
43
  model.eval()
@@ -48,62 +41,116 @@ def predict_pytorch(image_path: str,
48
  transforms.Normalize(MEAN, STD),
49
  ])
50
 
51
- img = Image.open(image_path).convert("RGB")
52
  tensor = transform(img).unsqueeze(0).to(device)
53
 
54
  with torch.no_grad():
55
  logits = model(tensor)
56
- probs = torch.softmax(logits, dim=1).squeeze().cpu().numpy()
57
 
58
- idx = int(np.argmax(probs))
59
  pred_class = class_names[idx]
60
  confidence = float(probs[idx])
61
 
62
  return {
63
- "predicted_class": pred_class,
64
- "confidence": round(confidence * 100, 2),
65
- "all_probabilities": {
66
  cls: round(float(p) * 100, 2)
67
  for cls, p in zip(class_names, probs)
68
  },
69
  }
70
 
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
-
74
-
75
-
76
-
77
- # TensorFlow Prediction
78
- def predict_tensorflow(image_path: str,
79
- model_path: str = "sara_model.keras") -> dict:
80
- """
81
- Load the Keras model and return class probabilities.
82
-
83
- Args:
84
- image_path : path to the input image
85
- model_path : path to the saved .keras file
86
-
87
- Returns:
88
- dict with keys: predicted_class, confidence, all_probabilities
89
- """
90
-
91
- model = tf.keras.models.load_model(model_path)
92
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  img = tf.keras.utils.load_img(image_path, target_size=IMAGE_SIZE)
94
- arr = tf.keras.utils.img_to_array(img)
95
- arr = arr / 255.0
96
- arr = np.expand_dims(arr, axis=0)
97
 
98
-
99
- probs = model.predict(arr, verbose=0)[0]
100
- idx = int(np.argmax(probs))
101
  pred_class = CLASS_NAMES[idx]
102
  confidence = float(probs[idx])
103
 
104
  return {
105
- "predicted_class": pred_class,
106
- "confidence": round(confidence * 100, 2),
107
  "all_probabilities": {
108
  cls: round(float(p) * 100, 2)
109
  for cls, p in zip(CLASS_NAMES, probs)
@@ -111,20 +158,16 @@ def predict_tensorflow(image_path: str,
111
  }
112
 
113
 
114
-
115
-
116
-
117
- # CLI Entry Point
118
- def parse_args():
119
- p = argparse.ArgumentParser(description="Predict image class")
120
- p.add_argument("--model", required=True, choices=["pytorch", "tensorflow"])
121
- p.add_argument("--image", required=True, help="Path to the image file")
122
- p.add_argument("--model_path", default=None, help="Override default model file path")
123
- return p.parse_args()
124
-
125
 
126
  if __name__ == "__main__":
127
- args = parse_args()
 
 
 
 
128
 
129
  if args.model == "pytorch":
130
  path = args.model_path or "sara_model.pth"
@@ -133,9 +176,9 @@ if __name__ == "__main__":
133
  path = args.model_path or "sara_model.keras"
134
  result = predict_tensorflow(args.image, model_path=path)
135
 
136
- print(f"\n Predicted class : {result['predicted_class']}")
137
- print(f" Confidence : {result['confidence']}%")
138
- print("\n All probabilities:")
139
  for cls, prob in sorted(result["all_probabilities"].items(), key=lambda x: -x[1]):
140
  bar = " " * int(prob / 5)
141
- print(f"{cls:<12} {prob:6.2f}% {bar}")
 
1
+ """
2
+ predict.py
3
+ ----------
4
+ Charge un modรจle sauvegardรฉ et prรฉdit la classe d'une image.
5
+ Corrigรฉ pour gรฉrer les incompatibilitรฉs de version Keras.
6
+ """
7
+
8
  import argparse
9
  import numpy as np
10
  from pathlib import Path
11
  from PIL import Image
 
 
 
 
 
12
 
13
  CLASS_NAMES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
14
+ IMAGE_SIZE = (150, 150)
15
+ MEAN = [0.485, 0.456, 0.406]
16
+ STD = [0.229, 0.224, 0.225]
 
17
 
18
 
19
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
20
+ # PyTorch Prediction
21
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
22
 
23
+ def predict_pytorch(image_path: str, model_path: str = "sara_model.pth") -> dict:
24
+ import torch
25
+ from torchvision import transforms
26
+ from model_pytorch import SaraCNN
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
29
 
30
+ checkpoint = torch.load(model_path, map_location=device)
31
  class_names = checkpoint.get("class_names", CLASS_NAMES)
 
32
 
33
+ model = SaraCNN(num_classes=len(class_names))
34
  model.load_state_dict(checkpoint["model_state"])
35
  model.to(device)
36
  model.eval()
 
41
  transforms.Normalize(MEAN, STD),
42
  ])
43
 
44
+ img = Image.open(image_path).convert("RGB")
45
  tensor = transform(img).unsqueeze(0).to(device)
46
 
47
  with torch.no_grad():
48
  logits = model(tensor)
49
+ probs = torch.softmax(logits, dim=1).squeeze().cpu().numpy()
50
 
51
+ idx = int(np.argmax(probs))
52
  pred_class = class_names[idx]
53
  confidence = float(probs[idx])
54
 
55
  return {
56
+ "predicted_class": pred_class,
57
+ "confidence": round(confidence * 100, 2),
58
+ "all_probabilities": {
59
  cls: round(float(p) * 100, 2)
60
  for cls, p in zip(class_names, probs)
61
  },
62
  }
63
 
64
 
65
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
66
+ # TensorFlow Prediction โ€” corrigรฉ pour incompatibilitรฉ Keras 2.x / 3.x
67
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
68
+
69
+ def predict_tensorflow(image_path: str, model_path: str = "sara_model.keras") -> dict:
70
+ import tensorflow as tf
71
+
72
+ # โ”€โ”€ Couche Dense patchรฉe qui ignore quantization_config โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
73
+ # Problรจme : Keras 3.x sauvegarde quantization_config=None dans le .keras
74
+ # mais Keras 2.x ne connaรฎt pas cet argument โ†’ erreur de dรฉsรฉrialisation
75
+ # Solution : on surcharge Dense pour accepter et ignorer cet argument
76
+ class PatchedDense(tf.keras.layers.Dense):
77
+ def __init__(self, *args, **kwargs):
78
+ # Supprimer les arguments inconnus avant d'appeler le vrai Dense
79
+ kwargs.pop("quantization_config", None)
80
+ super().__init__(*args, **kwargs)
81
+
82
+ # โ”€โ”€ Couche SeparableConv2D patchรฉe (mรชme raison) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
83
+ class PatchedSeparableConv2D(tf.keras.layers.SeparableConv2D):
84
+ def __init__(self, *args, **kwargs):
85
+ kwargs.pop("quantization_config", None)
86
+ super().__init__(*args, **kwargs)
87
+
88
+ # โ”€โ”€ Couche BatchNormalization patchรฉe โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
89
+ class PatchedBatchNorm(tf.keras.layers.BatchNormalization):
90
+ def __init__(self, *args, **kwargs):
91
+ kwargs.pop("quantization_config", None)
92
+ super().__init__(*args, **kwargs)
93
+
94
+ # โ”€โ”€ Chargement avec les couches patchรฉes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
95
+ custom_objects = {
96
+ "Dense": PatchedDense,
97
+ "SeparableConv2D": PatchedSeparableConv2D,
98
+ "BatchNormalization": PatchedBatchNorm,
99
+ }
100
 
101
+ try:
102
+ # Essai 1 : chargement normal (fonctionne si versions compatibles)
103
+ model = tf.keras.models.load_model(model_path)
104
+ print("[TF] Modรจle chargรฉ normalement.")
105
+
106
+ except Exception as e1:
107
+ print(f"[TF] Chargement normal รฉchouรฉ ({e1}), tentative avec custom_objects...")
108
+
109
+ try:
110
+ # Essai 2 : avec les couches patchรฉes
111
+ model = tf.keras.models.load_model(
112
+ model_path,
113
+ custom_objects=custom_objects
114
+ )
115
+ print("[TF] Modรจle chargรฉ avec custom_objects.")
116
+
117
+ except Exception as e2:
118
+ print(f"[TF] Echec avec custom_objects ({e2}), tentative safe_mode=False...")
119
+
120
+ try:
121
+ # Essai 3 : safe_mode=False (Keras 3.x uniquement)
122
+ model = tf.keras.models.load_model(
123
+ model_path,
124
+ safe_mode=False,
125
+ custom_objects=custom_objects
126
+ )
127
+ print("[TF] Modรจle chargรฉ avec safe_mode=False.")
128
+
129
+ except Exception as e3:
130
+ raise RuntimeError(
131
+ f"Impossible de charger le modรจle TensorFlow.\n"
132
+ f"Essai 1 : {e1}\n"
133
+ f"Essai 2 : {e2}\n"
134
+ f"Essai 3 : {e3}\n\n"
135
+ f"Solution : Rรฉ-entraรฎne et sauvegarde le modรจle avec "
136
+ f"la mรชme version de TensorFlow que celle du serveur."
137
+ )
138
+
139
+ # โ”€โ”€ Prรฉtraitement de l'image โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
140
  img = tf.keras.utils.load_img(image_path, target_size=IMAGE_SIZE)
141
+ arr = tf.keras.utils.img_to_array(img) # (H, W, 3), valeurs 0-255
142
+ arr = arr / 255.0 # normalisation โ†’ 0-1
143
+ arr = np.expand_dims(arr, axis=0) # ajout dimension batch โ†’ (1, H, W, 3)
144
 
145
+ # โ”€โ”€ Infรฉrence โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
146
+ probs = model.predict(arr, verbose=0)[0]
147
+ idx = int(np.argmax(probs))
148
  pred_class = CLASS_NAMES[idx]
149
  confidence = float(probs[idx])
150
 
151
  return {
152
+ "predicted_class": pred_class,
153
+ "confidence": round(confidence * 100, 2),
154
  "all_probabilities": {
155
  cls: round(float(p) * 100, 2)
156
  for cls, p in zip(CLASS_NAMES, probs)
 
158
  }
159
 
160
 
161
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
162
+ # CLI
163
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 
 
 
 
 
 
 
 
164
 
165
  if __name__ == "__main__":
166
+ parser = argparse.ArgumentParser()
167
+ parser.add_argument("--model", required=True, choices=["pytorch", "tensorflow"])
168
+ parser.add_argument("--image", required=True)
169
+ parser.add_argument("--model_path", default=None)
170
+ args = parser.parse_args()
171
 
172
  if args.model == "pytorch":
173
  path = args.model_path or "sara_model.pth"
 
176
  path = args.model_path or "sara_model.keras"
177
  result = predict_tensorflow(args.image, model_path=path)
178
 
179
+ print(f"\n Classe : {result['predicted_class']}")
180
+ print(f" Confiance : {result['confidence']}%")
181
+ print("\n Toutes les probabilitรฉs :")
182
  for cls, prob in sorted(result["all_probabilities"].items(), key=lambda x: -x[1]):
183
  bar = " " * int(prob / 5)
184
+ print(f" {cls:<12} {prob:6.2f}% {bar}")