dhani10 commited on
Commit
dc7794d
·
verified ·
1 Parent(s): 44a1e2b

Deploy Docker-based Streamlit app

Browse files
Files changed (4) hide show
  1. Dockerfile +16 -14
  2. README.md +5 -5
  3. requirements.txt +7 -7
  4. streamlit_app.py +116 -0
Dockerfile CHANGED
@@ -1,21 +1,23 @@
 
 
 
1
 
2
- FROM python:3.9-slim
 
3
 
 
4
  WORKDIR /app
5
 
6
- # Copy requirements and install dependencies
7
- COPY requirements.txt .
8
- RUN pip install --no-cache-dir -r requirements.txt
9
 
10
- # Copy application files
11
- COPY . .
 
12
 
13
- # Expose Streamlit port
14
- EXPOSE 8501
15
 
16
- # Health check
17
- HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
18
- CMD python -c "import requests; requests.get('http://localhost:8501/healthz')" || exit 1
19
-
20
- # Run Streamlit app
21
- CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
 
1
+ # Docker runtime for HF Space
2
+ # Use a slim Python base
3
+ FROM python:3.11-slim
4
 
5
+ # Basic hygiene
6
+ ENV PIP_NO_CACHE_DIR=1 PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
7
 
8
+ # Working directory
9
  WORKDIR /app
10
 
11
+ # Copy and install Python deps first (better layer caching)
12
+ COPY requirements.txt /app/
13
+ RUN pip install --upgrade pip && pip install -r requirements.txt
14
 
15
+ # Copy app code
16
+ COPY streamlit_app.py /app/
17
+ COPY README.md /app/
18
 
19
+ # Expose the port that the Space will connect to
20
+ EXPOSE 7860
21
 
22
+ # Run Streamlit on 0.0.0.0:7860
23
+ CMD ["streamlit", "run", "streamlit_app.py", "--server.port=7860", "--server.address=0.0.0.0"]
 
 
 
 
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
- title: Engine Condition App
3
- emoji: 🐠
4
- colorFrom: purple
5
  colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Predictive Maintenance App (Docker)
3
+ emoji: "🔧"
4
+ colorFrom: indigo
5
  colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  ---
9
+ This Space runs a Streamlit app inside a custom Docker image.
10
+ If the model repo is **private**, add a Space Secret **HF_TOKEN** (read token) and restart the Space.
requirements.txt CHANGED
@@ -1,7 +1,7 @@
1
- streamlit==1.28.0
2
- pandas==2.0.3
3
- numpy==1.24.3
4
- scikit-learn==1.3.0
5
- joblib==1.3.2
6
- huggingface_hub==0.19.0
7
- plotly==5.15.0
 
