FlameF0X commited on
Commit
3aa1bcf
Β·
verified Β·
1 Parent(s): ef893b4

Rename src/streamlit_app.py to app.py

Browse files
Files changed (2) hide show
  1. app.py +382 -0
  2. src/streamlit_app.py +0 -40
app.py ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import plotly.express as px
3
+ import plotly.graph_objects as go
4
+ from plotly.subplots import make_subplots
5
+ import pandas as pd
6
+ import numpy as np
7
+ import json
8
+ import uuid
9
+ from datetime import datetime, timedelta
10
+ import time
11
+ from huggingface_hub import HfApi, login
12
+ from streamlit_option_menu import option_menu
13
+ import requests
14
+ import hashlib
15
+ import os
16
+
17
+ # Configure page
18
+ st.set_page_config(
19
+ page_title="ML Tracker - Free W&B Alternative",
20
+ page_icon="πŸ“Š",
21
+ layout="wide",
22
+ initial_sidebar_state="expanded"
23
+ )
24
+
25
+ # Initialize session state
26
+ if 'authenticated' not in st.session_state:
27
+ st.session_state.authenticated = False
28
+ if 'user_token' not in st.session_state:
29
+ st.session_state.user_token = None
30
+ if 'api_key' not in st.session_state:
31
+ st.session_state.api_key = None
32
+ if 'experiments' not in st.session_state:
33
+ st.session_state.experiments = {}
34
+ if 'current_experiment' not in st.session_state:
35
+ st.session_state.current_experiment = None
36
+
37
+ # Custom CSS for better styling
38
+ st.markdown("""
39
+ <style>
40
+ .main-header {
41
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
42
+ padding: 1rem;
43
+ border-radius: 10px;
44
+ margin-bottom: 1rem;
45
+ }
46
+ .metric-card {
47
+ background: #f8f9fa;
48
+ padding: 1rem;
49
+ border-radius: 8px;
50
+ border-left: 4px solid #667eea;
51
+ margin-bottom: 1rem;
52
+ }
53
+ .api-key-box {
54
+ background: #f1f3f4;
55
+ padding: 1rem;
56
+ border-radius: 8px;
57
+ font-family: monospace;
58
+ margin: 1rem 0;
59
+ }
60
+ .stButton > button {
61
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
62
+ color: white;
63
+ border: none;
64
+ border-radius: 6px;
65
+ padding: 0.5rem 1rem;
66
+ font-weight: 500;
67
+ }
68
+ </style>
69
+ """, unsafe_allow_html=True)
70
+
71
+ def generate_api_key(user_token):
72
+ """Generate a unique API key for the user"""
73
+ return hashlib.sha256(f"{user_token}_{datetime.now().isoformat()}".encode()).hexdigest()[:32]
74
+
75
+ def authenticate_user():
76
+ """Handle HuggingFace authentication"""
77
+ st.markdown('<div class="main-header"><h1 style="color: white; margin: 0;">πŸ€— ML Tracker</h1><p style="color: white; margin: 0;">Free W&B Alternative on HuggingFace Spaces</p></div>', unsafe_allow_html=True)
78
+
79
+ col1, col2, col3 = st.columns([1, 2, 1])
80
+
81
+ with col2:
82
+ st.markdown("### πŸ” Connect with HuggingFace")
83
+ st.markdown("Enter your HuggingFace token to get started with experiment tracking!")
84
+
85
+ hf_token = st.text_input(
86
+ "HuggingFace Token",
87
+ type="password",
88
+ placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
89
+ help="Get your token from https://huggingface.co/settings/tokens"
90
+ )
91
+
92
+ if st.button("πŸš€ Connect & Generate API Key", use_container_width=True):
93
+ if hf_token:
94
+ try:
95
+ # Verify token
96
+ api = HfApi(token=hf_token)
97
+ user_info = api.whoami()
98
+
99
+ # Store authentication
100
+ st.session_state.authenticated = True
101
+ st.session_state.user_token = hf_token
102
+ st.session_state.api_key = generate_api_key(hf_token)
103
+ st.session_state.username = user_info['name']
104
+
105
+ st.success(f"βœ… Successfully connected as {user_info['name']}!")
106
+ time.sleep(1)
107
+ st.rerun()
108
+
109
+ except Exception as e:
110
+ st.error(f"❌ Authentication failed: {str(e)}")
111
+ else:
112
+ st.error("Please enter your HuggingFace token")
113
+
114
+ def show_api_key():
115
+ """Display API key and usage instructions"""
116
+ st.markdown("### πŸ”‘ Your API Key")
117
+ st.markdown(f'<div class="api-key-box"><strong>API Key:</strong> {st.session_state.api_key}</div>', unsafe_allow_html=True)
118
+
119
+ st.markdown("### πŸ“‹ Usage Instructions")
120
+ st.code(f"""
121
+ # Install the client
122
+ pip install requests
123
+
124
+ # Python usage example
125
+ import requests
126
+ import json
127
+
128
+ API_KEY = "{st.session_state.api_key}"
129
+ BASE_URL = "https://your-space-url.hf.space"
130
+
131
+ # Log metrics
132
+ def log_metrics(experiment_name, step, metrics):
133
+ response = requests.post(
134
+ f"{BASE_URL}/api/log",
135
+ json={{
136
+ "api_key": API_KEY,
137
+ "experiment": experiment_name,
138
+ "step": step,
139
+ "metrics": metrics,
140
+ "timestamp": time.time()
141
+ }}
142
+ )
143
+ return response.json()
144
+
145
+ # Example usage
146
+ log_metrics("my_experiment", 1, {{
147
+ "loss": 0.5,
148
+ "accuracy": 0.85,
149
+ "learning_rate": 0.001
150
+ }})
151
+ """, language="python")
152
+
153
+ def generate_sample_data():
154
+ """Generate sample experiment data for demonstration"""
155
+ experiments = {
156
+ "cnn_image_classification": {
157
+ "created_at": datetime.now() - timedelta(days=2),
158
+ "metrics": [],
159
+ "config": {
160
+ "model": "ResNet50",
161
+ "dataset": "CIFAR-10",
162
+ "epochs": 100,
163
+ "batch_size": 32,
164
+ "learning_rate": 0.001
165
+ }
166
+ },
167
+ "nlp_sentiment_analysis": {
168
+ "created_at": datetime.now() - timedelta(days=1),
169
+ "metrics": [],
170
+ "config": {
171
+ "model": "BERT",
172
+ "dataset": "IMDB",
173
+ "epochs": 50,
174
+ "batch_size": 16,
175
+ "learning_rate": 0.0001
176
+ }
177
+ }
178
+ }
179
+
180
+ # Generate sample metrics
181
+ for exp_name, exp_data in experiments.items():
182
+ metrics = []
183
+ for step in range(1, 101):
184
+ if exp_name == "cnn_image_classification":
185
+ loss = 2.3 * np.exp(-step/20) + 0.1 + np.random.normal(0, 0.05)
186
+ accuracy = 1 - 0.9 * np.exp(-step/15) + np.random.normal(0, 0.02)
187
+ val_loss = loss + np.random.normal(0, 0.1)
188
+ val_accuracy = accuracy - np.random.normal(0.05, 0.02)
189
+
190
+ metrics.append({
191
+ "step": step,
192
+ "loss": max(0, loss),
193
+ "accuracy": max(0, min(1, accuracy)),
194
+ "val_loss": max(0, val_loss),
195
+ "val_accuracy": max(0, min(1, val_accuracy)),
196
+ "timestamp": (datetime.now() - timedelta(days=2) + timedelta(minutes=step*2)).isoformat()
197
+ })
198
+ else:
199
+ loss = 1.8 * np.exp(-step/25) + 0.2 + np.random.normal(0, 0.03)
200
+ f1_score = 1 - 0.7 * np.exp(-step/20) + np.random.normal(0, 0.02)
201
+ precision = f1_score + np.random.normal(0, 0.02)
202
+ recall = f1_score + np.random.normal(0, 0.02)
203
+
204
+ metrics.append({
205
+ "step": step,
206
+ "loss": max(0, loss),
207
+ "f1_score": max(0, min(1, f1_score)),
208
+ "precision": max(0, min(1, precision)),
209
+ "recall": max(0, min(1, recall)),
210
+ "timestamp": (datetime.now() - timedelta(days=1) + timedelta(minutes=step*3)).isoformat()
211
+ })
212
+
213
+ exp_data["metrics"] = metrics
214
+
215
+ return experiments
216
+
217
+ def create_metric_charts(experiment_data):
218
+ """Create interactive charts for experiment metrics"""
219
+ if not experiment_data["metrics"]:
220
+ st.warning("No metrics data available for this experiment.")
221
+ return
222
+
223
+ df = pd.DataFrame(experiment_data["metrics"])
224
+
225
+ # Get all numeric columns (excluding step and timestamp)
226
+ numeric_cols = [col for col in df.columns if col not in ['step', 'timestamp'] and pd.api.types.is_numeric_dtype(df[col])]
227
+
228
+ if not numeric_cols:
229
+ st.warning("No numeric metrics found.")
230
+ return
231
+
232
+ # Create subplots
233
+ n_metrics = len(numeric_cols)
234
+ n_cols = 2
235
+ n_rows = (n_metrics + n_cols - 1) // n_cols
236
+
237
+ fig = make_subplots(
238
+ rows=n_rows,
239
+ cols=n_cols,
240
+ subplot_titles=numeric_cols,
241
+ vertical_spacing=0.1,
242
+ horizontal_spacing=0.1
243
+ )
244
+
245
+ colors = px.colors.qualitative.Set3
246
+
247
+ for i, metric in enumerate(numeric_cols):
248
+ row = i // n_cols + 1
249
+ col = i % n_cols + 1
250
+
251
+ fig.add_trace(
252
+ go.Scatter(
253
+ x=df['step'],
254
+ y=df[metric],
255
+ mode='lines+markers',
256
+ name=metric,
257
+ line=dict(color=colors[i % len(colors)], width=2),
258
+ marker=dict(size=4),
259
+ hovertemplate=f"<b>{metric}</b><br>Step: %{{x}}<br>Value: %{{y:.4f}}<extra></extra>"
260
+ ),
261
+ row=row,
262
+ col=col
263
+ )
264
+
265
+ fig.update_layout(
266
+ height=400 * n_rows,
267
+ showlegend=False,
268
+ title_text="Experiment Metrics Over Time",
269
+ title_x=0.5,
270
+ font=dict(size=12)
271
+ )
272
+
273
+ fig.update_xaxes(title_text="Step")
274
+ fig.update_yaxes(title_text="Value")
275
+
276
+ st.plotly_chart(fig, use_container_width=True)
277
+
278
+ def show_experiment_dashboard():
279
+ """Display the main experiment dashboard"""
280
+ st.markdown('<div class="main-header"><h1 style="color: white; margin: 0;">πŸ“Š ML Experiment Dashboard</h1></div>', unsafe_allow_html=True)
281
+
282
+ # Load sample data if no experiments exist
283
+ if not st.session_state.experiments:
284
+ st.session_state.experiments = generate_sample_data()
285
+
286
+ # Sidebar for experiment selection
287
+ with st.sidebar:
288
+ st.markdown("### πŸ”¬ Experiments")
289
+
290
+ exp_names = list(st.session_state.experiments.keys())
291
+ if exp_names:
292
+ selected_exp = st.selectbox(
293
+ "Select Experiment",
294
+ exp_names,
295
+ key="exp_selector"
296
+ )
297
+ st.session_state.current_experiment = selected_exp
298
+ else:
299
+ st.info("No experiments found. Start logging metrics to see them here!")
300
+ return
301
+
302
+ st.markdown("### πŸ“ˆ Quick Stats")
303
+ if st.session_state.current_experiment:
304
+ exp_data = st.session_state.experiments[st.session_state.current_experiment]
305
+ st.metric("Total Steps", len(exp_data["metrics"]))
306
+ st.metric("Created", exp_data["created_at"].strftime("%Y-%m-%d"))
307
+
308
+ # Main dashboard content
309
+ if st.session_state.current_experiment:
310
+ exp_data = st.session_state.experiments[st.session_state.current_experiment]
311
+
312
+ # Experiment header
313
+ col1, col2 = st.columns([3, 1])
314
+ with col1:
315
+ st.markdown(f"## {st.session_state.current_experiment}")
316
+ with col2:
317
+ if st.button("πŸ”„ Refresh", use_container_width=True):
318
+ st.rerun()
319
+
320
+ # Configuration section
321
+ with st.expander("βš™οΈ Configuration", expanded=False):
322
+ config_df = pd.DataFrame(list(exp_data["config"].items()), columns=["Parameter", "Value"])
323
+ st.dataframe(config_df, use_container_width=True)
324
+
325
+ # Metrics overview
326
+ if exp_data["metrics"]:
327
+ latest_metrics = exp_data["metrics"][-1]
328
+
329
+ st.markdown("### πŸ“Š Latest Metrics")
330
+ cols = st.columns(len([k for k in latest_metrics.keys() if k not in ['step', 'timestamp']]))
331
+
332
+ for i, (key, value) in enumerate(latest_metrics.items()):
333
+ if key not in ['step', 'timestamp']:
334
+ with cols[i]:
335
+ st.metric(key.replace('_', ' ').title(), f"{value:.4f}")
336
+
337
+ # Charts section
338
+ st.markdown("### πŸ“ˆ Metrics Over Time")
339
+ create_metric_charts(exp_data)
340
+
341
+ # Raw data section
342
+ with st.expander("πŸ“‹ Raw Data", expanded=False):
343
+ if exp_data["metrics"]:
344
+ df = pd.DataFrame(exp_data["metrics"])
345
+ st.dataframe(df, use_container_width=True)
346
+ else:
347
+ st.info("No metrics data available.")
348
+
349
+ def main():
350
+ """Main application logic"""
351
+ if not st.session_state.authenticated:
352
+ authenticate_user()
353
+ else:
354
+ # Navigation menu
355
+ selected = option_menu(
356
+ menu_title=None,
357
+ options=["Dashboard", "API Key", "Logout"],
358
+ icons=["graph-up", "key", "box-arrow-right"],
359
+ menu_icon="cast",
360
+ default_index=0,
361
+ orientation="horizontal",
362
+ styles={
363
+ "container": {"padding": "0!important", "background-color": "#fafafa"},
364
+ "icon": {"color": "#667eea", "font-size": "18px"},
365
+ "nav-link": {"font-size": "16px", "text-align": "center", "margin": "0px", "--hover-color": "#eee"},
366
+ "nav-link-selected": {"background-color": "#667eea"},
367
+ }
368
+ )
369
+
370
+ if selected == "Dashboard":
371
+ show_experiment_dashboard()
372
+ elif selected == "API Key":
373
+ show_api_key()
374
+ elif selected == "Logout":
375
+ st.session_state.authenticated = False
376
+ st.session_state.user_token = None
377
+ st.session_state.api_key = None
378
+ st.session_state.experiments = {}
379
+ st.rerun()
380
+
381
+ if __name__ == "__main__":
382
+ main()
src/streamlit_app.py DELETED
@@ -1,40 +0,0 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
- import streamlit as st
5
-
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))