定义输入和输出类型¶
创建 BentoML 服务 (Service) 时,您需要指定服务 API 的输入和输出 (IO) 类型。这些类型是塑造服务 API 逻辑的核心,指导数据进出服务的流程。BentoML 支持 Python、Pydantic 中常用的大量数据类型,以及机器学习 (ML) 工作流程特有的类型。这确保了 BentoML 服务可以与不同的数据源和 ML 框架无缝集成。
本文档全面概述了 BentoML 中支持的服务 API 架构,并附有代码示例来说明其实现。
概览¶
BentoML 中支持的输入和输出类型包括:
标准 Python 类型:基本类型如
str
、int
、float
、boolean
、list
和dict
。Pydantic 字段类型:BentoML 将其类型支持扩展到 Pydantic 字段类型,为处理复杂数据架构提供了更结构化和经过验证的方法。
ML 特定类型:为了满足不同 ML 用例的需求,BentoML 支持
numpy.ndarray
、torch.Tensor
和tensorflow.Tensor
等类型来处理张量数据,pandas.DataFrame
用于处理表格数据,PIL.Image.Image
用于处理图像数据,以及pathlib.Path
用于文件路径引用。根输入 (Root input):BentoML 支持根输入,它允许 API 接受单个仅位置参数,而无需在请求负载中指定键。
您可以使用 Python 的类型注解来定义每个 API 端点的预期输入和输出类型。这不仅有助于根据指定的架构验证数据,还增强了代码的清晰度和可读性。类型注解在生成 API、BentoML 客户端 和 UI 组件中起着重要作用,确保与服务交互的一致性和可预测性。
此外,您可以使用 pydantic.Field
来设置参数的附加信息,例如默认值和描述。这提高了 API 的可用性并提供了基本文档。详见以下示例。
定义 API 架构¶
本节提供了 BentoML 中支持的不同 API 架构的示例。每个示例都展示了如何为特定用例定义输入和输出类型。
标准 Python 类型¶
Python 的标准类型,如字符串、整数、浮点数、布尔值、列表和字典,通常用于简单的数据结构。您可以轻松地将这些类型集成到您的服务中。下面是一个示例:
from pydantic import Field
import bentoml
@bentoml.service
class LanguageModel:
@bentoml.api
def generate(
self, prompt: str = Field(description="The prompt text"),
temperature: float = Field(default=0.0, description="A sampling temperature between 0 and 2"),
max_tokens: int = Field(default=1000, description="max tokens to use"),
) -> Generator[str, None, None]:
# Implementation of the language generation model
...
此示例使用 Python 的类型注解(例如 str
、float
和 int
)来指定每个参数的预期数据类型。它返回一个生成器,可以流式传输响应。pydantic.Field
可用于设置参数的默认值和提供描述。
示例和可空输入¶
您可以定义接受带有示例或可空字段的输入的 API。
要设置示例值,您可以使用 pydantic.Field
。
from pydantic import Field
import bentoml
@bentoml.service
class IrisClassifier:
@bentoml.api
def classify(self, input: np.ndarray = Field(examples=[[0.1, 0.4, 0.2, 1.0]]) -> np.ndarray:
...
要处理可空输入,您可以使用 Optional
。
from pydantic import Field
from typing import Optional
import bentoml
@bentoml.service
class LanguageModel:
@bentoml.api
def generate(
self, prompt: int = Field(description="The prompt text"),
temperature: Optional[float] = Field(default=None, description="A sampling temperature between 0 and 2"),
max_tokens: Optional[float] = Field(default=None, description="max tokens to use"),
) -> Generator[str, None, None]:
...
在 LanguageModel
类中,temperature
和 max_tokens
字段被标记为 Optional
,这意味着它们可以是 None
。在 BentoML 中使用 Optional
类型时,必须提供默认值(此处为 default=None
)。不支持通用联合类型。
Pydantic¶
Pydantic 模型支持更结构化且带验证的数据。当您的服务需要处理具有严格验证要求的复杂数据结构时,它们尤其有用。下面是一个示例:
from pydantic import BaseModel, Field
import bentoml
# Define a Pydantic model for structured data input
class AdsGenerationParams(BaseModel):
prompt: str = Field(description="The prompt text")
industry: str = Field(description="The industry the company belongs to")
target_audience: str = Field(description="Target audience for the advertisement")
temperature: float = Field(default=0.0, description="A sampling temperature between 0 and 2")
@bentoml.service
class AdsWriter:
@bentoml.api
def generate(self, params: AdsGenerationParams) -> str:
# Implementation logic
...
在上面的代码片段中,AdsGenerationParams
类是一个 Pydantic 模型,定义了输入数据的结构和验证规则。类中的每个字段都带有类型注解,并且可以包含默认值和描述。Pydantic 会自动根据 AdsGenerationParams
架构验证接收到的数据。如果数据不符合架构,在方法执行之前会引发错误。
您还可以直接在顶层将 Pydantic 模型用于 BentoML 服务 API,而无需将负载包裹在一个键内。
from pydantic import BaseModel, Field
import typing as t
import bentoml
class AdsGenerationParams(BaseModel):
prompt: str = Field(description="The prompt text")
industry: str = Field(description="The industry the company belongs to")
target_audience: str = Field(description="Target audience for the advertisement")
temperature: float = Field(default=0.0, description="A sampling temperature between 0 and 2")
@bentoml.service
class AdsWriter:
@bentoml.api(input_spec=AdsGenerationParams)
def generate(self, **params: t.Any) -> str:
# Access parameters from the request
prompt = params['prompt']
industry = params['industry']
target_audience = params['target_audience']
temperature = params['temperature']
# Use the parameters in your Service logic
# Implementation logic
...
在上面的代码片段中,来自传入请求的所有已验证和解析的字段作为存储在 params
字典中的关键字参数传递给 generate
方法。您可以直接通过 AdsGenerationParams
中定义的字段名称作为字典中的键来访问这些参数。
Pydantic 的 BaseModel
仅支持 Python 中的内置类型作为字段类型。您可以使用 bentoml.IODescriptor
代替 pydantic.BaseModel
来支持 numpy.ndarray
、pandas.DataFrame
和 torch.Tensor
等类型。
import bentoml
class MyInputParams(bentoml.IODescriptor):
data: np.ndarray[tuple[int], np.dtype[np.float16]]
文件¶
您可以使用 pathlib.Path
处理文件输入和输出。这对于处理音频、图像和文档等文件的服务非常有用。
这是一个简单的示例,接受一个 Path
对象作为输入,表示音频文件的路径。
from pathlib import Path
import bentoml
@bentoml.service
class WhisperX:
@bentoml.api
def to_text(self, audio: Path) -> str:
# Implementation for converting audio files to text
...
要将文件类型限制为特定格式,例如音频文件,您可以使用带有 Annotated
类型的 ContentType
验证器。例如,您可以让 API 方法仅接受 MP3 音频文件:
from pathlib import Path
from bentoml.validators import ContentType
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
import bentoml
@bentoml.service
class WhisperX:
@bentoml.api
def to_text(self, audio: Annotated[Path, ContentType("audio/mp3")]) -> str:
...
要输出带有路径的文件,您可以使用 context.temp_dir
为每个请求提供一个唯一的临时目录来存储输出文件。例如:
from pathlib import Path
import bentoml
@bentoml.service
class Vits:
@bentoml.api
def to_speech(self, text: str, context: bentoml.Context) -> Path:
# Example text-to-speech synthesis implementation
audio_bytes = self.tts.synthesize(text)
# Writing the audio bytes to a file in the temporary directory
with open(Path(context.temp_dir) / "output.mp3", "wb") as f:
f.write(audio_bytes)
# Returning the path to the generated audio file directly
return Path(context.temp_dir) / "output.mp3"
当方法返回指向生成文件的 Path
对象时,BentoML 会序列化此文件并将其包含在对客户端的响应中。
处理文件的更实际示例:
from pathlib import Path
from bentoml.validators import ContentType
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
import bentoml
@bentoml.service
class AppendStringToFile:
@bentoml.api()
def append_string_to_eof(
self,
context: bentoml.Context,
txt_file: Annotated[Path, ContentType("text/plain")],
input_string: str,
) -> Annotated[Path, ContentType("text/plain")]:
with open(txt_file, "a") as file:
file.write(input_string)
return txt_file
from bentoml.validators import ContentType
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
from PIL import Image as im
import bentoml
@bentoml.service
class PDFtoImage:
@bentoml.api
def pdf_first_page_as_image(
self,
pdf: Annotated[Path, ContentType("application/pdf")],
) -> Image:
from pdf2image import convert_from_path
pages = convert_from_path(pdf)
return pages[0].resize(pages[0].size, im.ANTIALIAS)
from pathlib import Path
from bentoml.validators import ContentType
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
import bentoml
@bentoml.service
class AudioSpeedUp:
@bentoml.api
def speed_up_audio(
self,
context: bentoml.Context,
audio: Annotated[Path, ContentType("audio/mpeg")],
velocity: float,
) -> Annotated[Path, ContentType("audio/mp3")]:
import os
from pydub import AudioSegment
output_path = os.path.join(context.temp_dir, "output.mp3")
sound = AudioSegment.from_file(audio)
sound = sound.speedup(velocity)
sound.export(output_path, format="mp3")
return Path(output_path)
如果您不想将临时文件保存到磁盘,可以将数据作为 bytes
而不是 pathlib.Path
返回,并带有适当注解的 ContentType
。这对于即时生成数据的服务非常有效。
张量¶
BentoML 支持各种张量类型,例如 numpy.ndarray
、torch.Tensor
和 tensorflow.Tensor
。此外,您可以使用 bentoml.validators,例如 bentoml.Shape
和 bentoml.DType
,来强制对张量输入进行特定的形状和数据类型限制。下面是一个示例:
import torch
from bentoml.validators import Shape, DType
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
from pydantic import Field
import bentoml
@bentoml.service
class IrisClassifier:
@bentoml.api
def classify(
self,
input: Annotated[torch.Tensor, Shape((1, 4)), DType("float32")]
= Field(description="A 1x4 tensor with float32 dtype")
) -> np.ndarray:
...
在此示例中:
classify
方法期望torch.Tensor
输入。Annotated
类型与Shape
和Dtype
验证器一起使用,以指定预期的张量形状应为(1, 4)
,数据类型为float32
。pydantic.Field
为输入参数提供了附加描述,以提高 API 的可读性。
表格数据¶
Pandas DataFrame 常用于处理机器学习中的表格数据。BentoML 支持 Pandas DataFrame 输入,并允许您使用验证器对其进行注解,以确保数据符合预期的结构。
下面是一个示例:
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
import pandas as pd
from bentoml.validators import DataframeSchema
import bentoml
@bentoml.service
class IrisClassifier:
@bentoml.api
def classify(
self,
input: Annotated[pd.Dataframe, DataframeSchema(orient="records", columns=["petal_length", "petal_width"])
) -> int:
# Classification logic using the input DataFrame
...
在此示例中:
IrisClassifier
服务的classify
方法接受 Pandas DataFrame 作为输入。Annotated
类型与DataframeSchema
一起使用,以指定 DataFrame 的预期方向和列。orient="records"
表示 DataFrame 预期采用记录导向格式。columns=["petal_length", "petal_width"]
指定了 DataFrame 中预期的列。
DataframeSchema
验证器支持以下两种方向,它们决定了 API 接收数据时的结构方式:
records
:每行表示为一个字典,其中键是列名。columns
:数据按列组织,字典中的每个键代表一列,对应的值是列值的列表。
图像¶
BentoML 服务可以通过 PIL.Image.Image
和 pathlib.Path
处理图像。
您可以通过 PIL.Image.Image
直接传递图像对象。
from PIL import Image as im
from PIL.Image import Image
import bentoml
@bentoml.service
class ImageResize:
@bentoml.api
def generate(self, image: Image, height: int = 64, width: int = 64) -> Image:
size = height, width
return image.resize(size, im.LANCZOS)
您可以使用带有 ContentType
验证器的 pathlib.Path
来处理图像文件:
from pathlib import Path
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # Older than 3.9
from bentoml.validators import ContentType
import bentoml
@bentoml.service
class MnistPredictor:
@bentoml.api
def infer(self, input: Annotated[Path, ContentType('image/jpeg')]) -> int:
...
根输入¶
根输入是一种特殊的输入类型,它不需要在 API 请求体中指定键。相反,输入数据本身直接在请求中传递。这对于直接处理图像、音频或原始文本等二进制数据特别有用。
要定义根输入,请在 Python 中使用仅位置参数,在函数签名中用 /
表示。
重要事项
最多只能有一个仅位置参数(位于
/
之前的参数)。当您指定仅位置参数时,除了 bentoml.Context 之外,不允许使用其他参数。
示例实现:
from PIL import Image
import bentoml
@bentoml.service
class ImageProcessor:
@bentoml.api
def upload_image(self, image: Image.Image, /) -> int:
# Process the image and return a result
...
在此示例中,upload_image
方法有一个仅位置参数(image
),这意味着它必须在没有键的情况下传递。客户端必须将图像数据直接发送到 HTTP 请求体中,无需任何 JSON 包装。
使用 curl
进行 API 调用的示例:
curl -XPOST -sL https://:3000/upload_image --data-binary=@myimage.png
这是 HTTP 请求示例:
POST /upload_image HTTP/1.1
Content-Type: image/png
<image binary>
当使用 BentoML 客户端 调用带有根输入的 API 时,必须使用位置参数,而不指定参数名称:
client = bentoml.SyncClient("https://:3000")
image_path = Path("demo.png")
result = client.upload_image(image_path) # CORRECT
result = client.upload_image(image=image_path) # WRONG
复合类型¶
在高级用例中,仅处理单一数据类型往往不够。复杂场景可能需要处理不同数据类型的组合。
例如,您可以如下组合图像和 JSON 输入:
from pydantic import BaseModel, Field
from PIL import Image as PILImage
import bentoml
class ImageMetadata(BaseModel):
description: str = Field(description="Description of the image")
timestamp: str = Field(description="Timestamp of when the image was captured")
@bentoml.service
class ImageProcessingService:
@bentoml.api
def process_image(self, image: PILImage, metadata: ImageMetadata) -> dict:
# Implementation for processing the image and metadata
...
在此示例中,PILImage
处理图像数据,而 Pydantic 模型 ImageMetadata
处理 JSON 输入。
BentoML 还支持复杂类型(例如图像和文件路径)的列表输入和输出。下面是定义一次处理图像和路径列表的 API 的示例:
from PIL import Image as PILImage
from pathlib import Path
from typing import List, Dict
import bentoml
@bentoml.service
class BatchImageService:
@bentoml.api
def enhance_images(self, images: List[PILImage]) -> PILImage:
# Process images and return a single image
...
@bentoml.api
def process_files(self, files: List[Path]) -> List[Dict]:
# Process files and return a list of dictionaries
...
请注意,目前 BentoML 不支持包含多个原始二进制数据或将原始二进制数据(如图像或文件)与普通字典数据直接组合的输出。
验证数据¶
对输入数据进行适当的验证对于 BentoML 服务至关重要,以确保正在处理的数据采用预期的格式并符合必要的质量标准。BentoML 提供了一个简单的验证机制,并默认支持 Pydantic 提供的所有验证功能。这允许对输入数据的结构、类型和约束进行全面检查。
下面是一个示例:
from typing import Annotated # Python 3.9 or above
from typing_extensions import Annotated # older than 3.9
from annotated_types import Ge, Lt, Gt, MultipleOf, MaxLen
import bentoml
@bentoml.service
class LLMPredictor:
@bentoml.api
def predict(
self,
prompt: Annotated[str, MaxLen(1000)],
temperature: Annotated[float, Ge(0), Lt(2)],
max_tokens: Annotated[int, Gt(0), MultipleOf(100)]
) -> int:
...
在此示例中,验证器确保 prompt
字符串不超过 1000 个字符,temperature
在 0 到 2 之间,并且 max_tokens
是 100 的正倍数。
常用 ML 类型的验证¶
BentoML 为常见的 ML 数据类型(如张量和数据帧)提供验证功能,以确保输入到模型的数据完整性。您可以在上面的章节中找到这些数据类型的验证示例。
下表包含 BentoML 支持的额外输入和输出类型,这些类型专为 ML 用例设计。每种类型允许使用的注解可用于进一步细化和验证数据。
类型名称 |
描述 |
允许的注解 |
---|---|---|
|
用于数值数据的多维数组,常用于 ML 任务。 |
|
|
PyTorch 中用于表示张量数据的张量类型。 |
|
|
TensorFlow 中用于表示张量数据的张量类型。 |
|
|
用于表格数据的数据结构,常用于数据分析。 |
|
|
PIL 库中的图像数据类型,用于图像处理。 |
|
|
文件路径,用于文件输入和输出。 |
|
BentoML 还支持所有 Pydantic 注解类型进行验证。有关更多信息,请参阅 Pydantic 文档。
附录¶
本节提供了总结 BentoML 服务中支持的输入和输出类型的表格。
输入类型¶
类型 |
输入注解 |
HTTP 内容类型 |
示例输入 HTTP 请求体 |
---|---|---|---|
JSON |
|
|
|
张量 |
|
|
|
表格数据 |
|
|
|
图像 |
|
|
|
文件 |
|
|
|
输出类型¶
类型 |
输出注解 |
HTTP 内容类型 |
示例输出 HTTP 响应体 |
---|---|---|---|
普通 |
|
|
字符串 |
JSON |
|
|
|
张量 |
|
|
|
表格数据 |
|
|
|
图像 |
|
|
二进制响应体 |
文件 |
|
|
二进制响应体 |
自定义文件 |
|
|
二进制响应体 |