vLLM 大模型本地推理库

vLLM 是当今最令人兴奋的 LLM 项目之一。每月下载量超过 20 万次,并拥有宽松的 Apache 2.0 许可证,vLLM 正成为一种越来越流行的大规模提供 LLM 的方式。

在本教程中,我将向你展示如何配置和运行 vLLM 以在生产中提供开源 LLM。

1、开始使用 vLLM

对于 vLLM 新手,我们首先解释一下什么是 vLLM。

vLLM 是一个开源项目,允许你进行 LLM 推理和服务。推理意味着你可以下载模型权重并将其传递给 vLLM 以通过其 Python API 执行推理;以下是其文档中的一个示例:

from vllm import LLM, SamplingParams

prompts = [
    "Hello, my name is",
    "The president of the United States is",
    "The capital of France is",
    "The future of AI is",
]

# initialize
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(model="facebook/opt-125m")

# perform the inference
outputs = llm.generate(prompts, sampling_params)

# print outputs
for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

在这方面,vLLM 与 Hugginface 的 transformers 库类似,作为比较,以下是使用 transformers 对同一模型进行推理的方法:

from transformers import pipeline

generator = pipeline('text-generation', model="facebook/opt-125m")
generator("Hello, my name is")

正如我在前面的例子中所展示的那样,使用 Python API 运行推理对于快速测试来说很好,但在生产环境中,我们希望提供一个简单的界面来与模型交互,以便系统的其他部分可以轻松调用它,一个很好的解决方案是通过 API 公开我们的模型。

假设你发现了 vLLM,现在你想构建一个 REST API 来为模型提供服务,你可以构建一个像这样的 Flask 应用程序:

from flask import Flask, request, jsonify
from vllm import LLM, SamplingParams

app = Flask(__name__)
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(model="facebook/opt-125m")

@app.route('/generate', methods=['POST'])
def generate():
    data = request.get_json()
    prompts = data.get('prompts', [])

    outputs = llm.generate(prompts, sampling_params)

    # Prepare the outputs.
    results = []

    for output in outputs:
        prompt = output.prompt
        generated_text = output.outputs[0].text
        results.append({
            'prompt': prompt,
            'generated_text': generated_text
        })

    return jsonify(results)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

我们的用户现在可以通过访问 /generate 端点来使用我们的模型。但是,这有很多限制:如果许多用户同时点击端点,Flask 将尝试同时运行它们并崩溃。我们还需要实现我们的身份验证机制。最后,互操作性是有限的;用户必须阅读我们模型的 REST API 文档才能与我们的模型交互。

这就是 vLLM 的服务部分大放异彩的地方,因为它为我们提供了所有这些。如果 vLLM 的 Python API 类似于 transformers 库,那么 vLLM 的服务器类似于 TGI

现在我们已经解释了 vLLM 的基础知识;让我们安装它吧!

2、安装 vLLM

安装 vLLM 很简单:

pip install vllm

请记住,vLLM 需要 Linux 和 Python >=3.8。此外,它还需要计算能力 >=7.0 的 GPU(例如 V100、T4、RTX20xx、A100、L4、H100)。

最后,vLLM 是使用 CUDA 12.1 编译的,因此你需要确保你的机器正在运行此 CUDA 版本。要检查它,请运行:

nvcc --version

如果你没有运行 CUDA 12.1,可以安装使用你正在运行的 CUDA 版本编译的 vLLM 版本(请参阅安装说明以了解更多信息),或者安装 CUDA 12.1。

3、检查你的vLLM安装

在继续之前,我建议你通过运行一些健全性检查来检查你的vLLM安装:

# ensure torch is working with CUDA, this should print: True
python -c 'import torch; print(torch.cuda.is_available())'

现在,将以下内容存储在 check-vllm.py 文件中:

from vllm import LLM, SamplingParams
prompts = [
    "Mexico is famous for ",
    "The largest country in the world is ",
]

sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(model="facebook/opt-125m")
responses = llm.generate(prompts, sampling_params)

for response in responses:
    print(response.outputs[0].text)

并运行脚本:

python check-vllm.py

模型加载后,你将看到一些输出;就我而言,我得到了这个:

~~national~~ cultural and artistic art. They've already worked with him.

~~the country~~ a capitalist system with the highest GDP per capita in the world

4、启动 vLLM 服务器

现在我们已经安装了 vLLM,让我们启动服务器。基本命令如下:

python -m vllm.entrypoints.openai.api_server --model=MODELTORUN

其中 MODELTORUN 是你想要提供的模型,例如,提供 google/gemma-2b

python -m vllm.entrypoints.openai.api_server --model=google/gemma-2b

请注意,某些模型(例如 google/gemma-2b)需要你接受其许可证,因此你需要创建一个 HuggingFace 帐户,接受该模型的许可证并生成访问令牌。

例如,在 HugginFace 上打开 google/gemma-2b(需要登录)时,你会看到以下内容:

接受许可证后,转到令牌部分并获取访问令牌,然后在启动 vLLM 之前按如下方式设置令牌:

