Lecture5 LMDeploy大模型量化部署实践
在Lecture5中,我们首先深入了解了大模型的特点,包括其庞大的规模以及在部署过程中面临的挑战。接着,我们详细学习了LMDeploy
框架的三个核心功能,分别是轻量化、推理引擎TurboMind
和服务。此外,我们还进行了实际的安装、部署和量化操作,以加深对这些概念的理解和应用。
课前的一些问题~
什么是LMDeploy
?
LMDeploy
是大语言模型在英伟达设备上部署的全流程方案。包括模型轻量化、推理和服务。(还没有涉及到移动端),项目地址
什么是量化?为什么要量化?
显存降低了,存储空间增多了,同样的设备下能够容纳更多,提高速度。
一、大模型部署背景
什么是模型部署?
- 定义:将训练好的模型,在特定的硬软件环境中启动运行起来,能够接收一定的输入并返回一定的输出。
需要考虑模型的压缩和硬件的加速
大模型特点:
消耗显存内存大:大模型的参数量很大;需要token by token,占用显存很大
- 动态shape:请求数不确定,token逐个生成且数量不定
- 结构简单:transformer结构,大部分是decoder-only
大模型部署的挑战:
- 设备挑战:巨大的储存、如何部署
- 推理:怎么加速token生成速度;怎么有效管理内存;怎么把batch文本的随机数量,让其推理不间断
- 服务:怎么提升整体吞吐量;对个体用户的响应时间
大模型部署方案
- 技术点:模型并行、低比特量化、page attention、transformer计算和访存优化、continuous batch???
- 方案 云端方案:deepspeed、vllm、Imdeploy 移动端:llama.cpp、mlc-llm
二、LMDeploy简介
部署有很多成熟的框架,那LMDeploy是什么呢?
① LMDeploy的内容:
1、轻量化
2、推理引擎:turbomind(创新点、支持pytorch)
3、服务:api service、gradio、triton inference server
② 怎么做weight only的量化?
AWQ算法:在张量/矩阵进行计算的过程中,不量化最重要的少部分参数…
③ 推理引擎TurboMind
持续批处理: 请求可以及时加入batch推理,Batch中已经完成的请求及时退出。是动态的,保证不管怎么进来,service都不用等到整个batch结束。
有状态的推理:对话 token 被缓存在推理侧。用户侧请求无需带上历史对话记录。
高性能算子
Blocked k/v cache
④ 服务
可以通过一个简单的命令运行一个api服务
lmdeploy server api_server InterLM/internlm-chat-7b —model-name internlm-chat-6b —server-port 8080
三、LMDeploy实战
环境配置
设置开发机
在 InternStudio 平台中选择 A100(1/4) 的配置,打开刚刚租用服务器的
进入VScode
,并且打开其中的终端开始环境配置。进入
VScode
,并在终端输入bash
命令,进入conda
环境。进入
conda
环境之后,使用以下命令从本地一个已有的pytorch 2.0.1
的环境创建需要的环境1
2bash
conda create -n Lmdeploy --clone /share/conda_envs/internlm-base然后使用以下命令激活环境
1
conda activate Lmdeploy
下载
imdeploy
1
2
3pip install packaging
pip install /root/share/wheels/flash_attn-2.4.2+cu118torch2.0cxx11abiTRUE-cp310-cp310-linux_x86_64.whl
pip install 'lmdeploy[all]==v0.1.0'
服务部署
原理介绍CS架构:左边service(模型推理各种优化策略+暴露服务放在API),右边client(终端用户的使用)。
模型转换 ① 在线转换:hf通过imdeploy量化;hf上面其他LM模型 ② 离线转换:指定模型名称、模型路径,转换为
turbomind
需要的格式,如下图1
2
3lmdeploy convert internlm-chat-7b /root/share/temp/model_repos/internlm-chat-7b/
lmdeploy chat turbomind /share/temp/model_repos/internlm-chat-7b/ --model-name internlm-chat-7bTurboMind 推理+命令行本地对话 此时是跳过 API Server 直接调用 TurboMind。Server 就是本地跑起来的模型(TurboMind),命令行启用Client可以看作是前端。
TurboMind推理+API服务 运用 lmdepoy 进行服务化,使用Turbomind的API Server 可以提供对外的 API 服务来进行服务。 下面的参数中
server_name
和server_port
分别表示服务地址和端口,tp
参数我们之前已经提到过了,表示 Tensor 并行。还剩下一个instance_num
参数,表示实例数,可以理解成 Batch 的大小。执行后如下图所示。1
2
3
4
5
6# ApiServer+Turbomind api_server => AsyncEngine => TurboMind
lmdeploy serve api_server ./workspace \\
--server_name 0.0.0.0 \\
--server_port 23333 \\
--instance_num 64 \\
--tp 1
TurboMind 推理作为后端:Gradio 也可以直接和 TurboMind 连接
TurboMind 服务作为后端:
1
2
3
4
5# Gradio+ApiServer。必须先开启 Server,此时 Gradio 为 Client
lmdeploy serve gradio <http://0.0.0.0:23333> \\
--server_name 0.0.0.0 \\
--server_port 6006 \\
--restful_api True
分场景应用API 服务和 Client
- 我想对外提供类似 OpenAI 那样的 HTTP 接口服务。推荐使用 TurboMind推理 + API 服务。
- 我想做一个演示 Demo,Gradio 无疑是比 Local Chat 更友好的。推荐使用 TurboMind 推理作为后端的Gradio进行演示。
- 我想直接在自己的 Python 项目中使用大模型功能。推荐使用 TurboMind推理 + Python。
- 我想在自己的其他非 Python 项目中使用大模型功能。推荐直接通过 HTTP 接口调用服务。也就是用本列表第一条先启动一个 HTTP API 服务,然后在项目中直接调用接口。
- 我的项目是 C++ 写的,为什么不能直接用 TurboMind 的 C++ 接口?!必须可以!大佬可以右上角叉掉这个窗口啦。
模型配置和参数调整
这里主要介绍三个可能需要调整的参数。
- KV int8 开关:
- 对应参数为
quant_policy
,默认值为 0,表示不使用 KV Cache,如果需要开启,则将该参数设置为 4。 - KV Cache 是对序列生成过程中的 K 和 V 进行量化,用以节省显存。我们下一部分会介绍具体的量化过程。
- 当显存不足,或序列比较长时,建议打开此开关。
- 对应参数为
- 外推能力开关:
- 对应参数为
rope_scaling_factor
,默认值为 0.0,表示不具备外推能力,设置为 1.0,可以开启 RoPE 的 Dynamic NTK 功能,支持长文本推理。另外,use_logn_attn
参数表示 Attention 缩放,默认值为 0,如果要开启,可以将其改为 1。 - 外推能力是指推理时上下文的长度超过训练时的最大长度时模型生成的能力。如果没有外推能力,当推理时上下文长度超过训练时的最大长度,效果会急剧下降。相反,则下降不那么明显,当然如果超出太多,效果也会下降的厉害。
- 当推理文本非常长(明显超过了训练时的最大长度)时,建议开启外推能力。
- 对应参数为
- 批处理大小:
- 对应参数为
max_batch_size
,默认为 64,也就是我们在 API Server 启动时的instance_num
参数。 - 该参数值越大,吞度量越大(同时接受的请求数),但也会占用更多显存。
- 建议根据请求量和最大的上下文长度,按实际情况调整。
- 对应参数为
- KV int8 开关:
四、模型量化
首先我们需要明白一点,服务部署和量化是没有直接关联的,量化的最主要目的是降低显存占用,主要包括两方面的显存:模型参数和中间过程计算结果。前者对应《W4A16 量化》,后者对应《 KV Cache 量化》。
W4A16 量化
W4A16中的A是指Activation,保持FP16,只对参数进行 4bit 量化。使用过程也可以看作是三步。
量化步骤
第一步:同KV Cache量化,不再赘述。
第二步:量化权重模型。利用第一步得到的统计值对参数进行量化,具体又包括两小步:
- 缩放参数。主要是性能上的考虑(回顾 PPT)。
- 整体量化。
1
2
3
4
5
6
7
8
9
10
11# 第二步的执行命令如下:
# 量化权重模型
lmdeploy lite auto_awq \\
--model /root/share/temp/model_repos/internlm-chat-7b/ \\
--w_bits 4 \\
--w_group_size 128 \\
--work_dir ./quant_output
# 命令中 w_bits 表示量化的位数,w_group_size 表示量化分组统计的尺寸,
# work_dir 是量化后模型输出的位置。
# 这里需要特别说明的是,因为没有 torch.int4,所以实际存储时,8个 4bit 权重会被打包到一个 int32 值中。所以,如果你把这部分量化后的参数加载进来就会发现它们是 int32 类型的。最后一步:转换成 TurboMind 格式。
1
2
3
4# 转换模型的layout,存放在默认路径 ./workspace 下
lmdeploy convert internlm-chat-7b ./quant_output \\
--model-format awq \\
--group-size 128这个
group-size
就是上一步的那个w_group_size
。如果不想和之前的workspace
重复,可以指定输出目录:--dst_path
,比如:1
2
3
4lmdeploy convert internlm-chat-7b ./quant_output \\
--model-format awq \\
--group-size 128 \\
--dst_path ./workspace_quant接下来和上一节一样,可以正常运行前面的各种服务了,不过咱们现在用的是量化后的模型。
KV Cache 量化
KV Cache 量化是将已经生成序列的 KV 变成 Int8
量化步骤
第一步:计算 minmax。主要思路是通过计算给定输入样本在每一层不同位置处计算结果的统计情况。
1
2
3
4
5
6
7
8
9
10
11# 计算 minmax
lmdeploy lite calibrate \\
--model /root/share/temp/model_repos/internlm-chat-7b/ \\
--calib_dataset "c4" \\
--calib_samples 128 \\
--calib_seqlen 2048 \\
--work_dir ./quant_output
# 在这个命令行中,会选择 128 条输入样本,每条样本长度为 2048,
# 数据集选择 C4,输入模型后就会得到上面的各种统计值。
# 值得说明的是,如果显存不足,可以适当调小 samples 的数量或 sample 的长度。- 对于 Attention 的 K 和 V:取每个 Head 各自维度在所有Token的最大、最小和绝对值最大值。对每一层来说,上面三组值都是
(num_heads, head_dim)
的矩阵。这里的统计结果将用于本小节的 KV Cache。 - 对于模型每层的输入:取对应维度的最大、最小、均值、绝对值最大和绝对值均值。每一层每个位置的输入都有对应的统计值,它们大多是
(hidden_dim, )
的一维向量,当然在 FFN 层由于结构是先变宽后恢复,因此恢复的位置维度并不相同。这里的统计结果用于下个小节的模型参数量化,主要用在缩放环节(回顾PPT内容)。
第二步:通过 minmax 获取量化参数。主要就是利用下面这个公式,获取每一层的 K V 中心值(zp)和缩放值(scale)。
1
2
3
4zp = (min+max) / 2
scale = (max-min) / 255
quant: q = round( (f-zp) / scale)
dequant: f = q * scale + zp有这两个值就可以进行量化和解量化操作了。具体来说,就是对历史的 K 和 V 存储 quant 后的值,使用时在 dequant。
1
2
3
4
5
6
7
8
9
10# 第二步的执行命令如下:
# 通过 minmax 获取量化参数
lmdeploy lite kv_qparams \\
--work_dir ./quant_output \\
--turbomind_dir workspace/triton_models/weights/ \\
--kv_sym False \\
--num_tp 1
# 在这个命令中,num_tp 表示 Tensor 的并行数。每一层的中心值和缩放值会存储到 workspace 的参数目录中以便后续使用。
# kv_sym 为 True 时会使用另一种(对称)量化方法,它用到了第一步存储的绝对值最大值,而不是最大值和最小值。第三步:修改配置。也就是修改
weights/config.ini
文件,这个我们在《2.6.2 模型配置实践》中已经提到过了(KV int8 开关),只需要把quant_policy
改为 4 即可。这一步需要额外说明的是,如果用的是 TurboMind1.0,还需要修改参数
use_context_fmha
,将其改为 0。接下来就可以正常运行前面的各种服务了,只不过咱们现在可是用上了 KV Cache 量化,能更省(运行时)显存了。
- 对于 Attention 的 K 和 V:取每个 Head 各自维度在所有Token的最大、最小和绝对值最大值。对每一层来说,上面三组值都是
量化流程
建议是:在各种配置下尝试,看效果能否满足需要。这一般需要在自己的数据集上进行测试。具体步骤如下。
- Step1:优先尝试正常(非量化)版本,评估效果。
- 如果效果不行,需要尝试更大参数模型或者微调。
- 如果效果可以,跳到下一步。
- Step2:尝试正常版本+KV Cache 量化,评估效果。
- 如果效果不行,回到上一步。
- 如果效果可以,跳到下一步。
- Step3:尝试量化版本,评估效果。
- 如果效果不行,回到上一步。
- 如果效果可以,跳到下一步。
- Step4:尝试量化版本+ KV Cache 量化,评估效果。
- 如果效果不行,回到上一步。
- 如果效果可以,使用方案。
SUMMARY:
部署:前面我们实战中的大模型,其实已经进行过部署实战了,就是让本地(或者在InternStudio
上)跑起来一个chat
模型。一般采用streamlit
或者gradio
的方式给一个页面,或者命令行方式启动。本节课具体介绍了模型部署涉及到的性能和效率的要求,以及考虑模型压缩,硬件加速等环节所作的工作。然后介绍了大模型的不同部署方案,最后具体介绍了**LMDeploy**
这个全流程部署方案,及其部署策略优势和内容,并开展了基于**LMDeploy**
的实战练习。
量化:量化的最主要目的是降低显存占用,量化在降低显存的同时,一般还能带来性能的提升,因为更小精度的浮点数要比高精度的浮点数计算效率高,而整型要比浮点数高很多,KV Cache 量化就是如此。此外量化模型和 KV Cache 量化也可以一起使用,以达到最大限度节省显存。