0%

大模型应用系列(十一) LMDeploy量化部署

介绍量化的概念,使用lmdeploy进行模型量化与kv-cache量化部署。

一. 概述

1.1 大模型推理
  1. 内存开销巨大
  • 庞大的参数量,7B模型权重就需要14+GB显存
  • 采用自回归生成token,需要kv-cache,带来巨大的显存开销
  1. 动态shape
  • 请求数不固定
  • Token逐个生成,且数量不定
  1. 相对视觉模型,LLM结构简单
  • Transformer结构,大部分是decoder-only结构。
1.2 模型部署
  1. 定义:

    将训练好的模型在特定的软硬件环境中启动的过程,使模型能够接收输入并返回预测结果;为了满足性能和效率的需求,常常需要对模型进行优化,例如模型压缩(剪枝,量化,知识蒸馏)和硬件加速。

  2. 产品形态

    云端,边缘计算端,移动端

  3. 计算设备

    CPU, GPU, NPU, TPU等。

1.3 大模型部署挑战
  1. 设备

    如何应对巨大的存储问题?低存储设备(消费级显卡,手机等)如何部署?

  2. 推理

    • 如何加速token的生成
    • 如何解决动态shape,让推理可以不间断
    • 如何有效管理和利用显存
  3. 服务

    • 如何提升系统整体吞吐量?
    • 对于个体用户,如何降低相应时间?
1.4 大模型部署方案
  1. 常见技术

    • 张量并行
    • transformer计算和访存优化
    • 低比特量化
    • Continuous Batch
    • Page Attention
    • Blocked kv-cache
  2. 部署方案

    • huggingface transformers
    • 专门的推理加速框架
云端 移动端
lmdeploy, vllm, tensorrt-llm,deepspeed llama.cpp, mlc-llm

二. LMDeploy简介

LMDeploy是LLM在英伟达设备上部署的全流程解决方案。包括模型轻量化,推理和服务。

项目地址: InternLM/lmdeploy: LMDeploy is a toolkit for compressing, deploying, and serving LLMs.

中文文档: 欢迎来到 LMDeploy 的中文教程! — lmdeploy

2.1 核心功能
  1. 高效的推理: LMDeploy开发了continus Batch, Blocked KV-cache,动态拆分和融合,张量并行,高效的计算kernel等重要特性。推理性能是vLLM的1.8倍。
  2. 可靠的量化: LMDeploy支持权重量化和kv量化。4bit模型推理效率是FP16下的2.4倍。量化模型的可靠性已通过 OpenCompass评测得到充分验证。
  3. 便捷的服务: 通过请求分发服务,LMDeploy支持多模型在多机,多卡上的推理服务。
  4. 有状态推理: 通过缓存多轮对话过程中attention的KV,记住对话历史,从而避免重复处理历史绘画,显著提升长文本多轮对话场景中的效率。
  5. 卓越的兼容: LMDeploy 支持 KV-cache量化,AWQ, Automatic Prefix Caching同时使用
2.2 性能

LMDeploy TurboMind 引擎拥有卓越的推理能力,在各种规模的模型上,每秒处理的请求数是 vLLM 的 1.36 ~ 1.85 倍。在静态推理能力方面,TurboMind 4bit 模型推理速度(out token/s)远高于 FP16/BF16 推理。在小 batch 时,提高到 2.4 倍。

image-20250408222914416

LMDeploy支持2种推理引擎: TurboMind和PyTorch,它们侧重不同,前者追求性能的极致优化,后者纯用python开发,着重降低开发者门槛。在实际部署时,首选TurboMind。

2.3 量化
  1. 两个基本概念

    • 计算密集(compute-bound): 推理的绝大部分时间消耗在数值计算上;针对计算密集场景,可以通过使用更快的硬件计算单元来提升计算速度,比如量化为W8A8使用INT8 Tensor Core来加速计算。
    • 访存密集(memory-bound): 推理时,绝大部分时间消耗在数据读取上;针对访存密集型场景,一般是通过提高计算访存比来提升性能。

      LLM是典型的访存密集型任务,常见的LLM模型是Decode Only结构,推理时大部分时间消耗在逐Token生成阶段(Decoding阶段),时典型的访存密集型场景。可以通过roofline模型来判断时计算密集型还是访存密集型。