1
+ streamlit==1.39.0
2
+ pandas==2.2.2
3
+ numpy==2.0.2
4
+ scipy==1.13.1
5
+ scikit-learn==1.6.1
6
+ joblib==1.4.2
7
+ huggingface_hub==0.26.1
streamlit_app.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Streamlit UI that downloads a scikit-learn pipeline from HF
2
+ import os, sys, logging, joblib, numpy as np, pandas as pd
3
+ from huggingface_hub import hf_hub_download
4
+ from sklearn.exceptions import InconsistentVersionWarning
5
+ import warnings
6
+
7
+ warnings.filterwarnings("ignore", category=InconsistentVersionWarning)
8
+
9
+ # Silence noisy logs when not run via `streamlit run`
10
+ if "streamlit" not in " ".join(sys.argv).lower():
11
+ for name in ("streamlit.runtime.scriptrunner.script_run_context",
12
+ "streamlit.runtime.scriptrunner","streamlit"):
13
+ lg = logging.getLogger(name); lg.setLevel(logging.CRITICAL); lg.propagate=False; lg.disabled=True
14
+
15
+ HF_MODEL_REPO = os.getenv("HF_MODEL_REPO", "dhani10/engine-condition-model")
16
+ MODEL_FILE = os.getenv("MODEL_FILE", "model/best_engine_model.joblib")
17
+ HF_TOKEN = os.getenv("HF_TOKEN") # add as Space Secret if model repo is private
18
+
19
+ HF_CACHE_ROOT = os.getenv("HF_HOME", "/tmp/huggingface")
20
+ os.environ["HF_HOME"] = HF_CACHE_ROOT
21
+ os.environ["HF_HUB_CACHE"] = os.path.join(HF_CACHE_ROOT, "hub")
22
+ os.makedirs(os.environ["HF_HUB_CACHE"], exist_ok=True)
23
+
24
+ def _load_model_impl():
25
+ path = hf_hub_download(
26
+ repo_id=HF_MODEL_REPO,
27
+ filename=MODEL_FILE,
28
+ repo_type="model",
29
+ token=HF_TOKEN, # None if public
30
+ cache_dir=os.environ["HF_HUB_CACHE"],
31
+ )
32
+ return joblib.load(path)
33
+
34
+ def get_expected_input_columns(clf):
35
+ pre = getattr(getattr(clf, "named_steps", {}), "get", lambda *_: None)("preprocessor")
36
+ if pre is not None:
37
+ transformers = getattr(pre, "transformers_", getattr(pre, "transformers", []))
38
+ cols = []
39
+ for _, __, selected in transformers:
40
+ if selected in (None, "drop"): continue
41
+ if isinstance(selected, list): cols.extend(selected)
42
+ elif hasattr(selected, "__iter__"): cols.extend(list(selected))
43
+ cols = list(dict.fromkeys(cols))
44
+ if cols: return cols
45
+ fni = getattr(clf, "feature_names_in_", None)
46
+ return list(fni) if fni is not None else [
47
+ "engine_rpm","lub_oil_pressure","fuel_pressure",
48
+ "coolant_pressure","lub_oil_temp","coolant_temp"
49
+ ]
50
+
51
+ def coerce_numeric_df(df: pd.DataFrame) -> pd.DataFrame:
52
+ out = df.copy()
53
+ for c in out.columns: out[c] = pd.to_numeric(out[c], errors="ignore")
54
+ return out
55
+
56
+ def predict_with_pipeline(model, X: pd.DataFrame):
57
+ y = model.predict(X); p = None
58
+ if hasattr(model, "predict_proba"):
59
+ try:
60
+ P = model.predict_proba(X); p = P[:,1] if (P.ndim==2 and P.shape[1]>=2) else P.ravel()
61
+ except Exception: pass
62
+ return y, p
63
+
64
+ def main():
65
+ import streamlit as st
66
+ st.set_page_config(page_title="Engine Condition Predictor", layout="centered")
67
+ st.title("Predictive Maintenance — Engine Condition")
68
+ st.caption(f"Model: {HF_MODEL_REPO} → {MODEL_FILE}")
69
+
70
+ @st.cache_resource(show_spinner=True)
71
+ def load_model(): return _load_model_impl()
72
+
73
+ model = load_model()
74
+ EXPECTED_COLS = get_expected_input_columns(model)
75
+
76
+ with st.form("predict_form"):
77
+ col1, col2 = st.columns(2)
78
+ with col1:
79
+ engine_rpm = st.number_input("Engine RPM", min_value=0, max_value=5000, value=1200, step=10)
80
+ lub_oil_pressure = st.number_input("Lubricating Oil Pressure (bar)", value=3.0, step=0.1)
81
+ fuel_pressure = st.number_input("Fuel Pressure (bar)", value=5.0, step=0.1)
82
+ with col2:
83
+ coolant_pressure = st.number_input("Coolant Pressure (bar)", value=2.0, step=0.1)
84
+ lub_oil_temp = st.number_input("Lubricating Oil Temperature (°C)", value=80.0, step=0.1)
85
+ coolant_temp = st.number_input("Coolant Temperature (°C)", value=75.0, step=0.1)
86
+ submitted = st.form_submit_button("Predict")
87
+
88
+ if submitted:
89
+ row = pd.DataFrame({c:[np.nan] for c in EXPECTED_COLS})
90
+ for k,v in {
91
+ "engine_rpm":engine_rpm,"lub_oil_pressure":lub_oil_pressure,"fuel_pressure":fuel_pressure,
92
+ "coolant_pressure":coolant_pressure,"lub_oil_temp":lub_oil_temp,"coolant_temp":coolant_temp
93
+ }.items():
94
+ if k in row.columns: row.at[0,k]=v
95
+ try:
96
+ X = coerce_numeric_df(row)
97
+ y, p = predict_with_pipeline(model, X)
98
+ pred = int(y[0])
99
+ if pred==1:
100
+ msg = "⚠️ Faulty Engine Detected"
101
+ if p is not None: msg += f" (Confidence: {float(p[0]):.2f})"
102
+ import streamlit as st; st.error(msg)
103
+ else:
104
+ msg = "✅ Engine is Healthy"
105
+ if p is not None: msg += f" (Confidence: {1 - float(p[0]):.2f})"
106
+ import streamlit as st; st.success(msg)
107
+ with st.expander("Inputs sent to the model"):
108
+ st.dataframe(X)
109
+ except Exception as e:
110
+ import streamlit as st
111
+ st.error(f"Prediction failed: {e}")
112
+ st.write("Expected columns:", EXPECTED_COLS)
113
+
114
+ if __name__ == "__main__":
115
+ if "streamlit" in " ".join(sys.argv).lower(): main()
116
+ else: print("Tip: run this app with: streamlit run streamlit_app.py")