ch5-homework-基于LMDeploy的大模型量化部署实践

news2024/11/29 7:46:53

ch5-homework-基于LMDeploy的大模型量化部署实践

  • 主要内容
  • 教程复现
    • 环境配置
    • 服务部署
      • 模型转换
        • 在线转换
        • 离线转换
      • TurboMind 推理+命令行本地对话
      • TurboMind推理+API服务
      • 网页 Demo 演示
        • TurboMind 服务作为后端
        • TurboMind 推理作为后端
      • TurboMind 推理 + Python 代码集成
      • 最佳实践
        • 方案实践
        • 模型配置实践
    • 模型量化
      • KV Cache 量化
        • 量化步骤
        • 量化效果
      • W4A16 量化
        • 量化步骤
        • 量化效果
      • 最佳实践
  • 基础作业
  • 进阶作业

主要内容

  • 视频网址:https://www.bilibili.com/video/BV1iW4y1A77P/?spm_id_from=333.788&vd_source=b96c7e6e6d1a48e73edafa36a36f1697
  • 教程主页:https://github.com/InternLM/tutorial
  • LMDeploy主页:https://github.com/InternLM/lmdeploy

基础作业:

  • 使用 LMDeploy 以本地对话、网页Gradio、API服务中的一种方式部署 InternLM-Chat-7B 模型,生成 300 字的小故事(需截图)

进阶作业(可选做)

  • 将第四节课训练自我认知小助手模型使用 LMDeploy 量化部署到 OpenXLab 平台。
  • 对internlm-chat-7b模型进行量化,并同时使用KV Cache量化,使用量化后的模型完成API服务的部署,分别对比模型量化前后(将 bs设置为 1 和 max len 设置为512)和 KV Cache 量化前后(将 bs设置为 8 和 max len 设置为2048)的显存大小。
  • 在自己的任务数据集上任取若干条进行Benchmark测试,测试方向包括:
    (1)TurboMind推理+Python代码集成
    (2)在(1)的基础上采用W4A16量化
    (3)在(1)的基础上开启KV Cache量化
    (4)在(2)的基础上开启KV Cache量化
    (5)使用Huggingface推理

备注:由于进阶作业较难,完成基础作业之后就可以先提交作业了,在后续的大作业项目中使用这些技术将作为重要的加分点!

教程复现

环境配置

主要命令如下:

# 创建环境
conda create -n lmdeploy python=3.10 -y
# 激活环境
conda activate lmdeploy
# 解决 ModuleNotFoundError: No module named 'packaging' 问题
pip install packaging
# 解决flash-attention安装报错问题,需要根据自己的cuda版本和torch版本修改哦
# flash-attention安装这里是参考了这个博客https://blog.csdn.net/yjh_SE007/article/details/135484227
pip install https://github.com/Dao-AILab/flash-attention/releases/download/v2.4.2/flash_attn-2.4.2+cu118torch2.0cxx11abiFALSE-cp310-cp310-linux_x86_64.whl
pip install 'lmdeploy[all]==v0.1.0'

服务部署

这一部分主要涉及本地推理和部署。我们先看一张图。
在这里插入图片描述
我们把从架构上把整个服务流程分成下面几个模块。

  • 模型推理/服务。主要提供模型本身的推理,一般来说可以和具体业务解耦,专注模型推理本身性能的优化。可以以模块、API等多种方式提供。
  • Client。可以理解为前端,与用户交互的地方。
  • API Server。一般作为前端的后端,提供与产品和服务相关的数据和功能支持。

值得说明的是,以上的划分是一个相对完整的模型,但在实际中这并不是绝对的。比如可以把“模型推理”和“API Server”合并,有的甚至是三个流程打包在一起提供服务。

接下来,我们看一下lmdeploy提供的部署功能。

模型转换

使用 TurboMind 推理模型需要先将模型转化为 TurboMind 的格式,目前支持在线转换和离线转换两种形式。在线转换可以直接加载 Huggingface 模型,离线转换需需要先保存模型再加载。

TurboMind 是一款关于 LLM 推理的高效推理引擎,基于英伟达的 FasterTransformer 研发而成。它的主要功能包括:LLaMa 结构模型的支持,persistent batch 推理模式和可扩展的 KV 缓存管理器。

在线转换

lmdeploy 支持直接读取 Huggingface 模型权重,目前共支持三种类型:

  • 在 huggingface.co 上面通过 lmdeploy 量化的模型,如 llama2-70b-4bit, internlm-chat-20b-4bit
  • huggingface.co 上面其他 LM 模型,如 Qwen/Qwen-7B-Chat

示例如下:

# 需要能访问 Huggingface 的网络环境
lmdeploy chat turbomind internlm/internlm-chat-20b-4bit --model-name internlm-chat-20b
lmdeploy chat turbomind Qwen/Qwen-7B-Chat --model-name qwen-7b

