Skip to content

量化

swift支持使用awq、gptq、bnb、hqq、eetq技术对模型进行量化。其中awq、gptq量化技术支持vllm进行推理加速,需要使用校准数据集,量化性能更好,但量化速度较慢。而bnb、hqq、eetq无需校准数据,量化速度较快。这五种量化方法都支持qlora微调。

awq、gptq需要使用swift export进行量化。而bnb、hqq、eetq可以直接在sft和infer时进行快速量化。

从vllm推理加速支持的角度来看,更推荐使用awq和gptq进行量化。从量化效果的角度来看,更推荐使用awq、hqq和gptq进行量化。而从量化速度的角度来看,更推荐使用hqq进行量化。

环境

# 设置pip全局镜像 (加速下载)
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
# 安装ms-swift
git clone https://github.com/modelscope/swift.git
cd swift
pip install -e '.[llm]'

# 使用awq量化:
# autoawq和cuda版本有对应关系,请按照`https://github.com/casper-hansen/AutoAWQ`选择版本
pip install autoawq -U

# 使用gptq量化:
# auto_gptq和cuda版本有对应关系,请按照`https://github.com/PanQiWei/AutoGPTQ#quick-installation`选择版本
pip install auto_gptq -U

# 使用bnb量化:
pip install bitsandbytes -U

# 使用hqq量化:
# pip install transformers>=4.41
pip install hqq

# 使用eetq量化:
# pip install transformers>=4.41

# 参考https://github.com/NetEase-FuXi/EETQ
git clone https://github.com/NetEase-FuXi/EETQ.git
cd EETQ/
git submodule update --init --recursive
pip install .

# 环境对齐 (通常不需要运行. 如果你运行错误, 可以跑下面的代码, 仓库使用最新环境测试)
pip install -r requirements/framework.txt  -U
pip install -r requirements/llm.txt  -U

原始模型

# awq-int4量化 (使用A100大约需要18分钟, 显存占用: 13GB)
# 如果出现量化的时候OOM, 可以适度降低`--quant_n_samples`(默认256)和`--quant_seqlen`(默认2048).
# gptq-int4量化 (使用A100大约需要20分钟, 显存占用: 7GB)

# awq: 使用`alpaca-zh alpaca-en sharegpt-gpt4:default`作为量化数据集
CUDA_VISIBLE_DEVICES=0 swift export \
    --model_type qwen1half-7b-chat --quant_bits 4 \
    --dataset alpaca-zh alpaca-en sharegpt-gpt4:default --quant_method awq

# gptq: 使用`alpaca-zh alpaca-en sharegpt-gpt4:default`作为量化数据集
# gptq量化请先查看此issue: https://github.com/AutoGPTQ/AutoGPTQ/issues/439
OMP_NUM_THREADS=14 CUDA_VISIBLE_DEVICES=0 swift export \
    --model_type qwen1half-7b-chat --quant_bits 4 \
    --dataset alpaca-zh alpaca-en sharegpt-gpt4:default --quant_method gptq

# awq: 使用自定义量化数据集
# gptq同理
CUDA_VISIBLE_DEVICES=0 swift export \
    --model_type qwen1half-7b-chat --quant_bits 4 \
    --dataset xxx.jsonl \
    --quant_method awq

# 推理 swift量化产生的模型
# awq
CUDA_VISIBLE_DEVICES=0 swift infer \
    --model_type qwen1half-7b-chat \
    --model_id_or_path qwen1half-7b-chat-awq-int4
# gptq
CUDA_VISIBLE_DEVICES=0 swift infer \
    --model_type qwen1half-7b-chat \
    --model_id_or_path qwen1half-7b-chat-gptq-int4

# 推理 原始模型
CUDA_VISIBLE_DEVICES=0 swift infer --model_type qwen1half-7b-chat

模型效果

