functionNormally commited on
Commit
7a0bb55
·
1 Parent(s): 1d6e1d9

Revenir a ZeroGPU pour toutes les fonctions : le mode hybride plante

Browse files

Tentative de garder seulement train_cnn_callback sur @spaces.GPU et de
forcer torch.device("cpu") ailleurs (MLP, evaluation, prediction,
extraction de features) pour eviter de toucher au quota GPU sur ces
chemins. Ca plante en production : import spaces monkey-patche torch
pour que torch.cuda.is_available() reponde True partout (meme hors
decorateur), et un chemin bas niveau echappe a cette emulation des que
du vrai calcul (construction de modele, forward/backward) est declenche
hors du decorateur -> "CUDA error: no CUDA-capable device is detected"
malgre device=cpu explicite. L'exception est avalee par le try/except
du callback donc pas moyen d'avoir la trace complete sans instrumenter
davantage, ce qui ne vaut pas le detour pour un contournement qui reste
fragile de toute facon.

Retour a la configuration deja validee : les six fonctions (MLP, CNN,
extraction de features, evaluation, prediction, test aleatoire) sont a
nouveau decorees @spaces.GPU, avec les durees dynamiques calibrees pour
MLP et CNN conservees. Reste du meme avis que la calibration des durees
+ le resserrement des curseurs UI est la bonne facon de limiter la
pression sur le quota journalier, pas une bascule CPU partielle.

Files changed (4) hide show
  1. app.py +25 -8
  2. backbone_utils.py +1 -4
  3. predict_utils.py +3 -7
  4. train_utils.py +2 -9
app.py CHANGED
@@ -40,6 +40,27 @@ def refresh_gallery_callback(split_name, class_name, max_images):
40
  # Tab 2 — MLP (baseline)