上面两行命令分别展示了如何直接加载 Huggingface 的模型,第一条命令是加载使用 lmdeploy 量化的版本,第二条命令是加载其他 LLM 模型。

加载示例如下:
在这里插入图片描述
如果不行的话参考之前的博客,改下镜像应该就可以

export HF_ENDPOINT=https://hf-mirror.com

我们也可以直接启动本地的 Huggingface 模型,如下所示。

lmdeploy chat turbomind /share/temp/model_repos/internlm-chat-7b/  --model-name internlm-chat-7b

以上命令都会启动一个本地对话界面,通过 Bash 可以与 LLM 进行对话。

离线转换

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

# 转换模型(FastTransformer格式) TurboMind
lmdeploy convert internlm-chat-7b /path/to/internlm-chat-7b

这里我们使用官方提供的模型文件,就在用户根目录执行,如下所示。

lmdeploy convert internlm-chat-7b  /root/share/temp/model_repos/internlm-chat-7b/

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

目录如下图所示。
在这里插入图片描述
weightstokenizer 目录分别放的是拆分后的参数和 Tokenizer。如果我们进一步查看 weights 的目录,就会发现参数是按层和模块拆开的,如下图所示。
在这里插入图片描述
每一份参数第一个 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(也就是一张卡)。

关于Tensor并行

Tensor并行一般分为行并行或列并行,原理如下图所示。

在这里插入图片描述
简单来说,就是把一个大的张量(参数)分到多张卡上,分别计算各部分的结果,然后再同步汇总。

TurboMind 推理+命令行本地对话

模型转换完成后,我们就具备了使用模型推理的条件,接下来就可以进行真正的模型推理环节。

我们先尝试本地对话(Bash Local Chat),下面用(Local Chat 表示)在这里其实是跳过 API Server 直接调用 TurboMind。简单来说,就是命令行代码直接执行 TurboMind。所以说,实际和前面的架构图是有区别的。

这里支持多种方式运行,比如Turbomind、PyTorch、DeepSpeed。但 PyTorch 和 DeepSpeed 调用的其实都是 Huggingface 的 Transformers 包,PyTorch表示原生的 Transformer 包,DeepSpeed 表示使用了 DeepSpeed 作为推理框架。Pytorch/DeepSpeed 目前功能都比较弱,不具备生产能力,不推荐使用。

执行命令如下。

# Turbomind + Bash Local Chat
lmdeploy chat turbomind ./workspace

启动后就可以和它进行对话了,如下图所示。
在这里插入图片描述

输入后两次回车,退出时输入exit 回车两次即可。此时,Server 就是本地跑起来的模型(TurboMind),命令行可以看作是前端。

TurboMind推理+API服务

在上面的部分我们尝试了直接用命令行启动 Client,接下来我们尝试如何运用 lmdepoy 进行服务化。

”模型推理/服务“目前提供了 Turbomind 和 TritonServer 两种服务化方式。此时,Server 是 TurboMind 或 TritonServer,API Server 可以提供对外的 API 服务。我们推荐使用 TurboMind,TritonServer 使用方式详见《附录1》。

首先,通过下面命令启动服务。

# 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

上面的参数中 server_nameserver_port 分别表示服务地址和端口,tp 参数我们之前已经提到过了,表示 Tensor 并行。还剩下一个 instance_num 参数,表示实例数,可以理解成 Batch 的大小。执行后如下图所示。
在这里插入图片描述

然后,我们可以新开一个窗口,执行下面的 Client 命令。如果使用官方机器,可以打开 vscode 的 Terminal,执行下面的命令。

# ChatApiClient+ApiServer(注意是http协议,需要加http)
lmdeploy serve api_client http://localhost:23333

如下图所示。
在这里插入图片描述

网页 Demo 演示

这一部分主要是将 Gradio 作为前端 Demo 演示。在上一节的基础上,我们不执行后面的 api_clienttriton_client,而是执行 gradio

注:由于我之间设置了ssh-key,配合vscode自动做的端口转发,所以这里不需要像教程那样麻烦。

TurboMind 服务作为后端

API Server 的启动和上一节一样,这里直接启动作为前端的 Gradio。

# 得先启动服务
lmdeploy serve api_server ./workspace --server_name 0.0.0.0 --server_port 23333 --instance_num 64 --tp 1
# 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

结果如下图所示。
在这里插入图片描述
在这里插入图片描述

TurboMind 推理作为后端

当然,Gradio 也可以直接和 TurboMind 连接,如下所示。

# Gradio+Turbomind(local)
lmdeploy serve gradio ./workspace

可以直接启动 Gradio,此时没有 API Server,TurboMind 直接与 Gradio 通信。如下图所示。

