functionNormally commited on
Commit
52205a2
·
1 Parent(s): 72ef639

Retirer ZeroGPU : le Space passe en CPU

Browse files

Le quota GPU journalier par visiteur (2 a 5 min selon le compte) restait
un probleme structurel pour l'usage en classe, meme apres avoir calibre
les durees demandees sur des mesures reelles. Les mesures elles-memes
montraient que le temps d'entrainement sur ce jeu de donnees minuscule
est domine par un overhead fixe (chargement, tiny batches) plutot que
par le calcul GPU : multiplier les parametres du CNN par ~128x n'avait
fait varier le temps par epoque que de ~40%. Le CPU ne devrait donc pas
etre dramatiquement plus lent, et supprime completement le systeme de
quota par utilisateur.

Suppression des decorateurs @spaces.GPU et des fonctions de duree
dynamique associees (devenues inutiles), et de la dependance spaces.
Le changement de materiel du Space (ZeroGPU -> CPU basic) se fait cote
Settings HF, pas dans ce commit.

Files changed (2) hide show
  1. app.py +12 -55
  2. requirements.txt +0 -1
app.py CHANGED
@@ -1,7 +1,6 @@
1
  import json
2
 
3
  import gradio as gr
4
- import spaces
5
 
6
  from backbone_utils import extract_all_features, get_cached_features
7
  from classical_ml_utils import train_classical_model
