您的位置:首页 > 财经 > 金融 > 欺诈文本分类检测(十):QLora量化微调

欺诈文本分类检测(十):QLora量化微调

2024/11/18 0:43:02 来源:https://blog.csdn.net/xiaojia1001/article/details/141863947  浏览:    关键词:欺诈文本分类检测(十):QLora量化微调

1. 引言

前文微调方法概览总结了微调的各种方法,并且在更前面两篇文章Lora单卡训练
和 lora单卡二次调优中已经尝试过用Lora进行微调,本文出于好奇准备尝试下用QLora进行微调的效果。

QLoRA是一种新的微调大型语言模型(LLM)的方法,它的特点是能在节省内存的同时保持推理性能。它的出现是为了应对大型模型微调时内存需求大,成本昂贵的问题。

工作原理:首先将LLM进行4位量化,从而显著减少模型的内存占用,接着使用低阶适配器(LoRA)方法对量化的LLM进行微调,因此,QLora可以看成是量化+Lora的结合体。

采用以下核心技术:

  • 4位量化,它创新性的引入了特殊的4位浮点数表示方法NF4(Normal Float 4-bit),使用非均匀量化来平衡数据范围与精度。
  • 双量化,一种对量化后常数再次进行量化的方法,每个参数可平均节省约 0.37 位。
  • 分页优化,使用具有 NVIDIA 统一内存的分页优化器,以避免具有长序列长度的小批量时出现内存峰值。

依赖库安装:

pip install -q -U bitsandbytes

