【LLM模型微调】LLMs-PEFT[微调]-QLoRA总结笔记v6.0

学术   2024-12-28 12:11   江苏  

【导读】:本文是LLM模型微调第六篇,分享论文QLoRA: Efficient Finetuning of Quantized LLMs的解读。主要内容有论文解读(提出背景、技术原理,细节补充...),实验效果,细节解析 和代码详解。


QLoRA相关


【#】LoRA相关论文及文档

QLoRA论文:QLoRA: Efficient Finetuning of Quantized LLMs

论文地址:https://arxiv.org/abs/2305.14314

Github地址:https://github.com/artidoro/qlora

HF地址:https://huggingface.co/timdettmers

QLoRA oral presentation at NeurIPS 2023:

https://neurips.cc/media/neurips-2023/Slides/73855.pdf

QLora训练更大的GPT文档:

https://readpaper.feishu.cn/docx/CrMGdSVPKow5d1x1XQMcJioRnQe

8bit量化:https://huggingface.co/blog/zh/hf-bitsandbytes-integration

4bit量化:https://huggingface.co/blog/zh/4bit-transformers-bitsandbytes

PEFT: https://https://github.com/huggingface/peft


【#】QLoRA-文章目录

QLoRA-论文解读
【1】QLoRA的提出背景~ 【2】QLoRA的技术原理
【3】QLoRA的细节补充-4-bit NormalFloat(NF4)
【4】QLoRA的细节补充-Double Quantization -双重量化

QLoRA-实验效果
【1】QLoRA的实验结果QLoRA vs. Standard Finetuning
【2】QLoRA的实验结果Pushing the Chatbot State-of-the-art with QLoRA
【3】QLoRA vs. Standard Finetuning实验细节

QLoRA-细节解析
【1】QLoRA微调经验碎碎念
【2】💡LoRA微调大模型的实践经验总结

QLoRA-代码详解
【1】BitsAndBytesConfig参数详解
【2】QLoRA-bnb-4bit-training.ipynb
【3】QLoRA-权重合并推理
【4】量化分位数如何计算的



QLoRA-论文解读


【1】QLoRA的提出背景~

Quantization(量化):模型的低精度表示量化是将输入从 存储更多信息的表征 映射为 存储较少信息的表征的过程,如将FP32的数据转化为INT8,能够节省大量的内存。
全局量化方式存在问题,即当输入中存在极大值或者离群值时,一些较小的参数无法被精确的表示,因此量化后的神经网络效果会下降很多。为了缓解这个问题,作者采用了分块量化,即将输入划分为多个block,每个block分别量化。
Block-wise k-bit Quantization(分块k位量化)将float32量化为int8时,使用常数c缩小范围,但outliner会影响c的选取。Block-wise方法通过每批次独立选择c(本文中为64个数据)来解决此问题。

全局量化和分块量化示意图
从图中可以看到,分块量化能够明显减少量化过程中的误差(0.64 -> 0.241)。
Low-rank Adapters:LoRA微调是通过使用少量可训练参数来降低内存需求的方法,同时不更新完整模型中保持固定的参数。在随机梯度下降过程中,梯度通过固定的预训练模型权重传递到适配器,适配器被更新以优化损失函数。

对比LORA和QLORA:


【2】QLoRA的技术原理

