valegro commited on
Commit
520455f
·
verified ·
1 Parent(s): a438543

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +324 -545
app.py CHANGED
@@ -5,669 +5,448 @@ import seaborn as sns
5
  import matplotlib.pyplot as plt
6
  from statistics import mode, StatisticsError
7
 
8
- # Scikit-learn
9
- from sklearn.model_selection import train_test_split, GridSearchCV
10
  from sklearn.preprocessing import StandardScaler
11
  from sklearn.pipeline import Pipeline
12
  from sklearn.metrics import confusion_matrix, accuracy_score, f1_score
13
- from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
14
  from sklearn.linear_model import LogisticRegression
15
  from sklearn.svm import SVC
16
  from sklearn.neural_network import MLPClassifier
17
 
18
- # PyTorch
19
  import torch
20
  import torch.nn as nn
21
  import torch.nn.functional as F
22
 
23
- # Transformers per la GenAI testuale
24
  from transformers import pipeline
25
 
26
- ############### STREAMLIT SETUP ###############
27
- st.set_page_config(page_title="WEEKO - AI Reuse Analyzer",
28
- page_icon="♻️",
29
- layout="wide")
30
 
31
- ############### MODELLI PLACEHOLDER ############
 
 
32
  class DummyTabTransformerClassifier:
33
- """Finto modello: in realtà un MLP."""
34
  def __init__(self, input_dim=8):
35
- self.clf = MLPClassifier(hidden_layer_sizes=(max(16,input_dim*2), max(8,input_dim)),
36
- max_iter=100, random_state=42, alpha=0.01, learning_rate_init=0.01)
37
  def fit(self, X, y):
38
- self.clf.fit(X, y)
39
  return self
40
  def predict(self, X):
41
  return self.clf.predict(X)
42
  def predict_proba(self, X):
43
- if hasattr(self.clf, 'predict_proba'):
44
  return self.clf.predict_proba(X)
45
  else:
46
- preds = self.clf.predict(X)
47
  return np.array([[1.0,0.0] if p==0 else [0.0,1.0] for p in preds])
48
 
49
- class DummySAINTClassifier:
50
- """Finto modello: in realtà un MLP."""
51
- def __init__(self, input_dim=8):
52
- self.clf = MLPClassifier(hidden_layer_sizes=(max(20,input_dim*2), max(10,input_dim)),
53
- max_iter=120, random_state=42, alpha=0.005, learning_rate_init=0.005)
54
- def fit(self, X, y):
55
- self.clf.fit(X, y)
56
- return self
57
- def predict(self, X):
58
- return self.clf.predict(X)
59
- def predict_proba(self, X):
60
- if hasattr(self.clf, 'predict_proba'):
61
- return self.clf.predict_proba(X)
62
- else:
63
- preds = self.clf.predict(X)
64
- return np.array([[1.0,0.0] if p==0 else [0.0,1.0] for p in preds])
65
-
66
- MODELS = {
67
- "Random Forest": RandomForestClassifier(random_state=42, n_estimators=100, class_weight='balanced'),
68
- "Gradient Boosting": GradientBoostingClassifier(random_state=42, n_estimators=100),
69
- "Logistic Regression": LogisticRegression(random_state=42, max_iter=500, class_weight='balanced'),
70
- "Support Vector Machine": SVC(probability=True, random_state=42, class_weight='balanced'),
71
- "TabTransformer (Dummy)": DummyTabTransformerClassifier(),
72
- "SAINT (Dummy)": DummySAINTClassifier()
73
  }
74
 
75
- ############### VAE PER FASE 2 ###############
 
 
76
  class MiniVAE(nn.Module):
77
  def __init__(self, input_dim=5, latent_dim=2):
78
  super().__init__()
79
- self.fc1 = nn.Linear(input_dim, 32)
80
- self.fc21 = nn.Linear(32, latent_dim)
81
- self.fc22 = nn.Linear(32, latent_dim)
82
- self.fc3 = nn.Linear(latent_dim, 32)
83
- self.fc4 = nn.Linear(32, input_dim)
84
-
85
- def encode(self, x):
86
- h = F.relu(self.fc1(x))
87
  return self.fc21(h), self.fc22(h)
88
-
89
  def reparameterize(self, mu, logvar):
90
- std = torch.exp(0.5 * logvar)
91
  eps = torch.randn_like(std)
92
  return mu + eps*std
93
-
94
- def decode(self, z):
95
- h = F.relu(self.fc3(z))
96
  return self.fc4(h)
97
-
98
- def forward(self, x):
99
- mu, logvar = self.encode(x)
100
- z = self.reparameterize(mu, logvar)
101
- recon = self.decode(z)
102
  return recon, mu, logvar
103
 
104
- def vae_loss(recon_x, x, mu, logvar):
105
- recon_loss = F.mse_loss(recon_x, x, reduction='sum')
106
- kld = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
107
- return recon_loss + kld
108
-
109
- ############### Feature sets ###############
110
- DEFAULT_FEATURES_STEP1 = ['length','width','RUL','margin','shape','weight','thickness']
111
- ML_FEATURES_STEP1 = ['length','width','shape_code','weight','thickness','RUL','margin','compat_dim']
112
- VAE_FEATURES_STEP2 = ['length','width','weight','thickness','shape_code']
113
-
114
- ############### SHAPE MAPPING ###############
115
- SHAPE_MAPPING = {
116
- 'axisymmetric':0,
117
- 'sheet_metal':1,
118
- 'alloy_plate':2,
119
- 'complex_plastic':3
120
- }
121
-
122
- ############### GENERAZIONE DATI SINTETICI ###############
123
- def generate_synthetic_data(n_samples=300, seed=42):
124
- np.random.seed(seed)
125
- length = np.clip(np.random.normal(100,20,n_samples),50,250)
126
- width = np.clip(np.random.normal(50,15,n_samples),20,150)
127
- RUL = np.clip(np.random.normal(500,250,n_samples),0,1000).astype(int)
128
- margin = np.clip(np.random.normal(150,150,n_samples),-200,600).astype(int)
129
- shapes = np.random.choice(list(SHAPE_MAPPING.keys()), p=[0.4,0.3,0.2,0.1], size=n_samples)
130
- weight = np.clip(np.random.normal(80,30,n_samples),10,250)
131
- thickness= np.clip(np.random.normal(8,4,n_samples),0.5,30)
132
-
133
- return pd.DataFrame({
134
- 'length': length,
135
- 'width': width,
136
- 'RUL': RUL,
137
- 'margin': margin,
138
- 'shape': shapes,
139
- 'weight': weight,
140
- 'thickness': thickness
141
- })
142
-
143
- ############### dimension_match + assign_class ###############
144
- def dimension_match(row, target_length, target_width, target_shape,
145
- target_weight, target_thickness,
146
- tol_len, tol_wid, tol_weight, tol_thickness):
147
- cond_length = abs(row['length'] - target_length)<= tol_len
148
- cond_width = abs(row['width'] - target_width) <= tol_wid
149
- cond_shape = (row['shape']==target_shape)
150
- cond_weight = abs(row['weight']-target_weight)<=tol_weight
151
- cond_thick = abs(row['thickness']-target_thickness)<=tol_thickness
152
- return 1 if (cond_length and cond_width and cond_shape and cond_weight and cond_thick) else 0
153
-
154
- def assign_class(row, threshold_score=0.5, alpha=0.5, beta=0.5):
155
- rul_norm = row['RUL']/1000.0
156
- margin_norm = (row['margin']+200)/800.0
157
- score = alpha*rul_norm + beta*margin_norm
158
- if row['compat_dim']==1 and score>=threshold_score:
159
  return "Riutilizzo Funzionale"
