Deploying ML in the Factory: From Jupyter Notebook to Real Production
From Experiment to Production: The Real Challenge
Training a model in a Jupyter notebook is only the beginning. A model sitting in a notebook helps no one -- it needs to be accessible via an API, packaged in a container, and monitored for degradation. This lesson covers the complete path to a production service.
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import pickle
Exporting Models: pickle and ONNX
pickle: Python's Native Serialization
np.random.seed(42)
n = 5000
X = pd.DataFrame({
"thickness_mm": np.random.normal(3.0, 0.1, n),
"hardness_hrc": np.random.normal(58, 2, n),
"surface_um": np.random.exponential(1.2, n),
"temp_c": np.random.normal(70, 5, n)
})
y = ((X["thickness_mm"] < 2.8) | (X["hardness_hrc"] < 54) |
(X["surface_um"] > 4.0)).astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
with open("defect_model.pkl", "wb") as f:
pickle.dump(model, f)
print(f"Model accuracy: {model.score(X_test, y_test):.3f}")
ONNX: Cross-Platform Deployment
ONNX lets you train in Python and deploy in any runtime -- Rust, C++, or edge devices.
from skl2onnx import to_onnx
onnx_model = to_onnx(model, X_train.values[:1].astype(np.float32))
with open("defect_model.onnx", "wb") as f:
f.write(onnx_model.SerializeToString())
print(f"ONNX model size: {len(onnx_model.SerializeToString()) / 1024:.1f} KB")
Building an API With Flask or FastAPI
An API exposes your model as a network service that SCADA, MES, or dashboards can call.
# save as api_server.py
from fastapi import FastAPI
from pydantic import BaseModel
import pickle, numpy as np
app = FastAPI(title="Defect Detection API")
with open("defect_model.pkl", "rb") as f:
model = pickle.load(f)
class PartMeasurement(BaseModel):
thickness_mm: float
hardness_hrc: float
surface_um: float
temp_c: float
@app.post("/predict")
def predict(part: PartMeasurement):
features = np.array([[part.thickness_mm, part.hardness_hrc,
part.surface_um, part.temp_c]])
prediction = model.predict(features)[0]
confidence = float(model.predict_proba(features)[0].max())
return {"prediction": "FAIL" if prediction == 1 else "PASS",
"confidence": round(confidence, 3)}
@app.get("/health")
def health():
return {"status": "healthy", "model": "defect_detection_v1"}
uvicorn api_server:app --host 0.0.0.0 --port 8000
Docker Container for the Model
Docker packages your model, API, and dependencies into a container that runs identically everywhere.
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY defect_model.pkl .
COPY api_server.py .
EXPOSE 8000
CMD ["uvicorn", "api_server:app", "--host", "0.0.0.0", "--port", "8000"]
docker build -t defect-detector:v1 .
docker run -d -p 8000:8000 --name defect-api defect-detector:v1
Continuous Monitoring: Model Drift
Deployed models degrade over time as conditions change. Model drift detection prevents costly prediction failures.
from scipy import stats
def detect_drift(reference_data, new_data, threshold=0.05):
results = {}
for col in reference_data.columns:
stat, p_value = stats.ks_2samp(reference_data[col], new_data[col])
results[col] = {"p_value": round(p_value, 4),
"drift": p_value < threshold}
return results
new_batch = pd.DataFrame({
"thickness_mm": np.random.normal(3.05, 0.12, 500),
"hardness_hrc": np.random.normal(58, 2, 500),
"surface_um": np.random.exponential(1.8, 500),
"temp_c": np.random.normal(75, 5, 500)
})
drift = detect_drift(X_train, new_batch)
for feature, result in drift.items():
status = "DRIFT" if result["drift"] else "OK"
print(f"{feature:20s}: {status} (p={result['p_value']:.4f})")
Practical Example: Deploying a Defect Detection Model as a REST Service
Complete pipeline from training to production-ready deployment.
from sklearn.model_selection import cross_val_score
from sklearn.metrics import classification_report
np.random.seed(42)
n = 10000
X = pd.DataFrame({
"thickness_mm": np.random.normal(3.0, 0.1, n),
"hardness_hrc": np.random.normal(58, 2, n),
"surface_um": np.random.exponential(1.2, n),
"temp_c": np.random.normal(70, 5, n)
})
y = ((X["thickness_mm"] < 2.8) | (X["hardness_hrc"] < 54) |
(X["surface_um"] > 4.0)).astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42)
model = RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42)
model.fit(X_train, y_train)
cv_scores = cross_val_score(model, X, y, cv=5, scoring="f1")
print(f"CV F1: {cv_scores.mean():.3f} +/- {cv_scores.std():.3f}")
print(classification_report(y_test, model.predict(X_test)))
model_package = {
"model": model,
"feature_names": list(X.columns),
"version": "1.0.0",
"cv_f1_mean": float(cv_scores.mean())
}
with open("defect_model_v1.pkl", "wb") as f:
pickle.dump(model_package, f)
print("Production model exported with metadata.")
Summary
In this lesson you completed the journey from experiment to production. You exported models with pickle and ONNX for cross-platform deployment. You built a REST API with FastAPI, packaged it in Docker, and implemented drift detection to keep your model reliable over time. This completes the Dr. Machine industrial ML series -- you now have the tools to build, evaluate, and deploy machine learning solutions for real factory problems.