CertSafari
    Databricks Certified Machine Learning Professional· Lessons

    Domain 3 · Lesson 11/11

    Custom PyFunc Models: Log Artifacts and Register in Unity Catalog

    Custom Model Serving

    10 min read
    9.09% of exam
    4 sources
    Published 3 Oct 2026
    Docs as of 30 Sep 2026

    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.

    A custom pyfunc model: one-time loading in load_context, per-request logic in predictpython
    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?

    Sources12

    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.

    Packaging a Unity Catalog volume file and a local tokenizer cache into the modelpython
    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?

    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.

    Ways to declare what a logged model needs
    ParameterUse it for
    pip_requirementsThe full list of pip packages the model needs
    extra_pip_requirementsPackages in addition to the ones captured automatically
    conda_envA complete conda environment definition
    code_pathLocal 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",
    )

    Sources21

    4.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.

    Registering a logged model by its model ID (MLflow 3 form)python
    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?

    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?

    Sources41

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 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. 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. 1.
      “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. 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. 3.
      “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. 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

    Continue to page 2 of 2

    Deploy and Query Custom Model Serving Endpoints

    Spotted a mistake, or was something unclear? Tell us.