160
  else:
161
  return "Upcycling Creativo"
162
 
163
- ############### 1) PHASE: PREPARE DATASET ###############
164
- def prepare_dataset():
165
- st.header("♻️ 1. Preparazione Dataset EoL")
166
- tab1, tab2 = st.tabs(["Carica/Genera Dati","Definisci Compatibilità & Target"])
167
- data = None
168
- with tab1:
169
- data_opt = st.radio("Fonte Dati", ["Genera dati sintetici","Carica CSV"], horizontal=True)
170
- if data_opt=="Genera dati sintetici":
171
- ns=st.slider("Campioni sintetici",100,2000,500,step=100)
172
- if st.button("Genera Dati"):
173
- data=generate_synthetic_data(n_samples=ns)
174
- st.session_state.data_source="generated"
 
 
 
175
  else:
176
- upfile=st.file_uploader("Carica CSV con feature minime", type=["csv"])
177
- if upfile:
178
- try:
179
- data=pd.read_csv(upfile)
180
- if not all(col in data.columns for col in DEFAULT_FEATURES_STEP1):
181
- st.error(f"Mancano colonne minime: {DEFAULT_FEATURES_STEP1}")
182
- data=None
183
- else:
184
- st.session_state.data_source="uploaded"
185
- except Exception as e:
186
- st.error(f"Errore lettura CSV: {e}")
187
- data=None
188
-
189
- if data is not None:
190
- with tab2:
191
- st.subheader("Parametri per Compatibilità")
192
- c1,c2=st.columns(2)
193
- with c1:
194
- t_len=st.number_input("Lunghezza target (mm)",50.0,250.0,100.0)
195
- t_wid=st.number_input("Larghezza target (mm)",20.0,150.0,50.0)
196
- t_shape=st.selectbox("Forma target", list(SHAPE_MAPPING.keys()))
197
- with c2:
198
- t_weight=st.number_input("Peso target (kg)",10.0,300.0,80.0)
199
- t_thick=st.number_input("Spessore target (mm)",0.5,50.0,8.0)
200
-
201
- st.markdown("**Tolleranze**")
202
- col_tol1,col_tol2=st.columns(2)
203
- with col_tol1:
204
- tol_len=st.slider("Tol. lunghezza ±",0.0,30.0,5.0,step=0.5)
205
- tol_wid=st.slider("Tol. larghezza ±",0.0,20.0,3.0,step=0.5)
206
- with col_tol2:
207
- tol_we=st.slider("Tol. peso ±",0.0,50.0,10.0,step=1.0)
208
- tol_th=st.slider("Tol. spessore ±",0.0,5.0,1.0,step=0.1)
209
-
210
- st.markdown("**Score RUL & Margin**")
211
- thr_sc=st.slider("Soglia score",0.0,1.0,0.5,step=0.05)
212
- alpha=st.slider("Peso RUL(α)",0.0,1.0,0.5,step=0.05)
213
- beta =st.slider("Peso Margin(β)",0.0,1.0,0.5,step=0.05)
214
-
215
- # shape_code
216
- data['shape_code']=data['shape'].map(SHAPE_MAPPING).fillna(-1).astype(int)
217
- data['compat_dim']= data.apply(lambda r: dimension_match(r,
218
- target_length=t_len,
219
- target_width=t_wid,
220
- target_shape=t_shape,
221
- target_weight=t_weight,
222
- target_thickness=t_thick,
223
- tol_len=tol_len,
224
- tol_wid=tol_wid,
225
- tol_weight=tol_we,
226
- tol_thickness=tol_th), axis=1)
227
-
228
- data['Target'] = data.apply(lambda r: assign_class(r,
229
- threshold_score=thr_sc,
230
- alpha=alpha,beta=beta), axis=1)
231
-
232
- st.session_state.target_params={
233
- "target_length": t_len, "target_width": t_wid,
234
- "target_shape": t_shape, "target_weight": t_weight,
235
- "target_thickness": t_thick,
236
- "tol_len": tol_len, "tol_wid": tol_wid,
237
- "tol_weight": tol_we, "tol_thickness": tol_th
238
- }
239
- st.session_state.score_params={
240
- "threshold_score": thr_sc,
241
- "alpha": alpha,
242
- "beta": beta
243
- }
244
-
245
- st.dataframe(data.head(10))
246
- st.write("Distribuzione classi:", data["Target"].value_counts())
247
-
248
- numeric_cols=data.select_dtypes(include=np.number)
249
- if not numeric_cols.empty:
250
- fig,ax=plt.subplots()
251
- sns.heatmap(numeric_cols.corr(), annot=True, cmap='viridis', fmt=".2f", ax=ax)
252
- st.pyplot(fig)
253
-
254
- st.session_state.data=data
255
- csv_proc=data.to_csv(index=False).encode('utf-8')
256
- st.download_button("Scarica Dataset Elaborato", csv_proc, "dataset_processed.csv")
257
-
258
- ############### 2) PHASE: TRAIN MODELLI ML ############
259
- def train_models(data):
260
- st.header("🤖 2. Addestramento ML (Riutilizzo vs Upcycling)")
261
  if data is None:
262
- st.error("Dataset non disponibile (Fase 1).")
263
- return
264
- if 'Target' not in data.columns:
265
- st.error("Colonna 'Target' mancante. Rivedi la Fase 1.")
266
  return
267
 
268
- features_to_use=[f for f in ML_FEATURES_STEP1 if f in data.columns]
269
- if not features_to_use:
270
- st.error("Nessuna feature per l'addestramento ML.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  return
272
 
273
- X = data[features_to_use].copy()
274
- y = data['Target'].map({"Riutilizzo Funzionale":0,"Upcycling Creativo":1})
 
 
 
 
275
  if len(y.unique())<2:
276
  st.error("Il dataset ha una sola classe. Non si può addestrare.")
277
  return
278
- X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.25,random_state=42,stratify=y)
 
