个人技术分享

Qwen大模型实践之量化

接上篇内容。

1. AutoGPTQ量化

提供了基于AutoGPTQ的量化方案,并开源了Int4和Int8量化模型。量化模型的效果损失很小,但能显著降低显存占用并提升推理速度。

以下我们提供示例说明如何使用Int4量化模型。在开始使用前,请先保证满足要求(如torch 2.0及以上,transformers版本为4.32.0及以上,等等),并安装所需安装包:

pip install auto-gptq optimum

效果测试:

# 通过程序下载Qwen/Qwen-14B-Chat-Int4模型,并将模型放到测试脚本路径下。
# Int4量化模型文件相比原模型文件小很多
(base) root@intern-studio-50014188:~/Qwen# du -sh ./Qwen-14B-Chat/
27G     ./Qwen-14B-Chat/
(base) root@intern-studio-50014188:~/Qwen# du -sh ./Qwen-14B-Chat-Int4/
9.1G    ./Qwen-14B-Chat-Int4/

# 修改web_demo.py,使用Qwen-14B-Chat-Int4模型。然后运行程序
(qwen) root@intern-studio-50014188:~/Qwen# python3 web_demo.py 

经过测试显存占用明显降低,约10G;使用相同问答,推理速度提示较明显,包括第一个令牌时间(TTFT)和输出令牌吞吐量都提升明显,接近于可接受的水平。

附官网BF16,Int8和Int4模型在基准评测,量化模型效果损失较小,结果如下所示:

Quantization MMLU CEval (val) GSM8K Humaneval
Qwen-1.8B-Chat (BF16) 43.3 55.6 33.7 26.2
Qwen-1.8B-Chat (Int8) 43.1 55.8 33.0 27.4
Qwen-1.8B-Chat (Int4) 42.9 52.8 31.2 25.0
Qwen-7B-Chat (BF16) 55.8 59.7 50.3 37.2
Qwen-7B-Chat (Int8) 55.4 59.4 48.3 34.8
Qwen-7B-Chat (Int4) 55.1 59.2 49.7 29.9
Qwen-14B-Chat (BF16) 64.6 69.8 60.1 43.9
Qwen-14B-Chat (Int8) 63.6 68.6 60.0 48.2
Qwen-14B-Chat (Int4) 63.3 69.0 59.8 45.7
Qwen-72B-Chat (BF16) 74.4 80.1 76.4 64.6
Qwen-72B-Chat (Int8) 73.5 80.1 73.5 62.2
Qwen-72B-Chat (Int4) 73.4 80.1 75.3 61.6

2. KV cache量化

修改web_demo.py程序开启KV cache量化:

(qwen) root@intern-studio-50014188:~/Qwen# vim web_demo.py
...
    model = AutoModelForCausalLM.from_pretrained(
        args.checkpoint_path,
        device_map=device_map,
        trust_remote_code=True,
        resume_download=True,
        use_cache_quantization=True,
        use_cache_kernel=True,
        use_flash_attn=False,
    ).eval()
...

运行程序后,模型加载完毕,显存占用9G左右,有降低。但是问答测试过程中本机测试遇到如下问题待解决:

    return forward_call(*args, **kwargs)
  File "/root/.cache/huggingface/modules/transformers_modules/Qwen-14B-Chat-Int4/modeling_qwen.py", line 505, in forward
    torch.ones((key.size(1), key.size(1)), dtype=torch.bool, device=key.device)