@@ -40,27 +39,6 @@ def refresh_gallery_callback(split_name, class_name, max_images):
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,
@@ -100,31 +78,6 @@ def train_mlp_callback(
100
  # Tab 3 — SimpleCNN
101
  # ---------------------------------------------------------------------------
102
 
103
- def cnn_gpu_duration(
104
- num_conv_blocks, base_filters, kernel_size, use_batchnorm,
105
- dropout, fc_dim,
106
- learning_rate, weight_decay, batch_size, epochs,
107
- model_tag,
108
- request: gr.Request,
109
- ):
110
- # Calibré sur deux exécutions réelles :
111
- # 3 blocs, filtres=32, noyau=3, epochs=30 -> 65.3s (2.18 s/époque)
112
- # 5 blocs, filtres=128, noyau=5, epochs=50 -> 153.9s (3.08 s/époque)
113
- # Le nombre de paramètres varie de ~130x entre ces deux essais mais le temps
114
- # par époque seulement de 40% : sur ce jeu de données minuscule, le coût est
115
- # dominé par un overhead fixe (chargement/augmentation), pas par les FLOPs
116
- # du réseau — le nombre de paramètres surestimerait donc très largement.
117
- # On interpole plutôt sur un score d'architecture simple. Plafond 180s :
118
- # nettement sous le quota journalier d'un compte gratuit (300s).
119
- score = int(num_conv_blocks) * int(base_filters) * (int(kernel_size) / 3)
120
- baseline_score, worst_score = 96.0, 1066.7
121
- frac = max(0.0, min(1.0, (score - baseline_score) / (worst_score - baseline_score)))
122
- per_epoch = 2.18 + 0.9 * frac
123
- estimated = 15 + per_epoch * int(epochs)
124
- return min(180, max(45, int(estimated * 1.4)))
125
-
126
-
127
- @spaces.GPU(duration=cnn_gpu_duration)
128
  def train_cnn_callback(
129
  num_conv_blocks, base_filters, kernel_size, use_batchnorm,
130
  dropout, fc_dim,
@@ -168,7 +121,6 @@ def train_cnn_callback(
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()
@@ -254,14 +206,13 @@ def get_model_info_callback(model_name, request: gr.Request):
254
 
255
  def download_model_callback(model_name, request: gr.Request):
256
  if not model_name:
257
- return gr.update(value=None, visible=False)
258
  try:
259
- return gr.update(value=saved_model_file_path(model_name, request.session_hash), visible=True)
260
  except FileNotFoundError:
261
- return gr.update(value=None, visible=False)
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,7 +221,6 @@ def evaluate_callback(model_name, request: gr.Request):
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,7 +228,6 @@ def predict_callback(model_name, image, request: gr.Request):
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)
@@ -600,7 +549,11 @@ with gr.Blocks(title="Classification d'images microscopiques") as demo:
600
  refresh_btn = gr.Button("Actualiser la liste")
601
  load_info_btn = gr.Button("Afficher les informations du modèle")
602
  model_info = gr.JSON(label="Métadonnées du modèle")
603
- model_download = gr.File(label="Télécharger le modèle sélectionné", visible=False)
 
 
 
 
604
 
605
  with gr.Column():
606
  evaluate_btn = gr.Button("Évaluer sur le jeu de test", variant="primary")
@@ -711,6 +664,10 @@ with gr.Blocks(title="Classification d'images microscopiques") as demo:
711
  fn=download_model_callback, inputs=model_selector, outputs=model_download
712
  )
713
 
 
 
 
 
714
  evaluate_btn.click(
715
  fn=evaluate_callback,
716
  inputs=model_selector,
 
1
  import json
2
 
3
  import gradio as gr
 
4
 
5
  from backbone_utils import extract_all_features, get_cached_features
6
  from classical_ml_utils import train_classical_model
 
39
  # Tab 2 — MLP (baseline)
40
  # ---------------------------------------------------------------------------
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def train_mlp_callback(
43
  num_layers, hidden_dim, dropout,
44
  learning_rate, weight_decay, batch_size, epochs,
 
78
  # Tab 3 — SimpleCNN
79
  # ---------------------------------------------------------------------------
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  def train_cnn_callback(
82
  num_conv_blocks, base_filters, kernel_size, use_batchnorm,
83
  dropout, fc_dim,
 
121
  # Tab 4 — Backbone + ML classique
122
  # ---------------------------------------------------------------------------
123
 
 
124
  def extract_features_callback():
125
  try:
126
  _, class_names, counts = extract_all_features()
 
206
 
207
  def download_model_callback(model_name, request: gr.Request):
208
  if not model_name:
209
+ return None
210
  try:
211
+ return saved_model_file_path(model_name, request.session_hash)
212
  except FileNotFoundError:
213
+ return None
214
 
215
 
 
216
  def evaluate_callback(model_name, request: gr.Request):
217
  try:
218
  summary, report_df, cm_df, cm_path = evaluate_saved_model(model_name, request.session_hash)
 
221
  return {"Erreur": str(e)}, None, None, None
222
 
223
 
 
224
  def predict_callback(model_name, image, request: gr.Request):
225
  try:
226
  return predict_uploaded_image(model_name, image, request.session_hash)
 
228
  return f"Échec :\n{e}", None
229
 
230
 
 
231
  def random_test_callback(model_name, request: gr.Request):
232
  try:
233
  return test_random_sample(model_name, request.session_hash)
 
549
  refresh_btn = gr.Button("Actualiser la liste")
550
  load_info_btn = gr.Button("Afficher les informations du modèle")
551
  model_info = gr.JSON(label="Métadonnées du modèle")
552
+ download_btn = gr.Button("Préparer le fichier à télécharger")
553
+ model_download = gr.File(
554
+ label="Fichier du modèle (poids .pt ou pipeline .joblib) — cliquez sur "
555
+ "« Préparer le fichier à télécharger » puis sur la flèche de téléchargement ci-dessous",
556
+ )
557
 
558
  with gr.Column():
559
  evaluate_btn = gr.Button("Évaluer sur le jeu de test", variant="primary")
 
664
  fn=download_model_callback, inputs=model_selector, outputs=model_download
665
  )
666
 
667
+ download_btn.click(
668
+ fn=download_model_callback, inputs=model_selector, outputs=model_download
669
+ )
670
+
671
  evaluate_btn.click(
672
  fn=evaluate_callback,
673
  inputs=model_selector,
requirements.txt CHANGED
@@ -2,7 +2,6 @@ torchvision
2
  datasets
3
  huggingface_hub
4
  pillow
5
- spaces
6
  scikit-learn
7
  pandas
8
  matplotlib
 
2
  datasets
3
  huggingface_hub
4
  pillow
 
5
  scikit-learn
6
  pandas
7
  matplotlib