279
  st.write(f"Train={len(X_train)}, Test={len(X_test)}")
280
 
281
- tune_rf= st.checkbox("Ottimizza RandomForest (GridSearchCV)",False)
282
- trained_pipelines={}
283
  results=[]
284
-
285
- for name,model in MODELS.items():
286
- st.subheader(f"Modello: {name}")
287
  from sklearn.pipeline import Pipeline
288
- pipe=Pipeline([
289
- ("scaler",StandardScaler()),
290
  ("clf",model)
291
  ])
292
  try:
293
- if tune_rf and name=="Random Forest":
294
- st.write("Esecuzione GridSearchCV su RandomForest...")
295
- param_grid={
296
- 'clf__n_estimators':[50,100],
297
- 'clf__max_depth':[None,10],
298
- }
299
- from sklearn.model_selection import GridSearchCV
300
- grid=GridSearchCV(pipe,param_grid,cv=2,scoring='accuracy',n_jobs=-1)
301
- grid.fit(X_train,y_train)
302
- best_est=grid.best_estimator_
303
- st.write(f"Migliori parametri: {grid.best_params_}")
304
- y_pred=best_est.predict(X_test)
305
- pipe_to_use=best_est
306
- else:
307
- pipe.fit(X_train,y_train)
308
- y_pred=pipe.predict(X_test)
309
- pipe_to_use=pipe
310
-
311
- acc=accuracy_score(y_test,y_pred)
312
- f1=f1_score(y_test,y_pred,average='weighted')
313
- results.append({"Modello":name, "Accuracy":acc, "F1 Score":f1})
314
- trained_pipelines[name]=pipe_to_use
315
-
316
- cm=confusion_matrix(y_test,y_pred)
317
- fig,ax=plt.subplots()
318
- sns.heatmap(cm,annot=True,fmt='d',cmap="Greens",ax=ax)
319
  plt.xlabel("Pred")
320
  plt.ylabel("True")
321
  st.pyplot(fig)
 
322
  st.metric("Accuracy",f"{acc:.3f}")
323
  st.metric("F1 Score",f"{f1:.3f}")
324
-
325
  except Exception as e:
326
- st.error(f"Errore addestramento {name}: {e}")
327
 
328
  if results:
329
- df_res=pd.DataFrame(results).sort_values(by="Accuracy",ascending=False)
330
- st.dataframe(df_res)
331
- st.session_state.train_results=df_res
332
- st.session_state.models=trained_pipelines
333
  else:
334
  st.error("Nessun modello addestrato con successo.")
335
- st.session_state.models=None
336
 
337
- ############### 3) PHASE: INFERE ############
338
- def model_inference(trained_pipelines, data):
339
- st.header("🔮 3. Inferenza: Step 1 & Step 2 (VAE + GenAI)")
 
 
340
 
341
- if not trained_pipelines:
342
- st.error("Prima addestra i modelli ML (Fase 2).")
343
- return
344
  if data is None:
345
- st.error("Nessun dataset (Fase 1).")
346
  return
