跳到主要内容

模型签名和输入示例

模型签名和输入示例是定义模型如何使用的基础组件,可确保 MLflow 生态系统中的交互一致且可靠。

什么是模型签名和输入示例?

模型签名 - 定义模型输入、输出和参数的预期格式。可以将其视为一份契约,它精确地指定了模型期望什么数据以及将返回什么数据。

模型输入示例 - 提供有效模型输入的具体示例。这有助于开发人员理解所需的数据格式,并验证模型是否正常工作。

Model signatures comparison

它们为何重要

模型签名和输入示例提供以下关键优势

  • 一致性:确保所有模型交互都遵循相同的数据格式
  • 验证:在数据格式错误到达模型之前捕获它们
  • 文档:作为模型使用的实时文档
  • 部署安全性:使 MLflow 部署工具能够自动验证请求
  • UI 集成:允许 MLflow UI 显示清晰的模型要求
Databricks Unity Catalog 要求

在 Databricks Unity Catalog 中注册模型需要模型签名。 Unity Catalog 对所有已注册的模型强制执行具体的类型定义,并会拒绝没有适当签名的模型。在记录计划在 Databricks 环境中注册的模型时,请始终包含签名。

# ✅ Required for Databricks registration
mlflow.sklearn.log_model(
model,
name="my_model",
input_example=X_sample, # Generates required signature
signature=signature, # Or provide explicit signature
)

# ❌ Will fail in Databricks Unity Catalog
mlflow.sklearn.log_model(model, name="my_model") # No signature

快速入门:为模型添加签名

添加签名的最简单方法是在记录模型时提供输入示例

import mlflow
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
import pandas as pd

# Load data and train model
iris = load_iris(as_frame=True)
X = iris.data
y = iris.target
model = RandomForestClassifier().fit(X, y)

with mlflow.start_run():
# The input example automatically generates a signature
mlflow.sklearn.log_model(
model, name="iris_model", input_example=X.iloc[[0]] # First row as example
)

MLflow 自动

  1. 从输入示例推断签名
  2. 验证模型是否适用于该示例
  3. 将签名和示例与模型一起存储
自动签名推断

在模型记录期间提供 input_example 时,MLflow 会自动生成模型签名。这适用于所有模型风格,是大多数用例的推荐方法。

理解模型签名

模型签名由三个组件组成

定义模型期望的数据结构和类型

# Column-based signature (DataFrames)
input_schema = Schema(
[
ColSpec("double", "sepal_length"),
ColSpec("double", "sepal_width"),
ColSpec("string", "species", required=False), # Optional field
]
)

# Tensor-based signature (NumPy arrays)
input_schema = Schema(
[TensorSpec(np.dtype(np.float32), (-1, 28, 28, 1))] # Batch of 28x28 images
)

主要特性:支持表格(DataFrame)和张量(NumPy)数据,使用 required=False 的可选字段,以及包括数组和对象在内的丰富数据类型支持。

签名类型概述

MLflow 支持两种主要签名类型

基于列的签名 - 用于表格数据(DataFrames、字典)

# Perfect for traditional ML models
{"feature_1": 1.5, "feature_2": "category_a", "feature_3": [1, 2, 3]}

基于张量的签名 - 用于数组数据(图像、音频、嵌入)

# Perfect for deep learning models
np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [1, 2, 3]]]) # Shape: (2, 2, 3)

模型签名的类型提示

版本兼容性

类型提示支持在 MLflow 2.20.0 中引入。如果您使用的是早期版本的 MLflow,请参阅使用签名部分。

您可以使用 Python 类型提示自动定义模型签名并启用数据验证。这提供了一种更符合 Python 风格的方式来指定模型的接口,同时获得自动验证和模式推断。

类型提示快速入门

import mlflow
from typing import List, Dict, Optional
import pydantic


class Message(pydantic.BaseModel):
role: str
content: str
metadata: Optional[Dict[str, str]] = None


class CustomModel(mlflow.pyfunc.PythonModel):
def predict(self, model_input: List[Message]) -> List[str]:
# Signature automatically inferred from type hints!
return [msg.content for msg in model_input]