如图: 可以看到只有在Batch Size 达到这个量级时,计算才会成为推理的瓶颈,但由于LLM模型本身就很大,推理时的KV-cache也会占用很多显存,还有一些其他的因素影响(如Persistent Batch), 实际推理时很难做到batchSize=128这么大,所以一般LLM服务都是访存密集型。

image-20250408225102213

  1. Weight Only量化一举多得

    4 bit Weight Only量化,将FP16的模型权重量化为NT4, 访存量直接降为FP16模型的1/4,大幅降低了访存成本,提升了Decoding的速度。

    加速的同时还节省了显存,同样的设备能够支持更大的模型以及更长的对话长度。

2.4 如何做Weight Only的量化

LMDeploy使用MIT HAN LAB 开源的AWQ算法,量化为4bit模型,推理时,先把4bit反量化回FP16(在Kernel内部进行,从Global Memory读取时仍时4bit),使用的是FP16计算。这样会造成一定的精度损失,但比直接用INT4计算准确。

2.5 推理引擎TurboMind
  1. continue batching:
    • 请求可以及时加入batch中推理。
    • Batch中已经完成的推理请求及时退出。
  2. 有状态推理
    • 无状态推理: 每次请求均携带历史对话记录
    • 有状态推理: 请求不带历史记录,历史记录在推理侧缓存。
    • LMDeploy使用有状态推理,推理侧缓存历史对话的kv-cache。
  3. Blocked kv-cache
    • Attention支持不连续的kv-cache
    • block(Page attention)

三.LMDeploy量化部署

3.1 对量化前的模型做部署验证

模型下载参考大模型应用系列(二) Huggingface的安装和使用 | 乌漆嘛黑

这里以量化qwen2.5-7B为例,从模型的config.json文件可知,模型的权重被存储为bfloat16格式,对于一个7B(70亿)参数的模型,每个参数使用FP16表示(2个Byte),则模型的权重大小约为:

$70\times 10^9 parameters\times 2 Bytes /parameter=14GB$

所以需要大于14GB的显存。

3.2 创建环境

可以参考 大模型应用系列(六) ollama,vllm,LMDeploy 部署大模型 | 乌漆嘛黑

如果想要查看lmdeploy的具体命令,可以用以下命令

1
lmdeploy -h

可以看到lmdeploy支持以下几个子命令

image-20250410220957885

输入子命令查看指令可进一步查看子命令参数

1
lmdeploy chat -h

image-20250410221036121

接着使用LMdeploy验证模型,进入创建好的环境并启动模型

1
lmdeploy chat /root/autodl-tmp/Qwen2.5-7B-Instruct

image-20250409012836564

报错oom了,我的显存是24GB,上面计算模型需要14GB,模型在运行时,占用的显存可大致分为三部分:模型参数本身占用的显存、kv cache占用的显存,以及中间运算结果占用的显存。LMDeploy的kv cache管理器可以通过设置 控制kv缓存占用剩余显存的最大比例。默认的比例为0.8 。我们可以通过--cache-max-entry-count指定kv-cache的最大比例,比如设置为0.4:

1
lmdeploy chat /root/autodl-tmp/Qwen2.5-7B-Instruct --cache-max-entry-count 0.4

此时就可以正常启动了。

image-20250410221712560

3.3 LMDeploy量化部署
3.3.1 lmdeploy 部署

使用以下命令

1
lmdeploy serve api_server /root/autodl-tmp/Qwen2.5-7B-Instruct --model-format hf --quant-policy 0 --server-name 0.0.0.0 --server-port 23333 --tp 1 --cache-max-entry-count 0.4

对命令的各个参数解释如下,也可以通过lmdeploy serve api_server -h 查看说明