export HF_TOKEN=YOURTOKEN

一旦设置了令牌,你就可以启动服务器。

python -m vllm.entrypoints.openai.api_server --model=google/gemma-2b

请注意,即使下载了权重,也需要令牌。否则,你将收到以下错误:

  File "/opt/conda/lib/python3.10/site-packages/huggingface_hub/hf_file_system.py", line 863, in _raise_file_not_found
    raise FileNotFoundError(msg) from err
FileNotFoundError: google/gemma-2b (repository not found)

5、设置 dtype

需要考虑的一个重要设置是 dtype,它控制模型权重的数据类型。你可能需要根据你的 GPU 调整此参数,例如尝试运行 google/gemma-2b

# --dtype=auto is the default value
python -m vllm.entrypoints.openai.api_server --model=google/gemma-2b --dtype=auto

在 NVIDIA Tesla T4 上出现以下错误:

ValueError: Bfloat16 is only supported on GPUs with compute capability of at least 8.0.
Your Tesla T4 GPU has compute capability 7.5. You can use float16 instead by explicitly
setting the`dtype` flag in CLI, for example: --dtype=half.

更改 --dtype 标志允许我们在 T4 上运行模型:

python -m vllm.entrypoints.openai.api_server --model=google/gemma-2b --dtype=half

如果这是你第一次使用传递的 --model 启动 vLLM,则需要几分钟时间,因为它必须下载权重。由于权重存储在 ~/.cache 目录中,因此进一步的初始化将更快;但是,由于模型必须加载到内存中,因此仍然需要一些时间来加载(取决于模型大小)。

如果看到这样的消息:

INFO:     Started server process [428]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)

vLLM 已准备好接受请求!

6、发出请求

服务器运行后,你可以发出请求;以下是使用 google/gemma-2b 和 Pythonrequest库的示例:

# remember to run: pip install requests
import requests
import json

# change for your host
VLLM_HOST = "https://autumn-snow-1380.ploomberapp.io"
url = f"{VLLM_HOST}/v1/completions"

headers = {"Content-Type": "application/json"}
data = {
    "model": "google/gemma-2b",
    "prompt": "JupySQL is",
    "max_tokens": 100,
    "temperature": 0
}

response = requests.post(url, headers=headers, data=json.dumps(data))

print(response.json()["choices"][0]["text"])

这是我收到的回复:

JupySQL is a Python library that allows you to create and run SQL queries in Jupyter notebooks. It is a powerful tool for data analysis and visualization, and can be used to explore and manipulate large datasets.

How does JupySQL work?

JupySQL works by connecting to a database server and executing SQL queries. It supports a wide range of databases, including MySQL, PostgreSQL, and SQLite.

Once you have connected to a database, you can create and run SQL queries in

准确!

7、使用 OpenAI 客户端

vLLM 公开了一个模仿 OpenAI 的 API;这意味着你可以使用 OpenAI 的 Python 包直接调用你的 vLLM 服务器。让我们看一个例子:

# NOTE: remember to run: pip install openai
from openai import OpenAI

# we haven't configured authentication, we pass a dummy value
openai_api_key = "EMPTY"
# modify this value to match your host, remember to add /v1 at the end
openai_api_base = "https://autumn-snow-1380.ploomberapp.io/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)
completion = client.completions.create(model="google/gemma-2b",
                                      prompt="JupySQL is",
                                      max_tokens=20)
print(completion.choices[0].text)

我得到了以下输出:

a powerful SQL editor and IDE. It integrates with Google Jupyter Notebook,
which allows users to create and

8、使用OpenAI聊天API

上一个示例使用了完成 API;但你可能更熟悉聊天 API。请注意,如果你使用聊天 API,则必须确保使用指令调整模型。 google/gemma-2b 未针对指令进行调整;让我们改用 google/gemma-2b-it,让我们启动 vLLM 服务器以使用此类模型:

python -m vllm.entrypoints.openai.api_server \
    --host 0.0.0.0 --port 80 \
    --model google/gemma-2b \
    --dtype=half

现在我们可以使用 client.chat.completions.create函数:

# NOTE: remember to run: pip install openai
from openai import OpenAI

openai_api_key = "EMPTY"
openai_api_base = "https://autumn-snow-1380.ploomberapp.io/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

chat_response = client.chat.completions.create(
    model="google/gemma-2b-it",
    messages=[
        {"role": "user", "content": "Tell me in one sentence what Mexico is famous for"},
    ]
)
print(chat_response.choices[0].message.content)

输出:

Mexico is known for its rich culture, vibrant cities, stunning natural beauty,
and delicious cuisine.

看起来很准确!

如果你以前使用过 OpenAI 的 API,你可能还记得 messages 参数通常包含一些带有 {"role": "system", "content": ...} 的消息:

