【大模型入门指南 07】量化技术浅析

embedded/2025/1/11 18:57:16/

大模型入门指南】系列文章:


本文目录

  • 量化是什么
  • AutoGPTQ
  • Bitsandbytes
  • GGML
  • AWQ
  • NLP 大模型高频面题汇总
  • NLP基础篇
        • BERT 模型面
    • LLMs 微调面


量化是什么

前文中我们提到,模型的推理过程是一个复杂函数的计算过程,这个计算一般以矩阵乘法为主,也就是涉及到了并行计算。一般来说,单核CPU可以进行的计算种类更多,速度更快,但一般都是单条计算;而显卡能进行的都是基础的并行计算,做矩阵乘法再好不过。如果把所有的矩阵都加载到显卡上,就会导致显卡显存的占用大量增加,尤其是LLM模型大小从7b、14b、34b到几百b不等,占用显存的大小就是惊人的数字,如何在减少运算量和显存占用的条件下,做到推理效果不下降太多呢?在这里需要引入浮点数和定点数的概念。

双精度浮点数:在PyTorch中用torch.float64表示,或者在其他语言中也称为double类型,在LLM训练中一般比较少用

全精度浮点数:在PyTorch中用torch.float32表示

低精度浮点数:在PyTorch中用torch.bfloat16和torch.float16表示。这两个浮点数的差别在上图中可以表示:

  1. bfloat16的小数部分较短,整数部分较长,这会有利于在训练中减少梯度爆炸的情况(即梯度累加值超过了最大值),但是这种数据类型是在N系列显卡Ampere系列才支持的,即30系列显卡。
  2. float16的小数部分较长,这意味着在精度控制上float16更好,但整数部分较短,比较容易梯度爆炸。

那么是否有更加减少显存占用和计算量的数值表达方式呢?那么可以考虑是否把浮点数转换为定点数(整数),整数计算更快更省显存,如果计算精度下降不大就很完美了。这种用整数计算代替浮点数计算的方法就是量化

量化的基本原理是根据每个tensor的浮点型最大值和最小值,将其映射为一个固定范围的整形数值集合,比如[-127~127]。假设一个简单的公式:qweight=round(weight/scale),其中qweight代表量化后权重,weight代表量化前权重,scale代表缩放因子,可以看到在进行缩放后为了将浮点型转换为整数过程中增加了round操作丢失了小数部分。在后续计算或反量化为浮点型时存在无法完全还原的情况,这就是精度损失。

按照量化发生的步骤区分,可以划分为PTQ(训练后量化,或离线量化)和QAT(训练感知型量化,或在线量化)。PTQ量化可以分为data-free和calibration两种,前者不使用数据集进行校准直接计算量化因子,后者会根据少量真实数据进行统计分析并对量化因子进行额外校准,但耗费的时间更长。QAT量化会先在待量化的算子上增加一个伪量化结构,并在训练时模拟量化过程并实时更新计算量化因子(类似反向传播过程)及原始权重。QAT由于较为复杂一般作为辅助措施存在,用于改进PTQ量化的技术手段。

按照量化方法可以划分为线性量化、非线性量化(如对数量化)等多种方式,目前较为常用的是线性量化。其中线性量化又可以按照对称性划分为对称量化和非对称量化,非对称量化为了解决weight分布不均匀问题,其在公式中增加了zero_point项:qweight=round(weight/scale + zero_point),使稠密数据部分可以得到更宽泛的数值范围。

按照量化粒度划分可以分为**逐层量化(每层使用一套量化因子)、逐组量化(在每层中按照group使用一套量化因子)、逐通道量化(按channel划分量化因子)**等几种方式。

按照量化最大值的阈值区分,可以分为饱和量化和不饱和量化两种。不饱和量化按照浮点数最大值和量化后最大值的比例计算量化因子,由于原始weight的非均匀性会导致某些整形数值范围存在权重空缺。饱和量化会计算一个中间值以计算出量化因子,因此会舍弃一部分不重要数据,将重要数据尽量均匀的分布到量化数值范围内。

按照量化后的比特数划分,可以分为2比特量化,4比特量化,8比特量化等类型。

一般来说,PyTorch中量化模块的forward过程会先对量化权重进行反量化后使用浮点数进行计算。

下面介绍几种常用的量化库。

AutoGPTQ

该库需要引入额外的校准数据集进行量化校准。相比bitsandbytes量化精度较高,推理速度较快,但训练后不支持合并adapter