1
2
3
4
5
6
7
8
9
lmdeploy serve api_server:这个命令用于启动API服务器。
/root/autodl-tmp/Qwen2.5-7B-Instruct:这是模型的路径。
--model-format hf:这个参数指定了模型的格式。hf代表“Hugging Face”格式。
--quant-policy 0:这个参数指定了kv-cache的量化策略。取值为{0,4,8},0表示不量化,4表示INT4量化,8表示INT8量化
--server-name 0.0.0.0:这个参数指定了服务器的名称。在这里,0.0.0.0是一个特殊的IP地址,它表
示所有网络接口。
--server-port 23333:这个参数指定了服务器的端口号。在这里,23333是服务器将监听的端口号。
--tp 1:这个参数表示并行数量(GPU数量)。
--cache-max-entry-count 0.4: 指定分配给kv-cache的显存比例

接着以命令行形式连接API服务器,以脚本形式连接在之前的openai兼容形式连接已经介绍过。

1
lmdeploy serve api_client http://localhost:23333
3.3.2 LMDeploy Lite

随着模型变得越来越大,我们需要一些大模型压缩技术来降低模型部署的成本,并提升模型的推理性能。LMDeploy 提供了权重量化和 kv -cache两种策略。

部署时管理kv-cache主要有两个方式: 指定分配给kv-cache的内存大小(--cache-max-entry-count), 以及kv-cache量化策略(--quant-policy) , 自 v0.4.0 起,LMDeploy 支持在线 kv cache int4/int8 量化,量化方式为 per-head per-token 的非对称量化。此外,通过 LMDeploy 应用 kv 量化非常简单,只需要设定 quant_policy 和 cache-max-entrycount 参数。目前,LMDeploy 规定 qant_policy=4 表示 kv int4 量化, quant_policy=8 表示 kv int8量化。

可以通过以下命令使用kv-cache量化

1
lmdeploy serve api_server /root/autodl-tmp/Qwen2.5-7B-Instruct --model-format hf --quant-policy 0 --server-name 0.0.0.0 --server-port 23333 --tp 1 --cache-max-entry-count 0.4

可以看出,量化与不量化的显存占用时相同的,这是因为kv-cache在命令中指定了显存比例,不论是否量化都会预分配那么多,但是如果测试就可以体会到,量化后可以输入更长的文本

不量化 INT4量化 INT8量化
image-20250412211025200 image-20250412211032549 image-20250412211041353
3.3.3 W4A16模型量化部署

模型量化旨在减少模型大小并提高推理速度,同时留出更多显存给kv-cache,能支持更长的prompt。量化通过将模型参数的权重和激活从高精度(比如FP16)量化为低精度(INT4,INT8),LMDeploy模型量化使用的是W4A16:

  • W4: 表示权重量化为4位整数(int4)。这意味着模型中的权重参数将从它们原始的浮点表示(例如FP32、BF16或FP16)转换为4位的整数表示。这样做可以显著减少模型的大小。
  • A16: 这表示激活(或输入/输出)仍然保持在16位浮点数(例如FP16或BF16)。激活是在神经网络中传播的数据,通常在每层运算之后产生。

因此W4A16意味着权重(Weight)被量化为INT4,激活(Activation)保持FP16。

使用如下命令进行W4A16量化

1
lmdeploy lite auto_awq /root/autodl-tmp/Qwen2.5-7B-Instruct --calib-dataset 'ptb' --calib-samples 128 --calib-seqlen 2048 --w-bits 4 --w-group-size 128 --batch-size 1 --work-dir /root/autodl-tmp/Qwen2.5-7B-Instruct-W4A16

命令参数解释如下:也可以通过lmdeploy lite auto_awq -h命令查看

1
2
3
4
5
6
7
8
9
10
lmdeploy lite auto_awq: lite这是LMDeploy的命令,用于启动量化过程,而auto_awq代表自动权重
量化(auto-weight-quantization)。
/root/autodl-tmp/Qwen2.5-7B-Instruct: 模型文件的路径。
--calib-dataset 'ptb': 这个参数指定了一个校准数据集,这里使用的是’ptb’(Penn Treebank,一
个常用的语言模型数据集)。
--calib-samples 128: 这指定了用于校准的样本数量—128个样本
--calib-seqlen 2048: 这指定了校准过程中使用的序列长度—1024
--w-bits 4: 这表示权重(weights)的位数将被量化为4位。
--work-dir /root/autodl-tmp/Qwen2.5-7B-Instruct-W4A16: 这是工作目录的路径,用于存储量
化后的模型和中间结果。

