Sompote commited on
Commit
94d41b4
·
verified ·
1 Parent(s): dda35dd

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +385 -0
app.py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import torch
5
+ import matplotlib.pyplot as plt
6
+ import pickle
7
+ import os
8
+ import warnings
9
+ from lllm_model_all_token import LLMConcreteModel
10
+
11
+ # Optimize for deployment
12
+ warnings.filterwarnings('ignore')
13
+ torch.set_num_threads(2)
14
+
15
+ # Canva-style colors
16
+ CANVA_PURPLE = "#8B5CF6"
17
+ CANVA_LIGHT_PURPLE = "#A78BFA"
18
+ CANVA_DARK_PURPLE = "#7C3AED"
19
+ CANVA_BACKGROUND = "#FAFAFA"
20
+ CANVA_WHITE = "#FFFFFF"
21
+
22
+ # Set page config with Canva-style theme
23
+ st.set_page_config(
24
+ page_title="Concrete Creep Prediction",
25
+ page_icon="🏗️",
26
+ layout="centered",
27
+ initial_sidebar_state="collapsed"
28
+ )
29
+
30
+ # Custom CSS for Canva-style design
31
+ st.markdown(f"""
32
+ <style>
33
+ .main {{
34
+ background-color: {CANVA_BACKGROUND};
35
+ }}
36
+
37
+ .stApp {{
38
+ background-color: {CANVA_BACKGROUND};
39
+ }}
40
+
41
+ .css-1d391kg {{
42
+ background-color: {CANVA_WHITE};
43
+ padding: 2rem;
44
+ border-radius: 15px;
45
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
46
+ margin: 1rem 0;
47
+ }}
48
+
49
+ .stButton > button {{
50
+ background-color: {CANVA_PURPLE};
51
+ color: white;
52
+ border: none;
53
+ border-radius: 25px;
54
+ padding: 0.75rem 2rem;
55
+ font-weight: 600;
56
+ font-size: 16px;
57
+ transition: all 0.3s ease;
58
+ width: 100%;
59
+ }}
60
+
61
+ .stButton > button:hover {{
62
+ background-color: {CANVA_DARK_PURPLE};
63
+ transform: translateY(-2px);
64
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3);
65
+ }}
66
+
67
+ .stNumberInput > div > div > input {{
68
+ border-radius: 10px;
69
+ border: 2px solid #E5E7EB;
70
+ padding: 0.75rem;
71
+ }}
72
+
73
+ .stNumberInput > div > div > input:focus {{
74
+ border-color: {CANVA_PURPLE};
75
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
76
+ }}
77
+
78
+ .metric-card {{
79
+ background: linear-gradient(135deg, {CANVA_PURPLE}, {CANVA_LIGHT_PURPLE});
80
+ color: white;
81
+ padding: 1.5rem;
82
+ border-radius: 15px;
83
+ text-align: center;
84
+ margin: 0.5rem 0;
85
+ }}
86
+
87
+ .result-card {{
88
+ background-color: {CANVA_WHITE};
89
+ padding: 2rem;
90
+ border-radius: 15px;
91
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
92
+ margin: 1rem 0;
93
+ }}
94
+
95
+ h1 {{
96
+ color: {CANVA_DARK_PURPLE};
97
+ text-align: center;
98
+ font-weight: 700;
99
+ margin-bottom: 2rem;
100
+ }}
101
+
102
+ h2, h3 {{
103
+ color: {CANVA_DARK_PURPLE};
104
+ font-weight: 600;
105
+ }}
106
+
107
+ .stSuccess {{
108
+ background-color: #10B981;
109
+ color: white;
110
+ border-radius: 10px;
111
+ }}
112
+ </style>
113
+ """, unsafe_allow_html=True)
114
+
115
+ # Simple CreepScaler class
116
+ class CreepScaler:
117
+ def __init__(self, factor=1000):
118
+ self.factor = factor
119
+ self.mean_ = 0
120
+ self.scale_ = factor
121
+ self.is_standard_scaler = False
122
+
123
+ def transform(self, X):
124
+ if self.is_standard_scaler:
125
+ return (X - self.mean_) / self.scale_
126
+ return X / self.factor
127
+
128
+ def inverse_transform(self, X):
129
+ if self.is_standard_scaler:
130
+ return (X * self.scale_) + self.mean_
131
+ return X * self.factor
132
+
133
+ @st.cache_resource
134
+ def load_model():
135
+ """Load model and scalers"""
136
+ # Find model file
137
+ model_files = ['best_llm_model-17.pt', 'final_llm_model-5.pt']
138
+ model_path = None
139
+ for file in model_files:
140
+ if os.path.exists(file):
141
+ model_path = file
142
+ break
143
+
144
+ if model_path is None:
145
+ st.error("❌ Model file not found")
146
+ st.stop()
147
+
148
+ # Load scalers
149
+ try:
150
+ with open('scalers/feature_scaler.pkl', 'rb') as f:
151
+ feature_scaler = pickle.load(f)
152
+
153
+ try:
154
+ with open('scalers/creep_scaler.pkl', 'rb') as f:
155
+ creep_scaler = pickle.load(f)
156
+ except:
157
+ creep_scaler = CreepScaler(factor=1000)
158
+
159
+ try:
160
+ with open('scalers/time_values.pkl', 'rb') as f:
161
+ time_values = pickle.load(f)
162
+ except:
163
+ time_values = np.arange(1, 1001) # Default 1000 time points
164
+
165
+ except Exception as e:
166
+ st.error(f"❌ Error loading files: {e}")
167
+ st.stop()
168
+
169
+ # Load model
170
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
171
+ model = LLMConcreteModel(
172
+ feature_dim=3,
173
+ d_model=192,
174
+ num_layers=4,
175
+ num_heads=4,
176
+ d_ff=768,
177
+ dropout=0.057,
178
+ target_len=1,
179
+ pooling_method='hybrid'
180
+ )
181
+
182
+ try:
183
+ model.load_state_dict(torch.load(model_path, map_location=device))
184
+ model = model.to(device)
185
+ model.eval()
186
+ except Exception as e:
187
+ st.error(f"❌ Error loading model: {e}")
188
+ st.stop()
189
+
190
+ return model, feature_scaler, creep_scaler, time_values, device
191
+
192
+ def predict_creep(model, features, time_values, feature_scaler, creep_scaler, device, max_days=365):
193
+ """Simple prediction function"""
194
+ # Scale features
195
+ scaled_features = feature_scaler.transform(features)
196
+ scaled_features_tensor = torch.FloatTensor(scaled_features).to(device)
197
+
198
+ # Limit time values
199
+ pred_time_values = time_values[:max_days] if max_days < len(time_values) else time_values
200
+
201
+ predictions = [0.0] # Start with 0
202
+ scaled_predictions = [0.0]
203
+
204
+ with torch.no_grad():
205
+ for i in range(1, len(pred_time_values)):
206
+ history = np.array(scaled_predictions)
207
+ history_tensor = torch.FloatTensor(history).unsqueeze(0).to(device)
208
+
209
+ time_history = np.log1p(pred_time_values[:i])
210
+ time_tensor = torch.FloatTensor(time_history).unsqueeze(0).to(device)
211
+
212
+ length = torch.tensor([len(history)], device=device)
213
+
214
+ next_value = model(
215
+ creep_history=history_tensor,
216
+ features=scaled_features_tensor,
217
+ lengths=length,
218
+ time_history=time_tensor
219
+ ).item()
220
+
221
+ scaled_predictions.append(next_value)
222
+ next_creep = creep_scaler.inverse_transform(np.array([[next_value]])).flatten()[0]
223
+ predictions.append(next_creep)
224
+
225
+ return np.array(predictions), pred_time_values
226
+
227
+ # Load model
228
+ model, feature_scaler, creep_scaler, time_values, device = load_model()
229
+
230
+ def get_base64_of_image(path):
231
+ """Convert image to base64 string"""
232
+ import base64
233
+ try:
234
+ with open(path, "rb") as img_file:
235
+ return base64.b64encode(img_file.read()).decode()
236
+ except:
237
+ return ""
238
+
239
+ # App title with logo
240
+ st.markdown("""
241
+ <div style='text-align: center; padding: 2rem 0;'>
242
+ <div style='display: flex; justify-content: center; align-items: center; margin-bottom: 1.5rem; flex-wrap: wrap;'>
243
+ <img src='data:image/png;base64,{}' style='width: 120px; height: auto; max-height: 100px; margin-right: 1.5rem; margin-bottom: 1rem; border-radius: 10px; box-shadow: 0 4px 12px rgba(139, 92, 246, 0.2); object-fit: contain;'>
244
+ <div style='text-align: center;'>
245
+ <h1 style='margin: 0; color: {}; font-size: 2.5rem; font-weight: 700;'>🏗️ Concrete Creep Prediction</h1>
246
+ <p style='margin: 0; font-size: 18px; color: #6B7280; font-weight: 500;'>AI-Powered Concrete Analysis</p>
247
+ </div>
248
+ </div>
249
+ </div>
250
+ """.format(
251
+ get_base64_of_image("AI_logo.png"),
252
+ CANVA_DARK_PURPLE
253
+ ), unsafe_allow_html=True)
254
+
255
+ # Input form in a clean card
256
+ with st.container():
257
+ st.markdown('<div class="css-1d391kg">', unsafe_allow_html=True)
258
+
259
+ st.markdown("### 📝 Enter Concrete Properties")
260
+
261
+ col1, col2 = st.columns(2)
262
+
263
+ with col1:
264
+ density = st.number_input(
265
+ "Density (kg/m³)",
266
+ min_value=2000.0,
267
+ max_value=3000.0,
268
+ value=2490.0,
269
+ step=10.0
270
+ )
271
+
272
+ fc = st.number_input(
273
+ "Compressive Strength (ksc)",
274
+ min_value=10.0,
275
+ max_value=1000.0,
276
+ value=670.0,
277
+ step=10.0
278
+ )
279
+
280
+ with col2:
281
+ e_modulus = st.number_input(
282
+ "Elastic Modulus (ksc)",
283
+ min_value=10000.0,
284
+ max_value=1000000.0,
285
+ value=436000.0,
286
+ step=1000.0
287
+ )
288
+
289
+ st.markdown('</div>', unsafe_allow_html=True)
290
+
291
+ # Predict button
292
+ if st.button("🚀 Predict Creep Strain"):
293
+ # Set default prediction days
294
+ max_days = 365
295
+
296
+ # Create features
297
+ features_dict = {
298
+ 'Density': density,
299
+ 'fc': fc,
300
+ 'E': e_modulus
301
+ }
302
+ df_features = pd.DataFrame([features_dict])
303
+
304
+ # Run prediction
305
+ with st.spinner("🔄 Predicting..."):
306
+ try:
307
+ predictions, pred_time_values = predict_creep(
308
+ model, df_features, time_values,
309
+ feature_scaler, creep_scaler, device, max_days
310
+ )
311
+
312
+ # Results
313
+ st.markdown('<div class="result-card">', unsafe_allow_html=True)
314
+
315
+ # Key metrics
316
+ col1, col2 = st.columns(2)
317
+ with col1:
318
+ st.markdown(f"""
319
+ <div class="metric-card">
320
+ <h3>{predictions[-1]:.1f}</h3>
321
+ <p>Final Creep (µε)</p>
322
+ </div>
323
+ """, unsafe_allow_html=True)
324
+
325
+ with col2:
326
+ st.markdown(f"""
327
+ <div class="metric-card">
328
+ <h3>{np.max(predictions):.1f}</h3>
329
+ <p>Maximum Creep (µε)</p>
330
+ </div>
331
+ """, unsafe_allow_html=True)
332
+
333
+ # Simple plot
334
+ st.markdown("### 📊 Creep Strain Over Time")
335
+
336
+ # Set plot style to match Canva theme
337
+ plt.style.use('default')
338
+ fig, ax = plt.subplots(figsize=(10, 6))
339
+ fig.patch.set_facecolor('white')
340
+
341
+ ax.plot(pred_time_values, predictions,
342
+ color=CANVA_PURPLE, linewidth=3, alpha=0.8)
343
+ ax.fill_between(pred_time_values, predictions,
344
+ alpha=0.2, color=CANVA_LIGHT_PURPLE)
345
+
346
+ ax.set_xlabel('Time (days)', fontsize=12, color='#374151')
347
+ ax.set_ylabel('Creep Strain (µε)', fontsize=12, color='#374151')
348
+ ax.grid(True, alpha=0.3, color='#E5E7EB')
349
+ ax.set_facecolor('#FAFAFA')
350
+
351
+ # Remove top and right spines
352
+ ax.spines['top'].set_visible(False)
353
+ ax.spines['right'].set_visible(False)
354
+ ax.spines['left'].set_color('#E5E7EB')
355
+ ax.spines['bottom'].set_color('#E5E7EB')
356
+
357
+ plt.tight_layout()
358
+ st.pyplot(fig)
359
+
360
+ # Download data
361
+ results_df = pd.DataFrame({
362
+ 'Time (days)': pred_time_values,
363
+ 'Creep Strain (µε)': predictions
364
+ })
365
+
366
+ csv = results_df.to_csv(index=False)
367
+ st.download_button(
368
+ label="💾 Download Results",
369
+ data=csv,
370
+ file_name="creep_predictions.csv",
371
+ mime="text/csv"
372
+ )
373
+
374
+ st.markdown('</div>', unsafe_allow_html=True)
375
+
376
+ except Exception as e:
377
+ st.error(f"❌ Prediction failed: {e}")
378
+
379
+ # Simple footer
380
+ st.markdown("""
381
+ <div style='text-align: center; padding: 2rem 0; color: #9CA3AF;'>
382
+ <p>🏗️ Concrete Creep Prediction Tool</p>
383
+ <p style='margin-top: 0.5rem; font-size: 14px;'>Developed by <strong>CIFIR</strong> and <strong>AI Research Group KMUTT</strong></p>
384
+ </div>
385
+ """, unsafe_allow_html=True)