mukherjee78's picture
Upload app.py with huggingface_hub
875d8b4 verified
"""
Predictive Maintenance β€” Streamlit Application
Loads the best trained model (Decision Tree, Random Forest, Logistic Regression, or Gradient Boosting)
from local path or Hugging Face Model Hub and provides a web interface for engine failure prediction.
"""
import os
import joblib
import pandas as pd
import streamlit as st
from huggingface_hub import hf_hub_download
# ─── Config ───
MODEL_REPO_ID = "mukherjee78/predictive-maintenance-random-forest"
MODEL_FILENAME = "best_model.pkl"
LOCAL_MODEL_PATH = os.environ.get("MODEL_PATH", "models/best_model.pkl")
# Feature order must match training data (data_preparation.py / model_training.py)
FEATURE_COLUMNS = [
"engine_rpm",
"lub_oil_pressure",
"fuel_pressure",
"coolant_pressure",
"lub_oil_temp",
"coolant_temp",
]
# ─── Page Configuration ───
st.set_page_config(
page_title="Engine Predictive Maintenance",
page_icon="βš™οΈ",
layout="centered",
)
# ─── Load Model (local file or Hugging Face) ───
# Loaded lazily on first prediction so the Space UI appears immediately (avoids startup hang).
@st.cache_resource
def load_model():
"""Load model from LOCAL_MODEL_PATH if present, else from Hugging Face Model Hub."""
try:
if os.path.isfile(LOCAL_MODEL_PATH):
return joblib.load(LOCAL_MODEL_PATH)
model_path = hf_hub_download(
repo_id=MODEL_REPO_ID,
filename=MODEL_FILENAME,
repo_type="model",
)
return joblib.load(model_path)
except Exception as e:
st.error(f"Failed to load model: {e}")
st.info("Ensure the model exists at Hugging Face or set MODEL_PATH to a local file.")
raise
# ─── App Header ───
st.title("βš™οΈ Engine Predictive Maintenance")
st.markdown(
"Enter engine sensor readings below to predict whether the engine "
"requires **maintenance** or is operating under **normal** conditions."
)
st.divider()
# ─── Input Form ───
st.subheader("Sensor Readings")
col1, col2 = st.columns(2)
with col1:
engine_rpm = st.number_input(
"Engine RPM",
min_value=0, max_value=3000, value=700, step=10,
help="Rotational speed of the engine in revolutions per minute"
)
lub_oil_pressure = st.number_input(
"Lub Oil Pressure (bar)",
min_value=0.0, max_value=10.0, value=3.0, step=0.1,
help="Lubricating oil pressure in bar"
)
fuel_pressure = st.number_input(
"Fuel Pressure (bar)",
min_value=0.0, max_value=50.0, value=12.0, step=0.5,
help="Fuel supply pressure in bar"
)
with col2:
coolant_pressure = st.number_input(
"Coolant Pressure (bar)",
min_value=0.0, max_value=10.0, value=2.5, step=0.1,
help="Coolant system pressure in bar"
)
lub_oil_temp = st.number_input(
"Lub Oil Temperature (Β°C)",
min_value=0.0, max_value=200.0, value=80.0, step=1.0,
help="Lubricating oil temperature in degrees Celsius"
)
coolant_temp = st.number_input(
"Coolant Temperature (Β°C)",
min_value=0.0, max_value=200.0, value=82.0, step=1.0,
help="Coolant temperature in degrees Celsius"
)
st.divider()
# ─── Prediction ───
if st.button("πŸ” Predict Engine Condition", type="primary", use_container_width=True):
# Save inputs into a dataframe (column order must match training)
input_data = pd.DataFrame(
[[engine_rpm, lub_oil_pressure, fuel_pressure, coolant_pressure, lub_oil_temp, coolant_temp]],
columns=FEATURE_COLUMNS,
)
# Display input summary
st.markdown("**Input Summary:**")
st.dataframe(input_data, use_container_width=True, hide_index=True)
# Load model on first use (so Space shows UI immediately; download happens here with spinner)
with st.spinner("Loading model..."):
try:
artifact = load_model()
except Exception:
st.stop()
# Unwrap artifact: either raw estimator (legacy) or dict with model + optional scaler
if isinstance(artifact, dict) and "model" in artifact:
model = artifact["model"]
scaler = artifact.get("scaler")
else:
model = artifact
scaler = None
# Apply scaler for Logistic Regression (features were scaled at training time)
X = input_data
if scaler is not None:
X = scaler.transform(input_data)
# Make prediction
try:
prediction = model.predict(X)[0]
probability = model.predict_proba(X)[0]
except Exception as e:
st.error(f"Prediction failed: {e}")
st.stop()
st.divider()
# Display result (1 = needs maintenance, 0 = normal)
if prediction == 1:
st.error("πŸ”΄ **Prediction: Engine Needs Maintenance**")
st.metric("Failure Probability", f"{probability[1]:.1%}")
st.warning(
"⚠️ The sensor readings indicate potential engine degradation. "
"Schedule maintenance to prevent unplanned downtime."
)
else:
st.success("🟒 **Prediction: Engine Operating Normally**")
st.metric("Normal Probability", f"{probability[0]:.1%}")
st.info(
"βœ… The engine appears to be operating within normal parameters. "
"Continue routine monitoring."
)
# ─── Footer ───
st.divider()
st.caption(
"Model: Best of Decision Tree / Random Forest / Logistic Regression / Gradient Boosting / XGBoost | "
"Source: [Hugging Face Model Hub](https://huggingface.co/mukherjee78/predictive-maintenance-random-forest)"
)