可以复用 L2 G4000
创建的 lmdeploy
环境,可以顺利运行。
第一,运行下面的指令,启动API服务器:
lmdeploy serve api_server \
/root/models/internlm2_5-7b-chat \
--model-format hf \
--quant-policy 0 \
--server-name 0.0.0.0 \
--server-port 23333 \
--tp 1
第二,执行下面的指令,在终端连接API服务器,进行对话:
lmdeploy serve api_client http://localhost:23333
第三,输入以下命令,使用Gradio作为前端,启动网页:
lmdeploy serve gradio http://localhost:23333 \
--server-name 0.0.0.0 \
--server-port 6006
KV Cache的核心思想就是缓存已经计算过的Key和Value向量,避免重复计算。让我详细解释一下: 在 Transformer 架构中:
- 输入token经过词嵌入(embedding)后
- 分别通过WK矩阵得到Key向量,通过WV矩阵得到Value向量
- 这些Key和Value向量被用于注意力计算:
- Query和Key做点积得到注意力分数
- 注意力分数与Value加权求和得到输出
KV Cache的优化点在于:
- 当生成新token时,之前已经计算过的token的K、V向量可以直接复用
- 不需要重新:
- 对这些token做embedding
- 通过WK、WV矩阵计算它们的Key、Value向量
- 只需要:
- 计算新token的K、V向量
- 将其加入缓存中的序列
- 用完整的K、V序列计算注意力
这样做的好处:
- 减少重复计算,提高推理速度
- 特别是在长文本生成时效果明显,因为序列越长重复计算的部分就越多
- 代价是需要额外的显存来存储这些K、V向量
W4A16量化与kv cache量化: W4A16量化:
- "W4"代表将模型的权重(Weights)量化为4位整数(int4)
- "A16"表示激活值(Activations)保持16位精度(如FP16或BF16)
- 主要优势是显著减少模型大小:
- 例如将原本BF16精度的7B参数模型从14GB压缩到约3.5GB
- 因为从16位(2字节)压缩到4位(0.5字节),理论上可以减少到原来的1/4大小
KV Cache量化:
- KV cache是一种缓存键值对(Key-Value pairs)的技术,用于复用计算结果以提高推理速度
- KV Cache量化是指将这些缓存的数据进行压缩:
- 可以选择int4(4位)或int8(8位)精度
- 使用"per-head per-token"的非对称量化方式
- 优势:
- 在相同显存空间下可以存储更多数据
- 例如用int4量化后,相同4GB显存可以存储BF16精度4倍的数据量
两种量化方式可以结合使用:
- W4A16量化减少模型本身占用的显存
- KV Cache量化减少缓存占用的显存
让我通过一个具体例子来解释KV cache的量化过程。
假设我们有一个简单的场景:
- 一个transformer模型,hidden size为8
- 有2个attention heads,每个head的维度是4
- 使用int4量化(每个数用4位表示,范围是0-15)
让我详细解释量化的过程:
-
非对称量化的原理:
- 使用scale(缩放因子)和zero_point(零点偏移)将浮点数映射到int4范围(0-15)
- 对每个attention head分别计算量化参数
- 公式:quant_value = round((float_value / scale) + zero_point)
-
量化步骤:
- 找到每个head中的最大值和最小值
- 计算scale = (max - min) / (2^4 - 1) = (max - min) / 15
- 计算zero_point使得最小值映射到0,最大值映射到15
- 用公式将每个浮点数转换为0-15的整数
-
反量化(恢复)过程:
- float_value = (quant_value - zero_point) * scale
-
存储效率:
- 原始float16:每个数占16位
- 量化后:每个数占4位,外加每个head存储一个scale和zero_point
- 当序列很长时,存储空间可以节省约75%
这种"per-head per-token"的方式意味着:
- 每个attention head有自己的scale和zero_point
- 这些参数是针对当前token的K或V向量独立计算的
- 这样可以更精确地保持每个head中数值的相对关系
这种量化方式在保持模型性能的同时,显著减少了KV cache的显存占用。
W4A16量化是权重量化的一种形式,我来详细解释它是如何工作的,以一个简化的例子说明。
W4A16量化的具体流程如下:
-
W4:权重量化
- 将权重矩阵按group_size(如128)分成多个组
- 对每个组:
- 找到组内最大值和最小值
- 计算量化参数(scale和zero_point)
- 将权重量化为4位整数(0-15)
- 特点:
- 每组独立量化,保持局部数值分布
- 存储时每个组需要额外存储其scale和zero_point
-
A16:激活值保持16位
- 模型的输入和中间激活值保持BF16精度
- 不对激活值进行量化
- 原因:
- 保持激活值的精度对模型性能很重要
- 激活值相比权重数量少,占用显存较小
-
计算过程:
- 输入:BF16格式的向量
- 权重:INT4格式存储,计算时临时反量化到BF16
- 矩阵乘法在BF16精度下进行
- 输出:保持BF16格式
-
AWQ (Activation-aware Weight Quantization) 优化:
- 考虑激活值分布来优化量化
- 为不同层设置不同的量化参数
- 可以更好地保持模型性能
执行这条命令,默认的 cache-max-entry-count
是0.8
lmdeploy chat /root/models/internlm2_5-7b-chat
执行后占用24G
执行这条命令,设置 cache-max-entry-count
为0.4
lmdeploy chat /root/models/internlm2_5-7b-chat --cache-max-entry-count 0.4
执行后占用21G
通过 LMDeploy 应用 kv 量化非常简单,只需要设定 quant_policy 参数。
目前,LMDeploy 规定 quant_policy=4 表示 kv int4 量化,quant_policy=8 表示 kv int8 量化。
lmdeploy serve api_server \
/root/models/internlm2_5-7b-chat \
--model-format hf \
--quant-policy 4 \
--cache-max-entry-count 0.4\
--server-name 0.0.0.0 \
--server-port 23333 \
--tp 1
显存占用21G。但quant-policy 设置为4时,LMDeploy将会使用int4精度提前开辟4GB的kv cache。
lmdeploy lite auto_awq \
/root/models/internlm2_5-1_8b-chat \
--calib-dataset 'pileval' \
--calib-samples 128 \
--calib-seqlen 2048 \
--w-bits 4 \
--w-group-size 128 \
--batch-size 1 \
--search-scale False \
--work-dir /root/models/internlm2_5-1_8b-chat-w4a16-4bit
命令解释:
lmdeploy lite auto_awq
:lite
这是LMDeploy的命令,用于启动量化过程,而auto_awq
代表自动权重量化(auto-weight-quantization)。/root/models/internlm2_5-7b-chat
: 模型文件的路径。--calib-dataset 'ptb'
: 这个参数指定了一个校准数据集,这里使用的是’pileval’--calib-samples 128
: 这指定了用于校准的样本数量—128个样本--calib-seqlen 2048
: 这指定了校准过程中使用的序列长度—2048--w-bits 4
: 这表示权重(weights)的位数将被量化为4位。--work-dir /root/models/internlm2_5-7b-chat-w4a16-4bit
: 这是工作目录的路径,用于存储量化后的模型和中间结果。
存储占用对比:
量化前:
量化后:
显存占用对比:
量化前:
量化后:
分析1.8B模型的显存占用情况:
对于1.8B模型的BF16精度(量化前21.3GB):
-
BF16精度下权重占用:
- 1.8×10^9 参数 × 2 Bytes/参数 = 3.6GB 权重占用
-
KV cache占用:
- 剩余显存 = 24GB - 3.6GB = 20.4GB
- KV cache(80%) = 20.4GB × 0.8 = 16.3GB
-
其他项占用:1.4GB
总计:21.3GB = 权重(3.6GB) + KV cache(16.3GB) + 其他项(1.4GB)
对于W4A16量化后(20.9GB):
-
Int4精度下权重占用:
- 3.6GB ÷ 4 = 0.9GB 权重占用
-
KV cache占用:
- 剩余显存 = 24GB - 0.9GB = 23.1GB
- KV cache(80%) = 23.1GB × 0.8 = 18.5GB
-
其他项占用:1.5GB
总计:20.9GB = 权重(0.9GB) + KV cache(18.5GB) + 其他项(1.5GB)
lmdeploy serve api_server \
/root/models/internlm2_5-1_8b-chat-w4a16-4bit \
--model-format awq \
--cache-max-entry-count 0.4 \
--quant-policy 4 \
--server-name 0.0.0.0 \
--server-port 23333 \
--tp 1
让我分析一下当KV cache占比调整为40%(0.4)时的显存占用情况:
-
Int4精度下权重占用保持不变:
- 0.9GB 权重占用
-
KV cache占用调整:
- 剩余显存 = 24GB - 0.9GB = 23.1GB
- KV cache(40%) = 23.1GB × 0.4 = 9.2GB (之前是80%时的18.5GB)
-
其他项占用:2.3GB
总计:12.4GB = 权重(0.9GB) + KV cache(9.2GB) + 其他项(2.3GB)
lmdeploy serve api_client http://localhost:23333
- 当你运行这个命令时,背后发生了以下步骤:
lmdeploy serve api_server /share/new_models/Shanghai_AI_Laboratory/internlm2_5-7b-chat --model-name internlm2_5-7b-chat
主要步骤:
- 模型加载:加载指定路径的模型文件到内存
- TurboMind初始化:设置推理引擎参数
- API服务器启动:
- 创建FastAPI应用实例
- 注册路由端点(endpoints)
- 启动HTTP服务器(默认端口23333)
- WebUI初始化(如果启用)
- http://0.0.0.0:23333/ 页面内容解析:
- 这是一个OpenAPI(Swagger UI)文档页面,显示了所有可用的API端点
- 主要部分:
/v1/completions # 用于单轮对话 /v1/chat/completions # 用于多轮对话 /v1/models # 查询可用模型信息
- 每个端点都包含:
- HTTP方法(GET/POST)
- 请求参数说明
- 响应格式说明
- 可以直接在页面测试API
- 使用curl测试API:
基础调用:
# 查看可用模型
curl http://localhost:23333/v1/models
# 简单的聊天完成请求
curl -X POST http://localhost:23333/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "internlm2_5-7b-chat",
"messages": [
{"role": "user", "content": "你好"}
]
}'
更复杂的示例:
# 多轮对话
curl -X POST http://localhost:23333/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "internlm2_5-7b-chat",
"messages": [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么我可以帮你的吗?"},
{"role": "user", "content": "请介绍一下你自己"}
],
"temperature": 0.7,
"max_tokens": 1024,
"stream": false
}'
# 流式输出(stream模式)
curl -X POST http://localhost:23333/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "internlm2_5-7b-chat",
"messages": [
{"role": "user", "content": "写一首诗歌"}
],
"stream": true
}'
- 最初的问题:
# 运行量化命令时遇到错误
lmdeploy lite auto_awq ... --calib-dataset 'ptb' ...
错误:TypeError: 'NoneType' object is not callable
-
问题的根本原因:
- 量化过程需要校准数据集
- 默认的 'ptb' 数据集无法正确下载和使用
- 这导致数据集加载器返回了 None,引发了类型错误
-
解决步骤:
# 1. 首先安装解压工具(因为新数据集用了zstd压缩格式)
pip install zstandard
# 2. 更换为更稳定的校准数据集
# 把命令中的这部分:
--calib-dataset 'ptb' \
# 改成:
--calib-dataset 'pileval' \
# 3. 完整的修改后命令:
lmdeploy lite auto_awq \
/root/models/internlm2_5-1_8b-chat \
--calib-dataset 'pileval' \
--calib-samples 128 \
--calib-seqlen 2048 \
--w-bits 4 \
--w-group-size 128 \
--batch-size 1 \
--search-scale False \
--work-dir /root/models/internlm2_5-1_8b-chat-w4a16-4bit
- 为什么这样改有效:
- 'pileval' 是一个更稳定的校准数据集
- 通过安装 zstandard 解决了数据集的解压问题
- 更换数据集不影响量化效果,因为校准数据集只是用来帮助确定量化参数
核心就是:原来的数据集下载有问题,换一个更稳定的数据集并安装必要的解压工具就可以解决这个报错。
让我解释一下校准数据集(Calibration Dataset)的概念:
- 什么是校准数据集:
- 在模型量化过程中,校准数据集用于帮助确定最佳的量化参数
- 它就像是一个参考样本,用来帮助量化过程更好地保持模型的性能
- 校准数据集不会改变模型的知识,只是用来调整量化时的参数
- 为什么可以换:
- 不同的校准数据集只要具有足够的代表性和多样性就可以
- 比如 'ptb'、'pileval'、'c4'、'wikitext2' 这些都是经过验证的标准文本数据集
- 更换校准数据集不会影响模型本身的知识和能力
- 具体到你的情况:
- 原来用的 'ptb' 数据集出现了技术问题
- 换成 'pileval' 是完全没问题的,因为:
- 这也是一个被广泛使用的校准数据集
- 在 lmdeploy 的官方讨论中也推荐使用这个数据集
- 它包含了足够丰富的文本样例
所以你可以放心使用 'pileval' 或其他推荐的校准数据集,这不会影响模型的基本能力。