Project: churn model saved & served with joblib

Time to put everything together. You work for a prepaid telecom operator in Jaipur that loses customers every month. The business wants a weekly list of subscribers likely to churn so the retention team can offer a recharge discount.

1. Frame the problem

Item Decision
Target churned = 1 if no recharge in the next 30 days
Unit One row per subscriber per snapshot date
Features Tenure, average recharge (INR), data usage trend, complaints, days since last recharge, circle
Metric PR-AUC, plus recall in the top 10% of scores
Split Train on older snapshots, test on the latest month (time-based)

2. Build a single pipeline

Keeping preprocessing and the model in one object means exactly the same transformations run in training and in production.

import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import average_precision_score

df = pd.read_csv("churn_snapshots.csv", parse_dates=["snapshot_date"])
num = ["tenure_months", "avg_recharge_inr", "data_gb_trend", "complaints_90d", "days_since_recharge"]
cat = ["circle", "plan_type"]

train = df[df.snapshot_date < "2026-06-01"]
test = df[df.snapshot_date >= "2026-06-01"]

pre = ColumnTransformer([
    ("cat", OneHotEncoder(handle_unknown="ignore", min_frequency=20), cat),
], remainder="passthrough")

pipe = Pipeline([
    ("pre", pre),
    ("model", HistGradientBoostingClassifier(learning_rate=0.05, max_iter=400,
                                             class_weight="balanced", random_state=42)),
])
pipe.fit(train[num + cat], train["churned"])
proba = pipe.predict_proba(test[num + cat])[:, 1]
print("PR-AUC:", round(average_precision_score(test["churned"], proba), 3))

Check the business metric too: sort test rows by proba, take the top 10%, and measure what share of actual churners they contain. That tells the retention team how many calls convert into saved customers.

3. Save the model with joblib

import joblib, sklearn

joblib.dump(
    {"pipeline": pipe, "features": num + cat, "threshold": 0.42,
     "sklearn_version": sklearn.__version__, "trained_until": "2026-05-31"},
    "models/churn_v3.joblib", compress=3,
)

Store metadata with the model. Pickled scikit-learn objects are only reliably loadable with the same library versions, so pin them in requirements.txt. Never joblib.load a file from an untrusted source, because loading a pickle can execute arbitrary code.

4. Serve it from Django

Load the model once when the module is imported, not on every request.

# churn/views.py
import json
import joblib
import pandas as pd
from django.http import JsonResponse
from django.views.decorators.http import require_POST

BUNDLE = joblib.load("models/churn_v3.joblib")

@require_POST
def predict_churn(request):
    rows = json.loads(request.body)["subscribers"]
    X = pd.DataFrame(rows)[BUNDLE["features"]]
    proba = BUNDLE["pipeline"].predict_proba(X)[:, 1]
    return JsonResponse({"results": [
        {"id": r["id"], "churn_prob": round(float(p), 3), "at_risk": bool(p >= BUNDLE["threshold"])}
        for r, p in zip(rows, proba)
    ]})

For a weekly batch list you often do not need an API at all: a management command that scores all subscribers and writes a CSV or a database table is simpler and cheaper.

Key takeaways

  • Split by time when the model will predict the future.
  • One pipeline object from raw columns to prediction avoids training/serving skew.
  • Version your model files and log their inputs and outputs to monitor drift.
  • The same recipe works for demand forecasting: swap the target for next week's units sold and use a regressor.

Practice

Rebuild this project as a demand forecast for a sweets shop chain: predict next week's kilograms sold per store and item using lag features (last 1, 2 and 4 weeks) and a festival flag. Save the pipeline with joblib and write a Django management command that loads it and prints forecasts for all stores.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India