Home Wiki AI Fundamentals Deploying ML in the Factory: From Jupyter Notebook to Real Production
AI Fundamentals

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.

deployment API model-export monitoring MLOps production النشر واجهة البرمجة تصدير النموذج المراقبة الإنتاج التشغيل المستمر