欢迎来到 LMDeploy 的中文教程!¶
快速上手¶
LMDeploy提供了快速安装、模型量化、离线批处理、在线推理服务等功能。每个功能只需简单的几行代码或者命令就可以完成。
安装¶
使用 pip (python 3.8+) 安装 LMDeploy,或者源码安装
pip install lmdeploy
LMDeploy的预编译包默认是基于 CUDA 12 编译的。如果需要在 CUDA 11+ 下安装 LMDeploy,请执行以下命令:
export LMDEPLOY_VERSION=0.3.0
export PYTHON_VERSION=38
pip install https://github.com/InternLM/lmdeploy/releases/download/v${LMDEPLOY_VERSION}/lmdeploy-${LMDEPLOY_VERSION}+cu118-cp${PYTHON_VERSION}-cp${PYTHON_VERSION}-manylinux2014_x86_64.whl --extra-index-url https://download.pytorch.org/whl/cu118
编译和安装¶
LMDeploy 提供了预编译包,可以很方便的通过 pip install lmdeploy
安装和使用。
如果有源码编译的需求,请先下载 lmdeploy 源码:
git clone --depth=1 https://github.com/InternLM/lmdeploy
然后,参考以下章节编译和安装。
在 docker 内编译安装(强烈推荐)¶
LMDeploy 提供了编译镜像 openmmlab/lmdeploy-builder:cuda11.8
。使用之前,请确保 docker 已安装。
在 lmdeploy 源码的根目录下,运行以下命令:
# lmdeploy 源码根目录
cd lmdeploy
bash builder/manywheel/build_all_wheel.sh
即可在 builder/manywheel/cuda11.8_dist
文件夹下,得到 lmdeploy 在 py3.8 - py3.11 下所有的 wheel 文件。比如,
builder/manywheel/cuda11.8_dist/
├── lmdeploy-0.0.12-cp310-cp310-manylinux2014_x86_64.whl
├── lmdeploy-0.0.12-cp311-cp311-manylinux2014_x86_64.whl
├── lmdeploy-0.0.12-cp38-cp38-manylinux2014_x86_64.whl
└── lmdeploy-0.0.12-cp39-cp39-manylinux2014_x86_64.whl
如果需要固定 python 版本的 wheel 文件,比如 py3.8,可以执行:
bash builder/manywheel/build_wheel.sh py38 manylinux2014_x86_64 cuda11.8 cuda11.8_dist
wheel 文件存放在目录 builder/manywheel/cuda11.8_dist
下。
在宿主机上,通过 pip install
安装和宿主机python版本一致的 wheel 文件,即完成 lmdeploy 整个编译安装过程。
在物理机上编译安装(可选)¶
首先,请确保物理机环境的 gcc 版本不低于 9,可以通过gcc --version
确认。
然后,按如下步骤,配置编译环境:
安装编译和运行依赖包:
pip install -r requirements.txt apt-get install rapidjson-dev
安装 nccl,设置环境变量
export NCCL_ROOT_DIR=/path/to/nccl export NCCL_LIBRARIES=/path/to/nccl/lib
源码编译安装 openmpi:
wget https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.5.tar.gz tar xf openmpi-4.1.5.tar.gz cd openmpi-4.1.5 ./configure --prefix=/usr/local/openmpi make -j$(nproc) && make install export PATH=$PATH:/usr/local/openmpi/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/openmpi/lib
lmdeploy 编译安装:
# 安装更快的 Ninja apt install ninja-build # lmdeploy 源码的根目录 cd lmdeploy mkdir build && cd build sh ../generate.sh ninja && ninja install ninja -j$(nproc) && ninja install
安装 lmdeploy python package:
cd .. pip install -e .
静态推理性能测试¶
我们把推理引擎在固定 batch、固定输入输出 token 数量的前提下的推理,称之为静态推理。
评测脚本是 profile_generation.py
,在运行此脚本前,请安装 lmdeploy 预编译包,并下载评测脚本
pip install lmdeploy
git clone --depth=1 https://github.com/InternLM/lmdeploy
测量指标¶
LMDeploy 统计首token延时(first_token_latency)、token 吞吐量(tokens/s),每个token延时的百分位数据(P50,P75,P95,P99)、GPU mem 等测试结果。
first_token_latency
只有在流式推理的情况下才会输出。
吞吐量的计算公式为:
$$ token吞吐量 = 生成的token数量 / 总时间 $$
总时间包括 prefill 时间。
测试过程中,节点上所有的显卡不要运行其他任何程序,否则 GPU mem 的统计会不准确。
测量方法¶
我们以 internlm/internlm-7b 为例,分别介绍测试 LMDeploy 两个推理引擎 turbomind 和 pytorch 的静态推理性能测试方法
Turbomind 引擎¶
cd lmdeploy/benchmark
python3 profile_generation.py internlm/internlm-7b
PyTorch 引擎¶
cd lmdeploy/benchmark
python3 profile_generation.py internlm/internlm-7b --backend pytorch
关于 profile_generation
脚本的参数,比如批处理大小,输入输出token的数量等等,可以通过运行命令 python3 profile_generation.py -h
查阅。
请求吞吐量性能测试¶
在真实应用中,用户输入的 prompt 长度以及模型回复的 token 数量是动态变化的。而静态推理能力不足以反映推理引擎对动态输入输出的处理能力。
所以需要使用真实对话数据,评测推理引擎的动态推理能力。本文将介绍如何在 localhost 上测试 LMDeploy 的动态推理性能。
测试脚本是 profile_throughput.py
。测试之前,请安装 lmdeploy 预编译包,并下载评测脚本和测试数据集。
pip install lmdeploy
git clone --depth=1 https://github.com/InternLM/lmdeploy
cd lmdeploy/benchmark
wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json
测量指标¶
LMDeploy 统计首token延时(first_token_latency)、token吞吐量(tokens/s)和请求吞吐量(RPM)。
first_token_latency
只有在流式推理的情况下才会输出。
token吞吐量的计算公式为:
$$ token吞吐量 = 生成的token数量 / 总时间 $$
请求吞吐量的计算公式为:
$$ 吞吐量 = 请求数量 / 总时间 $$
总时间包括 prefill 时间
测量方法¶
我们以 internlm/internlm-7b 为例,分别介绍测试 LMDeploy 两个推理引擎 turbomind 和 pytorch 的离线请求处理速度
Turbomind 引擎¶
python3 profile_throughput.py ./ShareGPT_V3_unfiltered_cleaned_split.json internlm/internlm-7b
PyTorch 引擎¶
python3 profile_throughput.py ./ShareGPT_V3_unfiltered_cleaned_split.json internlm/internlm-7b --backend pytorch
有关 profile_throughput.py 的详细参数,比如并发数、采样参数、k/v内存分配比例等等,请执行 python3 profile_throughput.py -h
查阅
api_server 性能测试¶
api_server 的测试方式与求吞吐量测试方法类似。不同的是,在测试前,需要先启动 api_server,然后再通过测试脚本发送请求进行测试。
测试脚本是 profile_restful_api.py
。测试之前,请安装 lmdeploy 预编译包,并下载评测脚本和测试数据集。
pip install lmdeploy
git clone --depth=1 https://github.com/InternLM/lmdeploy
cd lmdeploy/benchmark
wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json
测量指标¶
LMDeploy 统计首token延时(first_token_latency)、token吞吐量(tokens/s)和请求吞吐量(RPM)。
first_token_latency
只有在流式推理的情况下才会输出。
token吞吐量的计算公式为:
$$ 吞吐量 = 生成的token数量 / 总时间 $$
请求吞吐量的计算公式为:
$$ 吞吐量 = 请求数量 / 总时间 $$
总时间包括 prefill 时间
测量方法¶
我们以 internlm/internlm-7b 为例,展示 api_server 的性能测试流程
启动服务¶
lmdeploy serve api_server internlm/internlm-7b
如果你想改变 server 的端口,或者诸如推理引擎、最大批处理值等参数,请运行 lmdeploy serve api_server -h
或者阅读这篇文档,查看详细的参数说明。
测速¶
python3 profile_restful_api.py http://0.0.0.0:23333 internlm/internlm-7b ./ShareGPT_V3_unfiltered_cleaned_split.json
关于 profile_restful_api.py
脚本中的参数,比如请求并发数、采样参数等等,可以通过运行命令 python3 profile_restful_api.py -h
查阅。
Triton Inference Server 性能测试¶
Triton Inference Server(TIS) 是 LMDeploy 支持的除了 api_server 之外的另一种 serving 方式。它的性能测试方式和测试指标和 api_server 的测试方式类似。
注解
LMDeploy 尚未实现 Triton Inference Server 的 ensemble 推理模式,所以推理性能要比 api_server 弱。对于追求性能的用户,我们推荐使用 api_server 部署服务。
TIS 性能测试脚本是 profile_serving.py
。测试之前,请安装 lmdeploy 预编译包,并下载评测脚本和测试数据集。
pip install 'lmdeploy[serve]'
git clone --depth=1 https://github.com/InternLM/lmdeploy
wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json
测量指标¶
LMDeploy 统计首token延时(first_token_latency)、token吞吐量(tokens/s)和请求吞吐量(RPM)。
first_token_latency
只有在流式推理的情况下才会输出。
token吞吐量的计算公式为:
$$ 吞吐量 = 生成的token数量 / 总时间 $$
请求吞吐量的计算公式为:
$$ 吞吐量 = 请求数量 / 总时间 $$
总时间包括 prefill 时间
测量方法¶
我们以 internlm/internlm-7b 为例,展示 triton inference server 的性能测试流程
启动服务¶
启动服务之前,必须先把模型转换为 turbomind 模型格式:
lmdeploy convert internlm internlm/internlm-7b --dst-path ./internlm-7b --trust-remote-code
然后,执行如下命令,启动服务:
bash ./internlm-7b/service_docker_up.sh
测速¶
python3 profile_serving.py 0.0.0.0:33337 ./internlm-7b/triton_models/tokenizer ./ShareGPT_V3_unfiltered_cleaned_split.json
关于 profile_serving.py
脚本中的参数,比如请求并发数、采样参数等等,可以通过运行命令 python3 profile_serving.py -h
查阅。
如何使用OpenCompass测评LLMs¶
LMDeploy设计了TurboMind推理引擎用来加速大模型推理,其推理精度也支持使用OpenCompass测评。
准备¶
我们将配置用于测评的环境
安装 OpenCompass¶
执行如下脚本,从源码安装OpenCompass。更多安装方式请参考installation。
git clone https://github.com/open-compass/opencompass.git
cd opencompass
pip install -e .
如果想快速了解OpenCompass基本操作,可翻阅Quick Start
下载数据集¶
OpenCompass提供了多个版本的数据集,在这里我们下载如下版本数据集
# 切换到OpenCompass根目录
cd opencompass
wget https://github.com/open-compass/opencompass/releases/download/0.1.8.rc1/OpenCompassData-core-20231110.zip
unzip OpenCompassData-core-20231110.zip
准备测评配置文件¶
OpenCompass采用OpenMMLab风格的配置文件来管理模型和数据集,用户只需添加简单的配置就可以快速开始测评。OpenCompass已支持通过python API来 测评TurboMind推理引擎加速的大模型。
数据集配置¶
在OpenCompass根目录,准备测评配置文件$OPENCOMPASS_DIR/configs/eval_lmdeploy.py
。
在配置文件开始,导入如下OpenCompass支持的数据集datasets
和格式化输出测评结果的summarizer
。
from mmengine.config import read_base
with read_base():
# choose a list of datasets
from .datasets.mmlu.mmlu_gen_a484b3 import mmlu_datasets
from .datasets.ceval.ceval_gen_5f30c7 import ceval_datasets
from .datasets.SuperGLUE_WiC.SuperGLUE_WiC_gen_d06864 import WiC_datasets
from .datasets.SuperGLUE_WSC.SuperGLUE_WSC_gen_7902a7 import WSC_datasets
from .datasets.triviaqa.triviaqa_gen_2121ce import triviaqa_datasets
from .datasets.gsm8k.gsm8k_gen_1d7fe4 import gsm8k_datasets
from .datasets.race.race_gen_69ee4f import race_datasets
from .datasets.crowspairs.crowspairs_gen_381af0 import crowspairs_datasets
# and output the results in a chosen format
from .summarizers.medium import summarizer
datasets = sum((v for k, v in locals().items() if k.endswith('_datasets')), [])
模型配置¶
这个部分展示如何在测评配置文件中添加模型配置。让我们来看几个示例:
from opencompass.models.turbomind import TurboMindModel
internlm_20b = dict(
type=TurboMindModel,
abbr='internlm-20b-turbomind',
path="internlm/internlm-20b", # this path should be same as in huggingface
engine_config=dict(session_len=2048,
max_batch_size=8,
rope_scaling_factor=1.0),
gen_config=dict(top_k=1, top_p=0.8,
temperature=1.0,
max_new_tokens=100),
max_out_len=100,
max_seq_len=2048,
batch_size=8,
concurrency=8,
run_cfg=dict(num_gpus=1, num_procs=1),
)
models = [internlm_20b]
对于Chat类大模型,用户需要在配置文件中指定meta_template
,该设置需要与训练设置对齐,可翻阅meta_template 查看其介绍。
from opencompass.models.turbomind import TurboMindModel
internlm_meta_template = dict(round=[
dict(role='HUMAN', begin='<|User|>:', end='\n'),
dict(role='BOT', begin='<|Bot|>:', end='<eoa>\n', generate=True),
],
eos_token_id=103028)
internlm_chat_20b = dict(
type=TurboMindModel,
abbr='internlm-chat-20b-turbomind',
path='internlm/internlm-chat-20b',
engine_config=dict(session_len=2048,
max_batch_size=8,
rope_scaling_factor=1.0),
gen_config=dict(top_k=1,
top_p=0.8,
temperature=1.0,
max_new_tokens=100),
max_out_len=100,
max_seq_len=2048,
batch_size=8,
concurrency=8,
meta_template=internlm_meta_template,
run_cfg=dict(num_gpus=1, num_procs=1),
end_str='<eoa>'
)
models = [internlm_chat_20b]
注
如果想在测评配置文件中
engine_config
和gen_config
字段传递更多参数,请参考TurbomindEngineConfig 和 EngineGenerationConfig
支持的模型¶
TurboMind 支持的模型¶
模型 | 模型规模 | FP16/BF16 | KV INT8 | KV INT4 | W4A16 |
---|---|---|---|---|---|
Llama | 7B - 65B | Yes | Yes | Yes | Yes |
Llama2 | 7B - 70B | Yes | Yes | Yes | Yes |
Llama3 | 8B, 70B | Yes | Yes | Yes | Yes |
InternLM | 7B - 20B | Yes | Yes | Yes | Yes |
InternLM2 | 7B - 20B | Yes | Yes | Yes | Yes |
InternLM-XComposer | 7B | Yes | - | - | - |
InternLM-XComposer2 | 7B, 4khd-7B | Yes | - | - | - |
QWen | 1.8B - 72B | Yes | Yes | Yes | Yes |
QWen1.5 | 1.8B - 72B | Yes | Yes | Yes | Yes |
QWen-VL | 7B | Yes | - | - | - |
DeepSeek-VL | 7B | Yes | - | - | - |
Baichuan | 7B | Yes | Yes | Yes | Yes |
Baichuan2 | 7B | Yes | Yes | Yes | Yes |
Code Llama | 7B - 34B | Yes | Yes | Yes | No |
YI | 6B - 34B | Yes | Yes | Yes | No |
LLaVA(1.5,1.6) | 7B - 34B | Yes | - | - | - |
InternVL-Chat | v1.1- v1.5 | Yes | - | - | - |
MiniGeminiLlama | 7B | Yes | No | No | No |
“-” 表示还没有验证。
注解
turbomind 引擎不支持 window attention。所以,对于应用了 window attention,并开启了对应的开关”use_sliding_window”的模型,在推理时,请选择 pytorch engine
PyTorch 支持的模型¶
模型 | 模型规模 | FP16/BF16 | KV INT8 | W8A8 |
---|---|---|---|---|
Llama | 7B - 65B | Yes | No | Yes |
Llama2 | 7B - 70B | Yes | No | Yes |
Llama3 | 8B, 70B | Yes | No | Yes |
InternLM | 7B - 20B | Yes | No | Yes |
InternLM2 | 7B - 20B | Yes | No | - |
Baichuan2 | 7B - 13B | Yes | No | Yes |
ChatGLM2 | 6B | Yes | No | No |
Falcon | 7B - 180B | Yes | No | No |
YI | 6B - 34B | Yes | No | No |
Mistral | 7B | Yes | No | No |
Mixtral | 8x7B | Yes | No | No |
QWen | 1.8B - 72B | Yes | No | No |
QWen1.5 | 0.5B - 72B | Yes | No | No |
QWen1.5-MoE | A2.7B | Yes | No | No |
DeepSeek-MoE | 16B | Yes | No | No |
Gemma | 2B-7B | Yes | No | No |
Dbrx | 132B | Yes | No | No |
StarCoder2 | 3B-15B | Yes | No | No |
Phi-3-mini | 3.8B | Yes | No | No |
LLM 离线推理 pipeline¶
本文通过一些例子展示 pipeline 的基本用法。
pipeline API 详细的接口说明,请阅读此处
使用方法¶
使用默认参数的例子:
from lmdeploy import pipeline
pipe = pipeline('internlm/internlm2-chat-7b')
response = pipe(['Hi, pls intro yourself', 'Shanghai is'])
print(response)
在这个例子中,pipeline 默认申请一定比例显存,用来存储推理过程中产生的 k/v。比例由参数 TurbomindEngineConfig.cache_max_entry_count
控制。
LMDeploy 在研发过程中,k/v cache 比例的设定策略有变更,以下为变更记录:
v0.2.0 <= lmdeploy <= v0.2.1
默认比例为 0.5,表示 GPU总显存的 50% 被分配给 k/v cache。 对于 7B 模型来说,如果显存小于 40G,会出现 OOM。当遇到 OOM 时,请按照下面的方法,酌情降低 k/v cache 占比:
from lmdeploy import pipeline, TurbomindEngineConfig # 调低 k/v cache内存占比调整为总显存的 20% backend_config = TurbomindEngineConfig(cache_max_entry_count=0.2) pipe = pipeline('internlm/internlm2-chat-7b', backend_config=backend_config) response = pipe(['Hi, pls intro yourself', 'Shanghai is']) print(response)
lmdeploy > v0.2.1
分配策略改为从空闲显存中按比例为 k/v cache 开辟空间。默认比例值调整为 0.8。如果遇到 OOM,类似上面的方法,请酌情减少比例值,降低 k/v cache 的内存占用量
如何设置 tp:
from lmdeploy import pipeline, TurbomindEngineConfig
backend_config = TurbomindEngineConfig(tp=2)
pipe = pipeline('internlm/internlm2-chat-7b',
backend_config=backend_config)
response = pipe(['Hi, pls intro yourself', 'Shanghai is'])
print(response)
如何设置 sampling 参数:
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig
backend_config = TurbomindEngineConfig(tp=2)
gen_config = GenerationConfig(top_p=0.8,
top_k=40,
temperature=0.8,
max_new_tokens=1024)
pipe = pipeline('internlm/internlm2-chat-7b',
backend_config=backend_config)
response = pipe(['Hi, pls intro yourself', 'Shanghai is'],
gen_config=gen_config)
print(response)
如何设置 OpenAI 格式输入:
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig
backend_config = TurbomindEngineConfig(tp=2)
gen_config = GenerationConfig(top_p=0.8,
top_k=40,
temperature=0.8,
max_new_tokens=1024)
pipe = pipeline('internlm/internlm2-chat-7b',
backend_config=backend_config)
prompts = [[{
'role': 'user',
'content': 'Hi, pls intro yourself'
}], [{
'role': 'user',
'content': 'Shanghai is'
}]]
response = pipe(prompts,
gen_config=gen_config)
print(response)
流式返回处理结果:
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig
backend_config = TurbomindEngineConfig(tp=2)
gen_config = GenerationConfig(top_p=0.8,
top_k=40,
temperature=0.8,
max_new_tokens=1024)
pipe = pipeline('internlm/internlm2-chat-7b',
backend_config=backend_config)
prompts = [[{
'role': 'user',
'content': 'Hi, pls intro yourself'
}], [{
'role': 'user',
'content': 'Shanghai is'
}]]
for item in pipe.stream_infer(prompts, gen_config=gen_config):
print(item)
使用 pytorch 后端
需要先安装 triton
pip install triton>=2.1.0
from lmdeploy import pipeline, GenerationConfig, PytorchEngineConfig
backend_config = PytorchEngineConfig(session_len=2048)
gen_config = GenerationConfig(top_p=0.8,
top_k=40,
temperature=0.8,
max_new_tokens=1024)
pipe = pipeline('internlm/internlm2-chat-7b',
backend_config=backend_config)
prompts = [[{
'role': 'user',
'content': 'Hi, pls intro yourself'
}], [{
'role': 'user',
'content': 'Shanghai is'
}]]
response = pipe(prompts, gen_config=gen_config)
print(response)
一个 slora 的例子
from lmdeploy import pipeline, GenerationConfig, PytorchEngineConfig
backend_config = PytorchEngineConfig(session_len=2048,
adapters=dict(lora_name_1='chenchi/lora-chatglm2-6b-guodegang'))
gen_config = GenerationConfig(top_p=0.8,
top_k=40,
temperature=0.8,
max_new_tokens=1024)
pipe = pipeline('THUDM/chatglm2-6b',
backend_config=backend_config)
prompts = [[{
'role': 'user',
'content': '您猜怎么着'
}]]
response = pipe(prompts, gen_config=gen_config, adapter_name='lora_name_1')
print(response)
FAQs¶
RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase.
如果你在使用 tp>1 和 pytorch 后端的时候,遇到了这个错误。请确保 python 脚本中有下面内容作为入口
if __name__ == '__main__':
一般来说,在多线程或多进程上下文中,可能需要确保初始化代码只执行一次。这时候,
if __name__ == '__main__':
可以帮助确保这些初始化代码只在主程序执行,而不会在每个新创建的进程或线程中重复执行。自定义对话模板,请参考chat_template.md
如果 lora 的权重有对应的对话模板,可以先注册对话模板到 lmdeploy,然后 adapter 名为对话模板名使用即可
VLM 离线推理 pipeline¶
LMDeploy 把视觉-语言模型(VLM)复杂的推理过程,抽象为简单好用的 pipeline。它的用法与大语言模型(LLM)推理 pipeline 类似。
目前,VLM pipeline 支持以下模型:
我们诚挚邀请社区在 LMDeploy 中添加更多 VLM 模型的支持。
本文将以 liuhaotian/llava-v1.6-vicuna-7b 模型为例,展示 VLM pipeline 的用法。你将了解它的最基础用法,以及如何通过调整引擎参数和生成条件来逐步解锁更多高级特性,如张量并行,上下文窗口大小调整,随机采样,以及对话模板的定制。
此外,我们还提供针对多图、批量提示词等场景的实际推理示例。
“Hello, world” 示例¶
from lmdeploy import pipeline
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b')
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe(('describe this image', image))
print(response)
如果在执行这个用例时,出现 ImportError
的错误,请按照提示安装相关的依赖包。
上面的例子中,推理时的提示词是 (prompt, image) 的 tuple 结构。除了这种结构外,pipeline 支持 openai 格式的提示词:
from lmdeploy import pipeline
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b')
prompts = [
{
'role': 'user',
'content': [
{'type': 'text', 'text': 'describe this image'},
{'type': 'image_url', 'image_url': {'url': 'https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg'}}
]
}
]
response = pipe(prompts)
print(response)
设置多卡并行¶
设置引擎参数 tp
,可激活多卡并行能力
from lmdeploy import pipeline, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b',
backend_config=TurbomindEngineConfig(tp=2))
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe(('describe this image', image))
print(response)
设置上下文长度¶
创建 pipeline 时,通过设置引擎参数 session_len
,可以定制上下文窗口的最大长度
from lmdeploy import pipeline, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b',
backend_config=TurbomindEngineConfig(session_len=8192))
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe(('describe this image', image))
print(response)
设置随机采样参数¶
可通过传入 GenerationConfig
修改 pipeline 的生成接口中的默认采样参数。
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b',
backend_config=TurbomindEngineConfig(tp=2, session_len=8192))
gen_config = GenerationConfig(top_k=40, top_p=0.8, temperature=0.6)
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe(('describe this image', image), gen_config=gen_config)
print(response)
设置对话模板¶
推理时,LMDeploy 会根据模型路径匹配内置的对话模板,并把对话模板应用到输入的提示词上。但是,对于类似 llava-v1.5-7b 视觉-语言模型,它使用的对话模板是 vicuna,但是这个模板名无法从模型路径中获取,所以需要用户指定。具体方式如下:
from lmdeploy import pipeline, ChatTemplateConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.5-7b',
chat_template_config=ChatTemplateConfig(model_name='vicuna'))
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe(('describe this image', image))
print(response)
关于如何自定义对话模版,请参考这里
多图推理¶
对于多图的场景,在推理时,只要把它们放在一个列表中即可。不过,多图意味着输入 token 数更多,所以通常需要增大推理的上下文长度
from lmdeploy import pipeline, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b',
backend_config=TurbomindEngineConfig(session_len=8192))
image_urls=[
'https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/demo/resources/human-pose.jpg',
'https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/demo/resources/det.jpg'
]
images = [load_image(img_url) for img_url in image_urls]
response = pipe(('describe these images', images))
print(response)
提示词批处理¶
做批量提示词推理非常简单,只要把它们放在一个 list 结构中:
from lmdeploy import pipeline, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b',
backend_config=TurbomindEngineConfig(session_len=8192))
image_urls=[
"https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/demo/resources/human-pose.jpg",
"https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/demo/resources/det.jpg"
]
prompts = [('describe this image', load_image(img_url)) for img_url in image_urls]
response = pipe(prompts)
print(response)
多轮对话¶
pipeline 进行多轮对话有两种方式,一种是按照 openai 的格式来构造 messages,另外一种是使用 pipeline.chat
接口。
from lmdeploy import pipeline, TurbomindEngineConfig, GenerationConfig
from lmdeploy.vl import load_image
pipe = pipeline('liuhaotian/llava-v1.6-vicuna-7b',
backend_config=TurbomindEngineConfig(session_len=8192))
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/demo/resources/human-pose.jpg')
gen_config = GenerationConfig(top_k=40, top_p=0.8, temperature=0.6)
sess = pipe.chat(('describe this image', image), gen_config=gen_config)
print(sess.response.text)
sess = pipe.chat('What is the woman doing?', session=sess, gen_config=gen_config)
print(sess.response.text)
部署 LLM 类 openai 服务¶
本文主要介绍单个模型在单机多卡环境下,部署兼容 openai 接口服务的方式,以及服务接口的用法。为行文方便,我们把该服务名称为 api_server
。对于多模型的并行服务,请阅读请求分发服务器一文。
在这篇文章中, 我们首先介绍服务启动的两种方法,你可以根据应用场景,选择合适的。
其次,我们重点介绍服务的 RESTful API 定义,以及接口使用的方式,并展示如何通过 Swagger UI、LMDeploy CLI 工具体验服务功能
最后,向大家演示把服务接入到 WebUI 的方式,你可以参考它简单搭建一个演示 demo。
启动服务¶
以 huggingface hub 上的 internlm2-chat-7b 模型为例,你可以任选以下方式之一,启动推理服务。
方式一:使用 lmdeploy cli 工具¶
lmdeploy serve api_server internlm/internlm2-chat-7b --server-port 23333
api_server 启动时的参数可以通过命令行lmdeploy serve api_server -h
查看。
比如,--tp
设置张量并行,--session-len
设置推理的最大上下文窗口长度,--cache-max-entry-count
调整 k/v cache 的内存使用比例等等。
方式二:使用 docker¶
使用 LMDeploy 官方镜像,可以运行兼容 OpenAI 的服务。下面是使用示例:
docker run --runtime nvidia --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HUGGING_FACE_HUB_TOKEN=<secret>" \
-p 23333:23333 \
--ipc=host \
openmmlab/lmdeploy:latest \
lmdeploy serve api_server internlm/internlm2-chat-7b
在这个例子中,lmdeploy server api_server
的命令参数与方式一一致。
每个模型可能需要 Docker 映像中未包含的特定依赖项。如果遇到问题,您可能需要根据具体情况自行安装这些依赖项。如有疑问,请参阅特定模型的项目以获取文档。
例如,对于 Llava
FROM openmmlab/lmdeploy:latest
RUN apt-get update && apt-get install -y python3 python3-pip git
WORKDIR /app
RUN pip3 install --upgrade pip
RUN pip3 install timm
RUN pip3 install git+https://github.com/haotian-liu/LLaVA.git --no-deps
COPY . .
CMD ["lmdeploy", "serve", "api_server", "liuhaotian/llava-v1.6-34b"]
方式三:部署到Kubernetes集群¶
使用kubectl命令行工具,连接到一个运行中Kubernetes集群并部署internlm2-chat-7b模型服务。下面是使用示例(需要替换<your token>
为你的huggingface hub token):
sed 's/{{HUGGING_FACE_HUB_TOKEN}}/<your token>/' k8s/deployment.yaml | kubectl create -f - \
&& kubectl create -f k8s/service.yaml
示例中模型数据来源于node上的本地磁盘(hostPath),多副本部署时考虑替换为高可用共享存储,通过PersistentVolume方式挂载到容器中。
RESTful API¶
LMDeploy 的 RESTful API 兼容了 OpenAI 以下 3 个接口:
/v1/chat/completions
/v1/models
/v1/completions
此外,LMDeploy 还定义了 /v1/chat/interactive
,用来支持交互式推理。交互式推理的特点是不用像v1/chat/completions
传入用户对话历史,因为对话历史会被缓存在服务端。
这种方式在多轮次的长序列推理时,拥有很好的性能。
服务启动后,你可以在浏览器中打开网页 http://0.0.0.0:23333,通过 Swagger UI 查看接口的详细说明,并且也可以直接在网页上操作,体验每个接口的用法,如下图所示。
也可以使用 LMDeploy 自带的 CLI 工具,在控制台验证服务的正确性。
# restful_api_url is what printed in api_server.py, e.g. http://localhost:23333
lmdeploy serve api_client ${api_server_url}
若需要把服务集成到自己的项目或者产品中,我们推荐以下用法:
使用 openai 接口¶
以下代码是通过 openai 包使用 v1/chat/completions
服务的例子。运行之前,请先安装 openai 包: pip install openai
。
from openai import OpenAI
client = OpenAI(
api_key='YOUR_API_KEY',
base_url="http://0.0.0.0:23333/v1"
)
model_name = client.models.list().data[0].id
response = client.chat.completions.create(
model=model_name,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": " provide three suggestions about time management"},
],
temperature=0.8,
top_p=0.8
)
print(response)
如果你想使用异步的接口,可以尝试下面的例子:
import asyncio
from openai import AsyncOpenAI
async def main():
client = AsyncOpenAI(api_key='YOUR_API_KEY',
base_url='http://0.0.0.0:23333/v1')
model_cards = await client.models.list()._get_page()
response = await client.chat.completions.create(
model=model_cards.data[0].id,
messages=[
{
'role': 'system',
'content': 'You are a helpful assistant.'
},
{
'role': 'user',
'content': ' provide three suggestions about time management'
},
],
temperature=0.8,
top_p=0.8)
print(response)
asyncio.run(main())
关于其他 openai 接口的调用,也可以如法炮制。详情请参考 openai 官方文档
使用 lmdeploy APIClient
接口¶
如果你想用 /v1/chat/completions
接口,你可以尝试下面代码:
from lmdeploy.serve.openai.api_client import APIClient
api_client = APIClient(f'http://{server_ip}:{server_port}')
model_name = api_client.available_models[0]
messages = [{"role": "user", "content": "Say this is a test!"}]
for item in api_client.chat_completions_v1(model=model_name, messages=messages):
print(item)
如果你想用 /v1/completions
接口,你可以尝试:
from lmdeploy.serve.openai.api_client import APIClient
api_client = APIClient(f'http://{server_ip}:{server_port}')
model_name = api_client.available_models[0]
for item in api_client.completions_v1(model=model_name, prompt='hi'):
print(item)
关于 /v1/chat/interactive
接口,我们默认是关闭的。在使用时,请设置interactive_mode = True
打开它。否则,它会退化为 openai 接口。
在交互式推理中,每个对话序列的 id 必须唯一,所有属于该独立的对话请求,必须使用相同的 id。这里的 id 对应与接口中的 session_id
。
比如,一个对话序列中,有 10 轮对话请求,那么每轮对话请求中的 session_id
都要相同。
from lmdeploy.serve.openai.api_client import APIClient
api_client = APIClient(f'http://{server_ip}:{server_port}')
messages = [
"hi, what's your name?",
"who developed you?",
"Tell me more about your developers",
"Summarize the information we've talked so far"
]
for message in messages:
for item in api_client.chat_interactive_v1(prompt=message,
session_id=1,
interactive_mode=True,
stream=False):
print(item)
使用 Java/Golang/Rust¶
可以使用代码生成工具 openapi-generator-cli 将 http://{server_ip}:{server_port}/openapi.json
转成 java/rust/golang 客户端。
下面是一个使用示例:
$ docker run -it --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/openapi.json -g rust -o /local/rust
$ ls rust/*
rust/Cargo.toml rust/git_push.sh rust/README.md
rust/docs:
ChatCompletionRequest.md EmbeddingsRequest.md HttpValidationError.md LocationInner.md Prompt.md
DefaultApi.md GenerateRequest.md Input.md Messages.md ValidationError.md
rust/src:
apis lib.rs models
使用 cURL¶
cURL 也可以用于查看 API 的输出结果
查看模型列表
v1/models
curl http://{server_ip}:{server_port}/v1/models
对话
v1/chat/completions
curl http://{server_ip}:{server_port}/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "internlm-chat-7b",
"messages": [{"role": "user", "content": "Hello! How are you?"}]
}'
文本补全
v1/completions
curl http://{server_ip}:{server_port}/v1/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "llama",
"prompt": "two steps to build a house:"
}'
交互式对话
v1/chat/interactive
curl http://{server_ip}:{server_port}/v1/chat/interactive \
-H "Content-Type: application/json" \
-d '{
"prompt": "Hello! How are you?",
"session_id": 1,
"interactive_mode": true
}'
接入 WebUI¶
LMDeploy 提供 gradio 和 OpenAOE 两种方式,为 api_server 接入 WebUI。
方式一:通过 gradio 接入¶
# api_server_url 就是 api_server 产生的,比如 http://localhost:23333
# server_name 和 server_port 是用来提供 gradio ui 访问服务的
# 例子: lmdeploy serve gradio http://localhost:23333 --server-name localhost --server-port 6006
lmdeploy serve gradio api_server_url --server-name ${gradio_ui_ip} --server-port ${gradio_ui_port}
FAQ¶
当返回结果结束原因为
"finish_reason":"length"
,这表示回话长度超过最大值。如需调整会话支持的最大长度,可以通过启动api_server
时,设置--session_len
参数大小。当服务端显存 OOM 时,可以适当减小启动服务时的
backend_config
的cache_max_entry_count
大小当同一个
session_id
的请求给/v1/chat/interactive
函数后,出现返回空字符串和负值的tokens
,应该是session_id
混乱了,可以先将交互模式关闭,再重新开启。/v1/chat/interactive
api 支持多轮对话, 但是默认关闭。messages
或者prompt
参数既可以是一个简单字符串表示用户的单词提问,也可以是一段对话历史。关于停止符,我们只支持编码后为单个 index 的字符。此外,可能存在多种 index 都会解码出带有停止符的结果。对于这种情况,如果这些 index 数量太多,我们只会采用 tokenizer 编码出的 index。而如果你想要编码后为多个 index 的停止符,可以考虑在流式客户端做字符串匹配,匹配成功后跳出流式循环即可。
自定义对话模板,请参考chat_template.md
部署 VLM 类 openai 服务¶
本文主要介绍单个VL模型在单机多卡环境下,部署兼容 openai 接口服务的方式,以及服务接口的用法。为行文方便,我们把该服务名称为 api_server
。对于多模型的并行服务,请阅读请求分发服务器一文。
在这篇文章中, 我们首先介绍服务启动的两种方法,你可以根据应用场景,选择合适的。
其次,我们重点介绍服务的 RESTful API 定义,以及接口使用的方式,并展示如何通过 Swagger UI、LMDeploy CLI 工具体验服务功能
最后,向大家演示把服务接入到 WebUI 的方式,你可以参考它简单搭建一个演示 demo。
启动服务¶
以 huggingface hub 上的 llava-v1.6-vicuna-7b 模型为例,你可以任选以下方式之一,启动推理服务。
方式一:使用 lmdeploy cli 工具¶
lmdeploy serve api_server liuhaotian/llava-v1.6-vicuna-7b --server-port 23333
api_server 启动时的参数可以通过命令行lmdeploy serve api_server -h
查看。
比如,--tp
设置张量并行,--session-len
设置推理的最大上下文窗口长度,--cache-max-entry-count
调整 k/v cache 的内存使用比例等等。
方式二:使用 docker¶
使用 LMDeploy 官方镜像,可以运行兼容 OpenAI 的服务。下面是使用示例:
docker run --runtime nvidia --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HUGGING_FACE_HUB_TOKEN=<secret>" \
-p 23333:23333 \
--ipc=host \
openmmlab/lmdeploy:latest \
lmdeploy serve api_server liuhaotian/llava-v1.6-vicuna-7b
在这个例子中,lmdeploy server api_server
的命令参数与方式一一致。
RESTful API¶
LMDeploy 的 RESTful API 兼容了 OpenAI 以下 3 个接口:
/v1/chat/completions
/v1/models
/v1/completions
其中使用图片交互的接口是 /v1/chat/completions
,与 OpenAI 的一致。
服务启动后,你可以在浏览器中打开网页 http://0.0.0.0:23333,通过 Swagger UI 查看接口的详细说明,并且也可以直接在网页上操作,体验每个接口的用法,如下图所示。
若需要把服务集成到自己的项目或者产品中,我们推荐以下用法:
使用 openai 接口¶
以下代码是通过 openai 包使用 v1/chat/completions
服务的例子。运行之前,请先安装 openai 包: pip install openai
。
from openai import OpenAI
client = OpenAI(api_key='YOUR_API_KEY', base_url='http://0.0.0.0:23333/v1')
model_name = client.models.list().data[0].id
response = client.chat.completions.create(
model=model_name,
messages=[{
'role':
'user',
'content': [{
'type': 'text',
'text': 'Describe the image please',
}, {
'type': 'image_url',
'image_url': {
'url':
'https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg',
},
}],
}],
temperature=0.8,
top_p=0.8)
print(response)
使用 lmdeploy APIClient
接口¶
如果你想用 /v1/chat/completions
接口,你可以尝试下面代码:
from lmdeploy.serve.openai.api_client import APIClient
api_client = APIClient(f'http://0.0.0.0:23333')
model_name = api_client.available_models[0]
messages = [{
'role':
'user',
'content': [{
'type': 'text',
'text': 'Describe the image please',
}, {
'type': 'image_url',
'image_url': {
'url':
'https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg',
},
}]
}]
for item in api_client.chat_completions_v1(model=model_name,
messages=messages):
print(item)
使用 Java/Golang/Rust¶
可以使用代码生成工具 openapi-generator-cli 将 http://{server_ip}:{server_port}/openapi.json
转成 java/rust/golang 客户端。
下面是一个使用示例:
$ docker run -it --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/openapi.json -g rust -o /local/rust
$ ls rust/*
rust/Cargo.toml rust/git_push.sh rust/README.md
rust/docs:
ChatCompletionRequest.md EmbeddingsRequest.md HttpValidationError.md LocationInner.md Prompt.md
DefaultApi.md GenerateRequest.md Input.md Messages.md ValidationError.md
rust/src:
apis lib.rs models
部署 gradio 服务¶
通过 LMDeploy 启动 LLM 模型的 gradio 服务,并在 WebUI 上和模型对话特别简单,一条命令即可。
pip install lmdeploy[serve]
lmdeploy serve gradio {model_path}
把上面命令中的 {model_path}
换成 huggingface hub 上的模型 id,比如 internlm/internlm2-chat-7b,或者换成模型的本地路径就可以了。
关于命令的详细参数,请使用 lmdeploy serve gradio --help
查阅。
创建 huggingface demo¶
如果想要在 huggingface 上创建模型的在线演示项目,请按以下步骤进行。
第一步:创建 space¶
首先,注册一个 huggingface 的账号,注册成功后,可以点击右上角头像,选择 New Space 创建。 根据 huggingface 的引导选择需要的配置,完成后即可得到一个空白的 demo。
第二步:编写 demo 入口代码 app.py¶
以 internlm/internlm2-chat-7b
模型为例,将 space 空间中的app.py
内容填写为:
from lmdeploy.serve.gradio.turbomind_coupled import run_local
from lmdeploy.messages import TurbomindEngineConfig
backend_config = TurbomindEngineConfig(max_batch_size=8)
model_path = 'internlm/internlm2-chat-7b'
run_local(model_path, backend_config=backend_config, server_name="huggingface-space")
创建requirements.txt
文本文件,填写如下安装包:
lmdeploy
FAQs¶
ZeroGPU 适配问题。ZeroGPU不适用 LMDeploy Turbomind 引擎,请选择普通 GPU,或者把上述代码中的 backend_config 改成 PyTorchEngineConfig,就可以用 ZeroGPU 了。
gradio 版本问题,目前不支持 4.0.0 以上版本,可以在
app.py
中修改,类似:import os os.system("pip uninstall -y gradio") os.system("pip install gradio==3.43.0")
请求分发服务器¶
请求分发服务可以将多个 api_server 服务,进行并联。用户可以只需要访问代理 URL,就可以间接访问不同的 api_server 服务。代理服务内部会自动分发请求,做到负载均衡。
启动¶
启动代理服务:
python3 -m lmdeploy.serve.proxy.proxy --server_name {server_name} --server_port {server_port} --strategy "min_expected_latency"
启动成功后,代理服务的 URL 也会被脚本打印。浏览器访问这个 URL,可以打开 Swagger UI。
API¶
通过 Swagger UI,我们可以看到多个 API。其中,和 api_server 节点管理相关的有:
/nodes/status
/nodes/add
/nodes/remove
他们分别表示,查看所有的 api_server 服务节点,增加某个节点,删除某个节点。
和使用相关的 api 有:
/v1/models
/v1/chat/completions
/v1/completions
这些 API 的使用方式和 api_server 一样。
分发策略¶
代理服务目前的分发策略如下:
random: 根据用户提供的各个 api_server 节点的处理请求的能力,进行有权重的随机。处理请求的吞吐量越大,就越有可能被分配。部分节点没有提供吞吐量,将按照其他节点的平均吞吐量对待。
min_expected_latency: 根据每个节点现有的待处理完的请求,和各个节点吞吐能力,计算预期完成响应所需时间,时间最短的将被分配。未提供吞吐量的节点,同上。
min_observed_latency: 根据每个节点过去一定数量的请求,处理完成所需的平均用时,用时最短的将被分配。
INT4 模型量化和部署¶
LMDeploy 使用 AWQ 算法,实现模型 4bit 权重量化。推理引擎 TurboMind 提供了非常高效的 4bit 推理 cuda kernel,性能是 FP16 的 2.4 倍以上。它支持以下 NVIDIA 显卡:
图灵架构(sm75):20系列、T4
安培架构(sm80,sm86):30系列、A10、A16、A30、A100
Ada Lovelace架构(sm89):40 系列
在量化和部署之前,请确保安装了 lmdeploy.
pip install lmdeploy[all]
本文由以下章节组成:
模型量化¶
仅需执行一条命令,就可以完成模型量化工作。量化结束后,权重文件存放在 $WORK_DIR
下。
export HF_MODEL=internlm/internlm2-chat-7b
export WORK_DIR=internlm/internlm2-chat-7b-4bit
lmdeploy lite auto_awq \
$HF_MODEL \
--calib-dataset 'ptb' \
--calib-samples 128 \
--calib-seqlen 2048 \
--w-bits 4 \
--w-group-size 128 \
--batch-size 1 \
--search-scale False \
--work-dir $WORK_DIR
绝大多数情况下,在执行上述命令时,可选参数可不用填写,使用默认的即可。比如量化 internlm/internlm2-chat-7b 模型,命令可以简化为:
lmdeploy lite auto_awq internlm/ianternlm2-chat-7b --work-dir internlm2-chat-7b-4bit
Note:
我们建议 –work-dir 参数带有模型名字,就像上面的例子展示的那样。这样在推理时,就不用指定对话模板了。因为推理接口会以模糊搜索方式,选出和 –work-dir 近似的对话模板
如果量化模型精度有损,建议开启 –search-scale 重新量化,并调大 –batch-size,比如 8。search_scale 开启后,量化过程会比较耗时。–batch-size 会影响内存占用量,可以根据实际情况,酌情调整。
量化后的模型,可以用一些工具快速验证对话效果。
比如,直接在控制台和模型对话,
lmdeploy chat ./internlm2-chat-7b-4bit --model-format awq
或者,启动gradio服务,
lmdeploy serve gradio ./internlm2-chat-7b-4bit --server-name {ip_addr} --server-port {port} --model-format awq
然后,在浏览器中打开 http://{ip_addr}:{port},即可在线对话
模型评测¶
我们使用 OpenCompass 评测量化模型在各个维度上的能力
模型推理¶
量化后的模型,通过以下几行简单的代码,可以实现离线推理:
from lmdeploy import pipeline, TurbomindEngineConfig
engine_config = TurbomindEngineConfig(model_format='awq')
pipe = pipeline("./internlm2-chat-7b-4bit", backend_config=engine_config)
response = pipe(["Hi, pls intro yourself", "Shanghai is"])
print(response)
关于 pipeline 的详细介绍,请参考这里
除了推理本地量化模型外,LMDeploy 还支持直接推理 huggingface hub 上的通过 AWQ 量化的 4bit 权重模型,比如 lmdeploy 空间和 TheBloke 空间下的模型。
# 推理 lmdeploy 空间下的模型
from lmdeploy import pipeline, TurbomindEngineConfig
pipe = pipeline("lmdeploy/llama2-chat-70b-4bit",
backend_config=TurbomindEngineConfig(model_format='awq', tp=4))
response = pipe(["Hi, pls intro yourself", "Shanghai is"])
print(response)
# 推理 TheBloke 空间下的模型(试试codellama行不行)
from lmdeploy import pipeline, TurbomindEngineConfig, ChatTemplateConfig
pipe = pipeline("TheBloke/LLaMA2-13B-Tiefighter-AWQ",
backend_config=TurbomindEngineConfig(model_format='awq'),
chat_template_config=ChatTemplateConfig(model_name='llama2')
)
response = pipe(["Hi, pls intro yourself", "Shanghai is"])
print(response)
推理服务¶
LMDeploy api_server
支持把模型一键封装为服务,对外提供的 RESTful API 兼容 openai 的接口。以下为服务启动的示例:
lmdeploy serve api_server ./internlm2-chat-7b-4bit --backend turbomind --model-format awq
服务默认端口是23333。在 server 启动后,你可以在终端通过api_client
与server进行对话:
lmdeploy serve api_client http://0.0.0.0:23333
还可以通过 Swagger UI http://0.0.0.0:23333
在线阅读和试用 api_server
的各接口,也可直接查阅文档,了解各接口的定义和使用方法。
推理性能¶
我们在 NVIDIA GeForce RTX 4090 上使用 profile_generation.py,分别测试了 4-bit Llama-2-7B-chat 和 Llama-2-13B-chat 模型的 token 生成速度。测试配置为 batch size = 1,(prompt_tokens, completion_tokens) = (1, 512)
model | llm-awq | mlc-llm | turbomind |
---|---|---|---|
Llama-2-7B-chat | 112.9 | 159.4 | 206.4 |
Llama-2-13B-chat | N/A | 90.7 | 115.8 |
Key-Value(KV) Cache 量化¶
自 v0.4.0 起,LMDeploy 支持在线 kv cache int4/int8 量化,量化方式为 per-head per-token 的非对称量化。原来的 kv 离线量化方式移除。
直观上看,量化 kv 利于降低内存占用量。和 fp16 相比,int4/int8 kv 的内存可以分别减到 1/4 和 1/2。这意味着,在相同的内存条件下,kv 量化后,系统能支撑的并发数可以大幅提升,从而最终提高吞吐量。
但是,通常,量化会伴随一定的模型精度损失。我们使用了 opencompass 评测了若干个模型在应用了 int4/int8 量化后的精度,int8 kv 精度几乎无损,int4 kv 略有损失。详细结果放在了精度评测章节中。大家可以参考,根据实际需求酌情选择。
LMDeploy kv 4/8 bit 量化和推理支持如下 NVIDIA 显卡型号:
volta 架构(sm70): V100
图灵架构(sm75):20系列、T4
安培架构(sm80,sm86):30系列、A10、A16、A30、A100
Ada Lovelace架构(sm89):40 系列
Hopper 架构(sm90): H100, H200
总结来说,LMDeploy kv 量化具备以下优势:
量化不需要校准数据集
支持 volta 架构(sm70)及以上的所有显卡型号
kv int8 量化精度几乎无损,kv int4 量化精度在可接受范围之内
推理高效,在 llama2-7b 上加入 int8/int4 kv 量化,RPS 相较于 fp16 分别提升近 30% 和 40%
接下来,我们以 internlm2-chat-7b 模型为例,介绍 kv 量化和推理的若干应用。而在此之前,请安装 lmdeploy
pip install lmdeploy
应用示例¶
通过 LMDeploy 应用 kv 量化非常简单,只需要设定 quant_policy
参数。
LMDeploy 规定 qant_policy=4
表示 kv int4 量化,quant_policy=8
表示 kv int8 量化。
离线推理¶
from lmdeploy import pipeline, TurbomindEngineConfig
engine_config = TurbomindEngineConfig(quant_policy=8)
pipe = pipeline("internlm/internlm2-chat-7b", backend_config=engine_config)
response = pipe(["Hi, pls intro yourself", "Shanghai is"])
print(response)
推理服务¶
lmdeploy serve api_server internlm/internlm2-chat-7b --quant-policy 8
精度评测¶
我们把 lmdeploy 的 kv 量化应用在若干 LLM 模型上,并使用 opencompass 评测推理精度,结果如下表所示:
- | - | - | llama2-7b-chat | - | - | internlm2-chat-7b | - | - | qwen1.5-7b-chat | - | - |
---|---|---|---|---|---|---|---|---|---|---|---|
dataset | version | metric | kv fp16 | kv int8 | kv int4 | kv fp16 | kv int8 | kv int4 | fp16 | kv int8 | kv int4 |
ceval | - | naive_average | 28.42 | 27.96 | 27.58 | 60.45 | 60.88 | 60.28 | 70.56 | 70.49 | 68.62 |
mmlu | - | naive_average | 35.64 | 35.58 | 34.79 | 63.91 | 64 | 62.36 | 61.48 | 61.56 | 60.65 |
triviaqa | 2121ce | score | 56.09 | 56.13 | 53.71 | 58.73 | 58.7 | 58.18 | 44.62 | 44.77 | 44.04 |
gsm8k | 1d7fe4 | accuracy | 28.2 | 28.05 | 27.37 | 70.13 | 69.75 | 66.87 | 54.97 | 56.41 | 54.74 |
race-middle | 9a54b6 | accuracy | 41.57 | 41.78 | 41.23 | 88.93 | 88.93 | 88.93 | 87.33 | 87.26 | 86.28 |
race-high | 9a54b6 | accuracy | 39.65 | 39.77 | 40.77 | 85.33 | 85.31 | 84.62 | 82.53 | 82.59 | 82.02 |
具体的评测方式可以参考这份指南。评测时,请在config文件中,为推理引擎添加 quant_policy
参数。
推理效率¶
model | kv type | test settings | RPS | v.s. kv fp16 |
---|---|---|---|---|
llama2-chat-7b | fp16 | tp1 / ratio 0.8 / bs 256 / prompts 10000 | 14.98 | 1.0 |
- | int8 | tp1 / ratio 0.8 / bs 256 / prompts 10000 | 19.01 | 1.27 |
- | int4 | tp1 / ratio 0.8 / bs 256 / prompts 10000 | 20.81 | 1.39 |
llama2-chat-13b | fp16 | tp1 / ratio 0.9 / bs 128 / prompts 10000 | 8.55 | 1.0 |
- | int8 | tp1 / ratio 0.9 / bs 256 / prompts 10000 | 10.96 | 1.28 |
- | int4 | tp1 / ratio 0.9 / bs 256 / prompts 10000 | 11.91 | 1.39 |
internlm2-chat-7b | fp16 | tp1 / ratio 0.8 / bs 256 / prompts 10000 | 24.13 | 1.0 |
- | int8 | tp1 / ratio 0.8 / bs 256 / prompts 10000 | 25.28 | 1.05 |
- | int4 | tp1 / ratio 0.8 / bs 256 / prompts 10000 | 25.80 | 1.07 |
上述结果使用的测试脚本是 benchmark/profile_throughput.py
W8A8 LLM 模型部署¶
LMDeploy 提供了使用 8 bit 整数对神经网络模型进行量化和推理的功能。
在开始推理前,需要确保已经正确安装了 lmdeploy 和 openai/triton。可以通过以下命令进行安装:
pip install lmdeploy
pip install triton>=2.1.0
8bit 权重模型推理¶
如果你需要进行 8 bit 权重模型推理,可以直接从 LMDeploy 的 model zoo 下载已经量化好的 8bit 权重模型。以8bit 的 Internlm-chat-7B 模型为例,可以从 model zoo 直接下载:
git-lfs install
git clone https://huggingface.co/lmdeploy/internlm-chat-7b-w8 (coming soon)
你也可以参考”8bit 权重量化”章节的内容手动将原 16bit 权重量化为 8bit,并保存至 internlm-chat-7b-w8
目录下,操作命令如下:
lmdeploy lite smooth_quant internlm/internlm-chat-7b --work-dir ./internlm-chat-7b-w8
然后,执行以下命令,即可在终端与模型对话:
lmdeploy chat ./internlm-chat-7b-w8 --backend pytorch
启动 gradio 服务¶
Coming soon…
推理速度¶
Coming soon…
8bit 权重量化¶
进行 8bit 权重量化需要经历以下三步:
权重平滑:首先对语言模型的权重进行平滑处理,以便更好地进行量化。
模块替换:使用
QRSMNorm
和QLinear
模块替换原模型DecoderLayer
中的RSMNorm
模块和nn.Linear
模块。lmdeploy/pytorch/models/q_modules.py
文件中定义了这些量化模块。保存量化模型:完成上述必要的替换后,我们即可保存新的量化模型。
我们在lmdeploy/lite/api/smooth_quantity.py
脚本中已经实现了以上三个步骤。例如,可以通过以下命令得到量化后的 Internlm-chat-7B 模型的模型权重:
lmdeploy lite smooth_quant internlm/internlm-chat-7b --work-dir ./internlm-chat-7b-w8
保存之后,你就可以通过调用from_pretrained接口来实例化你的量化模型。
TurboMind 框架¶
TurboMind 是一款关于 LLM 推理的高效推理引擎,基于英伟达的 FasterTransformer 研发而成。它的主要功能包括:LLaMa 结构模型的支持,persistent batch 推理模式和可扩展的 KV 缓存管理器。
TurboMind 结构¶
+--------------------+
| API |
+--------------------+
| ^
请 求 | | 流式回调
v |
+--------------------+ 获取 +-------------------+
| Persistent Batch | <-------> | KV Cache 管理器 |
+--------------------+ 更新 +-------------------+
^
|
v
+------------------------+
| LLaMa推理实现 |
+------------------------+
| FT kernels & utilities |
+------------------------+
Persistent Batch¶
你也许在别的项目中看到这项机制的另一个名字: continuous batching
。在开发这个功能时,我们将对话式 LLM 的推理建模为一个持续运行的 batch ,其生命周期跨越整个服务过程,故将其命名为 persistent batch
。简单来说是这样实现的:
该功能会预先准备好 N 个 batch slots。
当有空闲 slots 时, 请求就会加入到 batch 中。当请求对应的 tokens 都生成完毕后,对应的 batch slot 会立刻被释放,接收新的请求。
当一个 sequence 命中缓存时(见下文),它的历史 token 不必在每轮中都进行解码,所以它的 token 生成过程会即刻开始。
整个 batch 会自动扩缩容来避免不必要的计算。
KV 缓存管理器¶
TurboMind 的 KV 缓存管理器 是一个内存池类型的对象,并且在其中加入了 LRU 的实现,这样整个管理器可以被看作是一个 KV 缓存的缓存。大致工作方式如下:
KV 缓存由管理器分配。管理器会根据预先配置好的 slot 数量开辟空间。每个 slot 对应于一个 sequence 所需的 KV 缓存。分配的内存块大小可通过配置来实现预分配或者按需分配(或介于两者之间)。
当有新的请求,但是缓存池中没有空闲 slot时,根据 LRU 机制,管理器会踢除最近使用最少的 sequence,把它占据的 slot 分给新的请求。不仅仅如此,
sequence获取到了slot,类似缓存命中。它在缓存中的历史KV会被直接返回,而不用再进行context decoding 。
被踢除的 sequences 不会被完全的删除,而是会被转换成最简洁的形式,例如 token IDs 。当之后获取到相同的 sequence id 时 (即 cache-miss 状态),这些 token IDs 将被 FMHA 的 context decoder 解码并被转回 KV 缓存。
踢除和转换均由 TurboMind 内部自动管理所以对用户来说是透明的。从用户的使用角度来看,使用了 TurboMind 的系统就像是可以访问无限的设备内存。
TurboMind 的 LLaMa 实现¶
我们对 LLaMa 系列模型的实现是从 FasterTransformer 中的 Gpt-NeX 模型修改而来的。除了对 LLaMa 系列进行基本重构和修改外,我们还做了一些改进以实现会话模型的高性能推理,其中最重要的是:
支持多轮对话中的快速文本解码。我们用基于 cutlass 的 FMHA 实现替代了 context decoder 中的注意力机制实现,从而支持了 Q/K 长度不匹配的情况。
我们在 context FMHA 和 generation FMHA 中都加入了间接缓冲指针,支持 batch 中不连续的 KV 缓存。
为了支持 persistent batch 的并发推理,我们设计了新的同步机制来协调在张量并型模式下的工作线程。
我们实现了 INT8 KV cache,降低了内存开销,提高了批处理大小和系统吞吐量。这在实际场景中非常有用,因为相比权重和其他激活,KV cache 会消耗更多的内存和内存带宽。
我们解决了单个进程内多个模型实例在 TP 模式下运行时 NCCL 卡住的问题。NCCL APIs 现由 host 端的同步 barriers 保护。
API¶
TurboMind 的 Python API 支持流式结果返回和张量并行模式。
同时 TurboMind 也继承了 FasterTransformer 能够注册为 Triton Inference Server 推理后端的能力。但是为了支持 persistent batch 中的并发请求,我们不再像 FasterTransformer 那样使用 sequence batching 或者 dynamic batching 。相反,TurboMind 负责记录和管理请求序列的状态。
TurboMind 和 FasterTransformer 的区别¶
除了上文中提到的功能外,TurboMind 相较于 FasterTransformer 还有不少差别。譬如不少 FasterTransformer 的功能在 TurboMind 中都被去掉了,这其中包括前缀提示词、 beam search 、上下文 embedding、稀疏化 GEMM 操作和对应 GPT 或 T5 等结构的模型的支持等等。
FAQ¶
对 Huggingface 模型的支持¶
因为历史因素, TurboMind 的权重设计是基于 LLaMa 的官方实现 完成的,两者只相差一个转置操作。但是 Huggingface 版本的实现却是另一种形式,两种权重实现方式在 W_q
和 W_k
上的区别我们在 deploy.py 进行了适配处理,用户可前往查看。
lmdeploy.pytorch 架构¶
lmdeploy.pytorch
是 LMDeploy 提供的推理后端之一。与着重于性能的 turbomind 相比,lmdeploy.pytorch 以较小的性能开销为代价,提供了一套更容易开发与扩展的大模型推理实现。
设计¶
API¶
lmdeploy.pytorch 可以与 turbomind 共享同样的服务接口,这些服务接口通过 Engine 与 EngineInstance 与 lmdeploy.pytorch 进行交互。
EngineInstance 是推理请求的发起者,它会将推理请求组织成特定格式发送给 Engine,以此实现流式推理。EngineInstance 的推理接口是线程安全的,服务发起者可以在不同线程中启动各自的 EngineInstance,Engine 回根据当前资源与推理请求自动进行 batch 化处理。
Engine 是推理请求的接收与执行者。它包含如下的组件来完成这项任务:
ModelAgent 对象负责模型的加载、缓存管理以及 tensor parallelism 的管理。
Scheduler 对象负责 session 的管理,sequence 与 lora adapter 所需要的资源的分配。
RequestManager 负责请求的发送与接收,可以通过它与 EngineInstance 交互。
Engine¶
为了应对异步推理请求,Engine 在启动后会维护一个线程,循环如下操作:
通过 RequestManager 读取请求,对各种请求进行分类处理。
Scheduler 规划哪些请求可以被处理,以及它们所需的缓存和 adapters。
ModelAgent 根据步骤 2. 得到的信息为输入分配资源,然后使用 patch 后的模型进行推理
Scheduler 根据推理结果更新请求状态
RequestManager 将输出返回给发送者(EngineInstance),回到步骤 1.
下面我们将介绍上述步骤中用到的几个重要组件
Scheduler¶
在进行大模型的推理时,通常会把 attention 的历史输入 key 和 value 缓存起来,以避免在未来的推理中进行重复计算。这种情况下如果要进行多 batch 的推理,由于不同数据的序列长度可能不同,kv 会进行大量的填充,浪费很多显存资源,也限制了模型的并发推理能力上限。
vLLM 提了一种 paging 策略,以 page block 为单位为 key value 分配缓存,这样就可以避免由于 padding 导致的显存浪费。 lmdeploy.pytorch 中的 Scheduler 也遵循同样的设计,根据请求的长度合理分配所需的资源,并撤出暂时不使用的资源以保证存储资源的高效利用。
lmdeploy.pytorch 还对 S-LoRA 的支持,S-LoRA 是一种对单模型多 adapter 的支持方案。LoRA 在推理时通常会把 adapter 融合进模型权重当中,同时使用复数个 adapter 会导致显存使用量的激增;S-LoRA 不对 adapter 进行融合,通过使用 unified paging,在推理时动态换入需要使用的 adapter,大幅降低了使用 adapter 的显存开销。Scheduler 中也实现了相关的功能,让用户可以更方便的使用自己的 adapter.
ModelAgent¶
lmdeploy.pytorch 中对 Tensor Parallelism(TP)进行了支持,不同的 TP 参数对模型的构造、权重处理、分配 cache 都存在影响。ModelAgent 对这些内容进行了封装,让 Engine 不用再关心这部分细节。
ModelAgent 有两个重要组件:
patched_model 是更新后的 transformer 模型,更新后的模型添加了各种功能的支持,包括更高性能的子模块实现、TP、量化等等
cache_engine 是缓存的分配与交换模块。它接收来自 scheduler 的交换请求,执行 host-device 间显存交换,adapter 加载等工作
Patching¶
为了降低接入模型的门槛,我们实现了一套简单的 patch 机制来简化实现的替换。
以 Llama 模型的 LlamaAttention.forward 为例,我们可以重新写一个 forward 的实现:
class CustomLlamaAttention(nn.Module):
def forward(self, ...):
# custom forward
然后在 lmdeploy.pytorch.models.module_map
中注册模块的映射
MODULE_MAP.update({
'transformers.models.llama.modeling_llama.LlamaAttention':
'qualname.to.CustomLlamaAttention'})
经过 patch 后的模型就会使用新的 forward 实现。TP、量化等功能也依赖 patch 机制,请阅读 lmdeploy.pytorch 新模型支持 了解更多细节。
特性¶
Continuous Batching: 由于输入序列的长度不一样,batching 通常需要对输入进行 padding,这种 padding 会导致后续运算的计算量增加、影响速度,也会使得显存的占用大幅增加。遵循许多其他成熟框架的方案,lmdeploy.pytorch 采用了 continuous batching 的方式对输入做了连续化处理,避免了多余的资源占用。
Tensor Parallelism: 大模型可能会占用远超一张显卡的显存量,为了支持这样的大模型的推理,我们实现了 Tensor 并发,模型的权重会被分布在不同的设备中,每张 GPU 设备负责一部分计算,减少了单卡显存占用,也充分利用了多显卡的计算优势。
S-LoRA: LoRA adapter 可以帮助我们使用有限的显存来调优大模型,S-LoRA 可以帮助我们在有限的显存中同时使用复数个 LoRA 权重,扩展模型的能力。
Quantization: 量化可以帮助我们进一步减少显存占用,提高推理性能。lmdeploy.pytorch 分支中添加了 w8a8 模型量化的支持,可以阅读 w8a8 了解更多细节。
lmdeploy.pytorch 新模型支持¶
lmdeploy.pytorch 被设计用来简化新模型的支持以及原型的开发,新模型的支持依赖于 patch 机制,对原模型做修改以及功能添加,以期可以最大程度上复用模型的原始实现,减少工作量。
模型支持¶
我们以 transformers 中的 llama 实现来介绍模型支持的流程
在开始之前,我们首先要了解一下模型的输入。lmdeploy.pytorch 的输入与标准 transformers 模型的输入略有不同,差异主要体现在如下方面:
由于支持了 continuous batching,一个 batch 的输入
input_ids
会被拼接成一维的长序列,然后unsqueeze(0)
来保证输入维度与 transformers 中相同。这样的输入不会影响 MLP 以及 RMSNorm 等模块的计算。由于添加了对 paged attention 的支持,
past_key_value
不再是原来的大小,而是一组形状为[num_blocks, block_size, num_heads, head_dim]
的 cache 块,num_blocks 为总 block 数量,由可用显存大小决定,block_size 为预设的块大小。这样的输入改变会影响到 LlamaModel 和 LlamaAttention 的计算,因此要对这两个模块的实现进行修改。由于上述输入的改变,模型中需要一些额外的输入来支持推理,比如 batch 中的序列起始位置和长度,kv cache 的 block table 等。这些输入并不在模块的 forward 参数列表中,我们需要维护一个上下文以获得这些输入。
上面的输入改动会影响 LlamaModel 和 LlamaAttention,首先我们来实现新的 LlamaModel,这是对原始实现的简化,我们删除了很多检查代码,以避免由于输入改变造成的断言失败,仅保留了最小程度的代码:
# lmdeploy/pytorch/models/llama.py
class LlamaModel(nn.Module):
def forward(
self,
input_ids: torch.LongTensor = None,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_values: Optional[List[torch.FloatTensor]] = None,
inputs_embeds: Optional[torch.FloatTensor] = None,
use_cache: Optional[bool] = None,
output_attentions: Optional[bool] = None,
output_hidden_states: Optional[bool] = None,
return_dict: Optional[bool] = None,
) -> Union[Tuple, BaseModelOutputWithPast]:
"""Rewrite implementation of LlamaModel.forward."""
inputs_embeds = self.embed_tokens(input_ids)
hidden_states = inputs_embeds
# decoder layers
for idx, decoder_layer in enumerate(self.layers):
past_key_value = past_key_values[idx]
layer_outputs = decoder_layer(
hidden_states,
attention_mask=attention_mask,
position_ids=position_ids,
past_key_value=past_key_value,
output_attentions=output_attentions,
use_cache=use_cache,
)
hidden_states = layer_outputs[0]
hidden_states = self.norm(hidden_states)
return BaseModelOutputWithPast(
last_hidden_state=hidden_states,
past_key_values=past_key_values,
hidden_states=None,
attentions=None,
)
然后是对 LlamaAttention 模块的改写。按顺序实现如下操作:
kqv proj
rotary embedding
填充 kv cache
MHA 计算
o proj
continuous batching 和 kv cache 的改动对该模块的影响比较大
# lmdeploy/pytorch/models/llama.py
from lmdeploy.pytorch.kernels import apply_rotary_pos_emb, fill_kv_cache, paged_attention_fwd
class LlamaAttention(nn.Module):
def forward(
self,
hidden_states: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_value: Optional[Tuple[torch.Tensor]] = None,
output_attentions: bool = False,
use_cache: bool = False,
) -> Tuple[torch.Tensor, Optional[torch.Tensor],
Optional[Tuple[torch.Tensor]]]:
"""Rewrite of LlamaAttention.forward."""
context = self.context.context
history_lengths = context.history_lengths
position_ids_1d = context.position_ids_1d
block_offsets = context.block_offsets
# qkv proj
query_states = q_proj(hidden_states)
key_states = k_proj(hidden_states)
value_states = v_proj(hidden_states)
query_states = query_states.view(-1, num_heads, head_dim)
key_states = key_states.view(-1, num_kv_heads, head_dim)
value_states = value_states.view(-1, num_kv_heads, head_dim)
# rotary embedding
max_seq_len = position_ids.size(-1)
kv_seq_len = max_seq_len + max(history_lengths)
if kv_seq_len >= self.rotary_emb.max_seq_len_cached:
cos, sin = self.rotary_emb(value_states,
seq_len=kv_seq_len + 128)
query_states, key_states = apply_rotary_pos_emb(
query_states,
key_states,
self.rotary_emb.cos_cached,
self.rotary_emb.sin_cached,
position_ids,
position_ids_1d,
q_embed=query_states,
k_embed=key_states)
# fill kv cache
kv_seq_length = context.kv_seq_length
q_seq_length = context.q_seq_length
q_start_loc = context.q_start_loc
fill_kv_cache(key_states,
value_states,
past_key_value[0],
past_key_value[1],
q_start_loc,
q_seq_length,
block_offsets=block_offsets,
history_lengths=history_lengths,
context=context)
# attention
attn_output = query_states
block_size = past_key_value[0].size(1)
paged_attention_fwd(
query_states,
past_key_value[0],
past_key_value[1],
attn_output,
block_offsets,
q_start_loc=q_start_loc,
q_seqlens=q_seq_length,
kv_seqlens=kv_seq_length,
max_seqlen=max_seq_len,
)
hidden_size = num_heads * head_dim
attn_output = attn_output.reshape(*hidden_states.shape[:-1], hidden_size)
# o proj
attn_output = o_proj(attn_output)
return attn_output, None, past_key_value
上面的代码有几处值得注意的地方,首先是 context 对象。我们需要 history_lengths、block_offsets 等参数辅助运算,这些参数无法通过模型的 forward 函数传递进来。因此我们维护了一个 context 对象,把几乎所有可能用到的输入参数都保存在其中,方便在各个模块间共享。context 对象可以通过 self.context.context
来访问,结构可以参考 context-结构。
另一个值得注意的地方就是自定义 kernel,由于输入形式的改变,原来的 LlamaAttention 实现变得不再适用,为了保证推理的速度和正确性,我们在 lmdeploy.pytorch.kernels 中实现了许多自定义的 triton kernel,上面的模块中就用到了 apply_rotary_pos_emb
,fill_kv_cache
和 paged_attention_fwd
,分别负责实现 rotary embedding,填充 kv cache 还有 attention 的计算。
有了上述的两个模块后,还需要将他们注册到 lmdeploy/pytorch/models/module_map.py
中,进行原模块与 patch 模块的映射
# lmdeploy/pytorch/models/module_map.py
MODEL_MAP.update({
'transformers.models.llama.modeling_llama.LlamaAttention':
'lmdeploy.pytorch.models.llama.LlamaAttention',
'transformers.models.llama.modeling_llama.LlamaModel':
'lmdeploy.pytorch.models.llama.LlamaModel'
})
完成注册后,Engine 在启动时就会将这两个模块 patch 成新的实现,完成后续的部署任务。
Tensor 并发支持¶
为了支持 Tensor 并发,需要对模型的权重做切分。让我们试着为上面接入的 Llama 模型添加 TP 的支持。
Llama 中涉及到 Tensor 并发的模块是 LlamaAttention 中的 qkvo proj 和 LlamaMLP 中的 gate,up 和 down proj。其中 o_proj 和 down_proj 需要按行切分,剩下的按列切分。我们可以在对应的模块中实现 _distribution_partition_fn
函数:
# lmdeploy/pytorch/models/llama.py
from ..dist_utils import (colwise_parallelize_linear_fn,
rowwise_parallelize_linear_fn)
class LlamaAttention(nn.Module):
@classmethod
def _distribute_partition_fn(cls, mod_name: str, mod: nn.Module,
device_mesh: DeviceMesh):
"""Distribution partition callback."""
if mod_name in ['q_proj', 'k_proj', 'v_proj']:
colwise_parallelize_linear_fn(mod,
device_mesh=device_mesh,
to_local=True)
elif mod_name in ['o_proj']:
rowwise_parallelize_linear_fn(mod,
device_mesh=device_mesh,
to_local=True)
class LlamaMLP(nn.Module):
@classmethod
def _distribute_partition_fn(cls, mod_name: str, mod: nn.Module,
device_mesh: DeviceMesh):
"""Distribution partition callback."""
if mod_name in ['gate_proj', 'up_proj']:
colwise_parallelize_linear_fn(mod,
device_mesh=device_mesh,
to_local=True)
elif mod_name in ['down_proj']:
rowwise_parallelize_linear_fn(mod,
device_mesh=device_mesh,
to_local=True)
_distribute_partition_fn
会在加载模型权重时被调用,对应的权重会被按照特定的形式分配到对应的设备中。
按照目前的方案切分后的权重,需要对 o_proj 和 down_proj 的结果进行 all_reduce 操作才能得到正确的结果。可以选择将 all_reduce 放在模型的 forward 函数中,也可以选择另一种方案,添加 _distribute_output_fn
函数:
# lmdeploy/pytorch/models/llama.py
import torch.distributed as dist
class LlamaAttention(nn.Module):
@classmethod
def _distribute_output_fn(cls, outputs, device_mesh: DeviceMesh):
"""Distribution output hook."""
dist.all_reduce(outputs[0])
return outputs
class LlamaMLP(nn.Module):
@classmethod
def _distribute_output_fn(cls, outputs, device_mesh: DeviceMesh):
"""Distribution output hook."""
dist.all_reduce(outputs)
return outputs
最后别忘了将 LlamaMLP 也注册进 module_map 中
# lmdeploy/pytorch/models/module_map.py
MODEL_MAP.update({
'transformers.models.llama.modeling_llama.LlamaMLP':
'lmdeploy.pytorch.models.llama.LlamaMLP'
})
这样就可以利用多卡的优势,让更大的模型部署成为可能
模块调试¶
当模型的输出不符合预期时,我们会希望调试某个特定模块以确定添加的重写是否正确。lmdeploy.pytorch
提供了一些工具以帮助进行精度对齐。还是以上面提到的 LlamaAttention
模块为例。
首先,我们通过 transformers 的 API 得到想要调试的子模块的一个实例:
import torch
from transformers import AutoModelForCausalLM
# get module
model_path = 'meta-llama/Llama-2-7b-chat-hf'
dtype = torch.float16
model = AutoModelForCausalLM.from_pretrained(model_path).to(torch.float16).cuda()
self_attn = model.model.layers[0].self_attn
然后,使用 ModuleIOExtractor
工具可以生成该模块的一组输入输出
from lmdeploy.pytorch.tools.make_inputs import ModuleIOExtractor
# extract module input/output
input_ids = torch.tensor([[1, 2, 3, 4, 5]]).cuda()
extractor = ModuleIOExtractor(model, self_attn)
attn_args, attn_kwargs, attn_output = extractor.extract(input_ids)
重写模块的输入与原模块略有不同,主要体现在三方面:
模型需要一些特殊输入输出,他们以
StepContext
的形式传入,可以使用make_step_context
生成。input_ids
,hidden_states
等数据都被 continuous 化,可以使用continuous_tensor
进行处理。由于 paged caching 的需要,
past_key_value
需要被 page 化处理。
基于以上原因,我们要对提取的输入进行加工:
from lmdeploy.pytorch.tools.make_inputs import make_step_context
from lmdeploy.pytorch.tools.layout_convert import continuous_tensor
# create patched input/output
context = make_step_context(input_ids,
kv_cache_dtype=dtype,
num_key_value_heads=32)
seq_length = context.q_seq_length
attn_kwargs['hidden_states'] = continuous_tensor(
attn_kwargs['hidden_states'],
seq_length)
attn_kwargs['past_key_value'] = context.kv_caches[0]
然后就可以启动重写,并比较结果正确性了。(注意输出也要 continuous 化后进行比较)
from lmdeploy.pytorch.models import patch
# patch and test
patched_self_attn = patch(self_attn, extra_args=['context'])
with torch.inference_mode():
patched_output = patched_self_attn.patched_forward(*attn_args,
**attn_kwargs,
context=context)
torch.testing.assert_close(patched_output[0],
continuous_tensor(attn_output[0], seq_length))
可以通过上述方法调试重写模块,直到精度满足预期。
附录¶
context 结构¶
@dataclass
class StepContext:
"""context of Model.
"""
inputs: ModelInputs
block_offsets: torch.LongTensor
position_ids: torch.LongTensor
position_ids_1d: torch.LongTensor
q_start_loc: torch.LongTensor
history_lengths: torch.LongTensor
seq_length: torch.LongTensor
max_seq_length: int
kv_seq_length: torch.LongTensor
kv_caches: List
is_decoding: bool
world_size: int = 1
json_config: Dict = None
local_adapter_ids: torch.LongTensor = None
global_adapter_ids: torch.LongTensor = None
adapter_offsets: torch.LongTensor = None
max_rank: int = 0
FAQ¶
如何访问 patch 前的模块?
有时我们只希望在函数前后加一个 hook 代码,不希望大段的拷贝函数,可以通过 self.origin_mod
访问 patch 前的模块。
非 transformers 官方的模型该如何注册?
一些模型的实现代码可能是以 remote code 的形式添加的,这样的模块无法通过完整的 qualname 来定位。lmdeploy.pytorch 支持使用缩写的模块名进行注册:
MODULE_MAP.update({
'modeling_internlm.InternLMAttention':
'lmdeploy.pytorch.models.internlm.PatchedInternLMAttention',
})
[!NOTE]
缩写的优先级会更低,有条件的话还是鼓励使用完整的 qualname 进行注册。
模块出现同名但不同实现怎么处理?
目前推荐的做法是同名就映射到同一个实现中,然后在实现内部根据模块的固有参数来判断模型该使用的类型,以 baichuan2 7b/13b 为例:
class BaichuanModel(nn.Module):
def forward(self, ...):
if self.config.num_hidden_layers == 32:
return forward_7b(...)
else:
return forward_default(...)
如果希望在推理前对模块进行初始化?
可以实现模块的 _update_model_fn
函数,它会在模块的权重都加载完,完成 TP 权重切分后被调用
class LlamaAttention:
def _update_model_fn(self):
# ADD YOUR CODE HERE
长文本外推¶
长文本外推指 LLM 推理时处理比训练文本更长数据的能力。TurboMind 引擎目前支持 LlamaDynamicNTKScalingRotaryEmbedding, 并与 HuggingFace 的实现对齐。
如何使用¶
如果要直接加载 HuggingFace 格式的模型,可以通过修改 TurbomindEngineConfig 参数的方式赋予模型外推能力。将 session_len
修改为外推的长度,并将 rope_scaling_factor
修改为不小于 1.0 的值。
以 InternLM2 为例,可以使用如下方式,激活长文本推理能力:
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig
backend_config = TurbomindEngineConfig(rope_scaling_factor=2.0, session_len=160000)
pipe = pipeline('internlm/internlm2-chat-7b', backend_config=backend_config)
prompt = 'Use a long prompt to replace this sentence'
gen_config = GenerationConfig(top_p=0.8,
top_k=40,
temperature=0.8,
max_new_tokens=1024)
response = pipe(prompt, gen_config=gen_config)
print(response)
评测¶
我们使用多种方式评测 LMDeploy 长文本推理能力,分别是 passkey retrieval 实验、大海捞针实验 和计算困惑度
Passkey Retrieval¶
执行如下代码,可以测试在长文本中找到特殊 key 成功和失败的次数
import numpy as np
from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
session_len = 160000
backend_config = TurbomindEngineConfig(rope_scaling_factor=2.0, session_len=session_len)
pipe = pipeline('internlm/internlm2-chat-7b', backend_config=backend_config)
def passkey_retrival(session_len, n_round=5):
# create long context input
tok = pipe.tokenizer
task_description = 'There is an important info hidden inside a lot of irrelevant text. Find it and memorize them. I will quiz you about the important information there.'
garbage = 'The grass is green. The sky is blue. The sun is yellow. Here we go. There and back again.'
for _ in range(n_round):
n_times = (session_len - 1000) // len(tok.encode(garbage))
n_garbage_prefix = np.random.randint(0, n_times)
n_garbage_suffix = n_times - n_garbage_prefix
garbage_prefix = ' '.join([garbage] * n_garbage_prefix)
garbage_suffix = ' '.join([garbage] * n_garbage_suffix)
pass_key = np.random.randint(1, 50000)
information_line = f'The pass key is {pass_key}. Remember it. {pass_key} is the pass key.' # noqa: E501
final_question = 'What is the pass key? The pass key is'
lines = [
task_description,
garbage_prefix,
information_line,
garbage_suffix,
final_question,
]
# inference
prompt = ' '.join(lines)
response = pipe([prompt])
print(pass_key, response)
passkey_retrival(session_len, 5)
困惑度¶
下面展示使用 LMDeploy 计算困惑度的用法
from datasets import load_dataset
from lmdeploy import TurbomindEngineConfig
from lmdeploy.turbomind import TurboMind
import numpy as np
# load model and tokenizer
engine_config = TurbomindEngineConfig(rope_scaling_factor=2.0, session_len=160000)
engine = TurboMind.from_pretrained('internlm/internlm2-chat-7b', engine_config)
tokenizer = engine.tokenizer
generator = engine.create_instance()
# get perplexity
text = 'The grass is green. The sky is blue. The sun is yellow'
input_ids = tokenizer.encode(text)
loss = generator.get_ppl(input_ids)[0]
ppl = np.exp(loss)
自定义对话模板¶
被应用的对话模板效果,可以通过设置日志等级为INFO
进行观测。
LMDeploy 支持两种添加对话模板的形式:
一种是利用现有对话模板,直接配置一个如下的 json 文件使用。
{ "model_name": "your awesome chat template name", "system": "<|im_start|>system\n", "meta_instruction": "You are a robot developed by LMDeploy.", "eosys": "<|im_end|>\n", "user": "<|im_start|>user\n", "eoh": "<|im_end|>\n", "assistant": "<|im_start|>assistant\n", "eoa": "<|im_end|>", "separator": "\n", "capability": "chat", "stop_words": ["<|im_end|>"] }
model_name 为必填项,可以是 LMDeploy 内置对话模板名(通过
lmdeploy list
可查阅),也可以是新名字。其他字段可选填。 当 model_name 是内置对话模板名时,json文件中各非 null 字段会覆盖原有对话模板的对应属性。 而当 model_name 是新名字时,它会把将BaseChatTemplate
直接注册成新的对话模板。其具体定义可以参考BaseChatTemplate。这样一个模板将会以下面的形式进行拼接。
{system}{meta_instruction}{eosys}{user}{user_content}{eoh}{assistant}{assistant_content}{eoa}{separator}{user}...
在使用 CLI 工具时,可以通过
--chat-template
传入自定义对话模板,比如:lmdeploy serve api_server internlm/internlm2-chat-7b --chat-template ${JSON_FILE}
也可以在通过接口函数传入,比如:
from lmdeploy import ChatTemplateConfig, serve serve('internlm/internlm2-chat-7b', chat_template_config=ChatTemplateConfig.from_json('${JSON_FILE}'))
一种是以 LMDeploy 现有对话模板,自定义一个python对话模板类,注册成功后直接用即可。优点是自定义程度高,可控性强。 下面是一个注册 LMDeploy 对话模板的例子:
from lmdeploy.model import MODELS, BaseChatTemplate @MODELS.register_module(name='customized_model') class CustomizedModel(BaseChatTemplate): """A customized chat template.""" def __init__(self, system='<|im_start|>system\n', meta_instruction='You are a robot developed by LMDeploy.', user='<|im_start|>user\n', assistant='<|im_start|>assistant\n', eosys='<|im_end|>\n', eoh='<|im_end|>\n', eoa='<|im_end|>', separator='\n', stop_words=['<|im_end|>', '<|action_end|>']): super().__init__(system=system, meta_instruction=meta_instruction, eosys=eosys, user=user, eoh=eoh, assistant=assistant, eoa=eoa, separator=separator, stop_words=stop_words) from lmdeploy import ChatTemplateConfig, pipeline messages = [{'role': 'user', 'content': 'who are you?'}] pipe = pipeline('internlm/internlm2-chat-7b', chat_template_config=ChatTemplateConfig('customized_model')) for response in pipe.stream_infer(messages): print(response.text, end='')
在这个例子中,我们注册了一个 LMDeploy 的对话模板,该模板将模型设置为由 LMDeploy 创造,所以当用户提问模型是谁的时候,模型就会回答由 LMDeploy 所创。
如何调试 Turbomind¶
Turbomind 使用 C++ 实现,不像 Python 一样易于调试。该文档提供了调试 Turbomind 的基本方法。
配置 Python 调试环境¶
由于目前许多大公司在线上生产环境中使用 Centos 7,我们将以 Centos 7 为例来说明配置过程。
获取 glibc
和 python3
的版本¶
rpm -qa | grep glibc
rpm -qa | grep python3
结果类似于这样:
[username@hostname workdir]# rpm -qa | grep glibc
glibc-2.17-325.el7_9.x86_64
glibc-common-2.17-325.el7_9.x86_64
glibc-headers-2.17-325.el7_9.x86_64
glibc-devel-2.17-325.el7_9.x86_64
[username@hostname workdir]# rpm -qa | grep python3
python3-pip-9.0.3-8.el7.noarch
python3-rpm-macros-3-34.el7.noarch
python3-rpm-generators-6-2.el7.noarch
python3-setuptools-39.2.0-10.el7.noarch
python3-3.6.8-21.el7_9.x86_64
python3-devel-3.6.8-21.el7_9.x86_64
python3.6.4-sre-1.el6.x86_64
根据上述信息,我们可以看到 glibc
的版本是 2.17-325.el7_9.x86_64
,python3
的版本是 3.6.8-21.el7_9.x86_64
。
下载并安装 debuginfo
库¶
从 http://debuginfo.centos.org/7/x86_64 下载 glibc-debuginfo-common-2.17-325.el7.x86_64.rpm
、glibc-debuginfo-2.17-325.el7.x86_64.rpm
和 python3-debuginfo-3.6.8-21.el7.x86_64.rpm
。
rpm -ivh glibc-debuginfo-common-2.17-325.el7.x86_64.rpm
rpm -ivh glibc-debuginfo-2.17-325.el7.x86_64.rpm
rpm -ivh python3-debuginfo-3.6.8-21.el7.x86_64.rpm
升级 GDB¶
sudo yum install devtoolset-10 -y
echo "source scl_source enable devtoolset-10" >> ~/.bashrc
source ~/.bashrc
验证¶
gdb python3
输出类似于这样:
[username@hostname workdir]# gdb python3
GNU gdb (GDB) Red Hat Enterprise Linux 9.2-10.el7
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from python3...
(gdb)
如果显示 Reading symbols from python3
,说明配置成功。
对于其他操作系统,请参考 DebuggingWithGdb。
设置符号链接¶
设置符号链接后,不需要每次都通过 pip
进行本地安装。
# 更改目录到 lmdeploy,例如
cd /workdir/lmdeploy
# 因为编译文件在 build 文件夹中
# 设置 lib 的软链接
cd lmdeploy && ln -s ../build/lib . && cd ..
# (可选)创建 compile_commands.json 软链接,用于 clangd 构建 index
ln -s build/compile_commands.json .
开始调试¶
# 使用 gdb 启动 API Server,例如
gdb --args python3 -m lmdeploy serve api_server /workdir/Llama-2-13b-chat-hf
# 在 gdb 中设置 lmdeploy 文件夹路径
Reading symbols from python3...
(gdb) set directories /workdir/lmdeploy
# 使用相对路径设置断点,例如
(gdb) b src/turbomind/models/llama/BlockManager.cc:104
# 当出现
# ```
# No source file named src/turbomind/models/llama/BlockManager.cc.
# Make breakpoint pending on future shared library load? (y or [n])
# ```
# 输入 y 并回车
# 运行
(gdb) r
# (可选) 使用 https://github.com/InternLM/lmdeploy/blob/main/benchmark/profile_restful_api.py 发送请求
python3 profile_restful_api.py --server_addr 127.0.0.1:23333 --tokenizer_path /workdir/Llama-2-13b-chat-hf --dataset /workdir/ShareGPT_V3_unfiltered_cleaned_split.json --concurrency 1 --num_prompts 1
使用 GDB¶
参考 GDB Execution Commands 进行调试。
LMDeploy-QoS 介绍与用法¶
背景¶
在过去一段时间,推理框架伴随着LLM和AGI出现。许多推理框架为语言模型提供可扩展和高性能的在线工作负载服务。它们的工作负载通常涉及多个用户群体,而且工作负载在短时间内快速变化。许多推理框架在满足这些多租户流量模式的要求方面存在困难,而且未能很好的规范约束用户的行为,所以我们认为在LLM推理框架考虑多用户负载均衡是很有必要的。
多租户处理的用户分类¶
LMDeploy-QoS与LMDeploy 提供一系列多租户功能。它要求用户使用适当的用户标识(配置文件或代码库中的user_id)标记其推理请求。它是基于字典的配置作为多租户策略。在这个配置中,用户被映射到不同“用户组”中,并配备一个使用配额。我们的多租户策略可以读取配置,并根据其用户组的优先级和预定义配额与实时分配比率之间的差异安排用户推理请求的调度。经过完备的测试,我们的LMDeploy-QoS模块极大地提高了LLM的服务可靠性并提升了大型语言模型推理工作的GPU资源利用率。
LMDeploy将用户分为4组:
白金(Platinum)
金(Gold)
银(Silver)
青铜(Bronze)
根据我们在提供LLM服务方面的使用经验,我们可以将以下4种类型的用户映射到这些用户组中:
Platinum : VIP用户或管理员用户。包括需要不间断使用的的服务开发人员或演示人员。他们的工作负载频率低,对推理工作的资源需求也不高。
Gold : 签署定期服务的高级用户,他们需要可衡量的可靠服务。例如,某个公司A与LLM服务提供商签订了合同,购买了每秒X个请求的服务能力,可用性为Z%,供A公司员工使用,年付Y百万美元。
Silver : 绝大多数用户。大多数试用或每月订阅的用户被归类为此类别。他们需要相对较少的服务,但他们的用户体验对于LLM服务的声誉也很重要。
Bronze : 支付很少费用给LLM提供商的重度用户。
以上引入用户组分类的目的是为了提供指导,而不是为所有LMDeploy用户提供建议,因为这并不一定适用于所有LLM业务提供商。管理员可以对用户的日常负载进行统计,自行决定如何对用户进行分类。
接下来让我们讨论一下LMDeploy如何根据这些分类进行分配请求。
多租户策略¶
策略 1: 用户组之间的优先级调度¶
我们引入“用户组”概念。由模块使用者来定义哪些用户到用户组的映射(可以理解为 uid 到用户组的映射)。推荐用户组为4组如下:
Platinum
Gold
Silver
Bronze
四个用户组之间的优先级顺序是严格的 Platinum > Gold > Silver > Bronze 。当系统繁忙的时候,我们会优先执行排名靠前的请求。
下面的图表显示了优先级处理的工作原理。您可以看到 Platinum 请求已被重新设置优先级并移至队列头部。
策略 2: 用户组内均摊与软隔离¶
这个策略仅适用于用户组内部。我们引入了一个用户组内的用户配额配置表。该表定义了用户在 100% GPU 资源中的 “理想份额比例”。每个 “用户” 在列表中以 user_id 的形式出现,并且一个用户只能属于一个用户组。低于配额表上额定值的用户会比高于额定值的用户拥有更高的优先级获得被释放资源而进行更多的推理,直到双方使用量趋近于原始配额比例。此处调度只考虑请求队列中的用户,忽略没有出现在请求队列中的已配置用户。
以下图表展示了这种策略的典型示例。
策略3:混合机制¶
是指在一个系统中优先级+均摊/隔离同时开启。执行顺序是先用户组间优先级,再在组内做均摊/隔离实现。这里略去时序图描写。需要注意的是,用户组间的优先级可以压倒性覆盖组内的决策。例如,当低优先级内部的两个用户互相之间有请求顺序调度时,高优先级的请求一旦抵达,将会覆盖所有低优先级的分配逻辑而有限执行高优任务。
需要注意的是,混合机制可能有其他方法,本文档只介绍了一种在我们场景下有效的方法。其他混合方法需要考虑到优先级和按比例共享明显是相互冲突的策略,因此没有简单的方法将它们混合在单一维度内工作。
QoS 配置项模板¶
配置文件通过启动参数--qos-config-path
指定,并由程序在启动时加载。
配置会和lmdeploy启动脚本等文件放置在一起。配置内容包含:
QoS的启用开关,设置为True时后续的QoS和用户相关配置才会生效,设置为False后续配置不会生效;
user_groups 是一个列表,包含了多种不同的组间优先级;
user_group_map 的映射配置,包含了用户组优先级,组内用户id以及每个用户组内用户的配额分配。
配置项模板如下:
{
"enable_user_qos": true,
"user_groups": [
"Platinum",
"Gold",
"Silver",
"Bronze"
],
"user_group_map": {
"Platinum": [
{
"id": "user_id0",
"quota_pct": 100
},
{
"id": "default",
"quota_pct": 0
}
],
"Gold": [
{
"id": "user_id1",
"quota_pct": 50
},
{
"id": "user_id2",
"quota_pct": 50
}
],
"Silver": [
{
"id": "user_id3",
"quota_pct": 5
},
{
"id": "default",
"quota_pct": 95
}
],
"Bronze": [
{
"id": "user_id4",
"quota_pct": 30
},
{
"id": "user_id5",
"quota_pct": 30
},
{
"id": "user_id6",
"quota_pct": 40
},
{
"id": "default",
"quota_pct": 0
}
]
}
}
如何使用 LMDeploy-QoS 感知进行推理¶
我们提供以下代码链接,展示如何调用具有多租户策略感知的推理请求,在 HTTP Body 中,与 QoS 相关的参数如下:
/v1/chat/interactive_qos
curl -X POST http://localhost/v1/chat/interactive_qos \
-H "Content-Type: application/json" \
-d '{
"prompt": "Hello,Hello",
"session_id": -1,
"interactive_mode": false,
"stream": false,
"stop": false,
"request_output_len": 512,
"top_p": 0.8,
"top_k": 40,
"temperature": 0.8,
"repetition_penalty": 1,
"ignore_eos": false,
"user_id": "user_id0"
}'
/v1/chat/completions_qos
curl -X POST http://localhost/v1/chat/completions_qos \
-H "Content-Type: application/json" \
-d '{
"model": "internlm-chat-7b",
"messages": "Hello,Hello",
"temperature": 0.7,
"top_p": 1,
"n": 1,
"max_tokens": 512,
"stop": false,
"stream": false,
"presence_penalty": 0,
"frequency_penalty": 0,
"repetition_penalty": 1,
"session_id": -1,
"ignore_eos": false,
"user_id": "user_id0"
}'
/v1/completions_qos
curl -X POST http://localhost/v1/completions_qos \
-H "Content-Type: application/json" \
-d '{
"model": "internlm-chat-7b",
"prompt": "Hello,Hello",
"suffix": "string",
"temperature": 0.7,
"n": 1,
"max_tokens": 16,
"stop": "string",
"stream": false,
"top_p": 1,
"repetition_penalty": 1,
"session_id": -1,
"ignore_eos": false,
"user_id": "user_id0"
}'
配置文件修改¶
配置文件模板路径为:lmdeploy/server/qos_engine/qos_config.json.template
,可以根据实际需求添加需要配置的用户,设置正确的优先级以及quota值。
配置参数传入¶
启动api_server时,通过--qos-config-path
,将配置文件及路径传入,示例如下:
CUDA_VISIBLE_DEVICES=0 lmdeploy serve api_server internlm/internlm-chat-7b --server-port 8000 --qos-config-path lmdeploy/serve/qos_engine/qos_config.json.template
贡献者¶
推理 pipeline¶
pipeline¶
- lmdeploy.pipeline(model_path: str, model_name: Optional[str] = None, backend_config: Optional[Union[lmdeploy.messages.TurbomindEngineConfig, lmdeploy.messages.PytorchEngineConfig]] = None, chat_template_config: Optional[lmdeploy.model.ChatTemplateConfig] = None, log_level='ERROR', **kwargs)[源代码]¶
- 参数
model_path (str) –
the path of a model. It could be one of the following options:
A local directory path of a turbomind model which is
converted by lmdeploy convert command or download from ii) and iii).
The model_id of a lmdeploy-quantized model hosted
inside a model repo on huggingface.co, such as “InternLM/internlm-chat-20b-4bit”, “lmdeploy/llama2-chat-70b-4bit”, etc.
The model_id of a model hosted inside a model repo
on huggingface.co, such as “internlm/internlm-chat-7b”, “Qwen/Qwen-7B-Chat “, “baichuan-inc/Baichuan2-7B-Chat” and so on.
model_name (str) – needed when model_path is a pytorch model on huggingface.co, such as “internlm/internlm-chat-7b”, “Qwen/Qwen-7B-Chat “, “baichuan-inc/Baichuan2-7B-Chat” and so on.
backend_config (TurbomindEngineConfig | PytorchEngineConfig) – backend config instance. Default to None.
chat_template_config (ChatTemplateConfig) – chat template configuration. Default to None.
log_level (str) – set log level whose value among [CRITICAL, ERROR, WARNING, INFO, DEBUG]
实际案例
>>> # LLM >>> import lmdeploy >>> pipe = lmdeploy.pipeline('internlm/internlm-chat-7b') >>> response = pipe(['hi','say this is a test']) >>> print(response) >>> >>> # VLM >>> from lmdeploy.vl import load_image >>> from lmdeploy import pipeline, TurbomindEngineConfig, ChatTemplateConfig >>> pipe = pipeline('liuhaotian/llava-v1.5-7b', ... backend_config=TurbomindEngineConfig(session_len=8192), ... chat_template_config=ChatTemplateConfig(model_name='vicuna')) >>> im = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/demo/resources/human-pose.jpg') >>> response = pipe([('describe this image', [im])]) >>> print(response)
serving¶
- lmdeploy.serve(model_path: str, model_name: Optional[str] = None, backend: Literal[turbomind, pytorch] = 'turbomind', backend_config: Optional[Union[lmdeploy.messages.TurbomindEngineConfig, lmdeploy.messages.PytorchEngineConfig]] = None, chat_template_config: Optional[lmdeploy.model.ChatTemplateConfig] = None, server_name: str = '0.0.0.0', server_port: int = 23333, log_level: str = 'ERROR', api_keys: Optional[Union[str, List[str]]] = None, ssl: bool = False, **kwargs)[源代码]¶
This will run the api_server in a subprocess.
- 参数
model_path (str) –
the path of a model. It could be one of the following options:
A local directory path of a turbomind model which is
converted by lmdeploy convert command or download from ii) and iii).
The model_id of a lmdeploy-quantized model hosted
inside a model repo on huggingface.co, such as “InternLM/internlm-chat-20b-4bit”, “lmdeploy/llama2-chat-70b-4bit”, etc.
The model_id of a model hosted inside a model repo
on huggingface.co, such as “internlm/internlm-chat-7b”, “Qwen/Qwen-7B-Chat “, “baichuan-inc/Baichuan2-7B-Chat” and so on.
model_name (str) – needed when model_path is a pytorch model on huggingface.co, such as “internlm/internlm-chat-7b”, “Qwen/Qwen-7B-Chat “, “baichuan-inc/Baichuan2-7B-Chat” and so on.
backend (str) – either turbomind or pytorch backend. Default to turbomind backend.
backend_config (TurbomindEngineConfig | PytorchEngineConfig) – backend config instance. Default to none.
chat_template_config (ChatTemplateConfig) – chat template configuration. Default to None.
server_name (str) – host ip for serving
server_port (int) – server port
log_level (str) – set log level whose value among [CRITICAL, ERROR, WARNING, INFO, DEBUG]
api_keys (List[str] | str | None) – Optional list of API keys. Accepts string type as a single api_key. Default to None, which means no api key applied.
ssl (bool) – Enable SSL. Requires OS Environment variables ‘SSL_KEYFILE’ and ‘SSL_CERTFILE’.
- 返回
A client chatbot for LLaMA series models.
- 返回类型
APIClient
实际案例
>>> import lmdeploy >>> client = lmdeploy.serve('internlm/internlm-chat-7b', 'internlm-chat-7b') >>> for output in client.chat('hi', 1): ... print(output)
- lmdeploy.client(api_server_url: str = 'http://0.0.0.0:23333', api_key: Optional[str] = None, **kwargs)[源代码]¶
- 参数
api_server_url (str) – communicating address ‘http://<ip>:<port>’ of api_server
api_key (str | None) – api key. Default to None, which means no api key will be used.
- 返回
Chatbot for LLaMA series models with turbomind as inference engine.
PytorchEngineConfig¶
- class lmdeploy.PytorchEngineConfig(model_name: str = '', tp: int = 1, session_len: Optional[int] = None, max_batch_size: int = 128, cache_max_entry_count: float = 0.8, eviction_type: str = 'recompute', prefill_interval: int = 16, block_size: int = 64, num_cpu_blocks: int = 0, num_gpu_blocks: int = 0, adapters: Optional[Dict[str, str]] = None, max_prefill_token_num: int = 4096, thread_safe: bool = False, enable_prefix_caching: bool = False, download_dir: Optional[str] = None, revision: Optional[str] = None)[源代码]¶
PyTorch Engine Config.
- 参数
model_name (str) – name of the given model.
tp (int) – Tensor Parallelism. default 1.
session_len (int) – Max session length. Default None.
max_batch_size (int) – Max batch size. Default 128.
cache_max_entry_count (float) – the percentage of gpu memory occupied by the k/v cache. For lmdeploy versions greater than v0.2.1, it defaults to 0.8, signifying the percentage of FREE GPU memory to be reserved for the k/v cache
eviction_type (str) – What action to perform when kv cache is full, [‘recompute’, ‘copy’], Default ‘recompute’.
prefill_interval (int) – Interval to perform prefill, Default 16.
block_size (int) – paging cache block size, default 64.
num_cpu_blocks (int) – Num cpu blocks. If num is 0, cache would be allocate according to current environment.
num_gpu_blocks (int) – Num gpu blocks. If num is 0, cache would be allocate according to current environment.
adapters (dict) – The path configs to lora adapters.
max_prefill_token_num (int) – tokens per iteration.
thread_safe (bool) – thread safe engine instance.
enable_prefix_caching (bool) – Enable token match and sharing caches.
download_dir (str) – Directory to download and load the weights, default to the default cache directory of huggingface.
revision (str) – The specific model version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version.
TurbomindEngineConfig¶
- class lmdeploy.TurbomindEngineConfig(model_name: Optional[str] = None, model_format: Optional[str] = None, tp: int = 1, session_len: Optional[int] = None, max_batch_size: int = 128, cache_max_entry_count: float = 0.8, cache_block_seq_len: int = 64, enable_prefix_caching: bool = False, quant_policy: int = 0, rope_scaling_factor: float = 0.0, use_logn_attn: bool = False, download_dir: Optional[str] = None, revision: Optional[str] = None, max_prefill_token_num: int = 8192, num_tokens_per_iter: int = 0, max_prefill_iters: int = 1)[源代码]¶
TurboMind Engine config.
- 参数
model_name (str) – the name of the deployed model, deprecated and has no effect when version > 0.2.1
model_format (str) – the layout of the deployed model. It can be one of the following values [hf, llama, awq], hf meaning hf_llama, llama meaning meta_llama, awq meaning the quantized model by AWQ.
tp (int) – the number of GPU cards used in tensor parallelism, default to 1
session_len (int) – the max session length of a sequence, default to None
max_batch_size (int) – the max batch size during inference, default to 128
cache_max_entry_count (float) – the percentage of gpu memory occupied by the k/v cache. For versions of lmdeploy between v0.2.0 and v0.2.1, it defaults to 0.5, depicting the percentage of TOTAL GPU memory to be allocated to the k/v cache. For lmdeploy versions greater than v0.2.1, it defaults to 0.8, signifying the percentage of FREE GPU memory to be reserved for the k/v cache
cache_block_seq_len (int) – the length of the token sequence in a k/v block, default to 64
enable_prefix_caching (bool) – enable cache prompts for block reuse, default to False
quant_policy (int) – default to 0. When k/v is quantized into 8 bit, set it to 4
rope_scaling_factor (int) – scaling factor used for dynamic ntk, default to 0. TurboMind follows the implementation of transformer LlamaAttention
use_logn_attn (bool) – whether or not to use log attn: default to False
download_dir (str) – Directory to download and load the weights, default to the default cache directory of huggingface.
revision (str) – The specific model version to use. It can be a branch name, a tag name, or a commit id. If unspecified, will use the default version.
max_prefill_token_num (int) – the number of tokens each iteration during prefill, default to 8192
num_tokens_per_iter (int) – the number of tokens processed in each forward pass. Working with max_prefill_iters enables “Dynamic SplitFuse”-like scheduling
max_prefill_iters (int) – the max number of forward pass during prefill stage
GenerationConfig¶
- class lmdeploy.GenerationConfig(n: int = 1, max_new_tokens: int = 512, top_p: float = 1.0, top_k: int = 1, temperature: float = 0.8, repetition_penalty: float = 1.0, ignore_eos: bool = False, random_seed: Optional[int] = None, stop_words: Optional[List[str]] = None, bad_words: Optional[List[str]] = None, min_new_tokens: Optional[int] = None, skip_special_tokens: bool = True, logprobs: Optional[int] = None)[源代码]¶
generation parameters used by inference engines.
- 参数
n (int) – Define how many chat completion choices to generate for each input message
max_new_tokens (int) – The maximum number of tokens that can be generated in the chat completion
top_p (float) – An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass
top_k (int) – An alternative to sampling with temperature, where the model considers the top_k tokens with the highest probability
temperature (float) – Sampling temperature
repetition_penalty (float) – Penalty to prevent the model from generating repeated words or phrases. A value larger than 1 discourages repetition
ignore_eos (bool) – Indicator to ignore the eos_token_id or not
random_seed (int) – Seed used when sampling a token
stop_words (List[str]) – Words that stop generating further tokens
bad_words (List[str]) – Words that the engine will never generate
min_new_tokens (int) – The minimum numbers of tokens to generate, ignoring the number of tokens in the prompt.
skip_special_tokens (bool) – Whether or not to remove special tokens in the decoding. Default to be True.
logprobs (int) – Number of log probabilities to return per output token.
ChatTemplateConfig¶
- class lmdeploy.ChatTemplateConfig(model_name: str, system: Optional[str] = None, meta_instruction: Optional[str] = None, eosys: Optional[str] = None, user: Optional[str] = None, eoh: Optional[str] = None, assistant: Optional[str] = None, eoa: Optional[str] = None, separator: Optional[str] = None, capability: Optional[Literal[completion, infilling, chat, python]] = None, stop_words: Optional[List[str]] = None)[源代码]¶
Parameters for chat template.
- 参数
model_name (str) – the name of the deployed model. Determine which chat template will be applied. All the chat template names: lmdeploy list
system (str | None) – begin of the system prompt
meta_instruction (str | None) – system prompt
eosys (str | None) – end of the system prompt
user (str | None) – begin of the user prompt
eoh (str | None) – end of the user prompt
assistant (str | None) – begin of the assistant prompt
eoa (str | None) – end of the assistant prompt
capability – (‘completion’ | ‘infilling’ | ‘chat’ | ‘python’) = None