347
-
348
- # Usiamo mediane del dataset per default
349
- data_stats=data
350
-
351
- with st.form("inference_form"):
352
- st.subheader("Inserisci Dati EoL")
353
- c1,c2,c3=st.columns(3)
354
- with c1:
355
- length=st.number_input("Lunghezza (mm)",0.0,300.0,float(data_stats['length'].median()),step=1.0)
356
- width= st.number_input("Larghezza (mm)",0.0,200.0,float(data_stats['width'].median()),step=1.0)
357
- shape_name = st.selectbox("Forma", list(SHAPE_MAPPING.keys()))
358
- with c2:
359
- weight= st.number_input("Peso (kg)",0.0,300.0,float(data_stats['weight'].median()),step=1.0)
360
- thickness= st.number_input("Spessore (mm)",0.0,50.0,float(data_stats['thickness'].median()),step=0.5)
361
- RUL= st.number_input("RUL (0-1000)",0,1000,int(data_stats['RUL'].median()))
362
- with c3:
363
- val_merc= st.number_input("Valore Mercato (€)",0.0,1e5,float(data_stats['margin'].median()+200),step=10.0)
364
- costo_rip= st.number_input("Costo Riparazione (€)",0.0,1e5,50.0,step=10.0)
365
-
366
- sub=st.form_submit_button("Predizione Step 1")
367
-
368
- if sub:
369
- margin= val_merc - costo_rip
370
- shape_code = SHAPE_MAPPING.get(shape_name,-1)
371
-
372
- input_dict={
373
- "length":length,
374
- "width":width,
375
- "shape":shape_name,
376
- "weight":weight,
377
- "thickness":thickness,
378
- "RUL":RUL,
379
- "margin":margin
380
- }
381
- temp_df=pd.DataFrame([input_dict])
382
-
383
- # compat_dim
384
- if 'target_params' not in st.session_state:
385
- st.error("Parametri target non definiti. Fase 1 mancante.")
386
- return
387
- param_t=st.session_state.target_params
388
- temp_df['compat_dim'] = temp_df.apply(lambda r:
389
- dimension_match(r, **param_t), axis=1)
390
-
391
- # shape_code
392
- temp_df['shape_code']= shape_code
393
- # Manteniamo solo le col ML
394
- try:
395
- X_inference=temp_df[ML_FEATURES_STEP1]
396
- except KeyError as e:
397
- st.error(f"Mancano feature: {e}")
398
- return
399
-
400
- # Eseguiamo predizione con i modelli
401
- preds=[]
402
- details=[]
403
- for name,pipe in trained_pipelines.items():
404
- try:
405
- p_num=pipe.predict(X_inference)[0]
406
- proba=pipe.predict_proba(X_inference)[0]
407
- details.append({
408
- "Modello":name,
409
- "Pred(0=Riu,1=Upc)": p_num,
410
- "Prob_Riutilizzo": proba[0],
411
- "Prob_Upcycling": proba[1]
412
- })
413
- preds.append(p_num)
414
- except Exception as e:
415
- st.error(f"Errore predizione {name}: {e}")
416
-
417
- if not preds:
418
- st.error("Nessuna predizione valida.")
419
- return
420
- # Aggrega con mode
421
- from statistics import mode, StatisticsError
422
- try:
423
- final_pred=mode(preds)
424
- except StatisticsError:
425
- # Se c'è tie, guardiamo la media upcycling
426
- avg_upc= np.mean([d["Prob_Upcycling"] for d in details])
427
- final_pred=1 if avg_upc>=0.5 else 0
428
-
429
- final_label="Riutilizzo Funzionale" if final_pred==0 else "Upcycling Creativo"
430
- st.subheader("Risultato Aggregato")
431
- st.metric("Classe", final_label)
432
-
433
- with st.expander("Dettagli singoli modelli"):
434
- df_det=pd.DataFrame(details)
435
- df_det["Prob_Riutilizzo"]= df_det["Prob_Riutilizzo"].apply(lambda x:f"{x:.1%}")
436
- df_det["Prob_Upcycling"]= df_det["Prob_Upcycling"].apply(lambda x:f"{x:.1%}")
437
- st.dataframe(df_det)
438
-
439
- # Se Upcycling Creativo => Step 2 (VAE + GenAI)
440
- if final_label=="Upcycling Creativo":
441
- st.markdown("---")
442
- st.subheader("Fase 2: Generazione con VAE + GenAI Testuale")
443
-
444
- if not st.session_state.get("vae_trained_on_eol",False):
445
- st.error("VAE non addestrato. Vai a '🧬 Training VAE (Step 2)'.")
446
- return
447
- vae_model= st.session_state.get("vae",None)
448
- vae_scaler=st.session_state.get("vae_scaler",None)
449
- if vae_model is None or vae_scaler is None:
450
- st.error("VAE o scaler mancanti in session.")
451
- return
452
-
453
- n_ideas=st.number_input("Quante idee generare col VAE?",1,10,3)
454
- if st.button("Genera Configurazioni + Testo Upcycling"):
455
- vae_model.eval()
456
- with torch.no_grad():
457
- lat_dim= vae_model.fc21.out_features
458
- z=torch.randn(n_ideas, lat_dim)
459
- recon= vae_model.decode(z)
460
-
461
- arr=recon.numpy()
462
- try:
463
- arr_inv= vae_scaler.inverse_transform(arr)
464
- feat_names= vae_scaler.feature_names_in_
465
- df_gen= pd.DataFrame(arr_inv, columns=feat_names)
466
-
467
- # Riconverti shape_code -> shape
468
- if 'shape_code' in df_gen.columns:
469
- df_gen['shape_code']= df_gen['shape_code'].round().astype(int)
470
- inv_map={v:k for k,v in SHAPE_MAPPING.items()}
471
- df_gen['shape']= df_gen['shape_code'].map(inv_map).fillna('unknown')
472
-
473
- st.write("**Configurazioni generate (VAE)**")
474
- st.dataframe(df_gen.round(2))
475
-
476
- # --- Integrazione GenAI testuale con Transformers ---
477
- st.markdown("#### Suggerimenti testuali per ciascuna configurazione")
478
-
479
- # Carichiamo pipeline testuale (distilgpt2, ad es.)
480
- # Se su HF Spaces serve un modello leggero
481
- text_generator = pipeline(
482
- "text-generation",
483
- model="distilgpt2",
484
- device=0 if torch.cuda.is_available() else -1
485
- )
486
-
487
- def gen_upcycle_text(row):
488
- shape = row.get("shape","unknown")
489
- thick = row.get("thickness",0.0)
490
- wei = row.get("weight",0.0)
491
- prompt= (
492
- f"Ho un componente EoL con forma {shape}, spessore {thick:.1f} mm, peso {wei:.1f} kg.\n"
493
- "Dammi un'idea creativa di upcycling (in italiano) con passaggi principali:"
494
- )
495
- result= text_generator(prompt, max_new_tokens=50, do_sample=True, top_k=50)
496
- return result[0]["generated_text"]
497
-
498
- ideas_text=[]
499
- for i, r in df_gen.iterrows():
500
- text_sugg = gen_upcycle_text(r)
501
- ideas_text.append(text_sugg)
502
-
503
- for i, r in df_gen.iterrows():
504
- st.write(f"**Idea {i+1}** - shape={r['shape']}, thickness={r['thickness']:.1f}, weight={r['weight']:.1f}")
505
- st.info(ideas_text[i])
506
- st.markdown("---")
507
-
508
- except Exception as e:
509
- st.error(f"Errore decoding VAE: {e}")
510
- else:
511
- st.success("Predetto: Riutilizzo Funzionale. Nessun passaggio generativo richiesto.")
512
-
513
- ############### 4) PHASE: TRAINING VAE ############
514
- def vae_training_phase():
515
- st.header("🧬 Training VAE (Step 2)")
516
- if 'data' not in st.session_state or st.session_state['data'] is None:
517
- st.error("Nessun dataset in session. Torna a Fase 1.")
518
- return
519
- data=st.session_state['data']
520
-
521
- feats=[f for f in VAE_FEATURES_STEP2 if f in data.columns]
522
  if not feats:
523
- st.error(f"Nessuna feature da usare per VAE. Servirebbero: {VAE_FEATURES_STEP2}")
524
  return
525
- st.write(f"VAE userà feature: {feats}")
526
 
527
- lat_dim= st.slider("Dimensione Latente",2,10,2)
528
- ep= st.number_input("Epochs VAE",10,300,50)
529
- lr= st.number_input("Learning Rate VAE",1e-5,1e-2,1e-3,format="%e")
530
- bs= st.selectbox("Batch Size",[16,32,64], index=1)
531
 
532
- if not st.session_state.get("vae_trained_on_eol",False):
533
- st.warning("VAE non addestrato.")
534
- if st.button("Avvia Training VAE"):
535
- # Inizializza
536
  st.session_state["vae"]= MiniVAE(input_dim=len(feats), latent_dim=lat_dim)
537
- st.session_state["vae_trained_on_eol"]=False
538
-
539
  from sklearn.preprocessing import StandardScaler
540
  X_vae= data[feats].copy()
541
- # Riempi NaN
542
  for c in X_vae.columns:
543
  if X_vae[c].isnull().any():
544
- X_vae[c].fillna(X_vae[c].median(), inplace=True)
545
-
546
- # Scalatura
547
  scaler= StandardScaler()
548
- X_scaled= scaler.fit_transform(X_vae)
549
  st.session_state["vae_scaler"]= scaler
550
 
551
- dataset=torch.utils.data.TensorDataset(torch.tensor(X_scaled,dtype=torch.float32))
552
  loader= torch.utils.data.DataLoader(dataset,batch_size=bs,shuffle=True)
553
- vae=st.session_state["vae"]
554
- optimizer= torch.optim.Adam(vae.parameters(),lr=lr)
555
-
556
  losses=[]
557
  vae.train()
558
  for epoch in range(int(ep)):
559
- epoch_loss=0
560
  for (batch,) in loader:
