LMDeploy 部署 VLMs 的方法与探讨

简介: LMDeploy 部署 VLMs 的方法与探讨 LMDeploy 是一个高效且友好的大型语言模型(LLMs)和视觉-语言模型(VLMs)部署工具箱,由上海人工智能实验室模型压缩和部署团队开发,涵盖了模型量化、离线推理和在线服务等功能。

01 LMDeploy简介

LMDeploy 是一个高效且友好的大型语言模型(LLMs)和视觉-语言模型(VLMs)部署工具箱,由上海人工智能实验室模型压缩和部署团队开发,涵盖了模型量化、离线推理和在线服务等功能。

1.1 软硬件平台

支持的软硬件平台包括:

  • Linux、Windows 系统 + NVIDIA 显卡。运行时,cuda runtime的最低要求是11.3。支持的 NVIDIA 显卡型号包括:
  • Volta(sm70): V100
  • Turing(sm75): 20 系列,T4
  • Ampere(sm80,sm86): 30 系列,A10, A16, A30, A100 等
  • Ada Lovelace(sm89): 40 系列
  • Hopper(sm90):H100(尚未深度优化)
  • Huawei 910b

1.2 项目结构

image.png

1.2.1 接口层

Python:离线推理

RESTful:访问在线服务

gRPC:访问 triton inference server 接口。没有支持 VLM 模型

1.2.2 量化层

权重量化:支持 AWQ 和 SmoothQuant 算法

K/V Cache:KV在线量化

1.2.3 引擎层

TurboMind 引擎:起源于 FasterTransformer,由 C++ 和 CUDA 开发,致力于推理性能的优化

PyTorch 引擎:采用纯 Python 开发,kernel 部分使用 openai triton 编写,旨在降低开发者的门槛

两个引擎互为补充,相辅相成,共同打造了 LMDeploy 的基石

1.2.4 服务层

OpenAI-like Server:推理服务,兼容 openai 接口

Gradio:web demo的服务

Triton Inference Server:不支持 VLM。LLM 也不推荐

1.3 支持的模型

image.png

02 LMDeploy 使用指南之 VLMs 部署

2.1 环境安装

方案 1:创建一个干净的 conda 环境,pip安装 lmdeploy。支持的 python 版本是 3.8 - 3.12

conda create -n lmdeploy python=3.8 -y
conda activate lmdeploy
pip install lmdeploy

到这里为止,可以使用 lmdeploy 部署 LLM 模型。

但,如果要部署 VLM 模型,比如 InternVL系列,InternLM-XComposer系列、LLaVA 等等,需要安装上游模型库需要的依赖。原因,LMDeploy 对 VLMs 视觉部分的模型推理以及图像预处理,是复用上游库的。

以 InternVL2 模型为例,还需要安装:

pip install timm
# 建议从https://github.com/Dao-AILab/flash-attention/releases寻找和环境匹配的whl包
pip install flash-attn

因为复用VLMs上游库关于图像的预处理和视觉模型的推理,且不同的VLM依赖各不相同,出于维护性方面的考虑,LMDeploy 没有把 VLMs 的依赖,比如 timm,flash-attn,放在自己的依赖列表里。

但是,torchvision放入了依赖列表。原因是,LMDeploy 依赖 torch,担心用户安装 torchvision 时没留意torch的版本,安装了不匹配的。(后续考虑移除,相信用户)

方案 2:docker 镜像

LMDeploy 只提供可以部署 LLM 模型的镜像,不提供部署 VLM 的镜像,理由如上。

推荐用户基于 LMDeploy 镜像构建 VLM 部署镜像,比如:

ARG CUDA_VERSION=cu12
FROM openmmlab/lmdeploy:latest-cu12 AS cu12
FROM openmmlab/lmdeploy:latest-cu11 AS cu11
RUN python3 -m pip install timm
# 建议从https://github.com/Dao-AILab/flash-attention/releases寻找和环境匹配的whl包
RUN python3 -m pip install flash-attn

LMDeploy 镜像的命名方式如下:

openmmlab/lmdeploy:latest-cu12
openmmlab/lmdeploy:latest-cu11
openmmlab/lmdeploy:latest # 和 openmmlab/lmdeploy:latest-cu12 一致
openmmlab/lmdeploy:{tag}-cu12 # 比如 openmmlab/lmdeploy:v0.5.3-cu12
openmmlab/lmdeploy:{tag}-cu11

2.2 离线推理

以 InternVL2-8B 模型为例,最简单的 "Hello, world" 式的推理方式如下:

from lmdeploy import pipeline
from lmdeploy.vl import load_image
pipe = pipeline('OpenGVLab/InternVL2-8B')
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe(('describe this image', image))
print(response)

在构造 pipeline 时,如果没有指定使用 TurboMind 引擎或 PyTorch 引擎进行推理,LMDeploy 将根据它们各自的能力自动分配一个,默认优先使用 TurboMind 引擎。

