Course topics

By WebNest Studio

Python Tutorial

Saving, Serving and Next Steps in Machine Learning

A model is only useful when it can make predictions for real users. This final lesson shows how to save a trained pipeline with joblib, load it in another program, and serve predictions from a FastAPI endpoint — combining this module with the web module. It ends with the practices that keep models trustworthy in production and a roadmap into deep learning and working with large language models.

Saving and Loading Models

joblib.dump(pipeline, "model.joblib") saves the whole fitted pipeline — preprocessing and model — and joblib.load() restores it. Save the pipeline, not only the model, so production applies exactly the same preprocessing. Record the scikit-learn version and training date alongside it: loading a model with a different library version is not guaranteed to work. Only load model files you trust — joblib/pickle files can execute code when loaded. For cross-language serving, formats such as ONNX (skl2onnx) are an alternative.

ML in Production

  • Load the model once at start-up (a FastAPI lifespan handler), not on every request.
  • Validate inputs with a Pydantic model that mirrors the training features.
  • Log predictions and, later, the true outcomes to measure real-world accuracy.
  • Watch for data drift — when incoming data stops looking like the training data — and retrain regularly.
  • Check models for bias across groups of users before and after deployment.

Where to Go Next

  • Gradient boosting libraries — XGBoost, LightGBM, CatBoost: top performers on tabular data.
  • Deep learning — PyTorch (most popular in research and industry) or TensorFlow/Keras for images, audio, text and time series.
  • Large language models — calling LLM APIs from Python, embeddings for semantic search, retrieval-augmented generation (RAG) and agents.
  • MLOps — experiment tracking (MLflow), model registries, automated retraining and monitoring.
  • Practice on real datasets (Kaggle, UCI Machine Learning Repository) and build end-to-end projects.

Examples

Training, saving and loading a pipeline with joblib

Python
# train_model.py
import json
import joblib
import sklearn
from datetime import date
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

iris = load_iris(as_frame=True)
pipeline = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
pipeline.fit(iris.data, iris.target)

joblib.dump(pipeline, "iris_model.joblib")
metadata = {"sklearn_version": sklearn.__version__, "trained_on": str(date(2026, 9, 28)),
            "features": list(iris.data.columns), "classes": iris.target_names.tolist()}
json.dump(metadata, open("iris_model.json", "w"), indent=2)
print("saved model with features:", metadata["features"])

# later, in another program
loaded = joblib.load("iris_model.joblib")
sample = iris.data.iloc[[0, 75, 140]]
print("predictions:", [metadata["classes"][i] for i in loaded.predict(sample)])
print("same as original:", (loaded.predict(sample) == pipeline.predict(sample)).all())
Output
saved model with features: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
predictions: ['setosa', 'versicolor', 'virginica']
same as original: True

Serving predictions with FastAPI

Python
# app.py  (run with: fastapi dev app.py)
from contextlib import asynccontextmanager

import joblib
import pandas as pd
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field

CLASSES = ["setosa", "versicolor", "virginica"]
ml = {}

@asynccontextmanager
async def lifespan(app: FastAPI):
    ml["model"] = joblib.load("iris_model.joblib")      # load once at start-up
    yield
    ml.clear()

app = FastAPI(title="Iris classifier", lifespan=lifespan)

class Flower(BaseModel):
    sepal_length: float = Field(gt=0, le=10)
    sepal_width: float = Field(gt=0, le=10)
    petal_length: float = Field(gt=0, le=10)
    petal_width: float = Field(gt=0, le=10)

@app.post("/predict")
def predict(flower: Flower):
    row = pd.DataFrame([[flower.sepal_length, flower.sepal_width, flower.petal_length, flower.petal_width]],
                       columns=["sepal length (cm)", "sepal width (cm)", "petal length (cm)", "petal width (cm)"])
    probabilities = ml["model"].predict_proba(row)[0]
    best = int(probabilities.argmax())
    return {"species": CLASSES[best], "confidence": round(float(probabilities[best]), 3)}

with TestClient(app) as client:
    print(client.post("/predict", json={"sepal_length": 5.1, "sepal_width": 3.5,
                                        "petal_length": 1.4, "petal_width": 0.2}).json())
    print(client.post("/predict", json={"sepal_length": 6.7, "sepal_width": 3.0,
                                        "petal_length": 5.2, "petal_width": 2.3}).json())
    print(client.post("/predict", json={"sepal_length": -1, "sepal_width": 3.0,
                                        "petal_length": 5.2, "petal_width": 2.3}).status_code)
Output
{'species': 'setosa', 'confidence': 0.985}
{'species': 'virginica', 'confidence': 0.961}
422

Common Mistakes

  • Saving only the model and re-implementing preprocessing differently in production.
  • Loading the model inside the request handler, making every prediction slow.
  • Loading joblib/pickle files from untrusted sources.
  • Deploying once and never checking accuracy or data drift again.
  • Ignoring library versions, so a saved model fails to load after an upgrade.

Key Points to Remember

  • joblib.dump/load save and restore complete fitted pipelines.
  • Store metadata: library version, features, classes and training date.
  • Serve models from FastAPI: load once in lifespan, validate inputs with Pydantic.
  • Monitor predictions, drift and fairness; retrain as data changes.
  • Next steps: gradient boosting, PyTorch deep learning, LLM applications and MLOps.

Practice the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

Use your local project environment for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.