SaiBon99 commited on
Commit
484525f
·
1 Parent(s): d6c19c2

Load model metadata and images from Hopsworks, update model info display

Browse files
Files changed (1) hide show
  1. app.py +171 -41
app.py CHANGED
@@ -12,6 +12,7 @@ This app:
12
 
13
  import os
14
  import sys
 
15
  import logging
16
  import gradio as gr
17
  from typing import Tuple
@@ -27,6 +28,7 @@ logger = logging.getLogger(__name__)
27
 
28
  # Global inference pipeline
29
  pipeline = None
 
30
 
31
 
32
  def initialize_app():
@@ -48,47 +50,186 @@ def initialize_app():
48
  pipeline.load_model_from_hopsworks()
49
  logger.info("Inference pipeline initialized successfully!")
50
 
 
 
 
 
51
  return True
52
  except Exception as e:
53
  logger.error(f"Failed to initialize app: {e}")
54
  return False
55
 
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  def get_model_info() -> str:
58
  """Get model information as HTML."""
59
- if not pipeline:
60
- return "<p>Model information not available</p>"
61
 
62
- # TODO: Check if pipeline has model_metrics attribute
63
- metrics = getattr(pipeline, 'model_metrics', None)
64
- if not metrics:
65
- return "<p>Model metrics not available</p>"
66
 
67
- version = getattr(pipeline, 'model_version', 'latest')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  html = f"""
70
  <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; margin: 10px;">
71
- <h3>Model Information</h3>
72
- <p><strong>Model Name:</strong> {pipeline.model_name}</p>
73
- <p><strong>Model Version:</strong> {version}</p>
 
74
  <hr>
75
- <h4>Performance Metrics</h4>
76
- <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
77
- <div><strong>Test Accuracy:</strong> {metrics.get('test_accuracy', 'N/A'):.4f}</div>
78
- <div><strong>Test Precision:</strong> {metrics.get('test_precision', 'N/A'):.4f}</div>
79
- <div><strong>Test Recall:</strong> {metrics.get('test_recall', 'N/A'):.4f}</div>
80
- <div><strong>Test F1 Score:</strong> {metrics.get('test_f1_score', 'N/A'):.4f}</div>
81
- <div><strong>Test ROC-AUC:</strong> {metrics.get('test_roc_auc', 'N/A'):.4f}</div>
82
- <div><strong>Validation F1 Score:</strong> {metrics.get('val_f1_score', 'N/A'):.4f}</div>
 
 
 
 
 
 
 
 
 
83
  </div>
84
  <hr>
85
- <h4>Training Information</h4>
86
- <p><strong>Number of Features:</strong> {metrics.get('n_features', 'N/A')}</p>
87
- <p><strong>Training Samples:</strong> {metrics.get('n_train_samples', 'N/A')}</p>
88
- <p><strong>Test Samples:</strong> {metrics.get('n_test_samples', 'N/A')}</p>
89
- <p><strong>Best CV Score:</strong> {metrics.get('best_cv_score', 'N/A'):.4f}</p>
90
- </div>
91
  """
 
 
 
 
 
 
 
 
 
 
92
  return html
93
 
94
 
@@ -236,31 +377,20 @@ def create_gradio_app():
236
 
237
  gr.Markdown("### Model Evaluation Visualizations")
238
 
239
- # TODO: Check if pipeline has model_dir attribute to access images
240
- model_dir = getattr(pipeline, 'model_dir', None) if pipeline else None
241
-
242
  with gr.Row():
243
  with gr.Column():
244
  gr.Markdown("#### Confusion Matrix")
245
- if model_dir:
246
- confusion_path = os.path.join(model_dir, "evaluation_image_1.png")
247
- if os.path.exists(confusion_path):
248
- gr.Image(value=confusion_path, label="Confusion Matrix")
249
- else:
250
- gr.Markdown("*Confusion matrix image not found*")
251
  else:
252
- gr.Markdown("*Model directory not available*")
253
 
254
  with gr.Column():
255
  gr.Markdown("#### Feature Importance")
256
- if model_dir:
257
- importance_path = os.path.join(model_dir, "evaluation_image_2.png")
258
- if os.path.exists(importance_path):
259
- gr.Image(value=importance_path, label="Feature Importance")
260
- else:
261
- gr.Markdown("*Feature importance image not found*")
262
  else:
263
- gr.Markdown("*Model directory not available*")
264
 