chat_response = client.chat.completions.create(
    model="google/gemma-2b-it",
    messages=[
        {"role": "system", "content": "You're a helful assistant."},
        {"role": "user", "content": "Tell me in one sentence what Mexico is famous for"},
    ]

但是有些模型不支持系统角色,例如 google/gemma-2b-it 返回以下内容:

BadRequestError: Error code: 400 - {'object': 'error', 'message': 'System role not
supported', 'type': 'BadRequestError', 'param': None, 'code': 400}

查看你的模型的文档以了解如何使用聊天 API。

9、安全设置

默认情况下,你的服务器不会进行任何身份验证。如果你计划将服务器暴露给互联网,请确保设置 API 密钥;你可以按如下方式生成一个:

export VLLM_API_KEY=$(python -c 'import secrets; print(secrets.token_urlsafe())')
# print the API key
echo $VLLM_API_KEY

并启动 vLLM:

python -m vllm.entrypoints.openai.api_server --model google/gemma-2b-it --dtype=half

现在,我们的服务器将受到保护,所有没有 API 密钥的请求都将被拒绝。请注意,在上一个命令中,我们没有传递 --api-key,因为 vLLM 将自动读取 VLLM_API_KEY 环境变量。

通过使用任何先前的 Python 代码片段进行调用来测试你的服务器是否具有 API 密钥身份验证,你将看到以下错误:

No key: `AuthenticationError: Error code: 401 - {'error': 'Unauthorized'}`

要解决此问题,请使用正确的 API 密钥初始化 OpenAI 客户端:

from openai import OpenAI

openai_api_key = "THE_ACTUAL_API_KEY"
openai_api_base = "https://autumn-snow-1380.ploomberapp.io/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

另一个基本安全要求是通过 HTTPS 提供 API;但是,这需要额外的配置,例如获取 TLS 证书。

10、生产部署注意事项

以下是生产部署的一些注意事项:

  • 部署 vLLM 时,必须确保 API 在崩溃时(或物理服务器重新启动时)重新启动。你可以使用 systemd 等工具执行此操作。
  • 为了使你的部署更具可移植性,我们建议使用 docker。此外,请确保固定所有 Python 依赖项,以便升级不会破坏你的安装(例如,使用 pip freeze)。

11、使用 PyTorch 的 docker 镜像

我们建议使用 PyTorch 的官方 Docker 镜像,因为它已经安装了 torch 和 CUDA 驱动程序。

以下是可以使用的示例 Dockerfile:

FROM pytorch/pytorch:2.1.2-cuda12.1-cudnn8-devel

WORKDIR /srv
RUN pip install vllm==0.3.3 --no-cache-dir

# if the model you want to serve requires you to accept the license terms,
# you must pass a HF_TOKEN environment variable, also ensure to pass a VLLM_API_KEY
# environment variable to authenticate your API
ENTRYPOINT ["python", "-m", "vllm.entrypoints.openai.api_server", \
            "--host", "0.0.0.0", "--port", "80", \
            "--model", "google/gemma-2b-it", \
            # depending on your GPU, you might or might not need to pass --dtype
            "--dtype=half"]

12、关于 transformers==4.39.1的问题

tl;dr; 在官方 PyTorch docker 镜像中安装 vLLM 时,请确保使用具有正确 PyTorch 版本的镜像。为此,请检查相应的 pyproject.toml文件

在开发本指南时,我们在 transformers 包中遇到了一个错误。我们编写了一个使用 torch2.2.2(撰写本文时的最新版本)的 Dockerfile,然后安装了 vllm0.3.3

FROM pytorch/pytorch:2.2.2-cuda12.1-cudnn8-devel
RUN pip install vllm==0.3.3

但是,在启动 vLLM 服务器时,我们遇到了以下错误:

File /opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:478
    475     return output_type(**dict(zip(context, values)))
    477 if version.parse(get_torch_version()) >= version.parse("2.2"):
--> 478     _torch_pytree.register_pytree_node(
    479         ModelOutput,
    480         _model_output_flatten,
    481         partial(_model_output_unflatten, output_type=ModelOutput),
    482         serialized_type_name=f"{ModelOutput.__module__}.{ModelOutput.__name__}",
    483     )
    484 else:
    485     _torch_pytree._register_pytree_node(
    486         ModelOutput,
    487         _model_output_flatten,
    488         partial(_model_output_unflatten, output_type=ModelOutput),
    489     )

AttributeError: module 'torch.utils._pytree' has no attribute 'register_pytree_node'

经过进一步调查,我们意识到问题出在 transformers 包中,具体来说,是在 _is_package_available 函数中。此函数确定当前的 torch 版本,该版本在代码库的几个部分中使用。尽管 vLLM 不使用 transformers 进行推理,但它似乎使用它来加载模型配置参数。问题在于 transformers 库使用的方法可能会返回错误的版本。

在我们的例子中,Docker 镜像中有 torch2.2.2,但由于 vllm0.3.3 需要 pyotrch2.1.2,因此运行 pip install vllm0.3.3 会将 PyTorch 降级到版本 2.1.2,然而,transformers 认为它​​仍然有 torch==2.2.2,导致执行崩溃。

这发生在 transformers==4.39.1 上,因此它可能会在未来的版本中得到修复。


原文链接:Deploying vLLM: a Step-by-Step Guide

汇智网翻译整理,转载请标明出处