在这里插入图片描述

TurboMind 推理 + Python 代码集成

前面介绍的都是通过 API 或某种前端与”模型推理/服务“进行交互,lmdeploy 还支持 Python 直接与 TurboMind 进行交互,如下所示。将下述python脚本写入一个文件中,例如test_TruboMind_python.py,

from lmdeploy import turbomind as tm

# load model,需要修改成自己的模型路径
model_path = "/root/share/temp/model_repos/internlm-chat-7b/"
tm_model = tm.TurboMind.from_pretrained(model_path, model_name='internlm-chat-20b')
generator = tm_model.create_instance()

# process query
query = "你好啊兄嘚"
prompt = tm_model.model.get_prompt(query)
input_ids = tm_model.tokenizer.encode(prompt)

# inference
for outputs in generator.stream_infer(
        session_id=0,
        input_ids=[input_ids]):
    res, tokens = outputs[0]

response = tm_model.tokenizer.decode(res.tolist())
print(response)

在上面的代码中,我们首先加载模型,然后构造输入,最后执行推理。

加载模型可以显式指定模型路径,也可以直接指定 Huggingface 的 repo_id,还可以使用上面生成过的 workspace。这里的 tm.TurboMind 其实是对 C++ TurboMind 的封装。

构造输入这里主要是把用户的 query 构造成 InternLLM 支持的输入格式,比如上面的例子中, query 是“你好啊兄嘚”,构造好的 Prompt 如下所示。

"""
<|System|>: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 中文.

<|User|>:你好啊兄嘚
<|Bot|>:
"""

Prompt 其实就是增加了 <|System|> 消息和 <|User|> 消息(即用户的 query),以及一个 <|Bot|> 的标记,表示接下来该模型输出响应了。最终输出的响应内容如下所示。

"你好啊,有什么我可以帮助你的吗?"

终端运行python test_TruboMind_python.py,测试结果如下:
在这里插入图片描述
搞定~

最佳实践

选择太多了,现在看看官方推荐的最佳实践

方案实践

首先说 “模型推理/服务”,推荐使用 TurboMind,使用简单,性能良好,相关的 Benchmark 对比如下。

在这里插入图片描述

上面的性能对比包括两个场景:

  • 场景一(前4张图):固定的输入、输出 token 数(分别1和2048),测试Token输出吞吐量(output token throughput)。
  • 场景二(第5张图):使用真实数据,测试吞吐量(request throughput)。

场景一中,BatchSize=64时,TurboMind 的吞吐量超过 2000 token/s,整体比 DeepSpeed 提升约 5% - 15%;BatchSize=32时,比 Huggingface 的Transformers 提升约 3 倍;其他BatchSize时 TurboMind 也表现出优异的性能。

场景二中,对比了 TurboMind 和 vLLM 在真实数据上的吞吐量(request throughput)指标,TurboMind 的效率比 vLLM 高 30%。

可以亲自使用本地对话(Local Chat)感受一下性能差别,或者执行官方提供的 infer_compare.py 脚本:

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

from lmdeploy import turbomind as tm


def get_prompt(query: str) -> str:
    prompt = f"""<|System|>: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 中文.

<|User|>:{query}
<|Bot|>:"""
    return prompt



def gen_lmdeploy(query: str, tokenizer, model) -> str:
    prompt = get_prompt(query)
    input_ids = tokenizer.encode(prompt)
    for outputs in model.stream_infer(
            session_id=0,
            input_ids=[input_ids],
            request_output_len=512,
            temperature=0.0,
        ):
        ...
    res, _tokens = outputs[0]
    output = tokenizer.decode(res)
    return output



def gen_transformers(query: str, tokenizer, model) -> str:
    prompt = get_prompt(query)
    inputs = tokenizer([prompt], return_tensors="pt").to(device)
    res = model.generate(
        **inputs,
        max_new_tokens=512,
    )
    output = tokenizer.decode(res[0])
    output = output.replace(prompt, "").replace("<eoa>", "").strip()
    return output



import sys
import time

device = "cuda:0"

model_path = "/root/share/temp/model_repos/internlm-chat-7b/"
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)

engine = sys.argv[1]

if engine == "hf":
    model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.float16, trust_remote_code=True)
    model.to(device).eval();
    gen_func = gen_transformers
else:
    tm_model = tm.TurboMind.from_pretrained("./workspace/")
    model = tm_model.create_instance()
    gen_func = gen_lmdeploy


count = 0
start = time.time()
for season in ["春天", "夏天", "秋天", "冬天"]:
    q = f"写一篇关于{season}的300字小作文。"
    output = gen_func(q, tokenizer, model)
    count += len(output)
end = time.time()
cost = (end - start)
throughput = round(count / cost)

