Phi-3微调与量化

文摘   2024-07-27 20:56   新加坡  

本文涉及到的详细测试代码和测试步骤放置于:

https://github.com/davidsajare/david-share.git

下的:Deep-Learning/Phi3-Fine-Tuning

欢迎给repo点亮Star,您的点赞是作者持续创作的动力。


Phi3的微调与量化和Phi2在方法本质上没有太大的不同。但细节方面有小的差别,本文进行讨论。此外本文还会讨论Templates for Chat Models。

一、关于量化

一个神经网络模型,可以从静态部分和运行时状态两个角度来看。

静态部分

  1. 模型权重文件 (model_weights.h5 或 model_weights.pt): 存储了模型的权重和偏置,这些是在训练过程中通过优化算法学习得到的。

    在加载模型以后,模型权重文件会被读取并加载到内存中,成为模型的权重参数。这些权重参数是模型在进行推理或训练时所使用的具体数值。具体来说,模型权重文件在加载后会经历以下几个步骤:
    读取文件: 模型权重文件(如 .h5 或 .pt 文件)会被读取到内存中。解析和解码: 文件中的二进制数据会被解析和解码成模型的权重矩阵和偏置向量。赋值给模型: 解析后的权重和偏置会被赋值给模型的各个层和节点,初始化模型的参数。
  2. 配置文件 (config.json): 定义了模型的架构,包括层数、每层的类型(如卷积层、全连接层等)、层的参数(如过滤器大小、步长等)。

  3. 词汇表文件 (vocab.txt): 在处理文本数据时使用,存储了词汇和它们的索引,用于将文本转换为模型可以处理的数值形式。

  4. 附加工具和脚本 (preprocess.py, postprocess.py): 用于数据的预处理和后处理,如图像的大小调整、归一化,或文本的分词等。

  5. 训练/微调脚本 (train.py, finetune.py): 包含用于训练或微调模型的代码,这些脚本定义了训练过程,包括损失函数的选择、优化器的配置和训练周期的设置。

运行时部分

  1. 输入(Input) (input_data): 模型接收的原始数据,可以是各种形式,如图片、文本或音频等。

  2. 激活(Activation) (activations): 每一层通过激活函数处理后的输出值,这些值作为下一层的输入。

  3. 中间状态(Intermediate States) (layer_outputs): 每一层可能产生的其他形式的输出,如卷积层中的特征图。

  4. 梯度(Gradients) (gradients): 在训练过程中,通过反向传播算法计算得到的每个参数的梯度。

  5. 损失值(Loss Value) (loss_value): 损失函数计算得到的当前模型输出与真实标签之间的差异值。

  6. 状态更新(State Updates) (weight_updates, optimizer_states): 根据梯度和学习率更新的权重,以及优化器维护的状态(如动量和自适应学习率信息)。

  7. 缓存(Caches) (forward_cache): 在前向传播过程中缓存的某些值,用于在反向传播中有效地计算梯度。

    这些组成部分共同确保了模型在训练和推理过程中的正确运行和性能表现。静态部分定义了模型的结构和初始状态,而运行时部分则涉及到模型在处理数据时的动态行为和状态更新。

量化方法

从一个较高层次的分类来看,模型量化的手段通常可以分为两大类:量化感知训练(Quantization-Aware Training, QAT)和后训练量化(Post-Training Quantization, PTQ)。

  1. 量化感知训练(QAT): 在模型训练过程中模拟量化的效果,使模型在训练时就考虑到量化带来的误差。通常能够产生更高精度的量化模型,因为模型参数在训练时就适应了量化的约束。

  2. 后训练量化(PTQ): 在模型训练完成后应用的量化方法,不需要重新训练模型。

    LLM 有三种流行的量化方法:GPTQ、bitsandbytes 和 AWQ。这几种都属于后训练量化(PTQ)。

    在深度学习模型的量化中,主要关注的是模型的运行时部分,尤其是权重和激活值的量化。这是因为量化主要目的是减少模型在推理时的计算复杂度和内存使用,从而加速模型的执行并降低功耗,这对于部署在资源受限的设备上尤为重要。

量化的焦点

  1. 权重量化: 权重是模型的静态部分,存储在模型文件中。权重量化涉及将浮点数权重转换为低精度(如 int8 或 int16)格式。这一步是在模型部署前进行的,可以显著减少模型大小和加速模型的加载时间。

  2. 激活量化: 激活值是模型运行时产生的,代表层与层之间传递的数据。激活量化通常发生在模型推理过程中,即动态量化。这有助于减少运行时的计算需求和内存占用。

静态文件与运行时量化

静态文件(如模型权重文件)的量化主要是权重的量化,这是在模型部署前完成的,目的是减少模型文件的大小。运行时量化则涉及到激活值的量化,这是在模型执行时进行的,目的是优化推理性能。

总结来说,量化既涉及到模型的静态部分(如权重),也涉及到模型的运行时部分(如激活值)。这两者的量化都是为了优化模型的存储和执行效率,尤其是在资源受限的环境中.


