vLLM 引擎是当前执行大型语言模型 (LLM) 的顶级高性能方式之一。它提供了 vllm serve 命令,作为在单机上部署模型的简便选项。虽然这很方便,但在生产环境中大规模服务这些 LLM 需要一些高级功能。
TorchServe 提供了这些基本的生产功能(例如自定义指标和模型版本控制),并通过其灵活的自定义 handler 设计,非常容易集成检索增强生成 (RAG) 或 Llama Guard 等安全功能。因此,将 vLLM 引擎与 TorchServe 结合起来,创建一个完整的 LLM 生产服务解决方案是顺理成章的。
在深入探讨集成细节之前,我们将演示如何使用 TorchServe 的 vLLM docker 镜像部署 Llama-3.1-70B-Instruct 模型。
在 TorchServe + vLLM 上快速开始使用 Llama 3.1
要开始,我们需要通过检出 TorchServe 仓库并从主文件夹执行以下命令来构建 新的 TS LLM Docker 容器镜像
docker build --pull . -f docker/Dockerfile.vllm -t ts/vllm
该容器使用我们新的 LLM 启动脚本 ts.llm_launcher
,该脚本接受 Hugging Face 模型 URI 或本地文件夹,并在后端运行 vLLM 引擎的情况下启动本地 TorchServe 实例。要在本地服务模型,您可以使用以下命令创建容器实例
#export token=<HUGGINGFACE_HUB_TOKEN>
docker run --rm -ti --shm-size 10g --gpus all -e HUGGING_FACE_HUB_TOKEN=$token -p
8080:8080 -v data:/data ts/vllm --model_id meta-llama/Meta-Llama-3.1-70B-Instruct --disable_token_auth
您可以使用此 curl 命令在本地测试端点
curl -X POST -d '{"model":"meta-llama/Meta-Llama-3.1-70B-Instruct", "prompt":"Hello, my name is", "max_tokens": 200}' --header "Content-Type: application/json" "http://localhost:8080/predictions/model/1.0/v1/completions"
该 docker 将模型权重存储在本地文件夹“data”中,该文件夹在容器内部被挂载为 /data。要服务您自定义的本地权重,只需将它们复制到 data 文件夹中,并将 model_id 指向 /data/<您的权重>。
在内部,该容器使用我们新的 ts.llm_launcher
脚本来启动 TorchServe 并部署模型。该启动器将 LLM 与 TorchServe 的部署简化为单命令行,并且也可以在容器外部用作实验和测试的有效工具。要在 docker 外部使用该启动器,请按照 TorchServe 安装步骤进行操作,然后执行以下命令启动一个 8B Llama 模型
# after installing TorchServe and vLLM run
python -m ts.llm_launcher --model_id meta-llama/Meta-Llama-3.1-8B-Instruct --disable_token_auth
如果有多个 GPU 可用,启动器将自动声明所有可见设备并应用张量并行(参见 CUDA_VISIBLE_DEVICES 指定要使用的 GPU)。
虽然这非常方便,但重要的是要注意它并未涵盖 TorchServe 提供的所有功能。对于希望利用更高级功能的用户,需要创建一个模型存档。虽然这个过程比执行单命令更复杂一些,但它具有自定义 handler 和版本控制的优势。前者允许在预处理步骤中实现 RAG,后者允许您在更大规模部署之前测试不同版本的 handler 和模型。
在提供创建和部署模型存档的详细步骤之前,让我们深入了解 vLLM 引擎集成的细节。
TorchServe 的 vLLM 引擎集成
作为最先进的服务框架之一,vLLM 提供了大量高级功能,包括 PagedAttention、持续批处理、通过 CUDA graphs 实现快速模型执行,以及支持各种量化方法,如 GPTQ、AWQ、INT4、INT8 和 FP8。它还提供了对 LoRA 等重要参数高效适配器方法的集成,并支持包括 Llama 和 Mistral 在内的多种模型架构。vLLM 由 vLLM 团队和一个活跃的开源社区维护。
为了方便快速部署,它提供了一种基于 FastAPI 的服务模式,通过 HTTP 服务 LLM。为了更紧密、更灵活的集成,该项目还提供了 vllm.LLMEngine,它提供了持续处理请求的接口。我们利用了 异步变体 将其集成到 TorchServe 中。
TorchServe 是一个易于使用的开源解决方案,用于在生产环境中服务 PyTorch 模型。作为经过生产验证的服务解决方案,TorchServe 提供了许多对于大规模部署 PyTorch 模型非常有益的优势和功能。通过将其与 vLLM 引擎的推理性能相结合,这些优势现在也可用于大规模部署 LLM。
为了最大化硬件利用率,通常将来自多个用户的请求批量处理在一起是一个好做法。历史上,TorchServe 只提供同步模式来收集来自各种用户的请求。在该模式下,TorchServe 会等待预定义的时间(例如,batch_delay=200ms)或直到接收到足够的请求(例如,batch_size=8)。当其中一个事件触发时,批处理的数据被转发到后端,模型应用于该批次数据,然后模型输出通过前端返回给用户。这对于传统视觉模型尤其有效,因为每个请求的输出通常同时完成。
对于生成式用例,尤其是文本生成,请求同时就绪的假设不再成立,因为响应的长度会有所不同。尽管 TorchServe 支持持续批处理(动态添加和移除请求的能力),但此模式仅支持静态最大批处理大小。随着 PagedAttention 的引入,即使最大批处理大小的假设也变得更加灵活,因为 vLLM 可以以高度适应性的方式组合不同长度的请求,以优化内存利用率。
为了实现最优的内存利用率,即填充内存中的未使用空隙(想象俄罗斯方块),vLLM 需要完全控制在任何给定时间处理哪些请求的决策。为了提供这种灵活性,我们不得不重新评估 TorchServe 如何处理用户请求。我们引入了 异步模式(见下图),而不是之前的同步处理模式,在该模式下,传入的请求被直接转发到后端,使其可供 vLLM 使用。后端将请求输入到 vllm.AsyncEngine,该引擎现在可以从所有可用请求中进行选择。如果启用了流式传输模式并且请求的第一个 token 可用,后端将立即发送结果,并继续发送 token 直到生成最后一个 token。
我们对 VLLMHandler 的实现使用户能够通过配置文件快速部署任何与 vLLM 兼容的模型,同时仍然通过自定义 handler 提供相同的灵活性和可定制性。用户可以通过继承 VLLMHandler 并覆盖相应的类方法来添加例如自定义的 预处理 或 后处理 步骤。
我们还支持单节点多 GPU 分布式推理,其中我们将 vLLM 配置为使用模型的张量并行分片,以增加小型模型的容量或支持不适合单个 GPU 的大型模型,例如 70B Llama 变体。以前,TorchServe 仅支持使用 torchrun 进行分布式推理,其中会启动多个后端 worker 进程来分片模型。vLLM 在内部管理这些进程的创建,因此我们 为 TorchServe 引入了新的“custom” parallelType,该类型启动单个后端 worker 进程并提供分配的 GPU 列表。后端进程可以在必要时启动自己的子进程。
为了方便将 TorchServe + vLLM 集成到基于 docker 的部署中,我们提供了一个单独的 Dockerfile,该 Dockerfile 基于 TorchServe 的 GPU docker 镜像,并将 vLLM 添加为依赖项。我们选择将两者分开,以避免增加非 LLM 部署的 docker 镜像大小。
接下来,我们将演示在具有四个 GPU 的机器上使用 TorchServe + vLLM 部署 Llama 3.1 70B 模型所需的步骤。
循序渐进指南
对于本循序渐进指南,我们假设 TorchServe 的安装已成功完成。目前,vLLM 不是 TorchServe 的强制依赖项,因此我们使用 pip 安装该包
$ pip install -U vllm==0.6.1.post2
在接下来的步骤中,我们将(可选地)下载模型权重、解释配置、创建模型存档、部署和测试它
1.(可选)下载模型权重
此步骤是可选的,因为 vLLM 也可以在模型服务器启动时处理权重的下载。然而,预先下载模型权重并在 TorchServe 实例之间共享缓存文件,对于存储使用和模型 worker 的启动时间都有好处。如果您选择下载权重,请使用 huggingface-cli 并执行
# make sure you have logged into huggingface with huggingface-cli login before
# and have your access request for the Llama 3.1 model weights approved
huggingface-cli download meta-llama/Meta-Llama-3.1-70B-Instruct --exclude original/*
这会将文件下载到 $HF_HOME 下,如果您想将文件放在其他位置,可以修改该变量。请确保您在运行 TorchServe 的任何地方更新此变量,并确保它有权访问该文件夹。
2. 配置模型
接下来,我们创建一个 YAML 配置文件,其中包含模型部署所需的所有参数。配置文件的第一部分指定前端应如何启动后端 worker,该 worker 最终将在 handler 中运行模型。第二部分包含后端 handler 的参数,例如要加载的模型,以及 vLLM 本身的各种参数。有关 vLLM 引擎可能配置的更多信息,请参阅此链接。
echo '
# TorchServe frontend parameters
minWorkers: 1
maxWorkers: 1 # Set the number of worker to create a single model instance
startupTimeout: 1200 # (in seconds) Give the worker time to load the model weights
deviceType: "gpu"
asyncCommunication: true # This ensures we can cummunicate asynchronously with the worker
parallelType: "custom" # This lets TS create a single backend prosses assigning 4 GPUs
parallelLevel: 4
# Handler parameters
handler:
# model_path can be a model identifier for Hugging Face hub or a local path
model_path: "meta-llama/Meta-Llama-3.1-70B-Instruct"
vllm_engine_config: # vLLM configuration which gets fed into AsyncVLLMEngine
max_num_seqs: 16
max_model_len: 512
tensor_parallel_size: 4
served_model_name:
- "meta-llama/Meta-Llama-3.1-70B-Instruct"
- "llama3"
'> model_config.yaml
3. 创建模型文件夹
创建模型配置文件 (model_config.yaml) 后,我们现在将创建一个模型存档,其中包含配置和附加元数据,例如版本信息。由于模型权重很大,我们不会将它们包含在存档中。相反,handler 将通过模型配置中指定的 model_path 访问权重。请注意,在此示例中,我们选择使用“no-archive”格式,该格式会创建一个包含所有必要文件的模型文件夹。这使我们能够轻松修改配置文件进行实验,没有任何障碍。之后,我们也可以选择 mar 或 tgz 格式来创建更易于传输的制品。
mkdir model_store
torch-model-archiver --model-name vllm --version 1.0 --handler vllm_handler --config-file model_config.yaml --archive-format no-archive --export-path model_store/
4. 部署模型
下一步是启动 TorchServe 实例并加载模型。请注意,我们已为本地测试禁用了 token 认证。强烈建议在公开部署任何模型时实现某种形式的认证。
要启动 TorchServe 实例并加载模型,请运行以下命令
torchserve --start --ncs --model-store model_store --models vllm --disable-token-auth
您可以通过日志语句监视模型加载的进度。模型加载完成后,您可以继续测试部署。
5. 测试部署
vLLM 集成使用与 OpenAI API 兼容的格式,因此我们可以使用专门的工具或 curl 来实现。我们在此使用的 JSON 数据包括模型标识符以及提示文本。其他选项及其默认值可以在 vLLMEngine 文档中找到。
echo '{
"model": "llama3",
"prompt": "A robot may not injure a human being",
"stream": 0
}' | curl --header "Content-Type: application/json" --request POST --data-binary @- http://localhost:8080/predictions/vllm/1.0/v1/completions
请求的输出如下所示
{
"id": "cmpl-cd29f1d8aa0b48aebcbff4b559a0c783",
"object": "text_completion",
"created": 1727211972,
"model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
"choices": [
{
"index": 0,
"text": " or, through inaction, allow a human being to come to harm.\nA",
"logprobs": null,
"finish_reason": "length",
"stop_reason": null,
"prompt_logprobs": null
}
],
"usage": {
"prompt_tokens": 10,
"total_tokens": 26,
"completion_tokens": 16
}
当 streaming 为 False 时,TorchServe 将收集完整答案,并在最后一个 token 生成后一次性发送。如果我们翻转 stream 参数,我们将分段接收数据,每条消息包含一个 token。
结论
在这篇博文中,我们探讨了 vLLM 推理引擎与 TorchServe 的新的原生集成。我们演示了如何使用 ts.llm_launcher 脚本在本地部署 Llama 3.1 70B 模型,以及如何创建模型存档以在任何 TorchServe 实例上进行部署。此外,我们还讨论了如何在 Docker 容器中构建和运行解决方案,以便在 Kubernetes 或 EKS 上进行部署。在未来的工作中,我们计划启用 vLLM 和 TorchServe 的多节点推理,并提供预构建的 Docker 镜像以简化部署过程。
我们要感谢 Mark Saroufim 和 vLLM 团队在这篇博文准备过程中提供的宝贵支持。