print(f"{engine} 耗时 {cost:.2f}{throughput} 字/秒")

注意执行上述比较脚本,需要将52行的model_path换成自己下载的interlm_chat_7b模型的路径,还有经过上面LMDeploy离线转换,即

lmdeploy convert internlm-chat-7b  /root/share/temp/model_repos/internlm-chat-7b/

之后生成的workspace(在第62行的位置),然后对比hf和lmdeploy示例如下

# 执行 Huggingface 的 Transformer
python infer_compare.py hf
# 执行LMDeploy
python infer_compare.py lmdeploy

运行对比结果如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • Transformers:37.83s
  • LMDeploy:20.85s
    基本上速度提升了两倍左右,但是没有官方说的LMDeploy应该是Transformers的3-5倍左右,估计得多次测试吧,嘿嘿。

后面的 API 服务和 Client 就得分场景了。

  • 我想对外提供类似 OpenAI 那样的 HTTP 接口服务。推荐使用 TurboMind推理 + API 服务(2.3)。
  • 我想做一个演示 Demo,Gradio 无疑是比 Local Chat 更友好的。推荐使用 TurboMind 推理作为后端的Gradio进行演示(2.4.2)。
  • 我想直接在自己的 Python 项目中使用大模型功能。推荐使用 TurboMind推理 + Python(2.5)。
  • 我想在自己的其他非 Python 项目中使用大模型功能。推荐直接通过 HTTP 接口调用服务。也就是用本列表第一条先启动一个 HTTP API 服务,然后在项目中直接调用接口。
  • 我的项目是 C++ 写的,为什么不能直接用 TurboMind 的 C++ 接口?!必须可以!大佬可以右上角叉掉这个窗口啦。
模型配置实践

不知道大家还有没有印象,在离线转换一节,我们查看了 weights 的目录,里面存放的是模型按层、按并行卡拆分的参数,不过还有一个文件 config.ini 并不是模型参数,它里面存的主要是模型相关的配置信息。下面是一个示例。

[llama]
model_name = internlm-chat-7b
tensor_para_size = 1
head_num = 32
kv_head_num = 32
vocab_size = 103168
num_layer = 32
inter_size = 11008
norm_eps = 1e-06
attn_bias = 0
start_id = 1
end_id = 2
session_len = 2056
weight_type = fp16
rotary_embedding = 128
rope_theta = 10000.0
size_per_head = 128
group_size = 0
max_batch_size = 64
max_context_token_num = 1
step_length = 1
cache_max_entry_count = 0.5
cache_block_seq_len = 128
cache_chunk_size = 1
use_context_fmha = 1
quant_policy = 0
max_position_embeddings = 2048
rope_scaling_factor = 0.0
use_logn_attn = 0

其中,模型属性相关的参数不可更改,主要包括下面这些。

model_name = llama2
head_num = 32
kv_head_num = 32
vocab_size = 103168
num_layer = 32
inter_size = 11008
norm_eps = 1e-06
attn_bias = 0
start_id = 1
end_id = 2
rotary_embedding = 128
rope_theta = 10000.0
size_per_head = 128

和数据类型相关的参数也不可更改,主要包括两个。

weight_type = fp16
group_size = 0

weight_type 表示权重的数据类型。目前支持 fp16 和 int4。int4 表示 4bit 权重。当 weight_type 为 4bit 权重时,group_size 表示 awq 量化权重时使用的 group 大小。

剩余参数包括下面几个。

tensor_para_size = 1
session_len = 2056
max_batch_size = 64
max_context_token_num = 1
step_length = 1
cache_max_entry_count = 0.5
cache_block_seq_len = 128
cache_chunk_size = 1
use_context_fmha = 1
quant_policy = 0
max_position_embeddings = 2048
rope_scaling_factor = 0.0
use_logn_attn = 0

一般情况下,我们并不需要对这些参数进行修改,但有时候为了满足特定需要,可能需要调整其中一部分配置值。这里主要介绍三个可能需要调整的参数。

  • 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 Cache 量化和模型参数量化。总的来说,量化是一种以参数或计算中间结果精度下降换空间节省(以及同时带来的性能提升)的策略。

正式介绍 LMDeploy 量化方案前,需要先介绍两个概念:

  • 计算密集(compute-bound): 指推理过程中,绝大部分时间消耗在数值计算上;针对计算密集型场景,可以通过使用更快的硬件计算单元来提升计算速。
  • 访存密集(memory-bound): 指推理过程中,绝大部分时间消耗在数据读取上;针对访存密集型场景,一般通过减少访存次数、提高计算访存比或降低访存量来优化。

常见的 LLM 模型由于 Decoder Only 架构的特性,实际推理时大多数的时间都消耗在了逐 Token 生成阶段(Decoding 阶段),是典型的访存密集型场景。