量化过程中要下载校准数据集,校准数据集在huggface上,如果访问不了外网会报错,需要在服务器上安装梯子,如果没有,可以用一种麻烦的方法,见4.1

解决了以上问题,就可以开始量化,量化过程会花一些时间,要等一等。

image-20250411000254839

结束后进入目录/root/autodl-tmp使用如下命令查看文件及其大小:

du -sh * 显示如下:

image-20250411000421175

可以看到量化后的模型比原模型大小小了很多。量化后的模型可以和原模型一样使用

1
lmdeploy chat /root/autodl-tmp/Qwen2.5-7B-Instruct-W4A16 --model-format awq

image-20250411000806041

3.3.4 离线转换TurboMind格式

离线转换需要在启动服务之前,将模型转为 lmdeploy TurboMind 的格式,如下所示。

1
lmdeploy convert Qwen2.5-7B-Instruct /root/autodl-tmp/Qwen2.5-7B-Instruct

第一个参数是模型名称,用于选择对话模板。第二个参数是模型路径。

执行完成后将会在当前目录生成一个 workspace 的文件夹。这里面包含的就是 TurboMind 和 Triton “模型推理”需要到的文件。 里面的参数文件如下:

image-20250411002033159

每一份参数第一个 0 表示“层”的索引,后面的那个0表示 Tensor 并行的索引,因为我们只有一张卡,所以被拆分成 1 份。如果有两张卡可以用来推理,则会生成0和1两份,也就是说,会把同一个参数拆成两份。比如 layers.0.attention.w_qkv.0.weight 会变成 layers.0.attention.w_qkv.0.weightlayers.0.attention.w_qkv.1.weight。执行 lmdeploy convert 命令时,可以通过 --tp指定(tp 表示 tensor parallel,该参数默认值为1也就是一张卡) 。

转化为TurboMind格式后,就可以正常启动命令行本地对话了。

1
lmdeploy chat /root/autodl-tmp/workspace

启动后就可以和它进行对话了。

3.4 网页Demo演示

这里用Gradio作为前端Demo

先启动服务器,可以用上面的任一方式。这里用kv-cache量化部署方式,

1
lmdeploy serve api_server /root/autodl-tmp/workspace --model-format hf --quant-policy 0 --server-name 0.0.0.0 --server-port 23333 --tp 1 --cache-max-entry-count 0.4

接着在另一个终端启动Gradio, 如果报错缺少包,就pip缺啥装啥。

1
2
# Gradio+ApiServer。必须先开启 Server,此时 Gradio 为 Client
lmdeploy serve gradio http://0.0.0.0:23333

效果如下:

image-20250411004334933

四. 补充

4.1 LMDeploy 模型量化时报错

3.3.3 中可能会报错,TypeError: 'NoneType' object is not callable , 原因是datasets 3.0 无法下载数据集``

image-20250410231740355

将下载datasets的其他版本即可。运行下列指令

1
pip install datasets==2.19.2

如果服务器上没有梯子,下载数据集时还会报错ConnectionError: Couldn't reach https://raw.githubusercontent.com/wojzaremba/lstm/master/data/ptb.train.txt (error 429)image-20250410234058182

可以先在本地翻墙把文件下载下来,使用如下代码

1
2
3
from datasets import load_dataset

traindata = load_dataset('ptb_text_only', 'penn_treebank', split='train', trust_remote_code=True)

下载的文件缓存在C:\Users\用户名\.cache\huggingface\datasets中,把里面的ptb_text_only 上传到服务器的~/.cache/huggingface/datasets 目录下即可,其他huggingface下载的数据集都可以通过这种方式解决。服务器上查询隐藏文件可以用ls --all

如果您读文章后有收获,可以打赏我喝咖啡哦~