# 例子来自于https://github.com/PanQiWei/AutoGPTQ
from modelscope import AutoTokenizer, snapshot_download
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
import logging
import shutil
import oslogging.basicConfig(format="%(asctime)s %(levelname)s [%(name)s] %(message)s", level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S"
)pretrained_model_dir = snapshot_download("qwen/Qwen-1_8B-Chat")
quantized_model_dir = "qwen-1_8B-4bit"shutil.rmtree(quantized_model_dir, ignore_errors=True)
shutil.copytree(pretrained_model_dir, quantized_model_dir)
for _file in os.listdir(quantized_model_dir):if ".safetensors" in _file or ".bin" in _file:os.remove(os.path.join(quantized_model_dir, _file))tokenizer = AutoTokenizer.from_pretrained(pretrained_model_dir, use_fast=True, trust_remote_code=True)
examples = [tokenizer("auto-gptq is an easy-to-use model quantization library with user-friendly apis, based on GPTQ algorithm.")
]quantize_config = BaseQuantizeConfig(bits=4,  # quantize model to 4-bitgroup_size=128,  # it is recommended to set the value to 128desc_act=False,  # set to False can significantly speed up inference but the perplexity may slightly bad
)# load un-quantized model, by default, the model will always be loaded into CPU memory
model = AutoGPTQForCausalLM.from_pretrained(pretrained_model_dir, quantize_config, trust_remote_code=True).to(0)# quantize model, the examples should be list of dict whose keys can only be "input_ids" and "attention_mask"
model.quantize(examples)# save quantized model
model.save_quantized(quantized_model_dir)# save quantized model using safetensors
model.save_quantized(quantized_model_dir, use_safetensors=True)# load quantized model to the first GPU
model = AutoGPTQForCausalLM.from_quantized(quantized_model_dir, device="cuda:0", trust_remote_code=True)
# inference with model.generate
print(tokenizer.decode(model.generate(**tokenizer("auto_gptq is", return_tensors="pt").to(model.device))[0]))

在SWIFT中,可以使用已经量化好的AutoGPTQ模型直接进行训练:

swift sft --model_id_or_path qwen/Qwen-7B-Chat-Int4 --model_revision master --sft_type lora --tuner_backend swift --template_type qwen --dtype fp16 --output_dir output --dataset leetcode-python-en --train_dataset_sample -1 --num_train_epochs 1 --max_length 512 --check_dataset_strategy warning --lora_rank 8 --lora_alpha 32 --lora_dropout_p 0.05 --lora_target_modules ALL --gradient_checkpointing true --batch_size 1 --weight_decay 0.01 --learning_rate 1e-4

上面的命令行中,qwen/Qwen-7B-Chat-Int4是已经量化好的Qwen-7B-Chat模型。

Bitsandbytes

bitsandbytes是一种data-free的量化库。该量化方法速度较快(因为其不需要数据校准),因此可以在模型加载时动态量化,且该方法训练速度较快,因此训练兼容性较好,一般用于QLoRA训练中,且训练后可以合并adapter。当由于其没有数据校准过程,因此精度较AutoGPTQ较低。

from modelscope import AutoModelForCausalLM, AutoTokenizer
import torchmodel = AutoModelForCausalLM.from_pretrained('qwen/Qwen-1_8B-Chat',load_in_8bit=True,trust_remote_code=True)tokenizer = AutoTokenizer.from_pretrained('qwen/Qwen-1_8B-Chat', trust_remote_code=True)print(model(**tokenizer('how are you?', return_tensors='pt')))

GGML

GGML和GGUF是GGML C++推理库的两种量化格式,其中GGUF格式较新,可以保留模型版本等其他自定义信息。这两种格式也是PTQ形式的量化算法,但GGML和GGUF格式的量化算法更适配于CPU推理,因此在CPU上运行更快,而GPTQ量化对GPU更加友好,两者的推理精度相仿。因此,*.cpp类型使用了GGML推理库的推理框架都更适配于CPU推理。

AWQ

AWQ量化方式假设不是所有权重都影响模型性能,因此在量化过程中会对特殊权重进行特殊处理以减轻量化过程中的精度损失。因此在和GPTQ量化保持类似推理速度的同时可以具备更好的精度。

目前VLLM对AWQ的支持较好, 可以考虑在推理加速时使用AWQ量化方式。


NLP 大模型高频面题汇总