那么,如何优化 LLM 模型推理中的访存密集问题呢? 我们可以使用 KV Cache 量化4bit Weight Only 量化(W4A16)。KV Cache 量化是指将逐 Token(Decoding)生成过程中的上下文 K 和 V 中间结果进行 INT8 量化(计算时再反量化),以降低生成过程中的显存占用。4bit Weight 量化,将 FP16 的模型权重量化为 INT4,Kernel 计算时,访存量直接降为 FP16 模型的 1/4,大幅降低了访存成本。Weight Only 是指仅量化权重,数值计算依然采用 FP16(需要将 INT4 权重反量化)。

KV Cache 量化

量化步骤

KV Cache 量化是将已经生成序列的 KV 变成 Int8,使用过程一共包括三步:

第一步:计算 minmax。主要思路是通过计算给定输入样本在每一层不同位置处计算结果的统计情况。

  • 对于 Attention 的 K 和 V:取每个 Head 各自维度在所有Token的最大、最小和绝对值最大值。对每一层来说,上面三组值都是 (num_heads, head_dim) 的矩阵。这里的统计结果将用于本小节的 KV Cache。
  • 对于模型每层的输入:取对应维度的最大、最小、均值、绝对值最大和绝对值均值。每一层每个位置的输入都有对应的统计值,它们大多是 (hidden_dim, ) 的一维向量,当然在 FFN 层由于结构是先变宽后恢复,因此恢复的位置维度并不相同。这里的统计结果用于下个小节的模型参数量化,主要用在缩放环节(回顾PPT内容)。

第一步执行命令如下:

# 计算 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 的长度。

这一步由于默认需要从 Huggingface 下载数据集,国内经常不成功。所以我们导出了需要的数据,大家需要对读取数据集的代码文件做一下替换。共包括两步:

  • 第一步:复制 calib_dataloader.py 到安装目录替换该文件:cp /root/share/temp/datasets/c4/calib_dataloader.py /root/.conda/envs/lmdeploy/lib/python3.10/site-packages/lmdeploy/lite/utils/
  • 第二步:将用到的数据集(c4)复制到下面的目录:cp -r /root/share/temp/datasets/c4/ /root/.cache/huggingface/datasets/

由于小编没有使用官方提供的工作机,所以也无从复制,粗粗看了下,数据集有点大,先搞作业,后面再测试,先放上数据集地址备用

  • https://huggingface.co/datasets/allenai/c4

第二步:通过 minmax 获取量化参数。主要就是利用下面这个公式,获取每一层的 K V 中心值(zp)和缩放值(scale)。

zp = (min+max) / 2
scale = (max-min) / 255
quant: q = round( (f-zp) / scale)
dequant: f = q * scale + zp

有这两个值就可以进行量化和解量化操作了。具体来说,就是对历史的 K 和 V 存储 quant 后的值,使用时在 dequant。

第二步的执行命令如下:

# 通过 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_symTrue 时会使用另一种(对称)量化方法,它用到了第一步存储的绝对值最大值,而不是最大值和最小值。

第三步:修改配置。也就是修改 weights/config.ini 文件,这个我们在《2.6.2 模型配置实践》中已经提到过了(KV int8 开关),只需要把 quant_policy 改为 4 即可。

这一步需要额外说明的是,如果用的是 TurboMind1.0,还需要修改参数 use_context_fmha,将其改为 0。

接下来就可以正常运行前面的各种服务了,只不过咱们现在可是用上了 KV Cache 量化,能更省(运行时)显存了。

量化效果

官方给出了 internlm-chat-7b 模型在 KV Cache 量化前后的显存对比情况,如下表所示。

batch_sizefp16 memory(MiB)int8 memory(MiB)diff(MiB)
82233718241-4096
163059322369-8224
324707330625-16448
486355338881-24672

可以看出,KV Cache 可以节约大约 20% 的显存。

同时,还在 opencompass 平台上测试了量化前后的精准度(Accuracy)对比情况,如下表所示。

taskdatasetmetricint8fp16diff
Languagewinograndeaccuracy60.7761.48-0.71
Knowledgenqscore2.692.60+0.09
Reasoninggsm8kaccuracy33.2834.72-1.44
Reasoningbbhnaive_average20.1220.51-0.39
Understandingopenbookqa_factaccuracy82.4082.20+0.20
Understandingeprstmt-devaccuracy90.6288.75+1.87
Safetycrows_pairsaccuracy32.5631.43+1.13

可以看出,精度不仅没有明显下降,相反在不少任务上还有一定的提升。可能得原因是,量化会导致一定的误差,有时候这种误差可能会减少模型对训练数据的拟合,从而提高泛化性能。量化可以被视为引入轻微噪声的正则化方法。或者,也有可能量化后的模型正好对某些数据集具有更好的性能。

