Spaces:
Sleeping
Sleeping
File size: 7,734 Bytes
5db4d23 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
import os
# Disable OpenMP
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
os.environ['OMP_NUM_THREADS'] = '1'
os.environ['OPENBLAS_NUM_THREADS'] = '1'
os.environ['MKL_NUM_THREADS'] = '1'
os.environ['VECLIB_MAXIMUM_THREADS'] = '1'
os.environ['NUMEXPR_NUM_THREADS'] = '1'
import streamlit as st
import torch
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import shap
from sklearn.preprocessing import MinMaxScaler
import plotly.graph_objects as go
import io
from matplotlib.figure import Figure
# Set page config
st.set_page_config(
page_title="Friction Angle Predictor",
page_icon="π",
layout="wide"
)
# Custom CSS to improve the app's appearance
st.markdown("""
<style>
.stApp {
max-width: 1200px;
margin: 0 auto;
}
.main {
padding: 2rem;
}
.stButton>button {
width: 100%;
}
</style>
""", unsafe_allow_html=True)
# Load the trained model and recreate the architecture
class Net(torch.nn.Module):
def __init__(self, input_size):
super(Net, self).__init__()
self.fc1 = torch.nn.Linear(input_size, 64)
self.fc2 = torch.nn.Linear(64, 1000)
self.fc3 = torch.nn.Linear(1000, 200)
self.fc4 = torch.nn.Linear(200, 8)
self.fc5 = torch.nn.Linear(8, 1)
self.dropout = torch.nn.Dropout(0.2)
# Initialize weights
self.apply(self._init_weights)
def _init_weights(self, module):
if isinstance(module, torch.nn.Linear):
torch.nn.init.xavier_uniform_(module.weight)
if module.bias is not None:
module.bias.data.zero_()
def forward(self, x):
x = torch.nn.functional.relu(self.fc1(x))
x = self.dropout(x)
x = torch.nn.functional.relu(self.fc2(x))
x = self.dropout(x)
x = torch.nn.functional.relu(self.fc3(x))
x = self.dropout(x)
x = torch.nn.functional.relu(self.fc4(x))
x = self.dropout(x)
x = self.fc5(x)
return x
@st.cache_resource
def load_model_and_data():
# Set device and random seeds
np.random.seed(32)
torch.manual_seed(42)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Load data
data = pd.read_excel("Data_syw.xlsx")
X = data.iloc[:, list(range(1, 17)) + list(range(21, 23))]
y = data.iloc[:, 28].values
# Calculate correlation and select features
correlation_with_target = abs(X.corrwith(pd.Series(y)))
selected_features = correlation_with_target[correlation_with_target > 0.1].index
X = X[selected_features]
# Initialize and fit scalers
scaler_X = MinMaxScaler()
scaler_y = MinMaxScaler()
scaler_X.fit(X)
scaler_y.fit(y.reshape(-1, 1))
# Load model
model = Net(input_size=len(selected_features)).to(device)
model.load_state_dict(torch.load('friction_model.pt'))
model.eval()
return model, X.columns, scaler_X, scaler_y, device, X
def predict_friction(input_values, model, scaler_X, scaler_y, device):
# Scale input values
input_scaled = scaler_X.transform(input_values)
input_tensor = torch.FloatTensor(input_scaled).to(device)
# Make prediction
with torch.no_grad():
prediction_scaled = model(input_tensor)
prediction = scaler_y.inverse_transform(prediction_scaled.cpu().numpy().reshape(-1, 1))
return prediction[0][0]
def calculate_shap_values(input_values, model, X, scaler_X, scaler_y, device):
def model_predict(X):
X_scaled = scaler_X.transform(X)
X_tensor = torch.FloatTensor(X_scaled).to(device)
with torch.no_grad():
scaled_pred = model(X_tensor).cpu().numpy()
return scaler_y.inverse_transform(scaled_pred.reshape(-1, 1)).flatten()
try:
# Use a smaller background dataset and fewer samples for stability
background = shap.kmeans(X.values, k=5) # Reduced from 10 to 5
explainer = shap.KernelExplainer(model_predict, background)
shap_values = explainer.shap_values(input_values.values, nsamples=100) # Added nsamples parameter
if isinstance(shap_values, list):
shap_values = np.array(shap_values[0])
return shap_values[0], explainer.expected_value
except Exception as e:
st.error(f"Error calculating SHAP values: {str(e)}")
# Return dummy values in case of error
return np.zeros(len(input_values.columns)), 0.0
def create_waterfall_plot(shap_values, feature_names, base_value, input_data):
# Create SHAP explanation object
explanation = shap.Explanation(
values=shap_values,
base_values=base_value,
data=input_data,
feature_names=list(feature_names)
)
# Create figure
fig = plt.figure(figsize=(12, 8))
shap.plots.waterfall(explanation, show=False)
plt.title('Local SHAP Value Contributions')
plt.tight_layout()
# Save plot to a buffer
buf = io.BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight', dpi=300)
plt.close(fig)
buf.seek(0)
return buf
def main():
st.title("π Friction Angle Predictor")
st.write("This app predicts the friction angle based on waste composition and characteristics.")
try:
# Load model and data
model, feature_names, scaler_X, scaler_y, device, X = load_model_and_data()
# Create two columns for input
col1, col2 = st.columns(2)
# Dictionary to store input values
input_values = {}
# Create input fields for each feature
for i, feature in enumerate(feature_names):
with col1 if i < len(feature_names)//2 else col2:
min_val = float(X[feature].min())
max_val = float(X[feature].max())
mean_val = float(X[feature].mean())
input_values[feature] = st.number_input(
f"{feature}",
min_value=min_val,
max_value=max_val,
value=mean_val,
help=f"Range: {min_val:.2f} to {max_val:.2f}"
)
# Create DataFrame from input values
input_df = pd.DataFrame([input_values])
if st.button("Predict Friction Angle"):
with st.spinner("Calculating prediction and SHAP values..."):
# Make prediction
prediction = predict_friction(input_df, model, scaler_X, scaler_y, device)
# Calculate SHAP values
shap_values, base_value = calculate_shap_values(input_df, model, X, scaler_X, scaler_y, device)
# Display results
st.header("Results")
col1, col2 = st.columns(2)
with col1:
st.metric("Predicted Friction Angle", f"{prediction:.2f}Β°")
with col2:
st.metric("Base Value", f"{base_value:.2f}Β°")
# Create and display waterfall plot
st.header("SHAP Waterfall Plot")
waterfall_plot = create_waterfall_plot(
shap_values=shap_values,
feature_names=feature_names,
base_value=base_value,
input_data=input_df.values[0]
)
st.image(waterfall_plot)
except Exception as e:
st.error(f"An error occurred: {str(e)}")
st.info("Please try refreshing the page. If the error persists, contact support.")
if __name__ == "__main__":
main()
|