41
  # ---------------------------------------------------------------------------
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def train_mlp_callback(
44
  num_layers, hidden_dim, dropout,
45
  learning_rate, weight_decay, batch_size, epochs,
@@ -78,14 +99,6 @@ def train_mlp_callback(
78
  # ---------------------------------------------------------------------------
79
  # Tab 3 — SimpleCNN
80
  # ---------------------------------------------------------------------------
81
- # Seul le CNN reste sur ZeroGPU : entraîner un vrai CNN (convolutions, plusieurs
82
- # époques de backward) s'est révélé ~9x plus lent sur CPU basic (601s vs 65s
83
- # mesurés) alors que le MLP (~3.8x, 252s vs 66.6s) et tout ce qui n'est que de
84
- # l'inférence (extraction de features, évaluation, prédiction) restent
85
- # raisonnables sur CPU. Sur un Space matériel ZeroGPU, seules les fonctions
86
- # décorées @spaces.GPU empruntent un vrai GPU (et consomment le quota du
87
- # visiteur) ; tout le reste de ce fichier tourne sur le CPU du conteneur
88
- # sans jamais toucher au quota.
89
 
90
  def cnn_gpu_duration(
91
  num_conv_blocks, base_filters, kernel_size, use_batchnorm,
@@ -155,6 +168,7 @@ def train_cnn_callback(
155
  # Tab 4 — Backbone + ML classique
156
  # ---------------------------------------------------------------------------
157
 
 
158
  def extract_features_callback():
159
  try:
160
  _, class_names, counts = extract_all_features()
@@ -247,6 +261,7 @@ def download_model_callback(model_name, request: gr.Request):
247
  return None
248
 
249
 
 
250
  def evaluate_callback(model_name, request: gr.Request):
251
  try:
252
  summary, report_df, cm_df, cm_path = evaluate_saved_model(model_name, request.session_hash)
@@ -255,6 +270,7 @@ def evaluate_callback(model_name, request: gr.Request):
255
  return {"Erreur": str(e)}, None, None, None
256
 
257
 
 
258
  def predict_callback(model_name, image, request: gr.Request):
259
  try:
260
  return predict_uploaded_image(model_name, image, request.session_hash)
@@ -262,6 +278,7 @@ def predict_callback(model_name, image, request: gr.Request):
262
  return f"Échec :\n{e}", None
263
 
264
 
 
265
  def random_test_callback(model_name, request: gr.Request):
266
  try:
267
  return test_random_sample(model_name, request.session_hash)
 
40
  # Tab 2 — MLP (baseline)
41
  # ---------------------------------------------------------------------------
42
 
43
+ def mlp_gpu_duration(
44
+ num_layers, hidden_dim, dropout,
45
+ learning_rate, weight_decay, batch_size, epochs,
46
+ model_tag,
47
+ request: gr.Request,
48
+ ):
49
+ # Calibré sur deux exécutions réelles :
50
+ # 2 couches, hidden_dim=256, epochs=30 -> 66.6s (2.22 s/époque)
51
+ # 5 couches, hidden_dim=1024, epochs=50 -> 140.8s (2.82 s/époque)
52
+ # Le jeu de données est minuscule (peu de pas par époque) : le temps est
53
+ # dominé par un overhead fixe, hidden_dim ne le fait varier que doucement
54
+ # (x4 sur hidden_dim -> seulement +27% par époque). On plafonne à 180s :
55
+ # nettement sous le quota journalier d'un compte gratuit (300s), pour
56
+ # qu'un seul entraînement au pire réglage ne consomme pas tout le quota
57
+ # du jour d'un·e étudiant·e.
58
+ per_epoch = 2.22 + 0.0008 * max(0, int(hidden_dim) - 256)
59
+ estimated = 15 + per_epoch * int(epochs)
60
+ return min(180, max(45, int(estimated * 1.4)))
61
+
62
+
63
+ @spaces.GPU(duration=mlp_gpu_duration)
64
  def train_mlp_callback(
65
  num_layers, hidden_dim, dropout,
66
  learning_rate, weight_decay, batch_size, epochs,
 
99
  # ---------------------------------------------------------------------------
100
  # Tab 3 — SimpleCNN
101
  # ---------------------------------------------------------------------------
 
 
 
 
 
 
 
 
102
 
103
  def cnn_gpu_duration(
104
  num_conv_blocks, base_filters, kernel_size, use_batchnorm,
 
168
  # Tab 4 — Backbone + ML classique
169
  # ---------------------------------------------------------------------------
170
 
171
+ @spaces.GPU(duration=60)
172
  def extract_features_callback():
173
  try:
174
  _, class_names, counts = extract_all_features()
 
261
  return None
262
 
263
 
264
+ @spaces.GPU(duration=120)
265
  def evaluate_callback(model_name, request: gr.Request):
266
  try:
267
  summary, report_df, cm_df, cm_path = evaluate_saved_model(model_name, request.session_hash)
 
270
  return {"Erreur": str(e)}, None, None, None
271
 
272
 
273
+ @spaces.GPU(duration=60)
274
  def predict_callback(model_name, image, request: gr.Request):
275
  try:
276
  return predict_uploaded_image(model_name, image, request.session_hash)
 
278
  return f"Échec :\n{e}", None
279
 
280
 
281
+ @spaces.GPU(duration=60)
282
  def random_test_callback(model_name, request: gr.Request):
283
  try:
284
  return test_random_sample(model_name, request.session_hash)
backbone_utils.py CHANGED
@@ -37,10 +37,7 @@ def extract_all_features(batch_size: int = 64):
37
 
38
  from data_utils import prepare_splits, get_class_names, HFDatasetWrapper, get_eval_transform
39
 
40
- # extract_features_callback n'est pas décoré @spaces.GPU : forcer le CPU.
41
- # Sur du matériel ZeroGPU, torch.cuda.is_available() répond True même
42
- # hors décorateur (mode émulation), mais aucun GPU réel n'est alloué ici.
43
- device = torch.device("cpu")
44
  backbone = load_backbone(device)
45
  backbone.eval()
46
 
 
37
 
38
  from data_utils import prepare_splits, get_class_names, HFDatasetWrapper, get_eval_transform
39
 
40
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
 
 
41
  backbone = load_backbone(device)
42
  backbone.eval()
43
 
predict_utils.py CHANGED
@@ -6,7 +6,7 @@ from PIL import Image
6
 
7
  from config import CLASSICAL_MODEL_TYPES
8
  from data_utils import get_eval_transform, prepare_splits, get_class_names
9
- from train_utils import load_model, _load_meta
10
 
11
 
12
  def _extract_feature(image: Image.Image, device: torch.device) -> np.ndarray:
@@ -28,9 +28,7 @@ def predict_uploaded_image(model_name: str, image: Image.Image, session_id: str)
28
  meta = _load_meta(model_name, session_id)
29
  model_type = meta["config"].get("model_type", "cnn")
30
  class_names = meta["config"]["class_names"]
31
- # predict_callback n'est pas décoré @spaces.GPU : forcer le CPU (voir
32
- # le commentaire dans train_mlp, train_utils.py).
33
- device = torch.device("cpu")
34
 
35
  if model_type in CLASSICAL_MODEL_TYPES:
36
  from classical_ml_utils import load_classical_pipeline
@@ -64,9 +62,7 @@ def test_random_sample(model_name: str, session_id: str):
64
  meta = _load_meta(model_name, session_id)
65
  model_type = meta["config"].get("model_type", "cnn")
66
  class_names = get_class_names()
67
- # random_test_callback n'est pas décoré @spaces.GPU : forcer le CPU (voir
68
- # le commentaire dans train_mlp, train_utils.py).
69
- device = torch.device("cpu")
70
 
71
  splits = prepare_splits()
72
  test_dataset = splits["test"]
 
6
 
7
  from config import CLASSICAL_MODEL_TYPES
8
  from data_utils import get_eval_transform, prepare_splits, get_class_names
9
+ from train_utils import load_model, get_runtime_device, _load_meta
10
 
11
 
12
  def _extract_feature(image: Image.Image, device: torch.device) -> np.ndarray:
 
28
  meta = _load_meta(model_name, session_id)
29
  model_type = meta["config"].get("model_type", "cnn")
30
  class_names = meta["config"]["class_names"]
31
+ device = get_runtime_device()
 
 
32
 
33
  if model_type in CLASSICAL_MODEL_TYPES:
34
  from classical_ml_utils import load_classical_pipeline
 
62
  meta = _load_meta(model_name, session_id)
63
  model_type = meta["config"].get("model_type", "cnn")
64
  class_names = get_class_names()
65
+ device = get_runtime_device()
 
 
66
 
67
  splits = prepare_splits()
68
  test_dataset = splits["test"]
train_utils.py CHANGED
@@ -451,12 +451,7 @@ def train_mlp(
451
  epochs: int = 30,
452
  model_tag: str = "",
453
  ):
454
- # Pas de @spaces.GPU sur ce chemin (voir train_cnn_callback) : sur du
455
- # matériel ZeroGPU, torch.cuda.is_available() répond True même hors
456
- # décorateur (mode émulation), mais aucun GPU réel n'est alloué ici —
457
- # utiliser get_runtime_device() planterait avec "Low-level CUDA init
458
- # reached". On force donc le CPU explicitement.
459
- device = torch.device("cpu")
460
  train_loader, val_loader, test_loader, class_names = make_loaders(batch_size)
461
  num_classes = len(class_names)
462
  input_size = 3 * IMAGE_SIZE * IMAGE_SIZE
@@ -573,9 +568,7 @@ def evaluate_saved_model(model_name: str, session_id: str):
573
 
574
 
575
  def _evaluate_neural(model_name: str, meta: dict, session_id: str):
576
- # evaluate_callback n'est pas décoré @spaces.GPU : forcer le CPU (voir
577
- # le commentaire dans train_mlp).
578
- device = torch.device("cpu")
579
  model, meta = load_model(model_name, device, session_id)
580
 
581
  batch_size = int(meta["config"].get("batch_size", 16))
 
451
  epochs: int = 30,
452
  model_tag: str = "",
453
  ):
454
+ device = get_runtime_device()
 
 
 
 
 
455
  train_loader, val_loader, test_loader, class_names = make_loaders(batch_size)
456
  num_classes = len(class_names)
457
  input_size = 3 * IMAGE_SIZE * IMAGE_SIZE
 
568
 
569
 
570
  def _evaluate_neural(model_name: str, meta: dict, session_id: str):
571
+ device = get_runtime_device()
 
 
572
  model, meta = load_model(model_name, device, session_id)
573
 
574
  batch_size = int(meta["config"].get("batch_size", 16))