561
- optimizer.zero_grad()
562
  recon, mu, logvar= vae(batch)
563
- loss= vae_loss(recon, batch, mu, logvar)
564
  loss.backward()
565
- optimizer.step()
566
- epoch_loss+= loss.item()
567
- avg_loss= epoch_loss/len(dataset)
568
- losses.append(avg_loss)
569
  st.progress((epoch+1)/ep)
570
- st.session_state["vae_trained_on_eol"]=True
571
- st.success(f"VAE addestrato. Ultimo Loss ~ {avg_loss:.2f}")
572
  st.line_chart(losses)
 
573
  else:
574
- st.success("VAE già addestrato. Se vuoi rifare training, clicca 'Riallena'.")
575
- if st.button("Riallena"):
576
- st.session_state["vae_trained_on_eol"]=False
577
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
 
579
- ############### 5) PHASE: DASHBOARD ############
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
  def show_dashboard():
581
- st.header("📊 Dashboard")
582
- data= st.session_state.get('data', None)
583
  if data is None:
584
  st.error("Nessun dataset.")
585
  return
 
586
 
587
- st.subheader("Distribuzione Classi EoL")
588
- st.write(data['Target'].value_counts())
589
-
590
- if 'train_results' in st.session_state and st.session_state['train_results'] is not None:
591
- df_res=st.session_state['train_results']
592
- st.subheader("Risultati modelli ML")
593
- st.dataframe(df_res)
594
  else:
595
- st.info("Nessun risultato di training ML")
596
 
597
- st.subheader("Stato VAE")
598
- if st.session_state.get("vae_trained_on_eol",False):
599
- st.success("VAE addestrato. Pronto per generare idee di upcycling.")
600
  else:
601
- st.warning("VAE non addestrato")
602
 
603
- ############### 6) PHASE: GUIDA ############
 
 
604
  def show_help():
605
- st.header("ℹ️ Guida all'Uso")
606
  st.markdown("""
607
- **Flusso a due fasi (Riutilizzo vs Upcycling) e generazione creativa VAE + GenAI**:
608
-
609
- 1. **Fase 1 (Dataset)**:
610
- - Generi/carichi dati su dimensioni, shape, RUL, margin, ecc.
611
- - Definisci parametri e tolleranze per la compatibilità, assegni classi ("Riutilizzo Funzionale" vs "Upcycling Creativo").
612
-
613
- 2. **Fase 2 (Addestramento ML)**:
614
- - Alleni vari modelli (RF, SVM...) per predire la classe su nuovi EoL.
615
 
616
- 3. **Fase 3 (Inferenza)**:
617
- - Inserisci un nuovo EoL.
618
- - Se la classe è "Riutilizzo Funzionale", stop.
619
- - Se "Upcycling Creativo", prosegui con generazione di soluzioni (VAE)...
620
 
621
- 4. **Fase 4 (Training VAE)**:
622
- - Prima devi addestrare il VAE su feature geometriche.
623
- - Finito l'allenamento, potrai generare configurazioni fittizie per l'upcycling (dim, spessore...).
624
 
625
- 5. **Fase 3 (continuazione)**:
626
- - Appena generi col VAE, un modello di GenAI testuale (distilgpt2) crea suggerimenti di riuso creativo in linguaggio naturale.
627
 
628
- 6. **Fase 5 (Dashboard)**:
629
- - Visualizzi le metriche e lo stato dei modelli.
630
 
631
- Puoi resettare l'app in qualsiasi momento dal pulsante "Reset App" nella sidebar.
632
- """)
633
-
634
- ############### RESET ############
635
  def reset_app():
636
- for k in ["data","models","train_results","vae","vae_trained_on_eol","vae_scaler","target_params","score_params","data_source"]:
637
  if k in st.session_state:
638
  del st.session_state[k]
639
- st.success("App reset. Ricarico l'interfaccia.")
640
  st.experimental_rerun()
641
 
642
- ############### MAIN ############
 
 
643
  def main():
644
- st.sidebar.title("WEEKO - Menu")
645
- step= st.sidebar.radio("Fasi:", [
646
- "♻️ Dataset",
647
- "🤖 Addestramento ML (Step 1)",
648
- "🔮 Inferenza (Step 1 & 2)",
649
- "🧬 Training VAE (Step 2)",
650
- "📊 Dashboard",
651
- "ℹ️ Guida"
652
  ])
653
- st.sidebar.button("Reset App", on_click=reset_app)
654
-
655
- if step=="♻️ Dataset":
656
- prepare_dataset()
657
- elif step=="🤖 Addestramento ML (Step 1)":
658
- train_models(st.session_state.get('data',None))
659
- elif step=="🔮 Inferenza (Step 1 & 2)":
660
- if st.session_state.get('models') is None:
661
- st.error("Devi addestrare i modelli ML (Fase 2).")
662
- else:
663
- model_inference(st.session_state['models'], st.session_state.get('data',None))
664
- elif step=="🧬 Training VAE (Step 2)":
665
- vae_training_phase()
666
- elif step=="📊 Dashboard":
667
  show_dashboard()
668
- elif step=="ℹ️ Guida":
669
  show_help()
670
 
671
  if __name__=="__main__":
672
- main()
673
-
 
5
  import matplotlib.pyplot as plt
6
  from statistics import mode, StatisticsError
7
 
8
+ # scikit-learn
9
+ from sklearn.model_selection import train_test_split
10
  from sklearn.preprocessing import StandardScaler
11
  from sklearn.pipeline import Pipeline
12
  from sklearn.metrics import confusion_matrix, accuracy_score, f1_score
13
+ from sklearn.ensemble import RandomForestClassifier
14
  from sklearn.linear_model import LogisticRegression
15
  from sklearn.svm import SVC
16
  from sklearn.neural_network import MLPClassifier
17
 
18
+ # torch
19
  import torch
20
  import torch.nn as nn
21
  import torch.nn.functional as F
22
 
23
+ # HF Transformers per generazione testo
24
  from transformers import pipeline
25
 
26
+ #############################
27
+ # STREAMLIT config
28
+ #############################
29
+ st.set_page_config(page_title="WEEKO – 4 Step Flow", layout="wide")
30
 
31
+ #############################
32
+ # DIZIONARIO MODELLI ML
33
+ #############################
34
  class DummyTabTransformerClassifier:
 
35
  def __init__(self, input_dim=8):
36
+ self.clf = MLPClassifier(hidden_layer_sizes=(16,8), max_iter=100, random_state=42)
 
37
  def fit(self, X, y):
38
+ self.clf.fit(X,y)
39
  return self
40
  def predict(self, X):
41
  return self.clf.predict(X)
42
  def predict_proba(self, X):