AttributeError: 'tuple' object has no attribute 'size'
User: 你好
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/gradio/queueing.py", line 527, in process_events
    response = await route_utils.call_process_api(
  File "/usr/local/lib/python3.8/dist-packages/gradio/route_utils.py", line 270, in call_process_api
    output = await app.get_blocks().process_api(

在模型推理时,我们可以将中间结果key以及value的值量化后压缩存储,这样便可以在相同的卡上存储更多的key以及value,增加样本吞吐。

在config.json里提供了use_cache_quantization和use_cache_kernel两个参数来控制是否启用KV cache量化,具体使用方法如下:

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen-7B-Chat",
     device_map="auto",
     trust_remote_code=True,
     use_cache_quantization=True,
     use_cache_kernel=True,
     use_flash_attn=False
)

注意:
当前该功能不支持与flash attention同时开启,如果你开了KV cache量化的同时又开了flash attention(use_flash_attn=True, use_cache_quantization=True, use_cache_kernel=True),程序默认将关闭use_flash_attn。

效果方面,我们验证过Int8 KV Cache的使用对模型整体的精度指标基本无损。我们做了针对显存占用的性能测试。评测运行于单张A100-SXM4-80G GPU,模型默认使用BF16格式,默认生成1024个token,其中OOM表示内存不足。

开启了KV cache量化之后,模型在推理的时候可以开启更大的batch size (bs)。

USE KV Cache bs=1 bs=4 bs=16 bs=32 bs=64 bs=100
No 16.3GB 24.1GB 31.7GB 48.7GB oom oom
Yes 15.5GB 17.2GB 22.3GB 30.2GB 48.2GB 72.4GB

开启了KV cache量化之后,模型在推理时可在生成更长的序列(sl,生成的token数)时,节约更多的显存。

USE KV Cache sl=512 sl=1024 sl=2048 sl=4096 sl=8192
no 15.2GB 16.3GB 17.6GB 19.5GB 23.2GB
yes 15GB 15.5GB 15.8GB 16.6GB 17.6GB

开启KV cache量化后,模型在推理时会将原始存进layer-past的float格式的key/value转换成int8格式,同时存储量化部分的参数。

具体操作如下:

  1. 将key/value进行量化操作
    qv,scale,zero_point=quantize_cache_v(v)
  1. 存入layer_past中:

量化格式的layer-past:

    layer_past=((q_key,key_scale,key_zero_point),
                (q_value,value_scale,value_zero_point))

原始格式的layer-past:

    layer_past=(key,value)

如果需要将layer-past中存好的key,value直接取出使用,可以使用反量化操作将Int8格式的key/value转回float格式:

    v=dequantize_cache_torch(qv,scale,zero_point)

3. 推理性能测试

本机使用官网脚本测试遇到如下问题,待解决:

  File "/root/Qwen/profile.py", line 45, in <module>
    from auto_gptq import AutoGPTQForCausalLM
ImportError: cannot import name 'AutoGPTQForCausalLM' from partially initialized module 'auto_gptq' (most likely due to a circular import) (/usr/local/lib/python3.8/dist-packages/auto_gptq/__init__.py)

这里附官网数据和方法:

这一部分将介绍模型推理的速度和显存占用的相关数据。下文的性能测算使用 此脚本 完成。

我们测算了BF16、Int8和Int4模型在生成2048个token时的平均推理速度(tokens/s)和显存使用。结果如下所示:

Model Size Quantization Speed (Tokens/s) GPU Memory Usage
1.8B BF16 54.09 4.23GB
Int8 55.56 3.48GB
Int4 71.07 2.91GB
7B BF16 40.93 16.99GB
Int8 37.47 11.20GB
Int4 50.09 8.21GB
14B BF16 32.22 30.15GB
Int8 29.28 18.81GB
Int4 38.72 13.01GB
72B BF16 8.48 144.69GB (2xA100)
Int8 9.05 81.27GB (2xA100)
Int4 11.32 48.86GB
72B + vLLM BF16 17.60 2xA100

评测运行于单张A100-SXM4-80G GPU(除非提到使用2xA100),使用PyTorch 2.0.1、CUDA 11.8和Flash-Attention2。(72B + vLLM 使用 PyTorch 2.1.0和Cuda 11.8.)推理速度是生成2048个token的速度均值。

注意:以上Int4/Int8模型生成速度使用autogptq库给出,当前AutoModelForCausalLM.from_pretrained载入的模型生成速度会慢大约20%。我们已经将该问题汇报给HuggingFace团队,若有解决方案将即时更新。

我们还测量了不同上下文长度、生成长度、Flash-Attention版本的推理速度和 GPU 内存使用情况。可以在 Hugging Face 或 ModelScope 上的相应的模型介绍页面找到结果。