# Log model - signature is auto-generated from type hints
with mlflow.start_run():
mlflow.pyfunc.log_model(
name="chat_model",
python_model=CustomModel(),
input_example=[
{"role": "user", "content": "Hello"}
], # Validates against type hints
)

主要优势

  • 自动验证:运行时根据类型提示验证输入数据
  • 模式推断:从类型注解自动生成模型签名
  • 类型安全:在类型不匹配到达模型之前捕获它们
  • IDE 支持:在开发过程中提供更好的自动完成和错误检测
  • 文档:类型提示作为自文档代码
  • 一致性:PythonModel 实例和加载的 PyFunc 模型具有相同的验证

何时使用类型提示

✅ 推荐用于:复杂数据结构(聊天消息、工具定义、嵌套对象)、需要严格输入验证的模型、使用现代 Python 开发实践的团队以及具有结构化输入的 GenAI 和 LLM 应用程序。

⚠️ 考虑替代方案用于:简单表格数据(DataFrames 使用输入示例即可正常工作)、没有采用类型提示的遗留代码库以及具有高度动态输入结构的模型。

类型提示最佳实践

开发工作流

# ✅ Recommended pattern
class MyModel(mlflow.pyfunc.PythonModel):
def predict(self, model_input: List[MyPydanticModel]) -> List[str]:
# Clear type annotations
# Automatic validation
# Good IDE support
return [process(item) for item in model_input]

关键准则

  • 使用 Pydantic 模型处理复杂数据结构
  • 在 Pydantic 模型中为可选字段设置默认值
  • 使用类型提示时不要传递显式 signature 参数
  • 始终提供与您的类型提示匹配的输入示例
  • 当您希望在没有显式类型的情况下保持灵活性时,请使用 TypeFromExample
  • 部署前在本地测试验证
重要说明
  • 使用类型提示时切勿传递显式 signature 参数 - MLflow 将使用推断的签名,如果它们不匹配则会发出警告
  • 联合类型变为 AnyType - 使用 Pydantic 鉴别联合进行正确验证
  • TypeFromExample 和遗留类型提示需要输入示例

数据类型和示例

基本类型

Python 到 MLflow 类型映射

类型限制

这些类型仅支持标量定义或一维数组。不允许混合类型。

Python 类型MLflow 类型示例说明
str字符串"hello world"
intlong4264 位整数
np.int32integernp.int32(42)32 位整数
浮点数double3.1415964 位浮点数
np.float32浮点数np.float32(3.14)32 位浮点数
boolbooleanTrue
np.bool_booleannp.bool_(True)NumPy 布尔值
datetimedatetimepd.Timestamp("2023-01-01")
bytesbinaryb"binary data"
bytearraybinarybytearray(b"data")
np.bytes_binarynp.bytes_(b"data")NumPy 字节

复合类型

数组(列表/NumPy 数组)

{
"simple_list": ["a", "b", "c"],
"nested_array": [[1, 2], [3, 4], [5, 6]],
"numpy_array": np.array([1.1, 2.2, 3.3]),
}

对象(字典)

{"user_profile": {"name": "Alice", "age": 30, "preferences": ["sports", "music"]}}

可选字段

# Include None values to make fields optional
pd.DataFrame(
{
"required_field": [1, 2, 3],
"optional_field": [1.0, None, 3.0], # This becomes optional
}
)

兼容性说明

版本兼容性

版本要求

  • 数组和对象类型:需要 MLflow ≥ 2.10.0
  • Spark ML 向量:需要 MLflow ≥ 2.15.0
  • AnyType:需要 MLflow ≥ 2.19.0

签名强制执行和验证

Signature enforcement process

MLflow 在以下情况下自动根据您的模型签名验证输入

  • 将模型加载为 PyFunc (mlflow.pyfunc.load_model)
  • 使用 MLflow 部署工具
  • 通过 MLflow 的 REST API 服务模型

验证规则

输入验证

  • 必填字段:必须存在,否则验证失败
  • 可选字段:可以缺失,不会出错
  • 额外字段:被忽略(未传递给模型)
  • 类型转换:在可能的情况下应用安全转换

参数验证

  • 类型检查:参数必须与指定类型匹配
  • 形状验证:列表参数验证形状是否正确
  • 默认值:当未提供参数时应用
  • 未知参数:生成警告但不失败