43
+ if hasattr(self.clf,"predict_proba"):
44
  return self.clf.predict_proba(X)
45
  else:
46
+ preds=self.clf.predict(X)
47
  return np.array([[1.0,0.0] if p==0 else [0.0,1.0] for p in preds])
48
 
49
+ MODELS_ML = {
50
+ "RandomForest": RandomForestClassifier(random_state=42, n_estimators=100),
51
+ "LogisticRegression": LogisticRegression(random_state=42, max_iter=500),
52
+ "SVM": SVC(probability=True, random_state=42),
53
+ "TabTransformer(Dummy)": DummyTabTransformerClassifier()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
 
56
+ #############################
57
+ # VAE
58
+ #############################
59
  class MiniVAE(nn.Module):
60
  def __init__(self, input_dim=5, latent_dim=2):
61
  super().__init__()
62
+ self.fc1 = nn.Linear(input_dim,32)
63
+ self.fc21= nn.Linear(32,latent_dim)
64
+ self.fc22= nn.Linear(32,latent_dim)
65
+ self.fc3 = nn.Linear(latent_dim,32)
66
+ self.fc4 = nn.Linear(32,input_dim)
67
+ def encode(self,x):
68
+ h=F.relu(self.fc1(x))
 
69
  return self.fc21(h), self.fc22(h)
 
70
  def reparameterize(self, mu, logvar):
71
+ std = torch.exp(0.5*logvar)
72
  eps = torch.randn_like(std)
73
  return mu + eps*std
74
+ def decode(self,z):
75
+ h=F.relu(self.fc3(z))
 
76
  return self.fc4(h)
77
+ def forward(self,x):
78
+ mu,logvar=self.encode(x)
79
+ z=self.reparameterize(mu,logvar)
80
+ recon=self.decode(z)
 
81
  return recon, mu, logvar
82
 
83
+ def vae_loss(recon_x,x,mu,logvar):
84
+ mse = F.mse_loss(recon_x,x,reduction='sum')
85
+ kld = -0.5*torch.sum(1 + logvar - mu.pow(2)-logvar.exp())
86
+ return mse+kld
87
+
88
+ #############################
89
+ # UTILITY
90
+ #############################
91
+ SHAPE_MAPPING = {"axisymmetric":0,"sheet_metal":1,"alloy_plate":2,"complex_plastic":3}
92
+ ML_FEATURES = ["length","width","shape_code","weight","thickness","RUL","margin","compat_dim"]
93
+ VAE_FEATURES= ["length","width","weight","thickness","shape_code"]
94
+
95
+ def dimension_match(r, target_len, target_wid, t_shape, t_w, t_th, tol_len, tol_wid, tol_we, tol_th):
96
+ c_len= abs(r["length"]-target_len)<=tol_len
97
+ c_wid= abs(r["width"]-target_wid)<=tol_wid
98
+ c_shp= (r["shape"]==t_shape)
99
+ c_wei= abs(r["weight"]-t_w)<= tol_we
100
+ c_thi= abs(r["thickness"]-t_th)<= tol_th
101
+ return 1 if (c_len and c_wid and c_shp and c_wei and c_thi) else 0
102
+
103
+ def assign_class(r, thr_score=0.5, alpha=0.5, beta=0.5):
104
+ rul_norm = r["RUL"]/1000.0
105
+ margin_norm=(r["margin"]+200)/800.0
106
+ score= alpha*rul_norm + beta*margin_norm
107
+ if r["compat_dim"]==1 and score>=thr_score:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  return "Riutilizzo Funzionale"
109
  else:
110
  return "Upcycling Creativo"
111
 
112
+ #############################
113
+ # Step 1: Dataset
114
+ #############################
115
+ def step1_dataset():
116
+ st.header("Step 1: Dataset")
117
+ # Genera / Carica
118
+ col1,col2 = st.columns(2)
119
+ with col1:
120
+ data_choice= st.radio("Fonte Dati",["Genera","Carica CSV"],horizontal=True)
121
+ data=None
122
+ if data_choice=="Genera":
123
+ n= st.slider("Campioni sintetici",100,2000,300,step=50)
124
+ if st.button("Genera"):
125
+ data= generate_synthetic_data(n_samples=n)
126
+ st.session_state["data_source"]="generated"
127
  else:
128
+ upl=st.file_uploader("Carica CSV con col: length,width,RUL,margin,shape,weight,thickness",type=["csv"])
129
+ if upl:
130
+ df= pd.read_csv(upl)
131
+ needed_cols=["length","width","RUL","margin","shape","weight","thickness"]
132
+ if not all(c in df.columns for c in needed_cols):
133
+ st.error("CSV non valido. Manca qualche colonna.")
134
+ else:
135
+ data=df
136
+ st.session_state["data_source"]="uploaded"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  if data is None:
138
+ st.info("Genera o carica un dataset per proseguire.")
 
 
 
139
  return
140
 
141
+ # Param compat
142
+ with col2:
143
+ st.subheader("Param Compatibilità")
144
+ t_len= st.number_input("Lunghezza target (mm)",50.0,300.0,100.0)
145
+ t_wid= st.number_input("Larghezza target (mm)",20.0,200.0,50.0)
146
+ t_shp= st.selectbox("Forma target", list(SHAPE_MAPPING.keys()))
147
+ t_we = st.number_input("Peso target (kg)",5.0,300.0,80.0)
148
+ t_th = st.number_input("Spessore target (mm)",0.5,50.0,8.0)
149
+ tol_len = st.slider("Tol len ±",0.0,30.0,5.0)
150
+ tol_wid = st.slider("Tol wid ±",0.0,20.0,3.0)
151
+ tol_wei = st.slider("Tol weight ±",0.0,50.0,10.0)
152
+ tol_thi = st.slider("Tol thick ±",0.0,5.0,1.0)
153
+ st.markdown("**Score RUL & margin**")
154
+ thr= st.slider("Soglia score",0.0,1.0,0.5)
155
+ alpha= st.slider("Peso RUL(α)",0.0,1.0,0.5)
156
+ beta= st.slider("Peso Margin(β)",0.0,1.0,0.5)
157
+
158
+ # Prepara dataset
159
+ data['shape_code']= data['shape'].map(SHAPE_MAPPING).fillna(-1).astype(int)
160
+ data['compat_dim']= data.apply(lambda r: dimension_match(r,t_len,t_wid,t_shp,t_we,t_th,tol_len,tol_wid,tol_wei,tol_thi), axis=1)
161
+ data['Target'] = data.apply(lambda r: assign_class(r,thr_score=thr,alpha=alpha,beta=beta), axis=1)
162
+
163
+ st.dataframe(data.head(10))
164
+ st.write("Distribuzione classi:", data["Target"].value_counts())
165
+
166
+ st.session_state["data"] = data
167
+ st.session_state["params_dim"]={
168
+ "target_len": t_len, "target_wid": t_wid,
169
+ "target_shp": t_shp, "target_we": t_we, "target_th": t_th,
170
+ "tol_len": tol_len,"tol_wid": tol_wid,"tol_wei":tol_wei,"tol_thi":tol_thi,
171
+ "thr_score":thr,"alpha":alpha,"beta":beta
172
+ }
173
+ csv_data= data.to_csv(index=False).encode('utf-8')
174
+ st.download_button("Scarica dataset elaborato",csv_data,"dataset_processed.csv","text/csv")
175
+
176
+ #############################
177
+ # Step 2: Addestramento ML
178
+ #############################
179
+ def step2_trainML():
180
+ st.header("Step 2: Addestramento ML (Riutilizzo vs Upcycling)")
181
+ data= st.session_state.get("data",None)
182
+ if data is None:
183
+ st.error("Prima prepara dataset in Step 1.")
184
+ return
185
+ if "Target" not in data.columns:
186
+ st.error("Colonna 'Target' mancante. Rivedi step 1.")
187
  return
188
 
189
+ features_ml=[f for f in ML_FEATURES if f in data.columns]
190
+ if not features_ml:
191
+ st.error(f"Mancano feature minime per ML: {ML_FEATURES}")
192
+ return
193
+ X= data[features_ml].copy()
194
+ y= data["Target"].map({"Riutilizzo Funzionale":0,"Upcycling Creativo":1})
195
  if len(y.unique())<2:
196
  st.error("Il dataset ha una sola classe. Non si può addestrare.")
197
  return
198
+ from sklearn.model_selection import train_test_split
199
+ X_train,X_test,y_train,y_test= train_test_split(X,y,test_size=0.25,random_state=42,stratify=y)
200
  st.write(f"Train={len(X_train)}, Test={len(X_test)}")
201
 
202
+ # Addestra
203
+ trained_pipes={}
204
  results=[]
205
+ for nome, model in MODELS_ML.items():
206
+ st.subheader(f"Modello: {nome}")
 
207
  from sklearn.pipeline import Pipeline
208
+ pipe= Pipeline([
209
+ ("scaler", StandardScaler()),
210
  ("clf",model)
211
  ])
212
  try:
213
+ pipe.fit(X_train,y_train)
214
+ y_pred= pipe.predict(X_test)
215
+ acc= accuracy_score(y_test,y_pred)
216
+ f1= f1_score(y_test,y_pred,average='weighted')
217
+ results.append({"Modello":nome,"Accuracy":acc,"F1 Score":f1})
218
+ trained_pipes[nome]= pipe
219
+
220
+ cm= confusion_matrix(y_test,y_pred)
221
+ fig, ax= plt.subplots()
222
+ sns.heatmap(cm, annot=True, fmt='d', cmap="Greens", ax=ax)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  plt.xlabel("Pred")
224
  plt.ylabel("True")
225
  st.pyplot(fig)
226
+
227
  st.metric("Accuracy",f"{acc:.3f}")
228
  st.metric("F1 Score",f"{f1:.3f}")
 
229
  except Exception as e:
230
+ st.error(f"Errore addestramento {nome}: {e}")
231
 
232
  if results:
233
+ df_r= pd.DataFrame(results).sort_values(by="Accuracy",ascending=False)
234
+ st.dataframe(df_r)
235
+ st.session_state["models"]= trained_pipes
236
+ st.session_state["ml_results"]= df_r
237
  else:
238
  st.error("Nessun modello addestrato con successo.")
239
+ st.session_state["models"]=None
240
 
241
+ #############################
242
+ # Step 2B: Training VAE
243
+ #############################
244
+ def step2b_trainVAE():
245
+ st.header("Step 2B: Training VAE (per Upcycling)")
246
 
247
+ data=st.session_state.get("data",None)
 
 
248
  if data is None:
249
+ st.error("Prima prepara dataset in Step 1.")
250
  return
251
+ feats= [f for f in VAE_FEATURES if f in data.columns]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  if not feats:
253
+ st.error(f"Mancano feature per VAE: {VAE_FEATURES}")
254
  return
255
+ st.write("Useremo le feature:", feats)
256
 
257
+ lat_dim= st.slider("Dim latente",2,10,2)
258
+ ep= st.number_input("Epoch",10,300,50)
259
+ lr= st.number_input("Learning Rate",1e-5,1e-2,1e-3,format="%e")
260
+ bs= st.selectbox("Batch size",[16,32,64], index=1)
261
 
262
+ if not st.session_state.get("vae_trained",False):
263
+ st.warning("VAE non ancora addestrato.")
264
+ if st.button("Allena VAE"):
265
+ # Inizializzo
266
  st.session_state["vae"]= MiniVAE(input_dim=len(feats), latent_dim=lat_dim)
 
 
267
  from sklearn.preprocessing import StandardScaler
268
  X_vae= data[feats].copy()
 
269
  for c in X_vae.columns:
270
  if X_vae[c].isnull().any():
271
+ X_vae[c].fillna(X_vae[c].median(),inplace=True)
 
 
272
  scaler= StandardScaler()
273
+ X_s= scaler.fit_transform(X_vae)
274
  st.session_state["vae_scaler"]= scaler
275
 
276
+ dataset= torch.utils.data.TensorDataset(torch.tensor(X_s,dtype=torch.float32))
277
  loader= torch.utils.data.DataLoader(dataset,batch_size=bs,shuffle=True)
278
+ vae= st.session_state["vae"]
279
+ opt= torch.optim.Adam(vae.parameters(),lr=lr)
 
280
  losses=[]
281
  vae.train()
282
  for epoch in range(int(ep)):
283
+ ep_loss=0
284
  for (batch,) in loader:
285
+ opt.zero_grad()
286
  recon, mu, logvar= vae(batch)
287
+ loss= vae_loss(recon,batch,mu,logvar)
288
  loss.backward()
289
+ opt.step()
290
+ ep_loss+=loss.item()
291
+ avgL= ep_loss/len(dataset)
292
+ losses.append(avgL)
293
  st.progress((epoch+1)/ep)
294
+ st.success(f"Training VAE completato, Loss ~ {avgL:.2f}")
 
295
  st.line_chart(losses)
296
+ st.session_state["vae_trained"]= True
297
  else:
298
+ st.success("VAE già addestrato. Se vuoi rifarlo, resetta e ricarica.")
299
+
300
+ #############################
301
+ # STEP 3: Upcycling Generative
302
+ #############################
303
+ def step3_upcycling_generative():
304
+ st.header("Step 3: Upcycling Generative – Genera idee con VAE + GenAI")
305
+
306
+ # Verifica se VAE c'è
307
+ if not st.session_state.get("vae_trained",False):
308
+ st.error("Devi addestrare il VAE (Step 2B) prima.")
309
+ return
310
+ vae= st.session_state.get("vae",None)
311
+ vae_scaler= st.session_state.get("vae_scaler",None)
312
+ if vae is None or vae_scaler is None:
313
+ st.error("VAE o scaler mancanti.")
314
+ return
315
+
316
+ st.write("Inserisci manualmente i param EoL (opzionale) oppure generiamo n idee random dal VAE?")
317
 
318
+ if st.checkbox("Usa param EoL come base? (NON implementato)"):
319
+ st.info("Potresti implementare un partial-conditional VAE. Attualmente generiamo in modo random lat space.")
320
+ # In un VAE classico non-conditional, non possiamo forzare i param a una base specifica, a meno di trick
321
+
322
+ n_ideas= st.number_input("N. idee da generare",1,10,3)
323
+ if st.button("Genera Upcycling"):
324
+ vae.eval()
325
+ with torch.no_grad():
326
+ lat_dim= vae.fc21.out_features
327
+ z= torch.randn(n_ideas, lat_dim)
328
+ recon= vae.decode(z)
329
+ arr= recon.numpy()
330
+ try:
331
+ arr_inv= vae_scaler.inverse_transform(arr)
332
+ df_gen= pd.DataFrame(arr_inv, columns=vae_scaler.feature_names_in_)
333
+
334
+ # shape_code -> shape
335
+ if 'shape_code' in df_gen.columns:
336
+ df_gen['shape_code']= df_gen['shape_code'].round().astype(int)
337
+ rev_map= {v:k for k,v in SHAPE_MAPPING.items()}
338
+ df_gen['shape']= df_gen['shape_code'].map(rev_map).fillna('unknown')
339
+
340
+ st.subheader("Configurazioni Generate (VAE)")
341
+ st.dataframe(df_gen.round(2))
342
+
343
+ # Gen testo con pipeline HF
344
+ st.markdown("### Suggerimenti Testuali di Upcycling")
345
+ text_generator = pipeline("text-generation", model="distilgpt2",
346
+ device=0 if torch.cuda.is_available() else -1)
347
+
348
+ def gen_upcycle_text(shape, thick, wei):
349
+ prompt= (
350
+ f"Ho un componente EoL di forma {shape}, spessore {thick:.1f} mm, peso {wei:.1f} kg.\n"
351
+ "Dammi un'idea creativa di upcycling in italiano, con passaggi principali:"
352
+ )
353
+ out= text_generator(prompt, max_new_tokens=50, do_sample=True, top_k=50)
354
+ return out[0]["generated_text"]
355
+
356
+ for i, row in df_gen.iterrows():
357
+ s= row.get("shape","unknown")
358
+ t= row.get("thickness",1.0)
359
+ w= row.get("weight",10.0)
360
+ text_sug= gen_upcycle_text(s,t,w)
361
+ st.write(f"**Idea {i+1}**: shape={s}, thickness={t:.1f}, weight={w:.1f}")
362
+ st.info(text_sug)
363
+ st.markdown("---")
364
+
365
+ except Exception as e:
366
+ st.error(f"Errore decodifica VAE: {e}")
367
+
368
+ #############################
369
+ # DASHBOARD
370
+ #############################
371
  def show_dashboard():
372
+ st.header("Dashboard")
373
+ data= st.session_state.get("data",None)
374
  if data is None:
375
  st.error("Nessun dataset.")
376
  return
377
+ st.write("Distribuzione classi:\n", data["Target"].value_counts())
378
 
379
+ if 'ml_results' in st.session_state and st.session_state["ml_results"] is not None:
380
+ st.subheader("Risultati ML")
381
+ st.dataframe(st.session_state["ml_results"])
 
 
 
 
382
  else:
383
+ st.info("Nessun risultato ML salvato.")
384
 
385
+ if st.session_state.get("vae_trained",False):
386
+ st.success("VAE addestrato.")
 
387
  else:
388
+ st.warning("VAE non addestrato.")
389
 
390
+ #############################
391
+ # HELP
392
+ #############################
393
  def show_help():
394
+ st.header("ℹ️ Guida all'Uso - Quattro Step")
395
  st.markdown("""
396
+ **Flusso**:
397
+ 1. **Step 1: Dataset**
398
+ - Generi o carichi CSV, definisci param. compatibilità, ottieni classi 'Riutilizzo' vs 'Upcycling'.
 
 
 
 
 
399
 
400
+ 2. **Step 2: Addestramento ML**
401
+ - Alleni modelli per predire su nuovi EoL.
 
 
402
 
403
+ 3. **Step 2B: Training VAE**
404
+ - Alleni VAE su feature geometriche, per generare configurazioni creative.
 
405
 
406
+ 4. **Step 3: Upcycling Generative**
407
+ - Generi N idee dal VAE + un modello di text-generation (distilgpt2) fornisce suggerimenti creativi in italiano.
408
 
409
+ **Dashboard**: mostra le metriche finali.
410
+ """)
411
 
412
+ #############################
413
+ # RESET
414
+ #############################
 
415
  def reset_app():
416
+ for k in ["data","models","ml_results","vae","vae_trained","vae_scaler","data_source","params_dim"]:
417
  if k in st.session_state:
418
  del st.session_state[k]
419
+ st.success("App resettata.")
420
  st.experimental_rerun()
421
 
422
+ #############################
423
+ # MAIN
424
+ #############################
425
  def main():
426
+ st.sidebar.title("WEEKO 4 Step Flow")
427
+ step = st.sidebar.radio("Fasi:",[
428
+ "Step 1: Dataset",
429
+ "Step 2: Addestramento ML",
430
+ "Step 2B: Training VAE",
431
+ "Step 3: Upcycling Generative",
432
+ "Dashboard",
433
+ "Help"
434
  ])
435
+ if st.sidebar.button("Reset App"):
436
+ reset_app()
437
+
438
+ if step=="Step 1: Dataset":
439
+ step1_dataset()
440
+ elif step=="Step 2: Addestramento ML":
441
+ step2_trainML()
442
+ elif step=="Step 2B: Training VAE":
443
+ step2b_trainVAE()
444
+ elif step=="Step 3: Upcycling Generative":
445
+ step3_upcycling_generative()
446
+ elif step=="Dashboard":
 
 
447
  show_dashboard()
448
+ elif step=="Help":
449
  show_help()
450
 
451
  if __name__=="__main__":
452
+ main()