当然,你可以手动选择一个引擎,我们将在推理引擎配置章节中,详细介绍两种引擎的配置方法。

2.2.1 创建 pipeline

2.2.1.1 API

def pipeline(model_path: str,
            model_name: Optional[str] = None,
            backend_config: Optional[Union[TurbomindEngineConfig,
                                           PytorchEngineConfig]] = None,
            chat_template_config: Optional[ChatTemplateConfig] = None,
            log_level='ERROR',
            **kwargs):

model_path:模型路径

  • 可以是 huggingface hub 上的 model_repo_id
  • 可以是 modelscope hub 上的 model_repo_id。在这种情况下,需要安装modelscope,并设置环境变量
pip install modelscope
export LMDEPLOY_USE_MODELSCOPE=Tru
  • 对于 LLM 模型来说,它可以是经多 lmdeploy convert 转换后的模型的路径。不支持离线转换 VLMs 模型

model_name:内置对话模板名称

  • v0.6.0(尚未发布)将会移除这个参数,用 ChatTemplateConfig 中的 model_name 替代

backend_config

推理引擎配置参数

chat_template_config

对话模板参数

log_level

日志级别。默认为 ERROR

vision_config

视觉模型推理的配置参数

  • vision_config 被隐藏在 kwargs 中了

2.2.1.2 推理引擎配置

引擎参数的定义

Turbomind

@dataclass
class TurbomindEngineConfig:
    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

Pytorch

@dataclass
class PytorchEngineConfig:
    tp: int = 1
    session_len: 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: Dict[str, str] = None
    max_prefill_token_num: int = 4096
    thread_safe: bool = False
    enable_prefix_caching: bool = False
    device_type: str = 'cuda'
    download_dir: str = None
    revision: str = None

引擎通用的参数:

  • 单机多卡推理(tp)

tp用于张量并行时的GPU数量,缺省值 1,目前被约束为2n

LMDeploy 只支持单机多卡,不支持多机多卡

Turbomind

from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B',
    backend_config=TurbomindEngineConfig(
        tp=2))

Pytorch

from lmdeploy import pipeline
from lmdeploy import PytorchEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B',
    backend_config=PytorchEngineConfig(
        tp=2))
  • 设置内存使用量(cache_max_entry_count)

cache_max_entry_count 表示加载模型权重后 K/V 缓存占用的空闲 GPU 内存的比例。默认值是 0.8。

K/V 缓存分配方式是一次性申请,重复性使用,这就是为什么 pipeline 以及下文中的 api_server 在启动后会消耗大量 GPU 内存。

如果你遇到内存不足(OOM)错误的错误,可能需要考虑降低 cache_max_entry_count 的

Turbomind

from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B',
    backend_config=TurbomindEngineConfig(
        tp=2))

Pytorch

from lmdeploy import pipeline
from lmdeploy import PytorchEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B',
    backend_config=PytorchEngineConfig(
        tp=2))
  • 设置最大推理长度(session_len)

session_len表示上下文窗口的最大长度,包括输入的prompt token个数和输出的token个数

Turbomind

from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=TurbomindEngineConfig(
       session_len=8192))

Pytorch

from lmdeploy import pipeline
from lmdeploy import PytorchEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=PytorchEngineConfig(
       session_len=8192))
  • 设置推理最大batch(max_batch_size)

max_batch_size 表示 Continuous batching 推理时,最大的批处理数量

Turbomind

from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=TurbomindEngineConfig(
       max_batch_size=256))

Pytorch

from lmdeploy import pipeline
from lmdeploy import PytorchEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=PytorchEngineConfig(
       max_batch_size=256))
  • 设置 prefix caching 开关(enable_prefix_caching)

Turbomind

from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=TurbomindEngineConfig(
       enable_prefix_caching=True))

Pytorch

from lmdeploy import pipeline
from lmdeploy import PytorchEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=PytorchEngineConfig(
       enable_prefix_caching=
  • 设置prefill chunk的最大token数(max_prefill_token_num)

Turbomind

from lmdeploy import pipeline
from lmdeploy import TurbomindEngineConfig
pipe = pipeline(
   'OpenGVLab/InternVL2-8B',
   backend_config=TurbomindEngineConfig(
       max_prefill_token_num=8192))

Pytorch

from lmdeploy import pipeline
from lmdeploy import PytorchEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B',
    backend_config=PytorchEngineConfig(
        max_prefill_token_num=4096))
  • 设置模型下载参数(download_dir、revision)

当 model_path 不是本地路径时,LMDeploy 会从 huggingface hub 或者 modelscope hub上下载模型,默认下载最新版本,默认存储在 ~/.cache 下。用户可以通过 revision 指定模型版本,通过download_dir 指定模型存放路径