# swift量化产生的awq-int4模型
"""
<<< 你好
你好!有什么问题我可以帮助你吗?
--------------------------------------------------
<<< 2000年是闰年嘛?
是的,2000年是闰年。闰年规则是:普通年份能被4整除但不能被100整除,或者能被400整除的年份就是闰年。2000年满足后者,所以按照公历是闰年。
--------------------------------------------------
<<< 15869+587=?
15869 + 587 = 16456
--------------------------------------------------
<<< 浙江的省会在哪
浙江省的省会是杭州市。
--------------------------------------------------
<<< 这有什么好吃的
浙江美食非常丰富,以下列举一些具有代表性的:

1. **杭州菜**:如西湖醋鱼、东坡肉、龙井虾仁、叫化童鸡等,清淡而精致。
2. **宁波菜**:如红烧肉、汤圆、水磨年糕、宁波汤包等,口味鲜美。
3. **温州菜**:温州海鲜如蝤蛑、蝤蛑炒年糕、瓯菜,口味偏咸鲜。
4. **绍兴菜**:如东坡肉、霉干菜扣肉、绍兴黄酒等,酒香浓郁。
5. **嘉兴粽子**:如嘉兴肉粽,特别是五芳斋的粽子,口感软糯。
6. **金华火腿**:浙江特产,以金华地区最为著名,口感醇厚。
7. **浙东土菜**:如农家小炒、野菜、腌菜等,原汁原味。

各地还有许多特色小吃,如杭州的知味观、宋城的宋嫂鱼羹,以及各种小吃街如河坊街、西湖醋鱼一条街等,都值得一尝。如果你有具体地方或口味偏好,可以告诉我,我可以给出更具体的建议。
"""

# swift量化产生的gptq-int4模型
"""
<<< 你好
你好!很高兴为你提供帮助。有什么问题或需要咨询的吗?
--------------------------------------------------
<<< 2000年是闰年嘛?
是的,2000年是闰年。根据格里高利历(公历),闰年的规则是:普通年份能被4整除但不能被100整除,或者能被400整除的年份就是闰年。2000年满足后两个条件,所以是闰年。
--------------------------------------------------
<<< 15869+587=?
15869 + 587 = 16456
--------------------------------------------------
<<< 浙江的省会在哪
浙江省的省会是杭州市。
--------------------------------------------------
<<< 这有什么好吃的
浙江美食丰富多样,以下是一些具有代表性的:

1. **杭州菜**:如东坡肉、西湖醋鱼、龙井虾仁、叫化鸡等,注重原汁原味,讲究刀工和火候。
2. **宁波的海鲜**:如宁波汤圆、海鲜大餐,宁波炒肉丝也很有名。
3. **嘉兴粽子**:以嘉兴五芳斋的粽子最为人知,有各种口味如肉粽、豆沙粽等。
4. **绍兴黄酒**:搭配当地特色菜肴,如醉鸡、糟溜鱼片等。
5. **金华火腿**:浙江特产,口感醇厚,常作为节日礼品。
6. **温州瓯菜**:口味偏酸辣,如温州鱼生、白斩鸡等。
7. **浙东佛跳墙**:类似福建的佛跳墙,用多种海鲜和山珍炖制。

如果你对某种具体的食物感兴趣,可以告诉我,我可以提供更详细的推荐。
"""

# 原始模型
"""
<<< 你好
你好!有什么问题我可以帮助你吗?
--------------------------------------------------
<<< 2000年是闰年嘛?
是的,2000年是闰年。根据格里高利历(公历),闰年的规则是:普通年份能被4整除但不能被100整除,或者能被400整除的年份都是闰年。2000年满足后者,所以是闰年。
--------------------------------------------------
<<< 15869+587=?
15869 + 587 = 16456
--------------------------------------------------
<<< 浙江的省会在哪
浙江省的省会是杭州市。
--------------------------------------------------
<<< 这有什么好吃的
浙江的美食非常丰富,以下列举一些具有代表性的:

1. **杭州菜**:如西湖醋鱼、东坡肉、龙井虾仁、叫化童鸡等,清淡鲜美,注重原汁原味。
2. **宁波菜**:如宁波汤圆、红烧肉、海鲜类,如宁波海鲜面、清蒸河鳗等。
3. **绍兴菜**:如霉干菜扣肉、茴香豆、醉排骨,特色是酱香浓郁。
4. **温州菜**:如温州鱼丸、白斩鸡、楠溪江三鲜,口味偏咸鲜。
5. **嘉兴粽子**:特别是嘉兴五芳斋的粽子,闻名全国,甜咸皆有。
6. **金华火腿**:浙江名特产,口感鲜美,营养丰富。
7. **浙东土菜**:如东阳火腿、嵊州菜、台州海鲜等,地方特色鲜明。

当然,浙江各地还有许多特色小吃,如衢州的鸭头、湖州的粽子、舟山的海鲜等,你可以根据自己的口味选择。如果你需要更具体的推荐,可以告诉我你对哪种类型或者哪个地方的美食感兴趣。
"""

