ACA050 commited on
Commit
19abfdf
·
verified ·
1 Parent(s): ff75fc7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +150 -0
app.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ import os
4
+ import joblib
5
+ import gradio as gr
6
+ import matplotlib.pyplot as plt
7
+ import numpy as np
8
+ import pandas as pd
9
+ import shap
10
+ from sklearn.compose import ColumnTransformer
11
+ from sklearn.ensemble import RandomForestClassifier
12
+ from sklearn.pipeline import Pipeline
13
+ from sklearn.preprocessing import OneHotEncoder, StandardScaler
14
+
15
+ # =====================================================================================
16
+ # PART 1: MODEL CREATION AND LOADING (Self-contained for Hugging Face)
17
+ # This part creates, trains, and saves a mock model if one doesn't exist.
18
+ # This ensures the app is fully reproducible in any environment.
19
+ # =====================================================================================
20
+
21
+ MODEL_FILE = "machine_failure_model.joblib"
22
+
23
+ def create_and_train_model():
24
+ """Creates, trains, and saves a mock model pipeline."""
25
+ # Mock data that resembles the predictive maintenance dataset
26
+ mock_features = pd.DataFrame({
27
+ 'Type': ['L', 'M', 'H', 'L', 'M', 'H', 'L', 'M', 'H', 'L'],
28
+ 'Air temperature [K]': [298.1, 298.2, 298.3, 298.4, 299.0, 299.5, 300.1, 301.0, 302.5, 303.0],
29
+ 'Process temperature [K]': [308.6, 308.7, 308.8, 308.9, 309.1, 309.8, 310.5, 311.0, 312.0, 313.5],
30
+ 'Rotational speed [rpm]': [1551, 1428, 1455, 1600, 1750, 2000, 2200, 2500, 2850, 1300],
31
+ 'Torque [Nm]': [42.8, 46.3, 40.0, 50.1, 55.2, 60.0, 65.5, 70.0, 75.0, 35.0],
32
+ 'Tool wear [min]': [0, 5, 10, 15, 25, 50, 80, 120, 180, 210]
33
+ })
34
+ # Mock target: 0 = No Failure, 1 = Failure
35
+ mock_target = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 0])
36
+
37
+ # Define preprocessing steps for different column types
38
+ numeric_features = ['Air temperature [K]', 'Process temperature [K]', 'Rotational speed [rpm]', 'Torque [Nm]', 'Tool wear [min]']
39
+ categorical_features = ['Type']
40
+
41
+ preprocessor = ColumnTransformer(
42
+ transformers=[
43
+ ('num', StandardScaler(), numeric_features),
44
+ ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
45
+ ])
46
+
47
+ # Create the full pipeline with preprocessing and a classifier
48
+ model_pipeline = Pipeline(steps=[
49
+ ('preprocessor', preprocessor),
50
+ ('classifier', RandomForestClassifier(n_estimators=50, random_state=42))
51
+ ])
52
+
53
+ # Train the model
54
+ model_pipeline.fit(mock_features, mock_target)
55
+
56
+ # Save the trained model to a file
57
+ joblib.dump(model_pipeline, MODEL_FILE)
58
+ print(f"Model trained and saved to {MODEL_FILE}")
59
+ return model_pipeline
60
+
61
+ # Check if the model file exists; if not, create it.
62
+ if not os.path.exists(MODEL_FILE):
63
+ loaded_model = create_and_train_model()
64
+ else:
65
+ loaded_model = joblib.load(MODEL_FILE)
66
+ print(f"Model loaded from {MODEL_FILE}")
67
+
68
+
69
+ # =====================================================================================
70
+ # PART 2: BACKEND LOGIC (Prediction and SHAP Calculation)
71
+ # =====================================================================================
72
+
73
+ def predict_failure(Type, air_temperature, process_temperature, rotational_speed, torque, tool_wear):
74
+ """Predicts machine failure and calculates SHAP values using the loaded model."""
75
+ input_data = pd.DataFrame({
76
+ 'Type': [Type], 'Air temperature [K]': [air_temperature],
77
+ 'Process temperature [K]': [process_temperature], 'Rotational speed [rpm]': [rotational_speed],
78
+ 'Torque [Nm]': [torque], 'Tool wear [min]': [tool_wear]
79
+ })
80
+
81
+ preprocessor = loaded_model.named_steps['preprocessor']
82
+ classifier = loaded_model.named_steps['classifier']
83
+ input_processed = preprocessor.transform(input_data)
84
+ probability = classifier.predict_proba(input_processed)[:, 1]
85
+
86
+ explainer = shap.TreeExplainer(classifier)
87
+ shap_values = explainer.shap_values(input_processed)
88
+ feature_names = preprocessor.get_feature_names_out()
89
+
90
+ # SHAP values for the "Failure" class (index 1)
91
+ shap_val_failure = shap_values[1][0]
92
+ base_val_failure = explainer.expected_value[1]
93
+
94
+ return probability[0], shap_val_failure, feature_names, base_val_failure
95
+
96
+
97
+ # =====================================================================================
98
+ # PART 3: FRONTEND LOGIC (Plotting and Gradio Interface)
99
+ # =====================================================================================
100
+
101
+ def generate_shap_plot(shap_values, feature_names, base_value):
102
+ """Generates a SHAP waterfall plot for the Gradio interface."""
103
+ plt.close('all') # Ensure plots don't stack in memory
104
+ explanation = shap.Explanation(
105
+ values=shap_values, base_values=base_value, feature_names=feature_names
106
+ )
107
+ fig, _ = plt.subplots()
108
+ shap.waterfall_plot(explanation, max_display=10, show=False)
109
+ plt.tight_layout()
110
+ return fig
111
+
112
+ def predict_and_generate_plot(Type, air_temperature, process_temperature, rotational_speed, torque, tool_wear):
113
+ """Wrapper function that connects the backend prediction to the frontend plot."""
114
+ probability, shap_values, feature_names, base_value = predict_failure(
115
+ Type, air_temperature, process_temperature, rotational_speed, torque, tool_wear
116
+ )
117
+ shap_plot = generate_shap_plot(shap_values, feature_names, base_value)
118
+ return f"{probability:.2%}", shap_plot # Format probability as percentage
119
+
120
+ # Define the Gradio interface layout and components
121
+ with gr.Blocks(theme=gr.themes.Soft()) as iface_with_shap:
122
+ gr.Markdown("# Machine Failure Prediction with Live SHAP Analysis")
123
+ gr.Markdown("Adjust the sliders to see the real-time probability of machine failure and how each feature's value contributes to the prediction.")
124
+ with gr.Row():
125
+ with gr.Column(scale=1):
126
+ gr.Markdown("### Input Features")
127
+ type_input = gr.Dropdown(label="Type", choices=['L', 'M', 'H'], value='L')
128
+ air_temp_input = gr.Slider(minimum=295, maximum=305, value=300, label="Air temperature [K]")
129
+ proc_temp_input = gr.Slider(minimum=305, maximum=315, value=310, label="Process temperature [K]")
130
+ rpm_input = gr.Slider(minimum=1000, maximum=3000, value=1500, label="Rotational speed [rpm]")
131
+ torque_input = gr.Slider(minimum=5, maximum=80, value=40, label="Torque [Nm]")
132
+ wear_input = gr.Slider(minimum=0, maximum=250, value=100, label="Tool wear [min]")
133
+ inputs = [type_input, air_temp_input, proc_temp_input, rpm_input, torque_input, wear_input]
134
+
135
+ with gr.Column(scale=2):
136
+ gr.Markdown("### Prediction Outputs")
137
+ probability_output = gr.Textbox(label="Probability of Machine Failure")
138
+ plot_output = gr.Plot(label="Feature Contribution to Failure (SHAP Waterfall Plot)")
139
+
140
+ # Connect the inputs to the function and outputs
141
+ for input_comp in inputs:
142
+ input_comp.change(
143
+ fn=predict_and_generate_plot,
144
+ inputs=inputs,
145
+ outputs=[probability_output, plot_output]
146
+ )
147
+
148
+ # Launch the application
149
+ if __name__ == "__main__":
150
+ iface_with_shap.launch(debug=True)