处理常见问题

具有缺失值的整数列

# ❌ Problem: Integer column with NaN becomes float, causing type mismatch
df = pd.DataFrame({"int_col": [1, 2, None]}) # Becomes float64

# ✅ Solution: Define as double from the start
df = pd.DataFrame({"int_col": [1.0, 2.0, None]}) # Stays float64

类型转换示例

# ✅ Safe conversions (allowed)
int → long # 32-bit to 64-bit integer
int → double # Integer to float
float → double # 32-bit to 64-bit float

# ❌ Unsafe conversions (rejected)
long → double # Potential precision loss
string → int # No automatic parsing

使用签名

自动签名推断

最简单的方法 - 提供一个输入示例

import mlflow
from sklearn.ensemble import RandomForestClassifier

# Train your model
model = RandomForestClassifier().fit(X_train, y_train)

with mlflow.start_run():
mlflow.sklearn.log_model(
model,
name="my_model",
input_example=X_train.iloc[[0]], # Signature inferred automatically
)

手动创建签名

为了更好地控制,明确创建签名

from mlflow.models import ModelSignature
from mlflow.types.schema import Schema, ColSpec

# Define input schema
input_schema = Schema(
[
ColSpec("double", "feature_1"),
ColSpec("string", "feature_2"),
ColSpec("long", "feature_3", required=False), # Optional
]
)

# Define output schema
output_schema = Schema([ColSpec("double", "prediction")])

# Create signature
signature = ModelSignature(inputs=input_schema, outputs=output_schema)

# Log with explicit signature
with mlflow.start_run():
mlflow.sklearn.log_model(model, name="my_model", signature=signature)

签名推断助手

为自定义工作流使用 infer_signature

from mlflow.models import infer_signature

# Generate predictions for signature inference
predictions = model.predict(X_test)

# Infer signature from data
signature = infer_signature(X_test, predictions)

# Log with inferred signature
with mlflow.start_run():
mlflow.sklearn.log_model(model, name="my_model", signature=signature)

输入示例详情

输入示例除了签名推断之外还有多种重要用途

输入示例的优势

  • 签名推断:自动生成模型签名
  • 模型验证:在记录期间验证模型是否正常工作
  • 依赖项检测:帮助识别所需的包
  • 文档:向开发人员展示正确的输入格式
  • 部署测试:验证 REST 端点负载格式

输入示例格式

import pandas as pd

# Single record example
single_record = pd.DataFrame(
[{"sepal_length": 5.1, "sepal_width": 3.5, "petal_length": 1.4, "petal_width": 0.2}]
)

# Multiple records example
batch_example = pd.DataFrame(
[
{"feature_1": 1.0, "feature_2": "A"},
{"feature_1": 2.0, "feature_2": "B"},
{"feature_1": 3.0, "feature_2": "C"},
]
)

# Log model with DataFrame example
mlflow.sklearn.log_model(model, name="model", input_example=single_record)

模型服务和部署

服务输入示例

MLflow 自动生成服务兼容的示例

# When you log a model with input_example
input_example = {"question": "What is MLflow?"}

with mlflow.start_run():
model_info = mlflow.pyfunc.log_model(
python_model=MyModel(), name="model", input_example=input_example
)

# MLflow creates two files:
# 1. input_example.json - Original format
# 2. serving_input_example.json - REST API format

生成的文件

文件内容目的
input_example.json{"question": "What is MLflow?"}原始输入格式
serving_input_example.json{"inputs": {"question": "What is MLflow?"}}REST 端点格式

验证服务示例

在部署前测试您的模型

from mlflow.models.utils import load_serving_example
from mlflow.models import validate_serving_input

# Load serving example
serving_example = load_serving_example(model_info.model_uri)

# Validate it works
result = validate_serving_input(model_info.model_uri, serving_example)
print(f"Validation result: {result}")

# Test with local server
# mlflow models serve --model-uri <model_uri>
# curl -X POST -H "Content-Type: application/json" \
# -d '<serving_example>' https://:5000/invocations

签名演练场和示例

通过我们的交互式示例探索签名行为

下载签名示例 Notebook

或直接查看示例:签名示例 Notebook

快速参考示例

from mlflow.models import infer_signature

