The registry decouples "which model is live" from your serving code. Load a model by stage, serve it behind a REST API, and swap versions without a redeploy.
Why: serving code should ask for "the Production churn-classifier," not a hard-coded run ID — so promoting a new version in the registry changes what serves WITHOUT a code change or redeploy. When: reference models by name and stage in every consumer. Where: this indirection is what makes safe, gradual model rollouts possible.
import mlflow.pyfunc
# Serving code references a NAME and STAGE, never a specific run:
model = mlflow.pyfunc.load_model("models:/churn-classifier/Production")
predictions = model.predict(input_df)
# Promote v4 to Production in the registry -> this line loads v4 next restart.
# No code change, no redeploy of the serving logic itself.Why: MLflow can stand up a REST endpoint for any logged model with one command, giving you a working inference server for testing or simple deployments. When: use mlflow models serve to smoke-test a model before wiring it into your own service. Where: the model’s saved environment is recreated, so serving matches training.
# Serve the Production model as a local REST API:
mlflow models serve -m "models:/churn-classifier/Production" -p 5001
# Call it:
curl http://localhost:5001/invocations \
-H "Content-Type: application/json" \
-d '{"dataframe_split": {"columns": ["age","balance"], "data": [[42, 1200]]}}'Why: MLflow wraps every model flavor in a common "pyfunc" interface (a predict method over a DataFrame), so your serving code is identical whether the model is sklearn, PyTorch, or custom — and you can package preprocessing INTO the model. When: use a custom pyfunc to bundle feature transforms with the model, eliminating training/serving skew. Where: one artifact that does preprocessing + prediction cannot drift apart.
import mlflow.pyfunc
class ChurnModel(mlflow.pyfunc.PythonModel):
def load_context(self, context):
import joblib
self.pipeline = joblib.load(context.artifacts["pipeline"])
def predict(self, context, model_input):
return self.pipeline.predict(model_input) # preprocessing + model together
# Logged with mlflow.pyfunc.log_model(...), it serves like any other model —
# preprocessing travels with it, so offline and online transforms can't diverge.