AutoRound 即将发布0.4版本,主要会对vlm进行支持。在这过程中,我们也在量化一些模型中总结了一些技巧。当然由于量化的不是特别多,所有一些技巧也不一定对,供您参考。
1 VLM 量化及 标定数据集选用
背景: VLM模型一般包含两大主模块,一个是语言模型,一个是视觉模型,所以自然产生两个问题 1 量化的时候是否需要量化视觉模块 2 量化数据是需要带视觉元素还是不带
结论:优先只量化语言模块和使用语言数据集标定。AutoRound(>=0.4)将其设成模型,但用户也可以使用其他选择
原因:
-
1: 语言模块占比很大,量化语言模块性价比比较高
Models | 16 bits | Language 4 bits |
---|---|---|
Qwen2-VL-7B-Instruct | 15.6G | 6.5G |
Phi-3.5-vision-instruct | 7.9G | 2.8G |
llava-v1.5-7b | 12.6G | 4.3G |
Llama-3.2-11B-Vision-Instruct | 20.1G | 7.9G |
Cogvlm2 | 36.9G | 17.6G |
-
2 量化视觉模块会带来相对较大的准确率损失,我们在Qwen1和llava上做过实验,发现是这样。
-
3 量化视觉模块量化的工程和量化速度都有挑战。因为要量化视觉模块需要使用如带图片的数据集进行标定(当然我们发现有些模型如mllama就算只量化语言模块,也需要带图片的数据集)。 但是各个模型对于组织图片数据和文本数据都有一套不尽相同且有点复杂的预处理模块,所以工程上不好处理。 另外由于模型基本支持不同尺寸的图片,所以对于每一个标定样本训练是不能concat,只能用bs=1 accumulate_gradient_steps 来处理,这种不仅速度慢,而且有精度风险,可以参考博客,当然我们在autoround里面也参考他们做了修复。
-
4 量化语言模块只使用文本数据标定和跟带图片标定在大部分模型上的结果接近。
2 用对称量化还是非对称量化
背景: 一般理解上非对称量化比对称量化精度搞,但是性能对掉20%-30%,并且在权重量化的场景下下,由于对称量化可以使用marlin kernel, 性能有进一步的的提升,所以很多时候很难取舍
结论:推荐使用 full range 对称量化, auo-round (>=0.3.1)已经将其设成默认
原因: full range 对称量化首先是在llama cpp社区提出的,他的精度可以媲美非对称量化,并在低bit领域如2bit远远好于其他对称量化
average accuracy of 10 tasks
Models W2G32 | gptq_sym | asym | full_range_sym |
---|---|---|---|
Meta-Llama-3.1-8B-Instruct | 0.4500 | 0.52802 | 0.5381 |
Qwen2-7B | 0.5229 | 0.5559 | 0.5486 |
Models W4G128 | asym | full_range_sym |
---|---|---|
Meta-Llama-3.1-8B-Instruct | 0.6342 | 0.6370 |
Qwen2-7B | 0.6143 | 0.6167 |
Mistral-7B-Instruct-v0.2 | 0.6606 | 0.6635 |
Phi-3-mini-4k-instruct | 0.6475 | 0.6432 |
进一步理解:它的设计如下,由于也属于对称量化,它也是在数据分布具体一定的对称性比较好,在大模型上权重group_size>=32 基本符合。它有两个好处,第一个是它映射的范围更广,比如在2bit上,它的映射范围是-2,2,只是最后要clip到(-2,1),有点类似于NF4/FP4等数据类型在数值比较低的地方采样密度高,而在值高的地方采样密度低。而传统的对称是映射到(-1,1)。 另外相对于非对称量化, 由于没有zp,因此zp上的rouniding损失没有。
3 是否量化lm-head
背景: 随着llm的发展,模型采样的词汇表越来越大,导致了lm-head占的参数比例越来越高,但是量化lm-head也会带来一定的损失,所以到底需不需要量化
结论:可以在7B-14B中使用, auto-round中可以直接 “auto-round --model xxx --quant_lm_head”
原因: 在7B以上的模型使用lm-lead对精度影响不大,并且对于模型压缩的比例也比较客观。
glm4-chat Metric | BF16 | INT4(6.4G) | INT4-quanted-lm-head(5.5G) |
---|---|---|---|
Avg(16 tasks) | 0.6260 | 0.6230 | 0.6204 |
leaderboard_mmlu_pro 5shot | 0.3678 | 0.3616 | 0.3610 |
leaderboard_ifeval inst_level_strict_acc | 0.5504 | 0.5600 | 0.5588 |
leaderboard_ifeval prompt_level_strict_acc | 0.4067 | 0.4233 | 0.4067 |
cmmlu | 0.7213 | 0.7137 | 0.7086 |
ceval-valid | 0.7065 | 0.7058 | 0.6909 |
gsm8k 5shot strict match | 0.7794 | 0.7597 | 0.7589 |
Qwen2.5-7B-Instruct Metric | BF16 | INT4(5.3G) | INT4 lm-head (4.5G) |
---|---|---|---|
Avg(16 tasks) | 0.6649 | 0.6586 | 0.6577 |
leaderboard_mmlu_pro 5 shots | 0.4458 | 0.4436 | 0.4384 |
leaderboard_ifeval inst_level_strict_acc | 0.6859 | 0.6715 | 0.6595 |
leaderboard_ifeval prompt_level_strict_acc | 0.5730 | 0.5508 | 0.5379 |
mmlu | 0.7174 | 0.7147 | 0.7145 |
cmmlu | 0.8028 | 0.7888 | 0.7888 |
ceval-valid | 0.7935 | 0.7838 | 0.7741 |
gsm8k 5 shots | 0.7665 | 0.7544 | 0.8006 |
4 小心chat template 和FP16 kernel引入的overflow
结论:推荐不量化将output值很大的layer,在auto-round(>=0.4)中,使用--fp_layers "xxx,xxx"
在量化一些instruct模型的时候,我们发现用lm-eval-harness测出来的精度都是正常的,但是实际带上chat_template去跑的时候结果很差,比如结果都是“!!!!”,经过研究发现主要是量化模型的一些中间结果出现了INF。这是因为权重量化里面大部分的kernel都是fp16的,而最新的模型一般是BF16,BF16能表示的范围比FP16大的多。比如我们发现
Qwen2-57B-A14B-Instruct model.layers.3.mlp.shared_expert.down_proj 30464(不带chat tempalte)=>48128(带chat tmplate)
model.layers.5.mlp.down_proj 5952.0(不带chat tempalte)===>1w4(带chat tmplate)
虽然这些都在FP16的表达范围内,但在int4 tuning过程中,一般会调weight,,并且大多数都是矩阵乘,所以有可能weight稍为调一些output就会放大,另外我们也发现就算是fake qdq model是好的,但是实际中用int4 kernel跑的时候也可能都问题。
当然我们也发现了调整一些量化的参数或者clamp_to_range也会有用,但调参数很累,clamp_to_range不是很通用,所以直接fallback这些层是可以的,因为这些outlier层不是特别多。
5 对量化的scale进行保护
结论:对量化的scale做1e-5的保护
在量化LLaMA3.1 70B的时候发现我们的精度不太稳定,有些task精度很好,有些精度很差。前面提到由于cuda上kernel都是FP16,scale也要转成fp16, 而fp16小数字表达精度不是很高,所以最好做个保护
6 模型量化时的数据类型
背景: 大部分最新的模型都是BF16的,而很多int4的kernel是支持FP16,那是在量化前强转还是在量化后强转?
结论:跟随kernel在量化前强转,auto-round中可以使用 --model_dtype "fp16"
原因:我们发现对小模型精度更高