QLORA训练过程跟LORA基本上是一致的。区别在于QLORA模型是按照NF4保存的,训练时需要把参数反量化到bf16后进行训练。QLORA 有低精度存储数据类型(NF4)+计算数据类型(BFloat16)。
QLoRA使用以下技术—NF4量化 +双量化[实现高效4bit微调] +分页优化器[防止内存不足]。具体说明如下:
  • 4bit NormalFloat(NF4)4-bit NormlFLoat量化是对Quantile Quantization(分位量化)进行了改进,并结合Block-wise Quantization,降低计算复杂度和误差。即提出基于分块(Block-wise)的分位数量化(Quantile Quantization)的量化策略;
  • 双重量化(Double Quantization:是针对量化常数的二次量化。由于BnB的量化是块量化(block-wise),因此块级别的常数存储也会占用GPU memory。对第一次量化后的那些常量再进行一次量化,减少存储空间。
  • 分页优化器(Paged Optimizers):为防止梯度检查点所引起的内存波动,导致的内存不足错误。使用NVIDIA统一内存特性,该特性可以在在GPU偶尔OOM的情况下,进行CPU和GPU之间自动的页面切换,以实现无错误的 GPU 处理。使用此功能为优化器状态(Optimizer)分配分页内存,然后在 GPU 内存不足时将其自动卸载到 CPU 内存,并在优化器更新步骤需要时将其加载回 GPU 内存。
All-Linear-Layer-Adapter:QLoRA在所有全连接层都插入 LoRA Adapter,增加训练参数,弥补精度带来的性能损失,能匹配16位全参数微调的性能。在LoRA中,一般会选择在query和value的全连接层处插入adapter。


【3】QLoRA的细节补充-4-bit NormalFloat(NF4)

QLORA引入多项创新来节省内存而不牺牲性能:(a) 4位NormalFloat (NF4) 数据类型,理论上最优用于正态分布权重 (b) 双重量化,通过量化量化常数来减少平均内存占用 (c) 分页优化器来管理内存峰值。


分块k位量化的量化和反量化的过程

作者提出的4-bit NormlFLoat量化是对Quantile Quantization(分位量化)进行了改进,并结合Block-wise Quantization,降低计算复杂度和误差。

【1】NF4量化

【2】Quantile Quantization 分位量化

全局量化后参数整体的分布与原始的分布差别很大,例如出现极大outliner值,可能到大部分参数都会量化到0,这样效果就会下降很明显。
为了避免这种情况,作者采用分位量化(Quantile Quantization),可以先将输入参数从小到大进行排序再等分为16份,每一份映射一个值。这种分位量化方法量化出的参数就能保证分布尽可能与原始分布相差不大。

【3】NormFloat-NFK

(NormFloat在信息论上对于以零为中心的正态分布数据是最优的。)

上述 分位量化 会额外引入明显的计算开销,因为每次有参数输入进来都需要对齐进行排序并等分。
作者发现预训练的参数基本上都服从均值为0的正态分布,可以将其缩放到[-1, 1]的范围中。
同时可以将正态分N(0,1)划分为2 +1份,并缩放到[-1, 1]的范围中。
这样就能直接将参数映射到对应的分位,不用每次都对参数进行排序。
但是这样做饭也会有一个缺点,参数0量化后可能不在0的位置上了,就没法表达0的特殊意义了。
为处理零点问题,做了以下改进,即分别将负数和整数部分划分2¹,参数0还是放在0原本的位置上。


【4】QLoRA的细节补充-Double Quantization -双重量化

Double Quantization. 量化过程中,每组被量化的参数会被记录一个absmax(绝对值最大值),用于在计算时将参数反量化回原始值。这个absmax是高精度保存的,所以也会占用相当大的显存。因此,QLoRA采用与参数量化相似的策略,将这些absmax值也量化成4 bit,进一步降低显存占用。
QLORA 将每 64 个参数为做一个 block,即 block size=64,每个 block计算一个 Scale。由于量化后的 Scale 通常以 FP32 存储,在 block 数众多的情况下,Scale 占用的显存也不可忽视。因此,QLORA对Scale 进一步量化成FP8,取 Double Quant的 block size=256,因而进一步降低了显存消耗。
  • Double Quant 前,每个参数做量化会需要额外的32/64=0.5 bits 显存;

  • Double Quant后,每个参数做量化只需要额外的8/64+32/(64*256)= 0.127 bits 显存



QLoRA-实验效果


【1】QLoRA的实验结果QLoRA vs. Standard Finetuning

【4位NormalFloat比4位浮点数表现更好】在语言模型和零样本任务的评估中,4-bit NormalFloat数据类型(NF4)表现出更好的性能,优于4-bit浮点数(FP4)。如上表所示:研究采用了不同类型的量化LLMs(OPT,BLOOM,Pythia,LLaMA)以及不同大小(125M到65B),比较NF4、FP4和Int4的性能,并发现NF4能够显著提升性能,双重量化可以减少内存占用同时不影响性能。




【k位QLoRA匹配16位全微调和16位LoRA性能】比较RoBERTA和T5模型在GLUE和Super-NaturalInstructions数据集上结果表明:无论是使用16位、8位还是4位的适配器方法,都能够复制全16位微调的基准的性能。这说明,尽管量化过程中会存在性能损失,但通过适配器微调,完全可以恢复由于量化不准确而丢失这些性能。


【具有双重量化的NF4】测试4位QLoRA是否可以在7B到65B参数范围内匹配16位LoRA。为此,在两个指令跟随数据集Alpaca和FLAN v2上对LLaMA 7B至65B进行了微调,并在MMLU基准测试上通过5点精度进行了评估。结果上图所示,其中看到具有双重量化的NF4完全恢复了16位LoRA在MMLU性能。与FP4相比,QLoRA性能落后于16位Brain Float LoRA基准约1个百分点。这证实我们的发现:(1)QLoRA与NF4能够复制16位全微调和16位LoRA微调的性能,以及(2)NF4在量化精度方面优于FP4。



【2】QLoRA的实验结果Pushing the Chatbot State-of-the-art with QLoRA

使用MMLU(Massively Multitask Language Understanding)基准 来测量模型在一系列语言理解任务上的性能。报告5-shot测试准确率。


还通过自动化和人工评估来测试生成语言能力。使用nucleus采样(p=0.9)和温度0.7进行所有测试。

基于QLoRA调优方法,作者对OASST1进行调优得到系列模型Guanaco。并基于自动化和人工评估,发现QLoRA优化的顶级模型Guanaco 65B是性能最好的开源聊天机器人模型,其性能与ChatGPT相比具有竞争力。与GPT-4相比,Guanaco 65B和33B的预期获胜概率为30%。在Vicuna基准相对于ChatGPT的结果如下图所示。我们发现,Guanaco 65B是继GPT-4之后性能最好的型号,相对于ChatGPT实现了99.3%的性能。Guanaco 33B比Vicuna 13B型号有更多的参数,但其权重仅使用4位精度,因此在21 GB和26 GB时的存储效率要高得多,比Vicuna13B提高了三个百分点。此外,Guanaco 7B以5GB的容量轻松安装在手机上,同时仍比Alpaca 13B高出近20个百分点。


【#】QLoRA vs. Standard Finetuning实验细节



QLoRA-细节解析


【1】QLoRA微调经验碎碎念

介绍一下lora的原理和ptuning的原理。P-tuning与Lora有什么区别;

Lora方法的核心是在大型语言模型上对指定参数增加额外的低秩矩阵,也就是在原始PLM旁边增加一个旁路,做一个降维再升维的操作。并在模型训练过程中,固定PLM的参数,只训练降维矩阵A与升维矩阵B。

ptuning方法的核心是使用可微的virtual token替换了原来的discrete tokens,且仅加入到输入层,并使用prompt encoder(BiLSTM+MLP)对virtual token进行编码学习。


LoRA和全参数训练在计算量和显存上相比如何?为什么LoRA能提升大模型训练效率?

1、计算量上:LoRA训练时,在主干模型的(部分)全连接层增加了LoRA旁路,前向和后向的计算量都在主干模型的基础上,增加了旁路部分的计算,因此相比全参数训练,略有增加。

2、显存上:训练时,显存主要有①模型参数②梯度③中间激活值④优化器参数四个部分。模型参数/梯度/激活值相 比全参数训练也略微增加;而优化器则不需要再存储原模型参数的部分,只需要存储LoRA旁路部分,这部分节省较多显存。

3、使用LoRA能提升训练效率主要是因为(1)优化器部分的显存需要减少了,可以增大batch(2)优化器参数减少了,分布式训练中多卡之间的通信量减少了(3)(optional)主干模型由于不用更新,可以进一步量化到int8/int4等。

大模型算法题(2)【202403】

FP4 量化有硬件要求吗?(qlora使用NF4)FP4量化方法仅与 GPU 兼容,目前尚无法在 CPU 上对模型进行 4 比特量化。在 GPU 中,此方法没有任何硬件要求,只要安装了 CUDA>=11.2,任何 GPU 都可以用于运行 4 比特量化。另请记住,计算不是以 4 比特完成的,仅仅是权重和激活被压缩为该格式,而计算仍在指定的或者原始数据类型上进行。


可以训练 4 比特 / 8 比特模型吗?对这些模型进行全模型 4 比特训练是不可能的。但是,可以利用参数高效微调 (PEFT) 来训练这些模型,即在基础模型之上训练新增部分如适配器。QLoRA 论文就是这么做的,Hugging Face 的 PEFT 库也正式支持了该方法。


QLoRA 量化调用流程

  • PreTrainedModel.from_pretrained 调用了 bitsandbytes.py 的 replace_with_bnb_linear,将 nn.Linear 层替换成量化层。

  • 随后需要加载模型权重,调用函数 _load_pretrained_model -> _load_state_dict_into_meta_model -> set_module_quantized_tensor_to_device

  • set_module_quantized_tensor_to_device 在 bitsandbytes.py 中,用于将权重转化为 int4 量化权重,并更新量化层信息。

  • new_value = bnb.nn.Params4bit(new_value, requires_grad=False, **kwargs).to(device)
  • 转化过程在 Params4bit.cuda 中实现,它调用了 bnb.functional.quantize_4bit 函数,这个函数再调用 lib 的一个函数 lib.cquantize_blockwise_fp16_nf4,所以核心计算过程都在 cuda 上完成。

  • 在 kernel.cu 找到对应的函数 kQuantizeBlockwise,它用了 cub 做 block 内部的通信。

  • 最终,每个参数的量化交给 dQuantizeNF4 这个函数完成,而这个函数就是依照输入数值的区间赋值的,简单粗暴。

https://zhuanlan.zhihu.com/p/646235855

LoRA与其他微调大模型对比优点

Adapter Tuning 增加了模型层数,Adapter引入了额外的推理延迟(只能串行)

Prefix-Tuning 难于训练,且预留给 Prompt 的序列挤占了下游任务的输入序列空间,影响模型性能。

P-tuning v2 很容易导致旧知识遗忘,微调之后的模型,在之前的问题上表现明显变差。要占用context length,变相的降低模型能力。


【2】💡LoRA微调大模型的实践经验总结

LoRA的一致性:尽管训练模型通常具有随机性,但是多次进行LoRA微调的实验结果在测试集上的效果十分稳定。

QLoRA的计算与内存权衡:QLoRA是一种在微调时进一步降低内存使用的技术,通过将预训练的权重量化为4位精度,并使用分页优化器来处理内存峰值。QLoRA可以节省33%的GPU内存,但增加了39%的训练时间。

  • 默认的16位浮点LoRA:训练时间:1.85小时,内存使用:21.33GB

  • 这里的4位浮点QLoRA:训练时间:2.79小时,内存使用:14.18GB

学习率调度器:文章讨论了余弦退火学习率调度器如何调整学习率,模仿余弦曲线逐渐减少学习率以优化收敛并避免过度拟合。实验中引入这种调度器显著改善了SGD性能,但对Adam和AdamW优化器影响较小。

Adam与SGD的比较:在7B参数的Llama 2模型训练中,使用AdamW和LoRA的默认设置(r=8)需要14.18GB的GPU内存,而使用SGD需要14.15GB的内存,节省非常有限。

多次训练epoch:对于50k样本的Alpaca指令微调数据集,增加训练迭代次数后,模型性能出现下降,这表明多轮训练可能不适用于指令微调,因为可能会导致过拟合。

为更多层启用LoRA:实验显示,如果为更多层启用LoRA,虽然内存需求从14.18GB增加到16.62GB,但模型性能有显著提升。

平衡LoRA超参数R和Alpha:文章讨论了LoRA权重的缩放系数,发现虽然一般来说是较好的选择,但在某些情况下,不同的组合可能会产生更好的性能。

在单GPU上训练7B参数模型:LoRA技术使得在单个GPU上微调7B参数的模型成为可能。使用QLoRA最优设置(r=256和alpha=512)时,训练一个7B参数模型在A100 GPU上需要大约3小时。



QLoRA-代码详解

【1】BitsAndBytesConfig参数详解

使用bnb加载nf4量化的模型
from transformers import BitsAndBytesConfig
nf4_config = BitsAndBytesConfig(
   load_in_4bit=True,
   bnb_4bit_quant_type="nf4",
   bnb_4bit_use_double_quant=True,
   bnb_4bit_compute_dtype=torch.bfloat16
)
model_nf4 = AutoModelForCausalLM.from_pretrained(
            model_id,
            quantization_config=nf4_config
)


load_in_4bit=True: 指定在加载模型时使用4位量化。这意味着模型的权重将被量化为4位,以减少内存使用和提高计算效率。
bnb_4bit_quant_type="nf4": 指定量化的类型为"nf4"。NF4是一个特定的量化方法,可能指代某种压缩和优化的策略。
bnb_4bit_use_double_quant=True: 指定是否使用双重量化。双重量化是一种在量化过程中进一步优化的技术,可以提供更高的模型精度或更低的计算开销。
bnb_4bit_compute_dtype=torch.bfloat16: 指定计算的数据类型为bfloat16。bfloat16是一种半精度浮点格式,可以在保持计算精度的同时减少内存使用。


【2】QLoRA-bnb-4bit-training.ipynb

在消费级GPU上微调大型语言模型


!pip install -q -U bitsandbytes
!pip install -q -U git+https://github.com/huggingface/transformers.git 
!pip install -q -U git+https://github.com/huggingface/peft.git
!pip install -q -U git+https://github.com/huggingface/accelerate.git
!pip install -q datasets
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
model_id = "EleutherAI/gpt-neox-20b"
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(model_id)

#加载model 使用qlora
model = AutoModelForCausalLM.from_pretrained(model_id,
        quantization_config=bnb_config, device_map={"":0})

from peft import prepare_model_for_kbit_training
model.gradient_checkpointing_enable()
model = prepare_model_for_kbit_training(model)

def print_trainable_parameters(model):
    """
    Prints the number of trainable parameters in the model.
    """

    trainable_params = 0
    all_param = 0
    for _, param in model.named_parameters():
        all_param += param.numel()
        if param.requires_grad:
            trainable_params += param.numel()
    print(
        f"trainable params: {trainable_params} || all params: {all_param} || trainable%: {100 * trainable_params / all_param}"
    )
from peft import LoraConfig, get_peft_model
config = LoraConfig(
    r=8, 
    lora_alpha=32, 
    target_modules=["query_key_value"], 
    lora_dropout=0.05, 
    bias="none"
    task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)
print_trainable_parameters(model)
from datasets import load_dataset
data = load_dataset("Abirate/english_quotes")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)
import transformers
# needed for gpt-neo-x tokenizer
tokenizer.pad_token = tokenizer.eos_token
trainer = transformers.Trainer(
    model=model,
    train_dataset=data["train"],
    args=transformers.TrainingArguments(
        per_device_train_batch_size=1,
        gradient_accumulation_steps=4,
        warmup_steps=2,
        max_steps=10,
        learning_rate=2e-4,
        fp16=True,
        logging_steps=1,
        output_dir="outputs",
        optim="paged_adamw_8bit"
    ),
    data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
model.config.use_cache = False  # silence the warnings. Please re-enable for inference!
trainer.train()

基于QLoRa+Transformer的推理

 text = "Ask not what your country"
 device = "cuda:0"
 inputs = tokenizer(text, return_tensors="pt").to(device)
 
 outputs = model.generate(**inputs, max_new_tokens=20)
 print(tokenizer.decode(outputs[0], skip_special_tokens=True))


【推荐阅读】QLoRa:在消费级GPU上微调大型语言模型

https://colab.research.google.com/drive/1VoYNfYDKcKRQRor98Zbf2-9VQTtGJ24k?usp=sharing


【3】QLoRA-权重合并推理

模型权重合并脚本:export_hf_checkpoint.py,将lora权重合并回原始权重。

import os
import torch
import transformers
from peft import PeftModel
from transformers import LlamaForCausalLM, LlamaTokenizer  # noqa: F402
BASE_MODEL = os.environ.get("BASE_MODEL", None)
LORA_MODEL = os.environ.get("LORA_MODEL""tloen/alpaca-lora-7b")
HF_CHECKPOINT = os.environ.get("HF_CHECKPOINT""./hf_ckpt")
assert (
    BASE_MODEL
), "Please specify a value for BASE_MODEL environment variable, e.g. `export BASE_MODEL=decapoda-research/llama-7b-hf`"  # noqa: E501
tokenizer = LlamaTokenizer.from_pretrained(BASE_MODEL)

base_model = LlamaForCausalLM.from_pretrained(
    BASE_MODEL,
    #load_in_8bit=False,
    torch_dtype=torch.bfloat16,
    device_map={"""cpu"},
)
first_weight = base_model.model.layers[0].self_attn.q_proj.weight
first_weight_old = first_weight.clone()

lora_model = PeftModel.from_pretrained(
    base_model,
    # TODO
    # "tloen/alpaca-lora-7b",
    LORA_MODEL,
    #device_map={"": "cpu"},
    #torch_dtype=torch.float16,
)
lora_weight = lora_model.base_model.model.model.layers[0].self_attn.q_proj.weight
assert torch.allclose(first_weight_old, first_weight)

# merge weights
for layer in lora_model.base_model.model.model.layers:
    layer.self_attn.q_proj.merge_weights = True
    layer.self_attn.v_proj.merge_weights = True
lora_model.train(False)
# did we do anything?
#assert not torch.allclose(first_weight_old, first_weight)
lora_model_sd = lora_model.state_dict()

deloreanized_sd = {
    k.replace("base_model.model."""): v
    for k, v in lora_model_sd.items()
    if "lora" not in k
}

LlamaForCausalLM.save_pretrained(
    base_model, HF_CHECKPOINT , state_dict=deloreanized_sd, max_shard_size="400MB"

推理脚本:inference.py

from transformers import AutoModelForCausalLM, LlamaTokenizer
import torch
model_id = "/data/nfs/guodong.li/pretrain/hf-llama-model/llama-7b"
merge_model_id = "/home/guodong.li/output/llama-7b-merge"
#model = AutoModelForCausalLM.from_pretrained(model_id, load_in_4bit=True)
model = AutoModelForCausalLM.from_pretrained(merge_model_id, load_in_4bit=True, device_map="auto")
tokenizer = LlamaTokenizer.from_pretrained(model_id)
#print(model)
device = torch.device("cuda:0")
#model = model.to(device)
text = "Hello, my name is "
inputs = tokenizer(text, return_tensors="pt").to(device)
outputs = model.generate(**inputs, max_new_tokens=20, do_sample=True, top_k=30, top_p=0.85)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
print("\n------------------------------------------------\nInput: ")
line = input()
while line:
    inputs = tokenizer(line, return_tensors="pt").to(device)
    outputs = model.generate(**inputs, max_new_tokens=20, do_sample=True, top_k=30, top_p=0.85)
    print("Output: ",tokenizer.decode(outputs[0], skip_special_tokens=True))
    print("\n------------------------------------------------\nInput: ")
    line = input()

【4】量化分位数如何计算的

Quantile Quantization 分位量化的量化分位数如何计算的。其中核心代码片段摘抄如下。

https://github.com/TimDettmers/bitsandbytes/blob/main/bitsandbytes/functional.py#L236

from scipy.stats import norm
import torch

def create_normal_map(offset=0.9677083, use_extra_value=True):

    if use_extra_value:
        # one more positive value, this is an asymmetric type
        v1 = norm.ppf(torch.linspace(offset, 0.5, 9)[:-1]).tolist() # 正数部分
        v2 = [0]*(256-15) ## we have 15 non-zero values in this data type
        v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() #负数部分
        v = v1 + v2 + v3
    else:
        v1 = norm.ppf(torch.linspace(offset, 0.5, 8)[:-1]).tolist()
        v2 = [0]*(256-14) ## we have 14 non-zero values in this data type
        v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist()
        v = v1 + v2 + v3

    values = torch.Tensor(v)
    values = values.sort().values
    values /= values.max()
    assert values.numel() == 256
    return values

  Q = create_normal_map()

函数create_normal_map有两个入参:offset和use_extra_value。其中offset的作用是确定分位数的始末值。use_extra_value用来控制是使用对称量化还是非对称量化。

函数体内部有两个核心功能,其中if...else...部分是用来计算分位数。其中v1计算正数部分,v3计算负数部分。v2直接将0映射到0,并且根据要量化的单位计算0的个数。

源码是使用NF4来表示8比特的量化,如果是使用4比特的量化,我们将计算v2的256改成16就行。接下来最后几行用来将量化值归一化到[1,1] 。


这里有个疑问,offset的默认值为什么是0.9677083,作者解释,具体可以看[6]给出的解释。https://readpaper.feishu.cn/docx/CrMGdSVPKow5d1x1XQMcJioRnQe


·EN
 ■ ■ 

进技术交流群请添加AINLP小助手微信(id: ainlp2)

请备注具体方向+所用到的相关技术点

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括LLM、预训练模型、自动生成、文本摘要、智能问答、聊天机器人、机器翻译、知识图谱、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP小助手微信(id:ainlp2),备注工作/研究方向+加群目的。


AINLP
一个有趣有AI的自然语言处理公众号:关注AI、NLP、大模型LLM、机器学习、推荐系统、计算广告等相关技术。公众号可直接对话双语聊天机器人,尝试对对联、作诗机、藏头诗生成器、自动写作等,查询相似词,测试NLP相关工具包。
 最新文章