总结一下,KV Cache 量化既能明显降低显存占用,还有可能同时带来精准度(Accuracy)的提升。

W4A16 量化

量化步骤

W4A16中的A是指Activation,保持FP16,只对参数进行 4bit 量化。使用过程也可以看作是三步。

第一步:同 KV Cache 量化,不再赘述。

第二步:量化权重模型。利用第一步得到的统计值对参数进行量化,具体又包括两小步:

  • 缩放参数。主要是性能上的考虑(回顾 PPT)。
  • 整体量化。

第二步的执行命令如下:

# 量化权重模型
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 格式。

# 转换模型的layout,存放在默认路径 ./workspace 下
lmdeploy convert  internlm-chat-7b ./quant_output \
    --model-format awq \
    --group-size 128

这个 group-size 就是上一步的那个 w_group_size。如果不想和之前的 workspace 重复,可以指定输出目录:--dst_path,比如:

lmdeploy convert  internlm-chat-7b ./quant_output \
    --model-format awq \
    --group-size 128 \
    --dst_path ./workspace_quant

接下来和上一节一样,可以正常运行前面的各种服务了,不过咱们现在用的是量化后的模型。

最后再补充一点,量化模型和 KV Cache 量化也可以一起使用,以达到最大限度节省显存。

量化效果

官方在 NVIDIA GeForce RTX 4090 上测试了 4-bit 的 Llama-2-7B-chat 和 Llama-2-13B-chat 模型的 token 生成速度。测试配置为 BatchSize = 1,prompt_tokens=1,completion_tokens=512,结果如下表所示。

modelllm-awqmlc-llmturbomind
Llama-2-7B-chat112.9159.4206.4
Llama-2-13B-chatN/A90.7115.8

可以看出,TurboMind 相比其他框架速度优势非常显著,比 mlc-llm 快了将近 30%。

另外,也测试了 TurboMind 在不同精度和上下文长度下的显存占用情况,如下表所示。

model(context length)16bit(2048)4bit(2048)16bit(4096)4bit(4096)
Llama-2-7B-chat15.16.316.27.5
Llama-2-13B-chatOOM10.3OOM12.0

可以看出,4bit 模型可以降低 50-60% 的显存占用,效果非常明显。

总而言之,W4A16 参数量化后能极大地降低显存,同时相比其他框架推理速度具有明显优势。

最佳实践

本节是针对《模型量化》部分的最佳实践。

首先我们需要明白一点,服务部署和量化是没有直接关联的,量化的最主要目的是降低显存占用,主要包括两方面的显存:模型参数和中间过程计算结果。前者对应《W4A16 量化》,后者对应《KV Cache 量化》。

量化在降低显存的同时,一般还能带来性能的提升,因为更小精度的浮点数要比高精度的浮点数计算效率高,而整型要比浮点数高很多。

所以我们的建议是:在各种配置下尝试,看效果能否满足需要。这一般需要在自己的数据集上进行测试。具体步骤如下。

  • Step1:优先尝试正常(非量化)版本,评估效果。
    • 如果效果不行,需要尝试更大参数模型或者微调。
    • 如果效果可以,跳到下一步。
  • Step2:尝试正常版本+KV Cache 量化,评估效果。
    • 如果效果不行,回到上一步。
    • 如果效果可以,跳到下一步。
  • Step3:尝试量化版本,评估效果。
    • 如果效果不行,回到上一步。
    • 如果效果可以,跳到下一步。
  • Step4:尝试量化版本+ KV Cache 量化,评估效果。
    • 如果效果不行,回到上一步。
    • 如果效果可以,使用方案。

简单流程如下图所示。

在这里插入图片描述

另外需要补充说明的是,使用哪种量化版本、开启哪些功能,除了上述流程外,还需要考虑框架、显卡的支持情况,比如有些框架可能不支持 W4A16 的推理,那即便转换好了也用不了。

根据实践经验,一般情况下:

  • 精度越高,显存占用越多,推理效率越低,但一般效果较好。
  • Server 端推理一般用非量化版本或半精度、BF16、Int8 等精度的量化版本,比较少使用更低精度的量化版本。
  • 端侧推理一般都使用量化版本,且大多是低精度的量化版本。这主要是因为计算资源所限。

以上是针对项目开发情况,如果是自己尝试(玩儿)的话:

  • 如果资源足够(有GPU卡很重要),那就用非量化的正常版本。
  • 如果没有 GPU 卡,只有 CPU(不管什么芯片),那还是尝试量化版本。
  • 如果生成文本长度很长,显存不够,就开启 KV Cache。

建议大家根据实际情况灵活选择方案。

