What you will be able to do
- Structure a custom model as an mlflow.pyfunc.PythonModel with load_context and predict
- Package files such as weights and tokenizer caches into the model with the artifacts parameter and read them through context.artifacts
- Declare dependencies so that Model Serving can build the container, including the mlflow versus mlflow-skinny pitfall
- Register a signed custom model in Unity Catalog under a three-level name and validate it before deployment
Key concept
Self-contained pyfunc model — A custom model that can be served on Databricks has to carry everything it needs in the logged MLflow model: its Python code, its artifact files and its declared dependencies. The serving container is built from that package alone, so anything left outside it is missing at deployment time.
1.Why pyfunc, and the two methods it requires
Databricks uses the term *custom model* for any Python model or custom code that Model Serving deploys as a production API. You can log a custom model with a built-in MLflow flavor such as sklearn. You need pyfunc when a built-in flavor can't express what you want. Typical cases are preprocessing that has to run before the model's predict function, a framework that MLflow doesn't support natively, outputs that need post-processing, per-request branching logic, or code that is entirely custom.
You write a pyfunc model as a subclass of mlflow.pyfunc.PythonModel. It has two required methods, and the split between them affects performance.
load_context runs once. Load anything the model needs only once to operate here, such as weights, tokenizers and lookup objects. Databricks notes that doing so minimises what has to be loaded during predict, which speeds up inference. predict holds the logic that runs every time a request arrives. Helper methods for formatting input and output are ordinary class methods that predict calls. In the example below, load_context reads the weights and tokenizer paths from context.artifacts, and predict chains format, inference and post-processing.
class CustomModel(mlflow.pyfunc.PythonModel):
def load_context(self, context):
self.model = torch.load(context.artifacts["model-weights"])
from preprocessing_utils.my_custom_tokenizer import CustomTokenizer
self.tokenizer = CustomTokenizer(context.artifacts["tokenizer_cache"])
def format_inputs(self, model_input):
# insert some code that formats your inputs
pass
def format_outputs(self, outputs):
predictions = (torch.sigmoid(outputs)).data.numpy()
return predictions
def predict(self, context, model_input):
model_input = self.format_inputs(model_input)
outputs = self.model.predict(model_input)
return self.format_outputs(outputs)Checkpoint 1 of 5· Check yourself
Which method of an mlflow.pyfunc.PythonModel should construct a tokenizer that the model needs only once to operate?
load_context is the required method for one-time setup. predict runs on every request.
“anything that needs to be loaded just one time for the model to operate should be defined in this function.”Source: docs.databricks.com
2.Logging custom artifacts with the model
The context.artifacts lookups in that class only work if the files were packaged when the model was logged. In a Databricks notebook those files often live in Unity Catalog volumes. Some models also download items from the internet at load time, for example Hugging Face tokenizers. Databricks says real-time workloads at scale perform best when all dependencies are captured statically at deployment time. For that reason, Model Serving does not let a model read from a volume path at serving time. The files have to be copied into the model artifact itself. Network artifacts should also be packaged whenever possible.
You do this with the artifacts parameter of log_model. It's a dictionary that maps a name of your choice to a source path.
mlflow.pyfunc.log_model(
...
artifacts={'model-weights': "/Volumes/catalog/schema/volume/path/to/file", "tokenizer_cache": "./tokenizer_cache"},
...
)At load time the dictionary keys become your handles. context.artifacts["model-weights"] resolves to the packaged copy of the file, and you open it the usual way for that file type. Because each key in artifacts must match a lookup in load_context, keep the names identical in both places.
Checkpoint 2 of 5· Check yourself
A pyfunc model's load_context opens /Volumes/main/ml/weights/model.pt directly by its volume path, and nothing was passed to the artifacts parameter. What does Databricks require before this model is served?
Model Serving needs files from Unity Catalog volumes to be packaged into the model artifact through MLflow interfaces. The model then reaches them through context.artifacts.
“Model Serving requires that Unity Catalog volumes artifacts are packaged into the model artifact itself using MLflow interfaces.”Source: docs.databricks.com
Sources3
3.Declaring code and library dependencies
Artifacts cover files. Libraries and shared code are declared separately. During deployment, Databricks builds a production container from the logged model. The base image may include some system-level dependencies, but application-level dependencies must be listed explicitly in the MLflow model. Native flavors capture their packages automatically. For custom pyfunc models, you declare them yourself.
| Parameter | Use it for |
|---|---|
| pip_requirements | The full list of pip packages the model needs |
| extra_pip_requirements | Packages in addition to the ones captured automatically |
| conda_env | A complete conda environment definition |
| code_path | Local modules, such as preprocessing_utils/, that the model imports |
code_path is how the earlier CustomModel can import preprocessing_utils.my_custom_tokenizer on the endpoint. The folder is logged with the model and added to the import path when the model loads.
One trap is specific to Databricks Runtime ML. Those runtimes ship mlflow-skinny rather than the full mlflow package. If you log a pyfunc model there without pip_requirements, MLflow records mlflow-skinny in conda.yaml. Model Serving then can't build the container image, because it requires mlflow. The fix is to pin mlflow explicitly.
Checkpoint 3 of 5· Fill the gap
On a Databricks Runtime ML cluster, which parameter pins the full mlflow package so that Model Serving can build the container?
mlflow.pyfunc.log_model(
name="model",
python_model=your_model,
? =["mlflow==3.8.1"], # use mlflow, not mlflow-skinny
registered_model_name="catalog.schema.model_name",
)Databricks says to always specify mlflow==<version> in pip_requirements on DBR ML. Without it, mlflow-skinny ends up in conda.yaml and the image build fails.
Source: docs.databricks.com4.Registering in Unity Catalog and validating before you deploy
Once the model is logged, register it. Unity Catalog is recommended over the legacy workspace registry. Unity Catalog adds four requirements:
- A signature. New model versions in Unity Catalog must have a model signature. You can create one with infer_signature or by using Databricks autologging. With MLflow 2.5.0 and above, you can also pass an input_example to log_model, and MLflow infers the signature from it.
- The right registry. In MLflow 3 the default registry URI is databricks-uc. On older setups, call mlflow.set_registry_uri("databricks-uc") first.
- A three-level name in the form <catalog>.<schema>.<model>. You can pass it as registered_model_name in log_model (as in the snippet above), or call register_model afterwards.
- Privileges. To create a new registered model you need CREATE MODEL and USE SCHEMA on the schema, plus USE CATALOG on the catalog. To add versions to an existing model, you must own it or hold CREATE MODEL VERSION on it, plus USE SCHEMA and USE CATALOG.
mlflow.register_model("models:/{model_id}", "{registered_model_name}")Deploying a new version takes time. Databricks quotes about 10 minutes for packaging and provisioning, and longer for complex models. Before you deploy, Databricks recommends checking that the model can be served by validating it with mlflow.models.predict. If deployment does fail with dependency errors, Databricks recommends testing the model locally.
Checkpoint 4 of 5· Check yourself
A pyfunc model was logged with no signature and no input example. What happens when you try to register a new version of it in Unity Catalog?
Unity Catalog requires a signature on every new model version. You can supply one directly, or let MLflow infer it from an input_example.
“New ML model versions in UC must have a model signature.”Source: docs.databricks.com
Checkpoint 5 of 5· Exam question
A team wraps a scikit-learn preprocessing step and a downstream heuristic rule inside a single object before logging it for real-time serving. Which MLflow class should the object subclass so that `predict()` receives the raw request payload and can run this custom logic?
Correct answer: A — `mlflow.pyfunc.PythonModel`, implementing a `predict` method that accepts the context and the model input and returns the result.
- A. Subclassing `PythonModel` and implementing `predict(self, context, model_input)` is the documented pattern for wrapping arbitrary preprocessing and business logic as a custom pyfunc model that Model Serving can invoke.
- B. MLflow has no `SklearnModel` class with a `transform` method; the sklearn flavor logs an estimator directly and does not provide a hook for arbitrary combined preprocessing and rule logic.
- C. There is no `EvaluationModel` class in MLflow; evaluation harnesses consume an already-logged model rather than acting as the model implementation itself.
- D. `mlflow.entities.Model` is a metadata record describing a logged model version, not a base class with a `run` method that executes inference logic.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Logging a pyfunc model on Databricks Runtime ML without pip_requirements is fine, because MLflow captures its own package automatically.Why is that wrong?
DBR ML ships mlflow-skinny, which MLflow then records in conda.yaml. Model Serving needs the full mlflow package and can't build the image without it, so pin mlflow==<version> in pip_requirements.
Covered in Declaring code and library dependencies
2.A served model can keep reading its weights from a Unity Catalog volume path at inference time.Why is that wrong?
Files from volumes must be packaged into the model artifact with the artifacts parameter and read through context.artifacts.
Covered in Logging custom artifacts with the model
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://docs.databricks.com/aws/en/machine-learning/model-serving/deploy-custom-python-codeOfficial docs
“anything that needs to be loaded just one time for the model to operate should be defined in this function.”
↩︎ Why pyfunc, and the two methods it requires“authors of models can log full code references that load into the path”
↩︎ Declaring code and library dependencies“it is beneficial to verify that the model is capable of being served.”
↩︎ Registering in Unity Catalog and validating before you deploy“Model Serving requires mlflow (not mlflow-skinny) in conda.yaml and cannot build the container image otherwise.”
↩︎ Exam trap 1“predict - this function houses all the logic that is run every time an input request is made.”
↩︎ Prediction - 2.
“Model Serving can deploy any Python model or custom code as a production-grade API using CPU or GPU compute resources.”
↩︎ Why pyfunc, and the two methods it requires“application-level dependencies must be explicitly specified in your MLflow model.”
↩︎ Declaring code and library dependencies“This container includes libraries automatically captured or specified in the MLflow model.”
↩︎ Key concept - 3.https://docs.databricks.com/aws/en/machine-learning/model-serving/model-serving-custom-artifactsOfficial docs
“In PyFunc models, these artifacts' paths are accessible from the context object under context.artifacts”
↩︎ Logging custom artifacts with the model“With the MLflow command log_model() you can log a model and its dependent artifacts with the artifacts parameter.”
↩︎ Logging custom artifacts with the model“Model Serving requires that Unity Catalog volumes artifacts are packaged into the model artifact itself using MLflow interfaces.”
↩︎ Exam trap 2“Model Serving requires that Unity Catalog volumes artifacts are packaged into the model artifact itself using MLflow interfaces.”
↩︎ Checkpoint - 4.
“you need the CREATE MODEL and USE SCHEMA privileges on the enclosing schema, and USE CATALOG privilege on the enclosing catalog”
↩︎ Registering in Unity Catalog and validating before you deploy“Then, pass the three-level name of the model to MLflow APIs, in the form <catalog>.<schema>.<model>.”
↩︎ Registering in Unity Catalog and validating before you deploy“New ML model versions in UC must have a model signature.”
↩︎ Checkpoint