量化方法的具体实现: BitsandBytes主要量化部分: 主要量化模型的权重部分。特点: 使用一种特殊的4位数据类型,称为 NormalFloat (NF),来实现权重的量化。这种方法专注于减少模型权重的存储和计算需求,从而优化模型的大小和推理速度,同时尽量保持模型的性能。
GPTQ (Gradient-based Post-Training Quantization)主要量化部分: 主要量化模型的权重部分,但也可能涉及到激活的量化,尤其是在更低位宽的设置中。特点: 提供了更多的灵活性,支持将模型的精度降低到8位、4位、3位,甚至2位。这种方法利用梯度信息来优化量化过程,以减少量化对模型性能的影响。
AWQ (Activation-aware Weight Quantization)量化类型: 动态量化量化部分: 主要量化权重。特点:激活感知: AWQ 在量化权重时考虑了激活值的分布和特性。这意味着在量化过程中,AWQ 会根据激活值的统计特性自适应地调整量化参数,从而减少量化对模型性能的负面影响。自适应量化: AWQ 根据权重的统计特性自适应地调整量化参数,这可以在不同的训练阶段或不同的数据批次中动态变化。训练过程中的量化: 与静态量化不同,AWQ 可以在模型训练过程中应用,允许模型在考虑量化效应的情况下进行优化。
QAT (Quantization-Aware Training)量化类型: 混合量化量化部分: 同时量化权重和激活。特点:训练时模拟量化: QAT 在整个训练过程中模拟量化的效果,这有助于训练出在量化后依然保持高性能的模型。减少量化带来的精度损失: 通过在训练中考虑量化的影响,QAT 能够显著减少量化对模型精度的负面影响。
总结来说,BitsandBytes 和 GPTQ 都主要关注于模型权重的量化,这是因为权重量化通常可以带来显著的模型大小减少和计算效率提升,而对模型性能的影响相对较小。这两种方法都旨在通过优化权重的存储和计算来提高模型的部署效率和运行速度。AWQ 则在量化权重时考虑了激活值的分布和特性,从而进一步减少量化对模型性能的负面影响。


在各种主流的量化技术中,目前AWQ量化的精度是比较高的(相比于GPTQ,bnb等),量化后的模型推理速度也快。

代码中的AWQ的量化参数是:quant_config = { "zero_point": True, "q_group_size": 128, "w_bit": 4, "version": "GEMM" }  

quant_config 是一个字典,包含了用于量化模型的配置参数。以下是每个参数的详细解释:

1. zero_point

  • 类型: 布尔值 (True 或 False)

  • 解释:

    • 这个参数决定是否在量化过程中使用零点。零点是量化过程中用于对齐浮点数和整数表示的一个偏移量。

    • 好处: 使用零点可以提高量化的精度,特别是在处理具有负值的张量时。

    • 开销: 使用零点会增加一些计算复杂度,因为每次量化和反量化操作都需要考虑这个偏移量。

  • 其他选项:

    • True: 使用零点,提高精度。

    • False: 不使用零点,减少计算复杂度。

2. q_group_size

  • 类型: 整数

  • 解释:

    • 这个参数指定了量化过程中分组的大小。量化分组是指将权重或激活值分成多个小组,每个小组独立进行量化。

    • 好处: 较小的分组大小可以提高量化的灵活性和精度,但也会增加计算开销。128 是一个常见的分组大小,平衡了精度和计算效率。

    • 开销: 较小的分组大小会增加计算和存储开销,因为需要为每个小组存储和计算独立的量化参数。较大的分组大小则会降低这些开销,但可能会降低量化精度。

  • 其他选项:

    • 32: 提高精度和灵活性,但增加计算和存储开销。

    • 64: 平衡精度和计算开销。

    • 128: 常见的分组大小,平衡精度和计算效率。

    • 256: 减少计算和存储开销,但可能降低精度。

3. w_bit

  • 类型: 整数

  • 解释:

    • 这个参数指定了量化过程中使用的位宽(bit-width)。位宽决定了量化值的精度。

    • 好处: 较高的位宽可以提高量化的精度,但也会增加存储和计算的开销。4 位量化(即 w_bit=4)是一种常见的低位量化方法,可以显著减少模型大小和计算量,同时保持较高的精度。

    • 开销: 较高的位宽会增加存储和计算的开销,因为每个量化值需要更多的位来表示。较低的位宽(如 4 位或 2 位)可以显著减少模型大小和计算量,但可能会增加量化误差。

  • 其他选项:

    • 2: 极低的位宽,显著减少模型大小和计算量,但可能增加量化误差。

    • 4: 常见的低位量化方法,平衡精度和计算效率。

    • 8: 较高的位宽,提高精度,但增加存储和计算开销。

    • 16: 高位宽,最大化精度,但显著增加存储和计算开销。

4. version

  • 类型: 字符串

  • 解释:

    • 这个参数指定了量化算法的版本或实现方式。"GEMM" 通常指的是使用通用矩阵乘法(General Matrix Multiplication)优化的量化实现。

    • 好处: 使用优化的版本可以提高计算效率,特别是在硬件加速器上。

    • 开销: 不同版本的实现可能会有不同的硬件和软件要求,需要根据具体的应用场景和硬件平台进行选择。

  • 其他选项:

    • "GEMM": 使用通用矩阵乘法优化的量化实现,适用于大多数硬件平台。

    • "INT8": 使用 8 位整数优化的量化实现,适用于需要高效计算的场景。

    • "FP16": 使用 16 位浮点数优化的量化实现,适用于需要高精度的场景。

    • "Custom": 自定义量化实现,可以根据具体需求进行优化。


    例如,int4-awq-block-128,就是指的init4,q_group_size=128的量化结果。


在github repo中量化的执行结果:

几种量化后的推理代码在repo中都有,不再赘述。

二、关于微调

实际上,无论是LoRA,Q-LoRA,还是GA-LoRA,微调后的模型,相比与全微调,其精度损失都不会太大。

如果在LoRA,Q-LoRA中选:

如果你的GPU显存够大,想训练的更快,那使用LoRA。

如果你的GPU显存不够大,不介意训练的稍微慢一点,那使用Q-LoRA。

以下图为例:

From: https://github.com/davidsajare/david-share/tree/master/Deep-Learning/Memory-comsuption-in-Training-and-Inference

微调技术不复杂,复杂的是准备高质量的数据集。


大魏分享
https://github.com/davidsajare/david-share.git
 最新文章