bnb、hqq、eetq

CUDA_VISIBLE_DEVICES=0 swift infer \
    --model_type qwen1half-7b-chat \
    --quant_method bnb \
    --quantization_bit 4

CUDA_VISIBLE_DEVICES=0 swift infer \
    --model_type qwen1half-7b-chat \
    --quant_method hqq \
    --quantization_bit 4

CUDA_VISIBLE_DEVICES=0 swift infer \
    --model_type qwen1half-7b-chat \
    --quant_method eetq \
    --dtype fp16

Merge-LoRA & 量化

# 使用`alpaca-zh alpaca-en sharegpt-gpt4:default`作为量化数据集
CUDA_VISIBLE_DEVICES=0 swift export \
    --ckpt_dir 'output/qwen1half-4b-chat/vx-xxx/checkpoint-xxx' \
    --merge_lora true --quant_bits 4 \
    --dataset alpaca-zh alpaca-en sharegpt-gpt4:default --quant_method awq

# 使用微调时使用的数据集作为量化数据集
CUDA_VISIBLE_DEVICES=0 swift export \
    --ckpt_dir 'output/qwen1half-4b-chat/vx-xxx/checkpoint-xxx' \
    --merge_lora true --quant_bits 4 \
    --load_dataset_config true --quant_method awq

推理量化后模型

# awq/gptq量化模型支持vllm推理加速. 也支持模型部署.
CUDA_VISIBLE_DEVICES=0 swift infer --ckpt_dir 'output/qwen1half-4b-chat/vx-xxx/checkpoint-xxx-merged-awq-int4'

部署量化后模型

CUDA_VISIBLE_DEVICES=0 swift deploy --ckpt_dir 'output/qwen1half-4b-chat/vx-xxx/checkpoint-xxx-merged-awq-int4'
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen1half-4b-chat",
"messages": [{"role": "user", "content": "晚上睡不着觉怎么办?"}],
"max_tokens": 256,
"temperature": 0
}'

评估

写好自己的评测集

目前我们支持两种pattern的评测集:选择题格式的CEval和问答题格式的General-QA

选择题:CEval格式

CEval格式适合用户是选择题的场景。即从四个选项中选择一个正确的答案,评测指标是accuracy。建议直接修改CEval脚手架目录。该目录包含了两个文件:

default_dev.csv # 用于fewshot评测,至少要具有入参的eval_few_shot条数据,即如果是0-shot评测,该csv可以为空
default_val.csv # 用于实际评测的数据

CEval的csv文件需要为下面的格式:

id,question,A,B,C,D,answer,explanation
1,通常来说,组成动物蛋白质的氨基酸有____,4种,22种,20种,19种,C,1. 目前已知构成动物蛋白质的的氨基酸有20种。
2,血液内存在的下列物质中,不属于代谢终产物的是____。,尿素,尿酸,丙酮酸,二氧化碳,C,"代谢终产物是指在生物体内代谢过程中产生的无法再被利用的物质,需要通过排泄等方式从体内排出。丙酮酸是糖类代谢的产物,可以被进一步代谢为能量或者合成其他物质,并非代谢终产物。"

其中,id是评测序号,question是问题,ABCD是可选项(如果选项少于四个则对应留空),answer是正确选项,explanation是解释。

其中的default文件名是CEval评测的子数据集名称,可更换,下面的配置中会用到。

问答题:General-QA

General-QA适合用户是问答题的场景,评测指标是rougebleu。建议直接修改General-QA脚手架目录。该目录包含了一个文件:

default.jsonl

该jsonline文件需要为下面的格式:

{"history": [], "query": "中国的首都是哪里?", "response": "中国的首都是北京"}
{"history": [], "query": "世界上最高的山是哪座山?", "response": "是珠穆朗玛峰"}
{"history": [], "query": "为什么北极见不到企鹅?", "response": "因为企鹅大多生活在南极"}