其他参考资料:

  • InternLM/lmdeploy: LMDeploy is a toolkit for compressing, deploying, and serving LLMs.
  • 仅需一块 3090 显卡,高效部署 InternLM-20B 模型 - 知乎

基础作业

  • 使用 LMDeploy 以本地对话、网页Gradio、API服务中的一种方式部署 InternLM-Chat-7B 模型,生成 300 字的小故事(需截图)

感觉上面复现的过程已经顺手把作业做了,再运行一下吧,嘿嘿,选用最省事的方式,用TurboMind 推理作为后端,基于Gradio 直接和 TurboMind 连接,如下所示。

# Gradio+Turbomind(local)
lmdeploy serve gradio ./workspace

注:workspace是前面用LMDeploy模型离线转换internlm-chat-7b得到的,上面有命令~

终端运行上述命令,可以直接启动 Gradio,此时没有 API Server,TurboMind 直接与 Gradio 通信,默认打开6006端口服务,基于vscode自动端口转发打开本地web,如下图所示。

在这里插入图片描述
Emmm…不得不说,这故事写的很符合我的胃口,好评好评,哈哈哈哈哈~

进阶作业

进阶作业(可选做),所以先交基础作业,回头慢慢搞这个,嘿嘿~

  • 将第四节课训练自我认知小助手模型使用 LMDeploy 量化部署到 OpenXLab 平台。
  • 对internlm-chat-7b模型进行量化,并同时使用KV Cache量化,使用量化后的模型完成API服务的部署,分别对比模型量化前后(将 bs设置为 1 和 max len 设置为512)和 KV Cache 量化前后(将 bs设置为 8 和 max len 设置为2048)的显存大小。
  • 在自己的任务数据集上任取若干条进行Benchmark测试,测试方向包括:
    (1)TurboMind推理+Python代码集成
    (2)在(1)的基础上采用W4A16量化
    (3)在(1)的基础上开启KV Cache量化
    (4)在(2)的基础上开启KV Cache量化
    (5)使用Huggingface推理

备注:*由于进阶作业较难,完成基础作业之后就可以先提交作业了,在后续的大作业项目中使用这些技术将作为重要的加分点!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1450202.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第三十回 张都监血溅鸳鸯楼 武行者夜走蜈蚣岭-python可接受任意数量参数的函数

武松回到孟州城&#xff0c;来到张都监后花园墙外&#xff0c;这是一个马院&#xff0c;问清楚后槽张团练他们三人还在鸳鸯楼吃酒&#xff0c;直接一刀杀了。武松从后门这里爬过墙&#xff0c;来到了厨房&#xff0c;将两个还在服侍的丫环杀了。 武松认得路&#xff0c;蹑手蹑…

网络爬虫实战 | 上传以及下载处理后的文件

以实现爬虫一个简单的&#xff08;SimFIR (doctrp.top)&#xff09;网址为例&#xff0c;需要遵循几个步骤&#xff1a; 1. 分析网页结构 首先&#xff0c;需要分析该网页的结构&#xff0c;了解图片是如何存储和组织的。这通常涉及查看网页的HTML源代码&#xff0c;可能还包…

【剪辑必备】今天我教你如何手动去下载苹果官网4K预告片 完全免费

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

问题:用来表示证券收益的波动性,值越大说明()。 #媒体#经验分享

问题&#xff1a;用来表示证券收益的波动性&#xff0c;值越大说明()。 A.风险大 B.风险小 C.不确定 D.风险固定 参考答案如图所示

MySQL5.7升级到MySQL8.0的最佳实践分享

一、前言 事出必有因&#xff0c;在这个月的某个项目中&#xff0c;我们面临了一项重要任务&#xff0c;即每年一次的等保测评整改。这次测评的重点是Mysql的一些高危漏洞&#xff0c;客户要求我们无论如何必须解决这些漏洞。尽管我们感到无奈&#xff0c;但为了满足客户的要求…

命令行参数和环境变量

命令行参数 命令行参数是在用户在命令行中输入命令时&#xff0c;跟随命令一起输入的一些附加信息。这些参数可以用来配置命令的行为或传递一些数据给命令。 让同样的程序在不同的命令行参数下运行出不同的结果&#xff01; 将这些命令和参数可以传给 main 函数生&#xff0…

(15)Hive调优——数据倾斜的解决指南

目录 前言 一、什么是数据倾斜 二、发生数据倾斜的表现 2.1 MapReduce任务 2.2 Spark任务 三、如何定位发生数据倾斜的代码 四、发生数据倾斜的原因 3.1 key分布不均匀 3.1.1 某些key存在大量相同值 3.1.2 存在大量异常值或空值 3.2 业务数据本身的特性 3.3 SQL语句…

【更新】企业数字化转型-年度报告175个词频、文本统计

