BentoML:统一格式打包与部署机器学习服务

FreeGuideOnline 最新 2026-06-14

引言:为什么要用 BentoML 统一打包?

在机器学习工程化落地过程中,模型训练只是其中一环。真正让模型产生业务价值的,是将它部署为稳定、可复现、易于扩展的服务。而不同的模型框架(如 PyTorch、TensorFlow、scikit-learn)、不同的依赖环境、不同的推理逻辑,使得打包与部署变得高度碎片化。

BentoML 正是为了解决这一痛点而生的开源框架。它提供了一套统一的标准,将模型、代码、依赖、配置打包成名为 Bento 的标准化格式。无论你使用何种框架,都可以通过 BentoML 一键生成容器镜像,快速部署为 REST API、gRPC 服务或离线批量推理任务。

本篇教程将带你从零开始,掌握 BentoML 模型打包的核心概念与实操步骤,让你轻松迈出模型上线部署的第一步。

环境准备

在开始之前,请确保 Python 版本为 3.8 或以上。推荐使用虚拟环境进行安装:

pip install bentoml

为了演示打包流程,我们还将安装一个示例模型框架 scikit-learn:

pip install scikit-learn

核心概念:什么是 Bento?

在 BentoML 中,所有的打包产物都围绕 Bento 展开。一个 Bento 是一个包含以下内容的标准化归档:

  • 模型文件(.pt, .pkl, .h5 等)
  • 自定义推理代码(使用 Runner 封装)
  • 服务定义(API 输入输出规范)
  • 依赖描述bentofile.yaml 或自动检测)
  • Dockerfile 模板(可自动生成容器镜像)

你可以把它理解为“模型的 Docker 镜像”,但它远比裸模型更完整,并且能与模型注册中心、CI/CD 流程无缝集成。

第一步:保存模型为 BentoML 模型

首先,我们需要将一个训练好的模型导入 BentoML 的模型管理系统。BentoML 兼容所有主流机器学习框架,通过统一的 save_model 接口将模型存储到本地模型库。

下面的例子使用 scikit-learn 训练一个简单的 Iris 分类器,并用 BentoML 保存:

import bentoml
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifier

# 训练模型
iris = datasets.load_iris()
X, y = iris.data, iris.target
clf = RandomForestClassifier()
clf.fit(X, y)

# 将模型保存到 BentoML 本地模型库
bentoml.sklearn.save_model(
    name="iris_classifier",      # 模型名称,用于后续引用
    model=clf,                   # 模型实例
    signatures={                 # 定义模型的输入输出类型,可选但强烈推荐
        "predict": {
            "batchable": True,
            "batch_dim": 0,
        }
    }
)

执行后,模型文件及相关元数据会被写入 BentoML 的本地模型目录(默认 ~/bentoml/models/)。使用 bentoml models list 命令可以查看所有已保存的模型。

什么是签名(Signatures)?

签名描述了模型推理方法的输入和输出数据格式,例如维度、批处理能力等。通过定义签名,BentoML 可以自动生成 API 文档、进行参数校验,并优化批处理性能。如果不手动设置,BentoML 会尝试自动推断。

第二步:创建推理服务(Service)

保存好模型后,接下来需要定义一个 BentoML Service。Service 是 Bento 的运行时载体,它利用 Runner 来高效运行模型,并将推理逻辑暴露为 HTTP API。

创建 service.py 文件:

import numpy as np
import bentoml
from bentoml.io import NumpyNdarray

# 引用刚刚保存的模型,得到一个 Runner 实例
iris_runner = bentoml.sklearn.get("iris_classifier:latest").to_runner()

# 创建一个 Service,并将其命名为 "iris_service"
svc = bentoml.Service("iris_service", runners=[iris_runner])

# 定义一个 API 端点
@svc.api(input=NumpyNdarray(), output=NumpyNdarray())
def predict(input_array: np.ndarray) -> np.ndarray:
    # 调用 runner 的 predict 方法
    result = iris_runner.predict.run(input_array)
    return result

关键点解析:

  • get("iris_classifier:latest"):从本地模型库获取最新版本的模型。可以指定版本标签(如 v1),latest 表示使用最新保存的版本。
  • to_runner():将模型转换为一个 Runner。Runner 负责模型的生命周期管理、加载优化和批处理调度。
  • svc.api(input=..., output=...):定义 API 的输入输出适配器。这里使用 NumpyNdarray,表示接受和返回 NumPy 数组。BentoML 有丰富的内建 IO 类型(如 Pandas DataFrame、JSON、Image、File 等)。
  • 推理函数内部,通过 runner_instance.predict.run(input_array) 执行真正的模型推理。Runner 的异步执行机制可以有效处理并发请求。

第三步:定义 Bento 的构建配置