NLP基础篇

  • 【NLP 面试宝典 之 模型分类】 必须要会的高频面题
  • 【NLP 面试宝典 之 神经网络】 必须要会的高频面题
  • 【NLP 面试宝典 之 主动学习】 必须要会的高频面题
  • 【NLP 面试宝典 之 超参数优化】 必须要会的高频面题
  • 【NLP 面试宝典 之 正则化】 必须要会的高频面题
  • 【NLP 面试宝典 之 过拟合】 必须要会的高频面题
  • 【NLP 面试宝典 之 Dropout】 必须要会的高频面题
  • 【NLP 面试宝典 之 EarlyStopping】 必须要会的高频面题
  • 【NLP 面试宝典 之 标签平滑】 必须要会的高频面题
  • 【NLP 面试宝典 之 Warm up 】 必须要会的高频面题
  • 【NLP 面试宝典 之 置信学习】 必须要会的高频面题
  • 【NLP 面试宝典 之 伪标签】 必须要会的高频面题
  • 【NLP 面试宝典 之 类别不均衡问题】 必须要会的高频面题
  • 【NLP 面试宝典 之 交叉验证】 必须要会的高频面题
  • 【NLP 面试宝典 之 词嵌入】 必须要会的高频面题
  • 【NLP 面试宝典 之 One-Hot】 必须要会的高频面题
BERT 模型面
  • 【NLP 面试宝典 之 BERT模型】 必须要会的高频面题
  • 【NLP 面试宝典 之 BERT变体】 必须要会的高频面题
  • 【NLP 面试宝典 之 BERT应用】 必须要会的高频面题

LLMs 微调面

  • 【NLP 面试宝典 之 LoRA微调】 必须要会的高频面题
  • 【NLP 面试宝典 之 Prompt】 必须要会的高频面题
  • 【NLP 面试宝典 之 提示学习微调】 必须要会的高频面题
  • 【NLP 面试宝典 之 PEFT微调】 必须要会的高频面题
  • 【NLP 面试宝典 之 Chain-of-Thought微调】 必须要会的高频面题
  • ......
    

http://www.ppmy.cn/embedded/153094.html

相关文章

Spring Boot项目中增加MQTT对接

在Spring Boot项目中增加MQTT对接,通常涉及以下几个步骤: 一、搭建MQTT服务器 首先,你需要搭建一个MQTT服务器(Broker)。这可以通过多种方式实现,例如使用Docker来部署EMQX或Mosquitto等MQTT Broker。 以…

javaEE初阶————多线程初阶(1)

多线程初阶———— 1,认识线程 1.1 概念 1)线程是什么 线程就是一个“执行流”,可以理解为程序执行的最小单位; 可以看成轻量级的进程; 2)为啥要有线程 “并发编程” 的需要,但是我们不…

一个基于Spring Boot的简单网吧管理系统

一个基于Spring Boot的简单网吧管理系统的案例代码。这个系统包括用户管理、电脑管理、上机记录管理等功能。代码结构清晰,适合初学者学习和参考。 1. 项目结构 src/main/java/com/example/netbarmanagement├── controller│ ├── ComputerController.jav…

Data/Code/Algorithm

前段时间很久没更在忙考研的事情,现在初试过了也算是一阶段的学习尘埃落定 有时间和心力和探索更多未知的世界和新奇的领域 我在小红书上刷到帖子推荐了几部计算机专业学生必看的纪录片 个人认为这是一个了解计算机新领域的好时机,所以最近陆陆续续也…

awr报告无法生成:常见案例与解决办法

awr报告无法生成:常见案例与解决办法 STATISTICS_LEVEL设置过低数据库打开状态不对主库隐含参数设置错误MMON子进程被SuspendSYS模式统计信息过期WRH$_SQL_PLAN表数据量太大AWR绑定变量信息收集超时撞上数据库Bug 9040676STATISTICS_LEVEL设置过低 STATISTICS_LEVEL设置为BAS…

React函数组件中与生命周期相关Hooks详解

React 函数组件及其钩子渲染流程是 React 框架中的核心概念之一。以下是对该流程的详细解析: 一、React 函数组件基础 定义: React 函数组件是一个接收 props 作为参数并返回 React 元素的函数。它们通常用于表示 UI 的一部分,并且不保留内部…

从零用java实现 小红书 springboot vue uniapp (8)个人资料修改 消息页优化

前言 移动端演示 http://8.146.211.120:8081/#/ 前面的文章我们主要完成了点赞关注 im 聊天功能 下面我们将完善个人资料修改 和消息页优化 向产品迈进 个人资料修改 自定义头像背景图 以及网名等等修改之后个人资料卡片也会随之改变 这样看起来就美观多了 修改文字资料的话很…

uni-app持久化登录简单实现

想要实现持久化登录,原理就是在每次进入应用的时候获取上一次用户登录的信息。 那么就好办了,我们在每次登录成功后把用户的账号密码存储到本地,然后在进入应用的时候读取本地文件获取账号密码重新执行登录流程,在退出登录的时候删…