数据说明&#xff1a; 这份数据含数字化转型175个词频、各维度水平&#xff0c;保留2000-2021年数据。参考吴非、赵宸宇两位老师做法&#xff0c;根据上市公司年报文本&#xff0c;整理数字化转型175个词频数据&#xff0c;希望对大家有所帮助。 参考管理世界中吴非&#xff…

【正点原子STM32】TIMER 定时器(软件定时原理、定时器定时原理、分类和特性、基本定时器(影子寄存器和U事件和UI中断))

一、定时器概述 1.1、软件定时原理1.2、定时器定时原理1.3、STM32定时器分类1.4、STM32定时器特性表1.5、STM32基本、通用、高级定时器的功能整体区别 二、基本定时器 2.1、基本定时器简介2.2、基本定时器框图2.3、定时器计数模式及溢出条件2.4、定时器中断实验相关寄存器2.…

c语言(指针进阶)

指针 一.什么是字符指针二.使用指针数组模拟二维数组三.函数指针 一.什么是字符指针 字符指针&#xff1a;指向字符型数据的指针变量。每个字符串在内存中都占用一段连续的存储空间&#xff0c;并有唯一确定的首地址。即将字符串的首地址赋值给字符指针&#xff0c;可让字符指针…

用HTML Canvas和JavaScript创建美丽的花朵动画效果

目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>flower</title><style>* {margin: 0;padding: 0;overflow: hidden;backg…

公需课考试怎么搜题找答案? #学习方法#学习方法

这些软件以其强大的搜索引擎和智能化的算法&#xff0c;为广大大学生提供了便捷、高效的解题方式。下面&#xff0c;让我们一起来了解几款备受大学生欢迎的搜题软件吧&#xff01; 1.粉鹿搜题 这是一个公众号 在线搜题刷题平台&#xff0c;支持语言、文字、拍照多种搜索方式…

操作 Docker 存储卷的常用指令汇总

1. 什么是存储卷&#xff1f; 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。使得可以在宿主机和容器内共享数据库内容&#xff0c;让容器直接访问宿主机中的内容&#xff0c;也可以宿主机向容器写入内容&#xff0c;容…

.NET Core WebAPI中使用Log4net记录日志

一、安装NuGet包 二、添加配置 // log4net日志builder.Logging.AddLog4Net("CfgFile/log4net.config");三、配置log4net.config文件 <?xml version"1.0" encoding"utf-8"?> <log4net><!-- Define some output appenders -->…

类加载过程介绍

一、类的生命周期 类被加载到jvm虚拟机内存开始&#xff0c;到卸载出内存为止&#xff0c;他的生命周期可以分为&#xff1a;加载->验证->准备->解析->初始化->使用->卸载。 其中验证、准备、解析统一称为链接阶段 1、加载 将类的字节码载入方法区中&#xf…

蓝桥杯第十四届电子类单片机组程序设计

目录 前言 蓝桥杯大赛历届真题&#xff08;点击查看&#xff09; 一、第十四届比赛题目 1.比赛原题 2.题目解读 1&#xff09;任务要求 2&#xff09;注意事项 二、任务实现 1.NE555读取时机的问题 1&#xff09;缩短计数时间 2&#xff09;实时读取 2.温度传感器读…

unity的重中之重:组件

检查器&#xff08;Hierarchy&#xff09;面板中的所有东西都是组件。日后多数工作都是和组件打交道&#xff0c;包括调参、自定义脚本组件。 文章目录 12 游戏的灵魂&#xff0c;脚本组件13 玩转脚本组件14 尽职的一生&#xff0c;了解组件的生命周期15 不能插队&#xff01;…

专业130+总分420+厦门大学847信号与系统考研经验厦大信息系统与通信工程,真题,大纲,参考书。

今年很幸运被厦门大学录取&#xff0c;考研专业课847信号与系统130&#xff0c;数二130&#xff0c;总分420&#xff0c;回头看这将近一年的复习&#xff0c;还是有不少经验和大家分享&#xff0c;希望对大家复习有帮助。专业课&#xff1a; 厦门大学847信号与系统在全国各高校…

BossPlayerCTF

靶场环境问题 靶场下载之后&#xff0c;可能会出现扫描不到IP的情况&#xff0c;需要进行调整&#xff0c;参考&#xff1a; Vulnhub靶机检测不到IP地址_vulnhub靶机nmap扫不到-CSDN博客 该靶机没有vim&#xff0c;需要使用vi命令去修改&#xff1b;改成当前网卡即可&#x…

推荐《架构探险:从零开始写Java Web框架》

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 春节读了《架构探险&#xff1a;从零开始写Java Web框架》&#xff0c;一本大概10年前的好书。 本书的作者是阿里巴巴架构师黄勇。黄勇对分布式服务架构与大数据技术有深入…