TurbomindEngine 专用参数:

  • 设置Dynamic NTK外推参数(rope_scaling_factor)
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig
backend_config = TurbomindEngineConfig(
       rope_scaling_factor=2.5,
       session_len=1000000,
       max_batch_size=1,
       cache_max_entry_count=0.9,
       tp=2)
pipe = pipeline('internlm/internlm2_5-7b-chat-1m', 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)
  • 设置在线 KV Cache量化精度(quant_policy)

quant_policy 表示 LLM 模型 KV cache 量化策略的参数,4表示 4bit 量化,8表示 8bit 量化。8bit KV cache 几乎不掉点,可认为量化精度无损。

from lmdeploy import pipeline, TurbomindEngineConfig
pipe = pipeline('OpenGVLab/InternVL2-8B',
               backend_config=TurbomindEngineConfig(
                   quant_policy=8))
  • 设置模型格式(model_format)

取值范围{None,hf,llama,awq,gptq}。None 根据模型文件结构自动判断,hf 表示 huggingface上类llama结构的模型,llama 表示 meta_llama(pytorch权重格式),awq 表示 awq 量化模型,gptq 表示 gptq 量化模型(0.6.0支持)。

from lmdeploy import pipeline, TurbomindEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B-AWQ',
    backend_config=TurbomindEngineConfig(
        model_format='awq'))
from lmdeploy import pipeline, TurbomindEngineConfig
pipe = pipeline(
    'OpenGVLab/InternVL2-8B-AWQ',
    backend_config=TurbomindEngineConfig(
        model_format=None))
  • KV block 的大小(cache_block_seq_len)

一个 KV cache block 能够容纳的 token 的数量。默认是 64。如果 GPU compute_capability >= 8.0,应该是32的倍数,否则应该是64的倍数

image.png

image.png

  • num_tokens_per_iter

控制一次forward pass 处理的token数量。这包括预填充(prefill)和解码(decoding)。例如,当有8个解码序列和2个预填充序列时,首先为8个解码token分配所需要的资源,然后为num_tokens_per_iter - 8个预填充token分配资源。

它的默认值为 max_prefill_token_num

为了减少long prompt 序列的影响,num_tokens_per_iter的最优值与模型大小、GPU型号和工作负载有关。对于Llama3-8B、A100 80G和128个并发使用情况,该值在128到256之间。如果你使用的是4090显卡,可以从64-128开始。基本上,这个值越小,预填充的影响就越小(但它要大于max_batch_size)。

  • max_prefill_iters

控制单个序列预填充的最大迭代次数。

在max_prefill_iters被设定后,num_tokens_per_iter可能会被重新计算。例如,当一个请求有2000个 token 时,如果max_prefill_iters=1,意味着预填充在1次迭代中完成,不管num_tokens_per_iter被设置为多少。所以,为了让num_tokens_per_iter充分发挥其效果,max_prefill_iters应该设置为一个比较大的值。

它的默认值为 (session_len + max_prefill_token_num - 1) // max_prefill_token_num

当找到一个合适的num_tokens_per_iter后,max_prefill_iters用于平衡解码的平滑度和首token延迟。首先将其设置为一个大值(你会观察到首令牌延迟的增加),然后逐渐减小,直到达到可接受的首令牌延迟。

2.2.1.3 对话模板配置

@dataclass
class 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

model_name: 对话模板名称

system、meta_instruction、eosys 分别表示 system 角色的名称,system角色的提示词,system角色的提示词的结束符。

以 InternLM2 模型为例,它的对话模板的这3个属性如下:

# InternLM2
system = "<|im_start|>system"
meta_instruction = """You are an AI assistant whose name is InternLM (书生·浦语).
- InternLM (书生·浦语) is a conversational language model that is developed by Shanghai AI Laboratory (上海人工智能实验室). It is designed to be helpful, honest, and harmless.
- InternLM (书生·浦语) can understand and communicate fluently in the language chosen by the user such as English and 中文.
"""
eosys = "<|im_end|>\n"
  • user、eoh 分别表示 user 这个角色的名称,user角色的提示词的结束符
  • assistant、eoa 分别表示 AI assistant 这个角色的名称,以及这个角色回复内容的结束符
  • seperator:两轮对话之间的分隔符
  • capability:模型的能力。"completion" 表示文本补全,"chat" 表示对话,"infilling" 表示代码填空(codellama专用)、"python"表示python代码能力(codellama专用)
  • stop_words:结束符,用来停止 AI assistant 应答的。当前,LMDeploy 只支持每个 stop_word 只能是 1 个 token_id 的情况

对话模板的作用是用来拼接一个对话序列的。

我们假设一个对话序列为 U1A1U2A2...Un,其中Ui表示User在第i轮对话时输入的提示词(prompt),Ai表示模型或者说AI Assistant在第i轮对话中生成的答案。

在 LMDeploy 中,对话序列的拼接方式有 2 种,分别对应两种推理模式:交互式推理(有状态推理)、非交互式推理(无状态推理)。