265
  gr.Markdown(
266
  """
 
12
 
13
  import os
14
  import sys
15
+ import re
16
  import logging
17
  import gradio as gr
18
  from typing import Tuple
 
28
 
29
  # Global inference pipeline
30
  pipeline = None
31
+ model_info_cache = None
32
 
33
 
34
  def initialize_app():
 
50
  pipeline.load_model_from_hopsworks()
51
  logger.info("Inference pipeline initialized successfully!")
52
 
53
+ # Load model metadata (metrics and images)
54
+ logger.info("Loading model metadata and images...")
55
+ load_model_metadata()
56
+
57
  return True
58
  except Exception as e:
59
  logger.error(f"Failed to initialize app: {e}")
60
  return False
61
 
62
 
63
+ def load_model_metadata():
64
+ """Load model metadata and images from Hopsworks."""
65
+ global model_info_cache
66
+
67
+ try:
68
+ from phising_detection.utils.hopsworks_utils import connect_to_hopsworks
69
+
70
+ project = connect_to_hopsworks()
71
+ mr = project.get_model_registry()
72
+
73
+ # Get the same model that pipeline loaded
74
+ model_version = getattr(pipeline, 'model_version', None) if pipeline else None
75
+ model_name = pipeline.model_name if pipeline else "phishing_detector"
76
+
77
+ if model_version:
78
+ model_registry = mr.get_model(model_name, version=model_version)
79
+ else:
80
+ model_registry = mr.get_model(model_name)
81
+
82
+ # Download model artifacts to get images and metrics
83
+ model_dir = model_registry.download()
84
+
85
+ # Parse hyperparameters.txt for metrics
86
+ metrics = {}
87
+ hyperparams_path = os.path.join(model_dir, "hyperparameters.txt")
88
+ if os.path.exists(hyperparams_path):
89
+ with open(hyperparams_path, 'r') as f:
90
+ content = f.read()
91
+
92
+ # Extract model name (first line)
93
+ lines = content.split('\n')
94
+ if lines:
95
+ first_line = lines[0].strip()
96
+ if 'Phishing Detection Model' in first_line:
97
+ model_type = first_line.split('-')[-1].strip() if '-' in first_line else 'Unknown'
98
+ metrics['model_type'] = model_type
99
+
100
+ # Extract training info
101
+ if 'CV Folds:' in content:
102
+ cv_match = re.search(r'CV Folds:\s*(\d+)', content)
103
+ if cv_match:
104
+ metrics['cv_folds'] = int(cv_match.group(1))
105
+
106
+ iter_match = re.search(r'RandomizedSearchCV iterations:\s*(\d+)', content)
107
+ if iter_match:
108
+ metrics['search_iterations'] = int(iter_match.group(1))
109
+
110
+ cv_score_match = re.search(r'Best CV Score:\s*([\d.]+)', content)
111
+ if cv_score_match:
112
+ metrics['best_cv_score'] = float(cv_score_match.group(1))
113
+
114
+ # Extract Test Performance metrics
115
+ if 'Test Performance:' in content:
116
+ test_section = content.split('Test Performance:')[1].split('Features:')[0]
117
+
118
+ acc_match = re.search(r'Accuracy:\s*([\d.]+)', test_section)
119
+ if acc_match:
120
+ metrics['test_accuracy'] = float(acc_match.group(1))
121
+
122
+ prec_match = re.search(r'Precision:\s*([\d.]+)', test_section)
123
+ if prec_match:
124
+ metrics['test_precision'] = float(prec_match.group(1))
125
+
126
+ rec_match = re.search(r'Recall:\s*([\d.]+)', test_section)
127
+ if rec_match:
128
+ metrics['test_recall'] = float(rec_match.group(1))
129
+
130
+ f1_match = re.search(r'F1 Score:\s*([\d.]+)', test_section)
131
+ if f1_match:
132
+ metrics['test_f1_score'] = float(f1_match.group(1))
133
+
134
+ roc_match = re.search(r'ROC-AUC:\s*([\d.]+)', test_section)
135
+ if roc_match:
136
+ metrics['test_roc_auc'] = float(roc_match.group(1))
137
+
138
+ # Extract number of features
139
+ if 'Features:' in content:
140
+ features_section = content.split('Features:')[1].strip()
141
+ # Count features in the list
142
+ feature_list = re.findall(r"'([^']+)'", features_section)
143
+ metrics['n_features'] = len(feature_list)
144
+ metrics['feature_names'] = ', '.join(feature_list)
145
+
146
+ # Get image paths
147
+ confusion_path = os.path.join(model_dir, "evaluation_image_1.png")
148
+ importance_path = os.path.join(model_dir, "evaluation_image_2.png")
149
+
150
+ model_info_cache = {
151
+ 'version': model_registry.version,
152
+ 'metrics': metrics,
153
+ 'confusion_matrix_path': confusion_path if os.path.exists(confusion_path) else None,
154
+ 'feature_importance_path': importance_path if os.path.exists(importance_path) else None
155
+ }
156
+
157
+ logger.info(f"Loaded model metadata: version {model_registry.version}, {len(metrics)} metrics")
158
+
159
+ except Exception as e:
160
+ logger.error(f"Error loading model metadata: {e}")
161
+ model_info_cache = None
162
+
163
+
164
  def get_model_info() -> str:
165
  """Get model information as HTML."""
166
+ if not model_info_cache:
167
+ return "<p>Model information not available. Try refreshing the page.</p>"
168
 
169
+ metrics = model_info_cache.get('metrics', {})
170
+ version = model_info_cache.get('version', 'unknown')
171
+ model_name = pipeline.model_name if pipeline else "phishing_detector"
 
172
 
173
+ # Check if we have any metrics
174
+ if not metrics:
175
+ return "<p>No metrics found in model artifacts.</p>"
176
+
177
+ # Helper function to safely format metric values
178
+ def format_metric(key, default='N/A'):
179
+ value = metrics.get(key, default)
180
+ if value == default:
181
+ return default
182
+ if isinstance(value, float):
183
+ return f"{value:.4f}"
184
+ if isinstance(value, int):
185
+ return str(value)
186
+ return str(value)
187
+
188
+ # Get model type from metrics or use default
189
+ model_type = metrics.get('model_type', 'Unknown')
190
 
191
  html = f"""
192
  <div style="padding: 20px; background-color: #f5f5f5; border-radius: 10px; margin: 10px;">
193
+ <h3>🤖 Model Information</h3>
194
+ <p><strong>Model Type:</strong> {model_type}</p>
195
+ <p><strong>Model Name:</strong> {model_name}</p>
196
+ <p><strong>Version:</strong> {version}</p>
197
  <hr>
198
+ <h4>📊 Test Performance</h4>
199
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 15px 0;">
200
+ <div style="padding: 10px; background-color: white; border-radius: 5px;">
201
+ <strong>Accuracy:</strong> <span style="font-size: 1.2em; color: #2196F3;">{format_metric('test_accuracy')}</span>
202
+ </div>
203
+ <div style="padding: 10px; background-color: white; border-radius: 5px;">
204
+ <strong>Precision:</strong> <span style="font-size: 1.2em; color: #4CAF50;">{format_metric('test_precision')}</span>
205
+ </div>
206
+ <div style="padding: 10px; background-color: white; border-radius: 5px;">
207
+ <strong>Recall:</strong> <span style="font-size: 1.2em; color: #FF9800;">{format_metric('test_recall')}</span>
208
+ </div>
209
+ <div style="padding: 10px; background-color: white; border-radius: 5px;">
210
+ <strong>F1 Score:</strong> <span style="font-size: 1.2em; color: #9C27B0;">{format_metric('test_f1_score')}</span>
211
+ </div>
212
+ <div style="padding: 10px; background-color: white; border-radius: 5px; grid-column: span 2;">
213
+ <strong>ROC-AUC:</strong> <span style="font-size: 1.2em; color: #F44336;">{format_metric('test_roc_auc')}</span>
214
+ </div>
215
  </div>
216
  <hr>
217
+ <h4>🎯 Training Details</h4>
218
+ <p><strong>CV Folds:</strong> {format_metric('cv_folds')}</p>
219
+ <p><strong>Search Iterations:</strong> {format_metric('search_iterations')}</p>
220
+ <p><strong>Best CV Score:</strong> {format_metric('best_cv_score')}</p>
221
+ <p><strong>Number of Features:</strong> {format_metric('n_features')}</p>
 
222
  """
223
+
224
+ # Add feature names if available
225
+ if 'feature_names' in metrics:
226
+ html += f"""
227
+ <hr>
228
+ <h4>📝 Features Used</h4>
229
+ <p style="font-size: 0.9em; line-height: 1.6;">{metrics['feature_names']}</p>
230
+ """
231
+
232
+ html += "</div>"
233
  return html
234
 
235
 
 
377
 
378
  gr.Markdown("### Model Evaluation Visualizations")
379
 
 
 
 
380
  with gr.Row():
381
  with gr.Column():
382
  gr.Markdown("#### Confusion Matrix")
383
+ if model_info_cache and model_info_cache.get('confusion_matrix_path'):
384
+ gr.Image(value=model_info_cache['confusion_matrix_path'], label="Confusion Matrix")
 
 
 
 
385
  else:
386
+ gr.Markdown("*Confusion matrix image not available*")
387
 
388
  with gr.Column():
389
  gr.Markdown("#### Feature Importance")
390
+ if model_info_cache and model_info_cache.get('feature_importance_path'):
391
+ gr.Image(value=model_info_cache['feature_importance_path'], label="Feature Importance")
 
 
 
 
392
  else:
393
+ gr.Markdown("*Feature importance image not available*")
394
 
395
  gr.Markdown(
396
  """