一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
简述
BentoML 是一个用于机器学习模型服务的开源框架,旨在弥合数据科学和 DevOps 之间的差距(gap)。
数据科学家可以使用 BentoMl 轻松打包使用任何 ML 框架训练的模型,并重现该模型以用于生产。
BentoML 协助管理 BentoML 格式打包的模型,并允许 DevOps 将它们部署为任何云平台上的在线 API 服务端点或离线批量推理作业。
为什么选择 BentoML
- 将您的 ML 模型转换为生产就绪 API 非常简单。
- 高性能模型服务,并且全部使用 Python。
- 标准化模型打包和 ML 服务定义以简化部署。
- 支持所有主流的机器学习训练框架。
- 通过Yatai在 Kubernetes 上大规模部署和运行 ML 服务。
下面将演示了如何使用 BentoML 通过 REST API 服务为 sklearn 模型提供服务,然后将模型服务容器化以进行生产部署。
依赖库安装
BentoML 需要 Python 3.6 或更高版本,然后,通过 pip 安装依赖项:
# 安装本教程需要的依赖包,包括BentoML
pip install -q bentoml==v1.0.0-a6
pip install -q 'scikit-learn>=0.23.2' 'pandas>=1.1.1'
训练模型及保存模型
我们预先准备一个训练的模型来使用 BentoML 提供服务。 模型训练完成后,使用我们的工具将特定框架训练的模型保存为 BentoML 标准格式。
首先,在鸢尾花(Iris)数据集上训练一个分类模型:
# train.py
from sklearn import svm
from sklearn import datasets
import bentoml
# Load training data
iris = datasets.load_iris()
X, y = iris.data, iris.target
# Model Training
clf = svm.SVC(gamma='scale')
clf.fit(X, y)
# 调用`bentoml.<FRAMEWORK>.save(<MODEL_NAME>, model)` 为了在本地模型存储中保存为 BentoML 的标准格式
bentoml.sklearn.save("iris_clf", clf)
运行结果:
$ python train.py
03/31/22 16:15:13 INFO [cli] Successfully saved Model(tag="iris_clf:wdqpqfvqzk32yhqa", path="/Users/liguodong/bentoml/models/iris_clf/wdqpqfvqzk32yhqa/")
bentoml.sklearn.save()会将 Iris 分类模型保存到由 BentoML 管理的本地模型存储中。 目前所有受支持的建模库,请参阅ML framework specific API。
查看 BentoML 本地模型仓库的模型
模型也可以通过 bentoml models CLI 命令进行管理。 更多信息请使用 bentoml models --help。
$ bentoml models list
Tag Module Path Size Creation Time
iris_clf:wdqpqfvqzk32yhqa bentoml.sklearn /Users/liguodong/bentoml/models/iris_clf/wdqpqfvqzk32yhqa 5.64 KiB 2022-03-31 08:15:13
然后,您可以使用bentoml.<FRAMEWORK>.load(<TAG>) 加载要在线运行的模型。同时,您也可以使用 bentoml.<FRAMEWORK>.load_runner(<TAG>) API 使用性能优化的运行器。
验证模型是否可以作为运行器加载
# verify.py
import bentoml
runner = bentoml.sklearn.load_runner("iris_clf:latest")
print(runner.run([5.9, 3., 5.1, 1.8])) # => array(2)
使用 BentoML 定义和调试 ML 预测服务
服务是 BentoML 的核心组件,其中定义了服务逻辑。
通过保存在模型仓库中的模型,我们可以创建 Python 文件 service.py 来定义服务,其内容如下:
# service.py
import numpy as np
import bentoml
from bentoml.io import NumpyNdarray
# 加载我们刚刚保存的最新 ScikitLearn 模型的运行器
iris_clf_runner = bentoml.sklearn.load_runner("iris_clf:latest")
# 使用 ScikitLearn 运行器创建 iris_classifier 服务
# 如果需要,可以在runners数组中指定多个运行器
# 当包装为bento时,运行器(runners)也会被包括在里面
svc = bentoml.Service("iris_classifier", runners=[iris_clf_runner])
# 使用“svc”注解创建具有预处理和后处理逻辑的 API 函数
@svc.api(input=NumpyNdarray(), output=NumpyNdarray())
def classify(input_series: np.ndarray) -> np.ndarray:
# 定义预处理逻辑
result = iris_clf_runner.run(input_series)
# 定义后处理逻辑
return result
在此示例中,我们将输入和输出类型定义为 numpy.ndarray。 还支持更多选项,例如 pandas.DataFrame 和 PIL.image。 要查看所有支持的选项,请参阅API 和 IO 描述。
到此为止,我们现在拥有了满足第一个请求所需要的一切。
在本地启动一个 API server 进行测试
我们通过在当前工作目录中运行 bentoml serve 命令以调试模式启动服务。使用 --reload 选项允许服务显示对 service.py 模块所做的任何更改,而无需重新启动(热更新):
$ bentoml serve ./service.py:svc --reload
03/31/22 17:00:54 INFO [cli] Starting development BentoServer from "./service.py:svc" running on http://127.0.0.1:3000 (Press CTRL+C to quit)
03/31/22 17:00:55 INFO [dev_api_server] Service imported from source: bentoml.Service(name="iris_classifier", import_str="service:svc",
working_dir="/Users/liguodong/work/gitee/sample-all-py3/mlops/bentoml/bento_deploy")
03/31/22 17:00:55 INFO [dev_api_server] Will watch for changes in these directories: ['/Users/liguodong/work/gitee/sample-all-py3/mlops/bentoml/bento_deploy']
03/31/22 17:00:55 INFO [dev_api_server] Started reloader process [54356] using statreload
03/31/22 17:00:56 INFO [cli] Started server process [54359]
03/31/22 17:00:56 INFO [cli] Waiting for application startup.
03/31/22 17:00:56 INFO [cli] Application startup complete.
03/31/22 17:05:36 WARNING [dev_api_server] StatReload detected file change in 'service.py'. Reloading...
03/31/22 17:05:36 INFO [cli] Shutting down
03/31/22 17:05:36 INFO [cli] Waiting for application shutdown.
03/31/22 17:05:36 INFO [cli] Application shutdown complete.
03/31/22 17:05:36 INFO [cli] Finished server process [54359]
03/31/22 17:05:37 INFO [cli] Started server process [54372]
03/31/22 17:05:37 INFO [cli] Waiting for application startup.
03/31/22 17:05:37 INFO [cli] Application startup complete.
03/31/22 17:08:13 INFO [cli] 127.0.0.1:56051 (scheme=http,method=POST,path=/classify,type=application/json,length=9) (status=200,type=application/json,length=1) 3.123ms
(trace=108811182904080858968430329246303376687,span=3358824710013532286,sampled=0)
然后,我们可以使用任何 HTTP 客户端向新启动的服务发送请求。比如,通过python http request 请求:
# http_request.py
import requests
print(requests.post(
"http://127.0.0.1:3000/classify",
headers={"content-type": "application/json"},
data="[5,4,3,2]").text)
或者通过curl请求:
> curl \
-X POST \
-H "content-type: application/json" \
--data "[5,4,3,2]" \
http://127.0.0.1:3000/classify
BentoML 可以以多种方式优化您的服务,例如,BentoML 使用了两个最快的 Python Web 框架 Starlette 和 Uvicorn,以便大规模高效地为您的模型提供服务。
有关性能优化的更多信息,请参阅 BentoServer。
构建和部署 Bentos
一旦我们对服务定义感到满意,我们就可以将模型和服务构建成bento。 Bentos 是服务的分发格式,包含运行或部署这些服务所需的所有信息,例如模型和依赖项。 有关构建bento的更多信息,请参阅Building Bentos。
要构建 Bento,首先在项目目录中创建一个名为 bentofile.yaml 的文件:
# bentofile.yaml
service: "service.py:svc" # 定位服务的约定:<YOUR_SERVICE_PY>:<YOUR_SERVICE_ANNOTATION>
description: "file: ./README.md"
labels:
owner: bentoml-team
stage: demo
include:
- "*.py" # 用于匹配要包含在bento中的文件的格式
python:
packages:
- scikit-learn==0.23.2 # bento中将包含的其他的依赖库
- pandas>=1.1.1
接下来,在同样的目录中的bentoml build CLI 命令来构建一个bento。
$ bentoml build
03/31/22 17:41:06 INFO [cli] Building BentoML service "iris_classifier:v7eedwvq22c5ahqa" from build context
"/Users/liguodong/work/gitee/sample-all-py3/mlops/bentoml/bento_deploy"
03/31/22 17:41:06 INFO [cli] Packing model "iris_clf:wdqpqfvqzk32yhqa" from "/Users/liguodong/bentoml/models/iris_clf/wdqpqfvqzk32yhqa"
03/31/22 17:41:06 INFO [cli] Successfully saved Model(tag="iris_clf:wdqpqfvqzk32yhqa",
path="/var/folders/_7/b59dvhj56lq3lr4jpsx2h7t80000gn/T/tmpfekkt4b3bentoml_bento_iris_classifier/models/iris_clf/wdqpqfvqzk32yhqa/")
03/31/22 17:41:06 INFO [cli] Locking PyPI package versions..
03/31/22 17:47:00 INFO [cli]
██████╗░███████╗███╗░░██╗████████╗░█████╗░███╗░░░███╗██╗░░░░░
██╔══██╗██╔════╝████╗░██║╚══██╔══╝██╔══██╗████╗░████║██║░░░░░
██████╦╝█████╗░░██╔██╗██║░░░██║░░░██║░░██║██╔████╔██║██║░░░░░
██╔══██╗██╔══╝░░██║╚████║░░░██║░░░██║░░██║██║╚██╔╝██║██║░░░░░
██████╦╝███████╗██║░╚███║░░░██║░░░╚█████╔╝██║░╚═╝░██║███████╗
╚═════╝░╚══════╝╚═╝░░╚══╝░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═╝╚══════╝
03/31/22 17:47:00 INFO [cli] Successfully built Bento(tag="iris_classifier:v7eedwvq22c5ahqa") at "/Users/liguodong/bentoml/bentos/iris_classifier/v7eedwvq22c5ahqa/"
构建的Bentos将保存在本地bento商店中,您可以使用bentoml list CLI 命令查看。
$ bentoml list
Tag Service Path Size Creation Time
iris_classifier:v7eedwvq22c5ahqa service:svc /Users/liguodong/bentoml/bentos/iris_classifier/v7eedwvq22c5ahqa 15.21 KiB 2022-03-31 09:47:00
我们可以使用bentoml serve --production CLI 命令从bento商店提供bentos服务。其中,--production 选项将在生产模式下提供bento服务。
$ bentoml serve iris_classifier:veshoxfq3wzyqhqa --production
03/31/22 18:02:41 INFO [cli] Service loaded from Bento store: bentoml.Service(tag="iris_classifier:v7eedwvq22c5ahqa",
path="/Users/liguodong/bentoml/bentos/iris_classifier/v7eedwvq22c5ahqa")
03/31/22 18:02:41 INFO [cli] Starting production BentoServer from "bento_identifier" running on http://0.0.0.0:3000 (Press CTRL+C to quit)
03/31/22 18:02:42 INFO [api_server] Service loaded from Bento store: bentoml.Service(tag="iris_classifier:v7eedwvq22c5ahqa",
path="/Users/liguodong/bentoml/bentos/iris_classifier/v7eedwvq22c5ahqa")
03/31/22 18:02:42 INFO [iris_clf] Service loaded from Bento store: bentoml.Service(tag="iris_classifier:v7eedwvq22c5ahqa",
path="/Users/liguodong/bentoml/bentos/iris_classifier/v7eedwvq22c5ahqa")
03/31/22 18:02:42 INFO [iris_clf] Started server process [54672]
03/31/22 18:02:42 INFO [iris_clf] Waiting for application startup.
03/31/22 18:02:42 INFO [api_server] Started server process [54673]
03/31/22 18:02:42 INFO [api_server] Waiting for application startup.
03/31/22 18:02:42 INFO [api_server] Application startup complete.
03/31/22 18:02:45 INFO [iris_clf] Application startup complete.
Bento 目录说明
Bento 目录包含运行此服务所需的所有代码、文件、模型和配置。 BentoML 标准化了这个文件结构,使服务运行时和部署工具能够建立在它之上。
默认情况下,Bentos 在~/bentoml/bentos 目录下进行管理:
$ cd ~/bentoml/bentos/iris_classifier && cd $(cat latest)
$ tree
.
├── README.md
├── apis
│ └── openapi.yaml
├── bento.yaml
├── env
│ ├── docker
│ │ ├── Dockerfile
│ │ ├── entrypoint.sh
│ │ └── init.sh
│ └── python
│ ├── requirements.lock.txt
│ ├── requirements.txt
│ └── version.txt
├── models
│ └── iris_clf
│ ├── latest
│ └── y3f4ijcxj3i6zo6x2ie5eubhd
│ ├── model.yaml
│ └── saved_model.pkl
└── src
├── iris_classifier.py
└── train.py
8 directories, 14 files
将 Bento 容器化以进行部署
最后,我们可以使用bentoml containerize CLI 命令将 bentos 容器化为Docker 镜像,并使用模型和 bento 管理服务 来大规模管理bentos。
首先,需确保您已安装 docker 并运行了 docker deamon,以下命令将使用您的本地 docker 环境构建一个新的 docker 镜像,并包含来自此 Bento 的模型服务配置。
$ bentoml containerize iris_classifier:veshoxfq3wzyqhqa
# 运行容器测试镜像的构建
$ docker run -p 5000:5000 iris_classifier:invwzzsw7li6zckb2ie5eubhd
总结
BentoML 是一个开放平台,可简化 ML 模型部署并使您能够在几分钟内以生产规模为模型提供服务。