这两种推理方式的区别在于,第 i轮对话中,用户侧的输入是什么。交互式推理的输入是 Ui,非交互式推理的输入是 U1A1U2A2...Ui。换言之,交互式推理模式下,用户不需要输入历史对话记录,因为历史对话的信息已经被推理引擎缓存了(包括token、KV、游标等),而非交互式推理模式则需要输入历史对话记录。

LMDeploy 拼接对话序列的两种方式在 BaseChatTemplate 中实现。get_prompt 用户交互式推理模式,messages2prompt 用于非交互式推理模式

class BaseChatTemplate:
   def get_prompt(self, prompt, sequence_start=True):
       """Return decorated prompt in interactive inference mode
       """
       if sequence_start: # decorate $$U_0$$
           return f'{self.system}{self.meta_instruction}{self.eosys}' \
                   f'{self.user}{prompt}{self.eoh}' \
                   f'{self.assistant}'
       else: # decorate $$U_i$$
           return f'{self.user}{prompt}{self.eoh}' \
                  f'{self.assistant}'
   def messages2prompt(self, messages, sequence_start=True, **kwargs):
       """Return decorated prompt in non-interactive inference mode.
       Args:
           messages (str|List): user's input prompt which is supposed
           to be in OpenAI format
       """
       if isinstance(messages, str):
           # fallback to `get_prompt` when `messages` isn't a list
           return self.get_prompt(messages, sequence_start)
       
       # "box" indicates "begin of x (role)"
       box_map = dict(user=self.user,
                      assistant=self.assistant,
                      system=self.system)
       # "eox" indicates "end of x (role)"
       eox_map = dict(user=self.eoh,
                      assistant=self.eoa + self.separator,
                      system=self.eosys)
                     
       for message in messages:
           role = message['role']
           content = message['content']
           ret += f'{box_map[role]}{content}{eox_map[role]}'

在上述代码中,为了突出重点,简化了部分逻辑。完成的代码请参考:https://github.com/InternLM/lmdeploy/blob/main/lmdeploy/model.py

对话模板的使用方式有以下几种:

  • 在不指定对话模板配置时,LMDeploy 根据模型的路径名,匹配内置对话模板名
  • 指定内置对话模板
