1. 大模型部署的背景
在人工智能领域,模型部署是将深度学习算法落地应用的关键步骤。简单来说就是将训练好的深度学习模型在特定环境中运行的过程。
部署场景
服务器端:CPU部署,但GPU/TPU/NPU部署,多卡/集群部署。。。。。。
移动端/边缘端: 移动机器人,手机。。。。。。
2. 大模型部署面临的挑战
计算量巨大
- 20B的模型生成1个tocken约406亿次浮点计算,128个tocken约5.2万亿次运算。
- NVIDA A100每秒约77万亿浮点计算
内存开销巨大
- 以FP16为例,20B模型仅加载参数就需要40G+显存,175B模型(如GPT3)需要350G+显存
- 大模型在推理过程中会占用较大的缓存。20B的模型输入512哥tocken会产生10.3GB的缓存
- NVIDIA RTX 4060消费级显卡的显存仅有8GB, A100单卡仅80GB
分析transformer模型的参数量、计算量、中间激活、KV cache - 知乎 (zhihu.com)
访存瓶颈
硬件计算速度远高于显存带宽,存在严重的访存瓶颈。
3. 大模型部署的方法
模型剪枝:移除模型中不必要的组件,比如参数,以使模型更加高效。
模型剪枝包括:
- 非结构化剪枝
- 结构化剪枝
知识蒸馏:是经典的模型压缩方法,核心思想是通过引导轻量化的学生模型模仿性能更好、结构更复杂的教师模型,在不改变学生模型结构的情况下提高性能。
模型量化:模型量化技术将传统的表示方法中的浮点数转换为整数或其它离散形式,以减轻深度学习模型的存储和计算负担。
- 量化感知训练:量化目标无缝的集成到模型的训练过程中。
- 量化感知微调:在微调的过程中对LLM进行量化。
- 训练后量化:在LLM的训练阶段完成后对其参数进行量化。
4. LMDeploy简介
LMDeply 涵盖了LLM任务的全套轻量化、部署和服务器解决方案。核心功能包括高效的推理、可靠量化、便捷服务和有状态推理。
LMDeploy核心功能:
高效推理: lmdeploy chat-h
模型量化压缩: imdeploy lite-h
服务化部署:imdeploy serve-h
LMDeploy支持的模型:
4. LMDeploy实践
Tutorial/lmdeploy/README.md at camp2 · InternLM/Tutorial (github.com)
InternStudio开发机创建conda环境(推荐)
激活刚刚创建的虚拟环境
安装0.3.0版本的lmdeploy
InternStudio开发机上下载模型(推荐)
实验机上已经提前下载了模型,下面通过软链接
使用Transformer库运行模型
ransformer库是Huggingface社区推出的用于运行HF模型的官方库。
在2.2中,我们已经下载好了InternLM2-Chat-1.8B的HF模型。下面我们先用Transformer来直接运行InternLM2-Chat-1.8B模型,后面对比一下LMDeploy的使用感受。
现在,让我们点击左上角的图标,打开VSCode。
在左边栏空白区域单击鼠标右键,点击Open in Intergrated Terminal
。
等待片刻,打开终端。
在终端中输入如下指令,新建pipeline_transformer.py
。
touch /root/pipeline_transformer.py
回车执行指令,可以看到侧边栏多出了pipeline_transformer.py
文件,点击打开。后文中如果要创建其他新文件,也是采取类似的操作。
将以下内容复制粘贴进入pipeline_transformer.py
。
import torch from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("/root/internlm2-chat-1_8b", trust_remote_code=True) # Set `torch_dtype=torch.float16` to load model in float16, otherwise it will be loaded as float32 and cause OOM Error. model = AutoModelForCausalLM.from_pretrained("/root/internlm2-chat-1_8b", torch_dtype=torch.float16, trust_remote_code=True).cuda() model = model.eval() inp = "hello" print("[INPUT]", inp) response, history = model.chat(tokenizer, inp, history=[]) print("[OUTPUT]", response) inp = "please provide three suggestions about time management" print("[INPUT]", inp) response, history = model.chat(tokenizer, inp, history=history) print("[OUTPUT]", response)
按Ctrl+S
键保存(Mac用户按Command+S
)。
回到终端,激活conda环境。
conda activate lmdeploy
运行python代码:
python /root/pipeline_transformer.py
得到输出:
记住这种感觉,一会儿体验一下LMDeploy的推理速度,感受一下对比~
使用LMDeploy与模型对话
使用LMDeploy与模型进行对话的通用命令格式为:
lmdeploy chat [HF格式模型路径/TurboMind格式模型路径]
例如,您可以执行如下命令运行下载的1.8B模型:
lmdeploy chat /root/internlm2-chat-1_8b
lmdeploy chat /root/internlm2-chat-1_8b
记录显存占用为7856GB
LMDeploy模型量化(lite)
正式介绍 LMDeploy 量化方案前,需要先介绍两个概念:
- 计算密集(compute-bound): 指推理过程中,绝大部分时间消耗在数值计算上;针对计算密集型场景,可以通过使用更快的硬件计算单元来提升计算速度。
- 访存密集(memory-bound): 指推理过程中,绝大部分时间消耗在数据读取上;针对访存密集型场景,一般通过减少访存次数、提高计算访存比或降低访存量来优化。
常见的 LLM 模型由于 Decoder Only 架构的特性,实际推理时大多数的时间都消耗在了逐 Token 生成阶段(Decoding 阶段),是典型的访存密集型场景。
那么,如何优化 LLM 模型推理中的访存密集问题呢? 我们可以使用KV8量化和W4A16量化。KV8量化是指将逐 Token(Decoding)生成过程中的上下文 K 和 V 中间结果进行 INT8 量化(计算时再反量化),以降低生成过程中的显存占用。W4A16 量化,将 FP16 的模型权重量化为 INT4,Kernel 计算时,访存量直接降为 FP16 模型的 1/4,大幅降低了访存成本。Weight Only 是指仅量化权重,数值计算依然采用 FP16(需要将 INT4 权重反量化)。
设置最大KV Cache缓存大小
KV Cache是一种缓存技术,通过存储键值对的形式来复用计算结果,以达到提高性能和降低内存消耗的目的。在大规模训练和推理中,KV Cache可以显著减少重复计算量,从而提升模型的推理速度。理想情况下,KV Cache全部存储于显存,以加快访存速度。当显存空间不足时,也可以将KV Cache放在内存,通过缓存管理器控制将当前需要使用的数据放入显存。
模型在运行时,占用的显存可大致分为三部分:模型参数本身占用的显存、KV Cache占用的显存,以及中间运算结果占用的显存。LMDeploy的KV Cache管理器可以通过设置--cache-max-entry-count
参数,控制KV缓存占用剩余显存的最大比例。默认的比例为0.8。
下面通过几个例子,来看一下调整--cache-max-entry-count
参数的效果。首先保持不加该参数(默认0.8),运行1.8B模型。
改变--cache-max-entry-count
参数,设为0.5。
与模型对话,再次查看右上角资源监视器中的显存占用情况。
到显存占用明显降低,变为6608M。
下面来一波“极限”,把--cache-max-entry-count
参数设置为0.01,约等于禁止KV Cache占用显存。
使用W4A16量化
LMDeploy使用AWQ算法,实现模型4bit权重量化。推理引擎TurboMind提供了非常高效的4bit推理cuda kernel,性能是FP16的2.4倍以上。它支持以下NVIDIA显卡:
- 图灵架构(sm75):20系列、T4
- 安培架构(sm80,sm86):30系列、A10、A16、A30、A100
- Ada Lovelace架构(sm90):40 系列
运行前,首先安装一个依赖库。
仅需执行一条命令,就可以完成模型量化工作。
lmdeploy lite auto_awq \
/root/internlm2-chat-1_8b \
--calib-dataset 'ptb' \
--calib-samples 128 \
--calib-seqlen 1024 \
--w-bits 4 \
--w-group-size 128 \
--work-dir /root/internlm2-chat-1_8b-4bit
运行时间较长,请耐心等待。量化工作结束后,新的HF模型被保存到internlm2-chat-1_8b-4bit
目录。下面使用Chat功能运行W4A16量化后的模型。
lmdeploy chat /root/internlm2-chat-1_8b-4bit --model-format awq
速度明显变快
5. LMDeploy服务(serve)
启动API服务器
通过以下命令启动API服务器,推理internlm2-chat-1_8b
模型:
lmdeploy serve api_server \ /root/internlm2-chat-1_8b \ --model-format hf \ --quant-policy 0 \ --server-name 0.0.0.0 \ --server-port 23333 \ --tp 1
注意,这一步由于Server在远程服务器上,所以本地需要做一下ssh转发才能直接访问。在你本地打开一个cmd窗口,输入命令如下:
ssh -CNg -L 23333:127.0.0.1:23333 root@ssh.intern-ai.org.cn -p 你的ssh端口号
我们要新建一个命令行客户端去连接API服务器。首先通过VS Code新建一个终端:
激活conda环境。
conda activate lmdeploy
运行命令行客户端:
lmdeploy serve api_client http://localhost:23333
运行后,可以通过命令行窗口直接与模型对话:
网页客户端连接API服务器
使用Gradio作为前端,启动网页客户端。
lmdeploy serve gradio http://localhost:23333 \ --server-name 0.0.0.0 \ --server-port 6006
运行命令后,网页客户端启动。在电脑本地新建一个cmd终端,新开一个转发端口:
ssh -CNg -L 6006:127.0.0.1:6006 root@ssh.intern-ai.org.cn -p <你的ssh端口号>
打开浏览器,访问地址http://127.0.0.1:6006
6.Python代码集成
Python代码集成运行1.8B模型
首先激活conda环境。
conda activate lmdeploy
新建Python源代码文件pipeline.py
。
touch /root/pipeline.py
打开pipeline.py
,填入以下内容。
from lmdeploy import pipeline pipe = pipeline('/root/internlm2-chat-1_8b') response = pipe(['Hi, pls intro yourself', '上海是']) print(response)
代码解读:\
- 第1行,引入lmdeploy的pipeline模块 \
- 第3行,从目录“./internlm2-chat-1_8b”加载HF模型 \
- 第4行,运行pipeline,这里采用了批处理的方式,用一个列表包含两个输入,lmdeploy同时推理两个输入,产生两个输出结果,结果返回给response \
- 第5行,输出response
保存后运行代码文件:
python /root/pipeline.py
向TurboMind后端传递参数
在Python代码中,可以通过创建TurbomindEngineConfig,向lmdeploy传递参数。
以设置KV Cache占用比例为例,新建python文件pipeline_kv.py
。
touch /root/pipeline_kv.py
打开pipeline_kv.py
,填入如下内容:
from lmdeploy import pipeline, TurbomindEngineConfig # 调低 k/v cache内存占比调整为总显存的 20% backend_config = TurbomindEngineConfig(cache_max_entry_count=0.2) pipe = pipeline('/root/internlm2-chat-1_8b', backend_config=backend_config) response = pipe(['Hi, pls intro yourself', '上海是']) print(response)
保存后运行python代码:
python /root/pipeline_kv.py