为了让 Bento 包含所有必需的依赖和环境信息,我们需要创建一个描述文件 bentofile.yaml。这个文件是 Bento 构建的蓝图。

service: "service:svc"   # 指向 Service 实例的模块路径
labels:
  owner: my-team
  stage: dev
include:
  - "*.py"               # 包含所有 Python 文件
python:
  requirements_txt: "./requirements.txt"  # 指定依赖文件(可选)
  # 或者直接列出 packages:
  # packages:
  #   - scikit-learn
  #   - numpy
  #   - pandas
docker:
  base_image: "python:3.9-slim"

在你的项目目录下创建 requirements.txt

scikit-learn
numpy
bentoml

现在,所有源文件结构应该类似这样:

my_bento_project/
├── service.py
├── bentofile.yaml
├── requirements.txt
└── (其他需要包含的文件)

第四步:构建 Bento

一切就绪后,在项目根目录执行构建命令:

bentoml build

该命令会:

  1. 读取 bentofile.yaml,定位 Service。
  2. 将所有相关文件、Python 依赖、模型文件打包。
  3. 生成一个版本化的 Bento 包,存储在 ~/bentoml/bentos/ 下。

构建完成后,终端会显示类似信息:

Successfully built Bento(tag="iris_service:abc123...")

你可以通过 bentoml list 查看所有已构建的 Bento。

Bento 的内部结构

一个构建好的 Bento 实质上是一个包含标准化目录的文件集合,其内部结构大致如下:

.
├── env/               # 自动生成的 Python 虚拟环境或 Dockerfile
├── models/            # 模型文件存放处
│   └── iris_classifier/
├── src/               # 用户源代码
│   └── service.py
├── apis/              # API 定义元数据
└── bento.yaml         # Bento 元信息文件

这种结构确保了部署时的环境一致性。

第五步:在本地启动模型服务

构建完成即可直接本地运行,验证打包效果:

bentoml serve iris_service:latest

这会在 http://127.0.0.1:3000 启动一个基于 FastAPI 的开发服务器,并自动生成 Swagger UI 文档。你可以通过浏览器访问该地址,直接以 JSON 或 NumPy 格式测试预测端点。

第六步:容器化与部署

Bento 的终极价值在于能够“一键容器化”。执行以下命令可将 Bento 直接打包成符合 OCI 标准的 Docker 镜像:

bentoml containerize iris_service:latest

生成的镜像可直接推送至 Docker Hub 或任何私有镜像仓库,然后在 Kubernetes、AWS Lambda、Google Cloud Run 等平台上无缝部署。

如果需要更加定制化的 Docker 镜像,可以在 bentofile.yaml 中设置 docker 选项,或使用 bentoml containerize--opt 参数。

进阶技巧:多模型与自定义 Runner

当你的服务需要多个模型或复杂的推理流水线时,BentoML 同样游刃有余。你可以在 Service 中注册多个 Runner,并在 API 函数内组合调用。

# 假设还有另一个文本分类器 runner_text
text_runner = bentoml.pytorch.get("text_classifier:latest").to_runner()

svc = bentoml.Service("multi_model_service", runners=[iris_runner, text_runner])

@svc.api(input=JSON(), output=JSON())
def ensemble(input_data):
    # 分别调用两个模型,并将结果组合
    ...

Runner 还支持自适应微批处理(Adaptive Batching),这对于提升 GPU 利用率至关重要。只需在创建 Runner 时配置 bentoml.Runner(..., max_batch_size=32, max_latency_ms=100) 即可开启。

总结与最佳实践

通过本教程,你学会了使用 BentoML 将任意机器学习模型打包为标准化的 Bento,并快速生成 API 服务和 Docker 镜像。回顾整个流程:

  1. 使用 bentoml.<framework>.save_model(...) 保存模型并定义签名。
  2. 编写 service.py 创建 Service,并用 Runner 加载模型。
  3. 通过 bentofile.yaml 描述构建依赖和配置。
  4. 运行 bentoml build 生成 Bento 包。
  5. 使用 bentoml serve 本地测试,bentoml containerize 生成镜像部署。

最佳实践建议:

  • 总是为模型定义签名:清晰的输入输出规范能大幅减少部署调试时间。
  • 版本化管理模型和 Bento:利用 BentoML 的模型注册标签(如 productionstaging)来管理不同环境下的模型版本。
  • bentofile.yaml 纳入版本控制:它是 Bento 的基础设施即代码(IaC)描述。
  • 为生产环境使用 Docker:本地开发可以直接 serve,但生产部署推荐容器化后借助 Kubernetes 等编排工具,以获得更好的可靠性和伸缩性。

BentoML 统一了“从训练到生产”的最后一公里,让数据科学家和 MLOps 工程师能够用同一套工具链协作。现在,你可以开始将你的模型打包成 Bento,迈出模型服务化的第一步。