from lmdeploy import pipeline, ChatTemplateConfig
pipe = pipeline('/the/path/of/your/finetuned/internvl2/8b/model',
               chat_template_config=ChatTemplateConfig(
                   model_name='internvl-internlm2'
               )

lmdeploy list 可以显示内置对话模板。内置对话模板与支持的模型之间的映射关系请参考附录-内置对话模板

  • 改变内置对话模板的属性
from lmdeploy import pipeline, ChatTemplateConfig
pipe = pipeline('/the/path/of/your/finetuned/internvl2/8b/model',
               chat_template_config=ChatTemplateConfig(
                   model_name='internvl2-internlm2',
                   meta_instruction='You are a helpful assistant'
               )

LMDeploy 会把非None的属性信息更新到指定的对话模板里

  • 自定义对话模板

方式一:对话模板的属性定义和拼接方式完全符合BaseChatTemplate的定义

只需设置 ChatTemplateConfig 各字段即可。LMDeploy会创建BaseChatTemplate实例

方式二:对话模板的属性或者拼接方式不符合 BaseChatTemplate的定义

@register_module(name="awesome")
class MyChatTemplate:
   def __init__(*args, **kwargs):
       pass
   def get_prompt(self, prompt, sequence_start=True):
       if sequence_start:
           # TODO: return the decorated prompt when it is the first request of a sequence
           pass
       else:
           # TODO: return the decorated prompt when it is NOT the first request of a sequence
           pass
   def message2prompt(self, messages, sequence_start=True, **kwargs):
       if isinstance(messages, str):
           return self.get_prompt(messages, sequence_start)
       # TODO: return the prompt after applying the chat template
pipe = pipeline("/the/path/of/your/awesome/model",
               chat_template_config=ChatTemplateConfig(
                   model_name="awesome"))

2.2.1.4 视觉模型推理配置

@dataclass
class VisionConfig:
   max_batch_size: int = 1
   thread_safe: bool = False

max_batch_size 表示图像批量处理的大小。值越大,OOM 的风险越高,因为 VLM 模型中的 LLM 部分会提前预分配大量的内存。

2.2.2 使用 pipeline

2.2.2.1 API

def __call__(self,
            prompts: Union[VLPromptType, List[Dict], List[VLPromptType],
                           List[List[Dict]]],
            gen_config: Optional[GenerationConfig] = None,
            **kwargs):

prompts 用户输入的 prompt 和 image。它可以是以下几种形式:

  • str:纯文本
  • list[str]:纯文本序列
  • tuple(str, PIL.Image):文本 + 图像
  • tuple(str, list[PIL.Image]):文本 + 图像序列
  • list[tuple(str, PIL.Image)]:(文本+图像)序列
  • GPT4V的格式
[{
   "role":"user",
   "content": [{
       "type": "text",
       "text": "the input text prompt",
   },
   {
       "type": "image_url",
       "image_url": {
           "url": "data:image/jpeg;base64,{image_base64_data}"
       }
   },
   {
       "type": "image_data",
       "image_data": {
           "data": PIL.Image.Image
       }
   },
   ...
   {...}]
}]

LMDeploy 会把前 5 种格式处理为 GPT4V 的格式

gen_config 生成token的采样参数

@dataclass
class 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: int = None
   stop_words: List[str] = None
   bad_words: List[str] = None
   min_new_tokens: int = None
   skip_special_tokens: bool = True
   logprobs: int = None
  • n:输入请求要求生成的序列的数量,目前只支持 1
  • max_new_tokens:输入请求要求的最多生成的token的个数
  • top_p:采样时,考虑累积概率超过top_p的最小可能token集合中进行采样
  • top_k:采样时,考虑从概率最高的top_k个token中进行采样。top_k=1 表示贪心搜索
  • temperature:采样温度。temperature=0.f 表示贪心搜索
  • repetition_penalty:防止模型生成重复单词或短语的惩罚。大于1的值会抑制重复。
  • ignore_eos:是否忽略 eos_token_id
  • random_seed:采样token时使用的种子
  • stop_words:token生成的停止符。目前要求每个 stop_word 被 tokenizer 时,只能有一个token_id
  • bad_words:永远不会被生成的单词。目前要求每个 bad_word 被 tokenizer 时,只能有一个token_id
  • min_new_tokens:输入请求要求生成的最少token数量
  • skip_special_tokens:是否在解码中忽略特殊 token。默认为True
  • logprobs:每个输出 token 返回的对数概率数量
  • 关于生成时采样方式的介绍,推荐阅读 https://huggingface.co/blog/how-to-generate

2.2.2.2 更多示例

  • 多图输入

对于多图的场景,在推理时,只要把它们放在一个列表中即可。不过,多图意味着输入 token 数更多,所以通常需要增大推理的上下文长度

from lmdeploy import pipeline, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('OpenGVLab/InternVL2-8B',
               backend_config=TurbomindEngineConfig(session_len=10000))
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)
  • 批量图文输入
from lmdeploy import pipeline, TurbomindEngineConfig
from lmdeploy.vl import load_image
pipe = pipeline('OpenGVLab/InternVL2-8B',
               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 进行多轮对话有两种方式,一种是按照 GPT4V 的格式来构造 messages,另外一种是使用 pipeline.chat 接口。

from lmdeploy import pipeline, TurbomindEngineConfig, GenerationConfig
from lmdeploy.vl import load_image
pipe = pipeline('OpenGVLab/InternVL2-8B',
                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)
  • 自定义图像 token 的位置

默认情况下,LMDeploy 会根据算法 repo 提供的对话模版将表示图片的特殊 token 插入到 user prompt 中,但在一些模型中,图片 token 的位置并没有限制,如 deepseek-vl,或者用户需要自定义图片 token 插入的位置。这种情况下,用户需要手动将表示图片的 token 插入到 prompt 中。LMDeploy 使用 <IMAGE_TOKEN> 作为表示图片的特殊 token。

from lmdeploy import pipeline
from lmdeploy.vl import load_image
from lmdeploy.vl.constants import IMAGE_TOKEN
pipe = pipeline('deepseek-ai/deepseek-vl-1.3b-chat')
image = load_image('https://raw.githubusercontent.com/open-mmlab/mmdeploy/main/tests/data/tiger.jpeg')
response = pipe((f'describe this image{IMAGE_TOKEN}', image))
print(response)

2.3 在线服务

2.3.1 启动服务

2.3.1.1 方式一:使用 lmdeploy cli 工具

lmdeploy serve api_server OpenGVLab/InternVL2-8B

此命令将在本地主机上的端口 23333 启动一个与 OpenAI 接口兼容的模型推理服务。你可以使用 --server-port 选项指定不同的服务器端口。更多参数的说明请参考章节api_server参数

2.3.1.2 方式二:使用 docker

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 OpenGVLab/InternVL2-8B

2.3.1.3 api_server 参数

root@lmdeploy-on-121:~/lmdeploy# lmdeploy serve api_server -h
usage: lmdeploy serve api_server [-h] [--server-name SERVER_NAME] [--server-port SERVER_PORT]
                                [--allow-origins ALLOW_ORIGINS [ALLOW_ORIGINS ...]] [--allow-credentials]
                                [--allow-methods ALLOW_METHODS [ALLOW_METHODS ...]]
                                [--allow-headers ALLOW_HEADERS [ALLOW_HEADERS ...]] [--qos-config-path QOS_CONFIG_PATH]
                                [--backend {pytorch,turbomind}]
                                [--log-level {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET}]
                                [--api-keys [API_KEYS ...]] [--ssl] [--model-name MODEL_NAME]
                                [--chat-template CHAT_TEMPLATE] [--revision REVISION] [--download-dir DOWNLOAD_DIR]
                                [--adapters [ADAPTERS ...]] [--tp TP] [--session-len SESSION_LEN]
                                [--max-batch-size MAX_BATCH_SIZE] [--cache-max-entry-count CACHE_MAX_ENTRY_COUNT]
                                [--cache-block-seq-len CACHE_BLOCK_SEQ_LEN] [--enable-prefix-caching]
                                [--model-format {hf,llama,awq,gptq}] [--quant-policy {0,4,8}]
                                [--rope-scaling-factor ROPE_SCALING_FACTOR] [--num-tokens-per-iter NUM_TOKENS_PER_ITER]
                                [--max-prefill-iters MAX_PREFILL_ITERS] [--vision-max-batch-size VISION_MAX_BATCH_SIZE]
                                model_path
  • model_path
  • --server-name SERVER_NAME: 服务的主机IP地址。默认: 0.0.0.0。
  • --server-port SERVER_PORT: 服务端口。默认: 23333。
  • --allow-origins ALLOW_ORIGINS: 允许CORS的来源列表。默认: ['*']。
  • --allow-credentials: 是否允许CORS的凭证。默认: False。
  • --allow-methods ALLOW_METHODS: 允许的HTTP方法列表。默认: ['*']。
  • --allow-headers ALLOW_HEADERS: 允许的HTTP头部列表。默认: ['*']。
  • --backend {pytorch,turbomind}: 设置推理后端。默认: turbomind。
  • --log-level {LEVELS}: 设置日志级别。默认: ERROR。
  • --api-keys [API_KEYS]: 可选的API密钥列表。
  • --ssl: 启用SSL。需要操作系统环境变量'SSL_KEYFILE'和'SSL_CERTFILE'。
  • --model-name MODEL_NAME: 模型的服务名称。可以通过RESTful API /v1/models访问。如果未指定,将采用model_path。
  • --chat-template CHAT_TEMPLATE: 当它为字符串时,表示内置的对话模板名称。当它为JSON文件路径时,表示自定义对话模板。
  • --revision REVISION: 使用的特定模型版本。可以是分支名、标签名或提交ID。
  • --download-dir DOWNLOAD_DIR: 下载和加载权重的目录,默认为huggingface的默认缓存目录。

和 TurboMind 引擎相关的参数

  • --tp TP: 在张量并行中使用的GPU数量
  • --session-len SESSION_LEN: 序列的最大会话长度
  • --max-batch-size MAX_BATCH_SIZE: 最大批量大小。默认:128
  • --cache-max-entry-count CACHE_MAX_ENTRY_COUNT: KV 缓存占用的空闲GPU内存的百分比,不包括权重。默认:0.8
  • --cache-block-seq-len CACHE_BLOCK_SEQ_LEN: KV 缓存块容纳的token数量。对于Turbomind引擎,如果GPU compute_capability >= 8.0,应该是32的倍数,否则应该是64的倍数。默认:64。
  • --enable-prefix-caching: 是否启用前缀匹配KV缓存。默认:False。
  • --model-format {hf,llama,awq,gptq}: 输入模型的格式。hf 表示 hf_llama,llama 表示 meta_llama,awq 表示 awq 量化模型,gptq 表示 gptq 量化模型
  • --quant-policy {0,4,8}: 是否量化kv。0: 不量化;4: 4位kv;8: 8位kv。默认:0
  • --rope-scaling-factor ROPE_SCALING_FACTOR: Rope缩放因子。默认:0.0
  • --num-tokens-per-iter NUM_TOKENS_PER_ITER: 前向传递中处理的令牌数量。默认:0
  • --max-prefill-iters MAX_PREFILL_ITERS: prefill阶段的最大前向传递数量。默认:1

和 PyTorch 引擎相关的参数

  • --adapters [ADAPTERS ...]: 用于设置 lora 模型的路径。可以输入多个lora的键值对格式xxx=yyy。如果只有一个适配器,可以只输入适配器的路径。默认:无。类型:字符串。
  • --tp TP: 在张量并行中使用的GPU数量
  • --session-len SESSION_LEN: 序列的最大会话长度
  • --max-batch-size MAX_BATCH_SIZE: 最大批量大小。默认:128
  • --cache-max-entry-count CACHE_MAX_ENTRY_COUNT: KV 缓存占用的空闲GPU内存的百分比,不包括权重。默认:0.8
  • --cache-block-seq-len CACHE_BLOCK_SEQ_LEN: KV 缓存块容纳的token数量。如果指定了Lora适配器,此参数将被忽略。默认:64
  • --enable-prefix-caching: 是否启用前缀匹配KV缓存。默认:False。

视觉模型参数:

  • --vision-max-batch-size VISION_MAX_BATCH_SIZE: 视觉模型批量大小。默认:1。

2.3.2 访问服务

推荐使用 openai client package 接口访问服务

  • 图像 url
from openai import OpenAI
client = OpenAI(
   api_key='YOUR_API_KEY', # dummy key to pass openai checking 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)

如模型支持多图,可以在messages中 user 的content列表中追加图像

  • 图像base64编码
from openai import OpenAI
client = OpenAI(
   api_key='YOUR_API_KEY', # dummy key to pass openai checking key
   base_url='http://0.0.0.0:23333/v1')
def encode_image(image_path):
 with open(image_path, "rb") as image_file:
   return base64.b64encode(image_file.read()).decode('utf-8')
   
# Path to your image
image_path = "path_to_your_image.jpg"
# Getting the base64 string
base64_image = encode_image(image_path)
model_name = client.models.list().data[0].id
response = client.chat.completions.create(
   model=model_name,
   messages=[
   {
     "role": "user",
     "content": [
       {"type": "text", "text": "What’s in this image?"},
       {
         "type": "image_url",
         "image_url": {
           "url": f"data:image/jpeg;base64,{base64_image}",
         },
       },
     ],
   }
   ],
   max_tokens=300,
)
  • 加入额外信息
from openai import OpenAI
client = OpenAI(
   api_key='YOUR_API_KEY', # dummy key to pass openai checking 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,
   extra_body={"repetition_penalty": 1.02}
   )
print(response)

2.4 模型量化

2.4.1 权重量化

  • 4bit 权重量化。量化算法 AWQ,只对 VLM 中的语言模型部分做量化,视觉部分不做量化
  • 支持的显卡型号:
  • V100(0.6.0 支持,尚未发版)
  • Turing(sm75): 20 系列,T4
  • Ampere(sm80): A100
  • Ampere(sm86): 30 系列,A10, A16, A30 等
  • Ada Lovelace(sm89): 40 系列
  • Hopper(sm90): H100, H800(尚未深度优化)
lmdeploy lite auto_awq OpenGVLab/InternVL2-8B

2.4.2 KV Cache 量化

设置 TurbomindEngineConfig 中的参数 quant_policy。详细介绍请参考前文推理引擎配置章节中关于quant_policy的说明

03 推理性能

关于 VLM 推理性能评测方法暂无统一标准,LLM有

评测 LLM pipeline 的方式如下,测试指标 RPS(Request Per Second)

python benchmark/profile_pipeline_api.py \
   ShareGPT_V3_unfiltered_cleaned_split.json \
   meta-llama/Meta-Llama-3-8B-Instruct \
   --num-prompts 5000

在A100-SMX4-80G显卡上,测试结果如下:

--------------------------------------------------
concurrency: 256
elapsed_time: 208.390s
first token latency(s)(min, max, ave): 0.068, 3.880, 0.378
per-token latency(s) percentile(50, 75, 95, 99): [0, 0.09, 0.153, 0.207]
number of prompt tokens: 1136185
number of completion tokens: 1008966
token throughput (completion token): 4841.723 token/s
token throughput (prompt + completion token): 10293.932 token/s
RPS (request per second): 23.993 req/s
RPM (request per minute): 1439.609 req/min
--------------------------------------------------

测试 LLM serving 性能,推荐使用 vLLM 的测试脚本 https://github.com/vllm-project/vllm/blob/main/benchmarks/benchmark_serving.py。该脚本的测试指标是:TTFT、TPOT

# 首先启动服务
lmdeploy api_server meta_llama/Meta-Llama-3-8B-Instruct --max-batch-size 256
# 打开新的终端,使用 vLLM 的测试脚本 benchmark_serving.py 测试

image.png

vllm 0.4.2

lmdeploy 0.4.1

tensorrt-llm v0.9.0

04 VLM推理实现方式

初始化 pipeline

image.png

prompt 预处理

image.png

在离线推理章节的示例 1,模型为 OpenGVLab/InternVL2-8B,pipe推理的请求为('describe this image', image)

经过"转GPT4V请求格式"后,请求变成:

{
   "role": "user",
   "content": [
       {
           "type": "text",
           "text": "describe this image"
       },
       {
           "type": "image_data",
           "image_data": {
               "data": image
           }
       }
   ]
}

经过 "加image标记符",请求变成:

{'role': 'user', 'content': '<img><IMAGE_TOKEN></img>\ndescribe this image'}

InternVL2-8B 是把 image放到text前的

经过 "装饰对话模板 message2prompt",请求变成:

<|im_start|>system\n你是由上海人工智能实验室联合商汤科技开发的书生多模态大模型,英文名叫InternVL, 是一个有用无害的人工智能助手。<|im_end|>\n<|im_start|>user\n<img><IMAGE_TOKEN></img>\ndescribe this image<|im_end|>\n<|im_start|>assistant\n

图像编码

image.png

self.vl_encoder 复用上游库的图像预处理和vision模型推理

推理

  • TurboMind 推理

image.png

  • PyTorch 推理

TODO

05 未来规划

image.png

06 附录

6.1 内置对话模板

模型

模型类型

模型结构

内置对话模板名称

说明

InternLM-XComposer2

MLLM

InternLMXComposer2ForCausalLM

internlm-xcomposer2

InternLM-XComposer2.5

MLLM

InternLMXComposer2ForCausalLM

internlm-xcomposer2d5

Qwen-VL

MLLM

QWenLMHeadModel

qwen

DeepSeek-VL

MLLM

MultiModalityCausalLM

deepseek-vl

Phi-3-vision

MLLM

Phi3VForCausalLM

phi-3

CogVLM-Chat

MLLM

CogVLMForCausalLM

cogvlm

CogVLM2-Chat

MLLM

CogVLMForCausalLM

cogvlm2

Yi-VL

MLLM

LlavaLlamaForCausalLM

yi-vl

LLaVA-v1.5

MLLM

LlavaLlamaForCausalLM  

llava-vl

LLaVA-v1.6-vicuna

MLLM

LlavaLlamaForCausalLM

llava-v1

llava-v1.6-34b

MLLM

LlavaLlamaForCausalLM

llava-chatml

llava-v1.6-mistral-7b

MLLM

LlavaMistralForCausalLM

mistral

InternVL-Chat-V1-5

MLLM

InternLM2ForCausalLM

internvl-internlm2

InternVL系列需要看llm_config中的architectures

Mini-InternVL-Chat-2B-V1-5

MLLM

InternLM2ForCausalLM

internvl-internlm2

Mini-InternVL-Chat-4B-V1-5

MLLM

Phi3ForCausalLM

internvl-phi3

InternVL2 (2B、8B、26B)

MLLM

InternLM2ForCausalLM

internvl2-internlm2

InternVL2 (4B)

MLLM

Phi3ForCausalLM

internvl2-phi3

InternVL2 (40B)

MLLM

LlamaForCausalLM

internvl2-internlm2

InternVL2-Llama3-76B

MLLM

LlamaForCausalLM

internvl2-internlm2

MiniCPM-Llama3-V-2_5

MLLM

MiniCPMV

llama3

llm的种类在代码里面才能看出来

MiniGeminiLlama

MLLM

MiniGeminiLlamaForCausalLM

mini-gemini-vicuna

GLM-4V

MLLM

ChatGLMModel

glm4

相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
相关文章
|
24天前
|
监控 Docker 容器
Docker Swarm集群的扩展与缩容策略,涵盖其意义、方法、步骤及注意事项
本文深入探讨了Docker Swarm集群的扩展与缩容策略,涵盖其意义、方法、步骤及注意事项,旨在帮助用户高效管理集群资源,适应业务变化,确保服务稳定性和资源优化。
46 6
|
1月前
|
监控 前端开发 jenkins
Jenkins 在前端项目持续部署中的应用,包括其原理、流程以及具体的实现方法
本文深入探讨了Jenkins在前端项目持续部署中的应用,涵盖其基本原理、流程及具体实现方法。首先介绍了Jenkins的基本概念及其在自动化任务中的作用,随后详细解析了从前端代码提交到生产环境部署的全过程,包括构建、测试、部署等关键步骤。最后,强调了持续部署中的代码质量控制、环境一致性、监控预警及安全管理等注意事项,旨在帮助开发者高效、安全地实施持续部署。
65 5
|
3月前
|
监控 JavaScript Java
部署应用程序的具体步骤
部署应用程序的具体步骤
138 4
|
4月前
|
边缘计算 运维 Kubernetes
在K8S中,常见部署K8S方式有哪些?
在K8S中,常见部署K8S方式有哪些?
|
7月前
|
中间件 Linux API
容器改造传统应用的流程
【5月更文挑战第1天】如何评估和执行将传统应用进行Docker化改造的过程。
|
7月前
|
Kubernetes 测试技术 开发者
ChaosBlade常见问题之对isulad容器引擎做注入如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
|
7月前
|
存储 程序员 C++
C++容器初始化方式详解:优缺点、性能与应用场景
C++容器初始化方式详解:优缺点、性能与应用场景
124 0
|
7月前
|
编译器 C++ 容器
【C++】vector容器接口要点的补充
【C++】vector容器接口要点的补充
|
7月前
|
监控 测试技术 数据库
mPaaS的私有化部署主要涉及到以下几个步骤
mPaaS的私有化部署主要涉及到以下几个步骤
136 1
|
7月前
|
SQL XML JSON
Hasor【部署 04】Dataway接口配置服务扩展能力实例分享
Hasor【部署 04】Dataway接口配置服务扩展能力实例分享
111 0

热门文章

最新文章

相关实验场景

更多