注意history目前为保留字段,尚不支持。

定义一个配置文件传入eval命令

定义好上面的文件后,需要写一个json文件传入eval命令中。建议直接修改官方配置脚手架文件。该文件内容如下:

[
    {
        "name": "custom_general_qa", # 评测项名称,可以随意指定
        "pattern": "general_qa", # 该评测集的pattern
        "dataset": "eval_example/custom_general_qa", # 该评测集的目录,强烈建议使用绝对路径防止读取失败
        "subset_list": ["default"] # 需要评测的子数据集,即上面的`default_x`文件名
    },
    {
        "name": "custom_ceval",
        "pattern": "ceval",
        "dataset": "eval_example/custom_ceval", # 该评测集的目录,强烈建议使用绝对路径防止读取失败
        "subset_list": ["default"]
    }
]

下面就可以传入这个配置文件进行评测了:

# 使用arc评测,每个子数据集限制评测10条,推理backend使用pt
# cd examples/pytorch/llm
# eval_dataset也可以设置值,官方数据集和自定义数据集一起跑
swift eval \
    --model_type "qwen-7b-chat" \
    --eval_dataset no \
    --infer_backend pt \
    --custom_eval_config eval_example/custom_config.json

运行结果如下:

2024-04-10 17:21:33,275 - llmuses - INFO - *** Report table ***
+------------------------------+----------------+---------------------------------+
| Model                        | custom_ceval   | custom_general_qa               |
+==============================+================+=================================+
| qa-custom_ceval_qwen-7b-chat | 1.0 (acc)      | 0.8888888888888888 (rouge-1-r)  |
|                              |                | 0.33607503607503614 (rouge-1-p) |
|                              |                | 0.40616618868713145 (rouge-1-f) |
|                              |                | 0.39999999999999997 (rouge-2-r) |
|                              |                | 0.27261904761904765 (rouge-2-p) |
|                              |                | 0.30722525589718247 (rouge-2-f) |
|                              |                | 0.8333333333333334 (rouge-l-r)  |
|                              |                | 0.30742204655248134 (rouge-l-p) |
|                              |                | 0.3586824745225346 (rouge-l-f)  |
|                              |                | 0.3122529644268775 (bleu-1)     |
|                              |                | 0.27156862745098037 (bleu-2)    |
|                              |                | 0.25 (bleu-3)                   |
|                              |                | 0.2222222222222222 (bleu-4)     |
+------------------------------+----------------+---------------------------------+


召回率(Recall):
召回率是指在所有实际为正例的样本中,被正确预测为正例的比例。简单来说,就是模型能够找出多少真正的正例。比如,在一个疾病检测系统中,所有真正患病的人中,模型正确检测出多少人是患病的。

精确率(Precision):
精确率是指在所有被预测为正例的样本中,实际为正例的比例。简单来说,就是模型预测为正例的样本中,有多少是真正的正例。比如,在一个垃圾邮件检测系统中,模型预测为垃圾邮件的邮件中,有多少实际上是垃圾邮件。

F1值:
F1值是召回率和精确率的调和平均值。它综合考虑了召回率和精确率,是一个平衡两者的指标。当召回率和精确率都很重要时,F1值是一个很好的评价指标。比如,在一个推荐系统中,既要保证推荐的内容是用户感兴趣的(精确率),又要保证不遗漏用户可能感兴趣的内容(召回率),这时F1值就能很好地反映系统的性能。
((召回率*精确率)/(召回率+精确率))*2


BLEU评分考虑了以下几个方面:
词组匹配:计算机器翻译中的词组(通常是n-gram,即连续的n个词)在参考翻译中出现的频率。
长度惩罚:如果机器翻译的句子太短,即使所有词都匹配,也不应该得高分,因为可能会遗漏信息。
简洁性:避免机器翻译只是简单地复制参考翻译中的词组,而没有真正理解内容。
BLEU评分越高,表示机器翻译越接近人工翻译的质量。不过,需要注意的是,BLEU评分并不是完美的,它主要关注词汇层面的匹配,而可能忽略了一些语义和语境上的细微差别。