bitsandbytes主要是针对llm和transformers模型提供了优化和量化模型的功能,专门为8位优化器、矩阵乘法和量化而设计,提供了像8位Adam/AdamW之类的函数。目标是通过8位操作实现高效的计算和内存使用从而使llm更易于访问。```

本文将用欺诈文本分类这个业务场景来测试下QLora进行量化微调的实际效果。

2. 训练过程

2.1 初始化

引入之前封装好的trainer.py脚本,定义模型路径和数据集路径,以及要使用的GPU设备。

%run trainer.py
traindata_path = '/data2/anti_fraud/dataset/train0819.jsonl'
evaldata_path = '/data2/anti_fraud/dataset/eval0819.jsonl'
model_path = '/data2/anti_fraud/models/modelscope/hub/Qwen/Qwen2-1___5B-Instruct'
output_path = '/data2/anti_fraud/models/Qwen2-1___5B-Instruct_ft_0830_2'
os.environ["CUDA_VISIBLE_DEVICES"] = "2"
device = 'cuda'
2.2 加载模型和数据集

由于QLora需要以量化的方式来加载模型,所以加载模型的方法需要作调整,这里的改动是引入BitsAndBytesConfig类构建一个量化配置quantization_config, 具体配置项释义:

  • load_in_4bit:决定了模型参数以4位量化格式加载,加载后的模型参数占用空间会比较小;
  • bnb_4bit_compute_dtype=bfloat16:决定了矩阵乘法的计算精度使用bfloat16,输入数据也会被转换成bfloat16位进行计算;
  • bnb_4bit_quant_type:指定量化数据类型nf4;
  • bnb_4bit_use_double_quant:是否启用双重量化;
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfigdef load_model(model_path, device='cuda'):tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False, trust_remote_code=True)model = AutoModelForCausalLM.from_pretrained(model_path,torch_dtype=torch.bfloat16,quantization_config=BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_compute_dtype=torch.bfloat16,bnb_4bit_use_double_quant=False, bnb_4bit_quant_type='nf4'),)return model, tokenizer

注1:普通的量化通常是将数值分成均匀的区间,比如,将0到1之间的数值分成16个区间,每个区间的宽度相同。而NF4则根据数据的分布情况,使用不均匀的区间来表示数值,这样可以更有效地表示模型中的重要数值,特别是那些频繁出现的数值。

注2:双重量化是指在已经量化的基础上再进行量化,第二次量化并不会改变位数本身(即仍然是4位),它的目的是通过更紧凑地表示数值,使得存储和计算更加高效。由于每一次量化都会引入一些量化误差,双重量化可能会带来更大的数值误差,所以一般只用于极端内存受限的情况下。

注3:之所以模型参数加载使用4位而计算时使用16位,是因为量化本身已经带来了误差,计算时需要采用更高的精度是为了减少量化误差带来的影响。

加载模型参数和token序列化器。

%%time
model, tokenizer = load_model(model_path, device)
model.device
    CPU times: user 1min 55s, sys: 3.96 s, total: 1min 59sWall time: 1min 52sdevice(type='cuda', index=0)

在这里插入图片描述

占用内存方面,量化加载的1.9GB相比于bfloat16加载的3.9GB减少了将近一半,模型加载时的显存优化比较明显。

加载耗时方面,使用量化方式加载模型的过程是比较慢的,耗时了1分52秒,中间涉及到将模型参数从高精度的浮点数(如 FP32)转换为低精度的 NF4 格式。与之对比,不带量化时基本是秒加载。

加载数据集,复用前文的方法。

train_dataset, eval_dataset = load_dataset(traindata_path, evaldata_path, tokenizer)
2.3 构建训练参数

训练参数:引入分页内存优化器来优化训练过程中的内存分配。

train_args = build_train_arguments(output_path)
train_args.optim="paged_adamw_32bit"   

paged_adamw_32bit 是一种优化器配置,它使用了分页内存管理和32位浮点数来优化训练过程,可以帮助你在训练大规模模型时更有效地管理内存和计算资源。

lora配置:同前文Lora训练一样使用大小为16的秩。

lora_config = build_loraconfig()
lora_config.lora_dropout = 0.2   
lora_config.r = 16
lora_config.lora_alpha = 32
2.4 开始训练

构造训练器开始训练。

trainer = build_trainer(model, tokenizer, train_args, lora_config, train_dataset, eval_dataset)
trainer.train()
StepTraining LossValidation Loss
1000.0433000.028851
2000.0413000.045375
3000.0156000.025569
4000.0287000.023149
5000.0244000.022869
6000.0309000.021145
7000.0196000.019462
8000.0189000.023610
9000.0188000.019515
10000.0174000.018651
11000.0187000.018088
12000.0120000.019770
13000.0124000.023283
14000.0166000.017231
15000.0118000.020865
16000.0120000.018183
17000.0080000.017868
18000.0114000.017251
19000.0172000.017052
20000.0158000.016834
21000.0074000.019656
22000.0101000.016112
23000.0063000.016171
24000.0072000.019825
25000.0046000.022892
26000.0064000.023701
27000.0031000.025771
    TrainOutput(global_step=2700, training_loss=0.028382157780299032, metrics={'train_runtime': 5695.8171, 'train_samples_per_second': 9.895, 'train_steps_per_second': 0.618, 'total_flos': 1.1883908308313702e+17, 'train_loss': 0.028382157780299032, 'epoch': 2.2998296422487225})

训练过程中观察内存变化:
在这里插入图片描述

训练时的显存占用相比非量化时并没有明显变化,基本上占满了24G显卡的显存。

推测原因可能是:QLoRA只是通过量化技术减少了模型参数加载时的显存占用,但训练时仍然会反量化为16位进行矩阵计算,尤其是前向和反向传播阶段,显存的主要消耗来自于激活值、梯度和优化器状态,模型参数仅仅是一小部分,这就导致真正训练过程中占用的显存相比非量化时并没有减少。

QLoRA 主要通过量化模型参数来减小显存占用,但在需要更大的 batch size 的场景下,其显存优化效果可能并不显著。

3. 评估测试

用验证损失最低的checkpoint-2200进行测评。

%run evaluate.py
checkpoint_path='/data2/anti_fraud/models/Qwen2-1___5B-Instruct_ft_0830_2/checkpoint-2200'
evaluate(model_path, checkpoint_path, evaldata_path, device, batch=True, debug=True)
run in batch mode, batch_size=8progress: 100%|██████████| 2348/2348 [03:19<00:00, 11.80it/s]tn:1145, fp:20, fn:167, tp:1016
precision: 0.9806949806949807, recall: 0.8588334742180896

这个训练结果和前文的召回率0.86区别不大,说明使用量化后的模型参数进行训练确实能保持和16位精度的参数训练几乎一样的效果。

小结:本文通过实际训练来测试QLora对于显存占用和推理性能方面的效果,在我们这个验证结果里,推理性能方面几乎可以保持同先前一样的效果,但显存占用只在加载时降低了不到1/2, 而在训练过程中相比于非量化时没有明显减少。原因可能是由于我们的模型太小,而训练时的批量相对较大,模型参数加载时所优化的这部分内存,与整个训练过程所需要的内存相比较小,所以内存优化的整体效果就不太明显。

基于QLora主要是通过减少模型参数所占用的显存这个原理出发,个人理解可能在参数更大的模型并且batch_size更小的训练时效果可能会比较显著。

相关阅读

  • 微调方法概览
  • 欺诈文本分类微调(六):Lora单卡训练
  • 欺诈文本分类微调(七):lora单卡二次调优
  • QLoRA量化微调策略与实践

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com