# Simple dictionary
simple_dict = {"name": "Alice", "age": 30, "active": True}
print(infer_signature(simple_dict))
# → Schema: [name: string, age: long, active: boolean]

# With optional fields
optional_fields = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": None}, # email becomes optional
]
print(infer_signature(optional_fields))
# → Schema: [name: string, email: string (optional)]

# Arrays and nested objects
complex_data = {
"user": {"id": 123, "tags": ["premium", "beta"]},
"scores": [0.8, 0.9, 0.7],
}
print(infer_signature(complex_data))
# → Nested schema with arrays and objects

最佳实践和技巧

开发工作流

始终包含输入示例

# ✅ Good: Always provide examples
mlflow.sklearn.log_model(model, name="model", input_example=X_sample)

# ❌ Avoid: Logging without examples
mlflow.sklearn.log_model(model, name="model") # No signature or validation

测试您的签名

# Validate signature works as expected
signature = infer_signature(X_test, y_pred)
loaded_model = mlflow.pyfunc.load_model(model_uri)

# Test with your signature
try:
result = loaded_model.predict(X_test)
print("✅ Signature validation passed")
except Exception as e:
print(f"❌ Signature issue: {e}")

性能考量

对于大型 DataFrames

# Use a representative sample for input_example
large_df = pd.DataFrame(...) # 1M+ rows
sample_df = large_df.sample(n=100, random_state=42) # Representative sample

mlflow.sklearn.log_model(model, name="model", input_example=sample_df)

对于复杂对象

# Provide minimal but representative examples
minimal_example = {
"required_field": "example_value",
"optional_field": None, # Shows field is optional
"array_field": ["sample"], # Shows it's an array
}

常见陷阱

整数处理

# ❌ Problem: Integers with NaN become floats
df = pd.DataFrame({"int_col": [1, 2, None]}) # Type becomes float64

# ✅ Solution: Use consistent types
df = pd.DataFrame({"int_col": [1.0, 2.0, None]}) # Explicit float64

嵌套结构一致性

# ❌ Problem: Inconsistent nesting
inconsistent = [
{"level1": {"level2": "value"}},
{"level1": "direct_value"}, # Different structure
]

# ✅ Solution: Consistent structure
consistent = [
{"level1": {"level2": "value1"}},
{"level1": {"level2": "value2"}}, # Same structure
]

PythonModel 的类型提示(MLflow 2.20.0+)

from typing import Dict, List


class TypedModel(mlflow.pyfunc.PythonModel):
def predict(self, context, model_input: List[Dict[str, str]]) -> List[str]:
# Signature automatically inferred from type hints!
return [item["text"].upper() for item in model_input]

故障排除

常见错误消息

"缺少必填输入字段"

当您的模型期望输入数据中不存在的必填字段时,会发生此错误。

# Example: Model expects field "age" but input only has "name"
input_data = {"name": "Alice"} # Missing required "age" field

解决方案:在输入数据中包含所有必填字段,或者通过在输入示例中包含 None 值将字段标记为可选。

"无法将类型 X 转换为类型 Y"

当您尝试传递一种类型的数据,而签名期望另一种类型时,会发生这种情况。

# Example: Trying to pass string where integer expected
input_data = {"score": "85"} # String value
# But signature expects: {"score": 85} # Integer value

解决方案:修复您的输入数据类型以匹配签名,或者如果类型更改是故意的,则更新签名。

"张量形状不匹配"

当张量输入与签名中定义的预期形状不匹配时,会发生此错误。

# Example: Model expects shape (None, 784) but got (None, 28, 28)
input_tensor = np.random.random((10, 28, 28)) # Wrong shape
# But signature expects: (10, 784) # Flattened shape

解决方案:重塑您的输入数据以匹配预期的维度,或者如果形状要求已更改,则更新签名。

调试签名

使用这些技术诊断与签名相关的问题

# Inspect existing model signature
from mlflow.models.model import get_model_info

model_info = get_model_info(model_uri)
print("Current signature:")
print(model_info.signature)

# Compare with inferred signature
inferred = infer_signature(your_input_data)
print("Inferred signature:")
print(inferred)

# Check compatibility
if model_info.signature != inferred:
print("⚠️ Signatures don't match - consider updating")

其他资源