【LLM训练系列01】Qlora如何加载、训练、合并大模型

示例1:Qlora训练Qwen2.5

参考脚本:https://github.com/QwenLM/Qwen/blob/main/recipes/finetune/deepspeed/finetune_qlora_multi_gpu.ipynb

训练命令如下:

!torchrun --nproc_per_node 2 --nnodes 1 --node_rank 0 --master_addr localhost --master_port 6601 ../../finetune.py \--model_name_or_path "Qwen/Qwen-1_8B-Chat-Int4/" \--data_path "Belle_sampled_qwen.json" \--bf16 True \--output_dir "output_qwen" \--num_train_epochs 5 \--per_device_train_batch_size 1 \--per_device_eval_batch_size 1 \--gradient_accumulation_steps 16 \--evaluation_strategy "no" \--save_strategy "steps" \--save_steps 1000 \--save_total_limit 10 \--learning_rate 1e-5 \--weight_decay 0.1 \--adam_beta2 0.95 \--warmup_ratio 0.01 \--lr_scheduler_type "cosine" \--logging_steps 1 \--report_to "none" \--model_max_length 512 \--gradient_checkpointing True \--lazy_preprocess True \--deepspeed "../../finetune/ds_config_zero2.json" \--use_lora \--q_lora

选择底座模型

上面命令选用的Qwen/Qwen-1_8B-Chat-Int4/

加载模型

from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# Load model and tokenizer
model = transformers.AutoModelForCausalLM.from_pretrained(model_args.model_name_or_path,config=config,cache_dir=training_args.cache_dir,device_map=device_map,trust_remote_code=True,quantization_config=GPTQConfig(bits=4, disable_exllama=True)if training_args.use_lora and lora_args.q_loraelse None,**model_load_kwargs,
)
tokenizer = transformers.AutoTokenizer.from_pretrained(model_args.model_name_or_path,cache_dir=training_args.cache_dir,model_max_length=training_args.model_max_length,padding_side="right",use_fast=False,trust_remote_code=True,
)
tokenizer.pad_token_id = tokenizer.eod_idif training_args.use_lora:if lora_args.q_lora or is_chat_model:modules_to_save = Noneelse:modules_to_save = ["wte", "lm_head"]lora_config = LoraConfig(r=lora_args.lora_r,lora_alpha=lora_args.lora_alpha,target_modules=lora_args.lora_target_modules,lora_dropout=lora_args.lora_dropout,bias=lora_args.lora_bias,task_type="CAUSAL_LM",modules_to_save=modules_to_save  # This argument serves for adding new tokens.)if lora_args.q_lora:model = prepare_model_for_kbit_training(model, use_gradient_checkpointing=training_args.gradient_checkpointing)model = get_peft_model(model, lora_config)# Print peft trainable paramsmodel.print_trainable_parameters()if training_args.gradient_checkpointing:model.enable_input_require_grads()

prepare_model_for_kbit_training函数说明

调用 prepare_model_for_kbit_training() 函数来预处理用于训练的量化模型。我们在peft库中可以看到源码:

def prepare_model_for_kbit_training(model, use_gradient_checkpointing=True, gradient_checkpointing_kwargs=None):r"""Note this method only works for `transformers` models.This method wraps the entire protocol for preparing a model before running a training. This includes:1- Cast the layernorm in fp32 2- making output embedding layer require grads 3- Add the upcasting of the lmhead to fp32Args:model (`transformers.PreTrainedModel`):The loaded model from `transformers`use_gradient_checkpointing (`bool`, *optional*, defaults to `True`):If True, use gradient checkpointing to save memory at the expense of slower backward pass.gradient_checkpointing_kwargs (`dict`, *optional*, defaults to `None`):Keyword arguments to pass to the gradient checkpointing function, please refer to the documentation of`torch.utils.checkpoint.checkpoint` for more details about the arguments that you can pass to that method.Note this is only available in the latest transformers versions (> 4.34.1)."""loaded_in_kbit = getattr(model, "is_loaded_in_8bit", False) or getattr(model, "is_loaded_in_4bit", False)is_gptq_quantized = getattr(model, "quantization_method", None) == "gptq"is_aqlm_quantized = getattr(model, "quantization_method", None) == "aqlm"is_eetq_quantized = getattr(model, "quantization_method", None) == "eetq"is_hqq_quantized = getattr(model, "quantization_method", None) == "hqq" or getattr(model, "hqq_quantized", False)if gradient_checkpointing_kwargs is None:gradient_checkpointing_kwargs = {}for name, param in model.named_parameters():# freeze base model's layersparam.requires_grad = Falseif not is_gptq_quantized and not is_aqlm_quantized and not is_eetq_quantized and not is_hqq_quantized:# cast all non INT8 parameters to fp32for param in model.parameters():if ((param.dtype == torch.float16) or (param.dtype == torch.bfloat16)) and param.__class__.__name__ != "Params4bit":param.data = param.data.to(torch.float32)if (loaded_in_kbit or is_gptq_quantized or is_aqlm_quantized or is_eetq_quantized or is_hqq_quantized) and use_gradient_checkpointing:# When having `use_reentrant=False` + gradient_checkpointing, there is no need for this hackif "use_reentrant" not in gradient_checkpointing_kwargs or gradient_checkpointing_kwargs["use_reentrant"]:# For backward compatibilityif hasattr(model, "enable_input_require_grads"):model.enable_input_require_grads()else:def make_inputs_require_grad(module, input, output):output.requires_grad_(True)model.get_input_embeddings().register_forward_hook(make_inputs_require_grad)# To support older transformers versions, check if the model supports gradient_checkpointing_kwargs_supports_gc_kwargs = "gradient_checkpointing_kwargs" in list(inspect.signature(model.gradient_checkpointing_enable).parameters)if not _supports_gc_kwargs and len(gradient_checkpointing_kwargs) > 0:warnings.warn("gradient_checkpointing_kwargs is not supported in this version of transformers. The passed kwargs will be ignored."" if you want to use that feature, please upgrade to the latest version of transformers.",FutureWarning,)gc_enable_kwargs = ({} if not _supports_gc_kwargs else {"gradient_checkpointing_kwargs": gradient_checkpointing_kwargs})# enable gradient checkpointing for memory efficiencymodel.gradient_checkpointing_enable(**gc_enable_kwargs)return model

这个函数 prepare_model_for_kbit_training 主要用于准备一个 transformers 库的预训练模型(PreTrainedModel),以便进行 低比特(k-bit)量化训练 或其他特定情况下的训练。函数提供了一些设置和优化步骤,使模型更适合量化训练环境。

核心功能:
  1. 将 LayerNorm 层参数转换为 FP32(32 位浮点数)。
  2. 设置模型的输出嵌入层参数为需要计算梯度(即使冻结了其他参数,嵌入层可以被微调)。
  3. 将语言模型头(lm head)的计算强制提升为 FP32,以提高训练的数值稳定性。
参数说明:
  • model:
    • 一个从 transformers 加载的预训练模型对象(如 GPT、BERT)。
  • use_gradient_checkpointing:
    • 是否启用梯度检查点功能,用于在内存和计算速度之间进行权衡(减少内存占用,牺牲反向传播速度)。
  • gradient_checkpointing_kwargs:
    • 一个可选字典,传递给梯度检查点的配置参数。需要 transformers 版本大于 4.34.1 才支持。

函数分步骤解析:
  1. 识别模型的量化情况

    • 检查模型是否被加载为低比特格式(8 位或 4 位),以及是否采用了特定的量化方法(如 GPTQ、AQLM、EETQ 或 HQQ 等)。
  2. 冻结所有参数

    • 遍历模型的所有参数,设置 requires_grad = False,即冻结所有层,不计算梯度。这是低比特量化训练常见的步骤,用于只训练部分特定参数。
  3. 非量化模型处理

    • 如果模型未被量化,所有的非 INT8 参数(比如 FP16 或 BF16)都会被强制转换为 FP32。这是为了确保数值稳定性,特别是在低精度下训练时。
  4. 启用梯度检查点(可选)

    • 如果模型是低比特量化模型且启用了 use_gradient_checkpointing
      • 检查是否需要启用输入张量的梯度。对于某些老版本的 transformers,可能需要通过 forward_hook 的方式显式设置输入张量的 requires_grad
      • 检查模型是否支持 gradient_checkpointing_kwargs,并发出警告(如果版本过旧)。
  5. 启用梯度检查点功能

    • 调用模型的 gradient_checkpointing_enable 方法,根据是否支持额外参数传递对应配置,最终节省内存。

输出:

经过此函数处理后的模型:

  • 更适合在量化或低精度(FP16/BF16)环境下训练
  • 非量化模型的关键参数被转换为 FP32,以提升稳定性。
  • 冻结大部分参数,只保留需要训练的部分。
  • 在内存有限的情况下启用梯度检查点功能,优化 GPU 显存占用。
使用场景:

这个函数特别适用于以下情境:

  • 使用低比特(如 8-bit 或 4-bit)的模型进行训练。
  • 微调大模型时希望通过梯度检查点功能减少显存消耗。
  • 对特定参数(如语言模型头或嵌入层)进行微调,而冻结其他层的参数。

合并模型

这里注意:合并模型需要使用Base模型合并,不是量化模型

from modelscope.hub.snapshot_download import snapshot_download
snapshot_download('Qwen/Qwen-1_8B-Chat', cache_dir='.', revision='master')from transformers import AutoModelForCausalLM
from peft import PeftModel
import torchmodel = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-1_8B-Chat/", torch_dtype=torch.float16, device_map="auto", trust_remote_code=True)
model = PeftModel.from_pretrained(model, "output_qwen/")
merged_model = model.merge_and_unload()
merged_model.save_pretrained("output_qwen_merged", max_shard_size="2048MB", safe_serialization=True)

在 LoRA 和 Q-LoRA 的训练过程中,仅保存了适配器参数(adapter parameters),而不是完整的模型权重。需要注意的是,权重不能直接合并到量化模型(quantized models)中。相反,我们可以基于原始的非量化模型来合并权重。

这意味着,合并权重的过程需要加载原始的基础模型,并将微调的适配器参数与之结合,生成一个新的模型权重文件。以下是实现权重合并的示例代码:

示例2:Qlora微调Llama

fine-tuning-llama-2-using-lora-and-qlora-a-comprehensive-guide

选择模型

model_name = "NousResearch/Llama-2-7b-chat-hf"
dataset_name = "mlabonne/guanaco-llama2-1k"
new_model = "Llama-2-7b-chat-finetune-qlora"

参数设置

lora_r = 64 #lora attention dimension/ rank
lora_alpha = 16 #lora scaling parameter
lora_dropout = 0.1 #lora dropout probability

量化设置

use_4bit = True
bnb_4bit_compute_dtype = "float16"
bnb_4bit_quant_type = "nf4"
use_nested_quant = False

BitsAndBytes 配置项中文解释

  1. use_4bit = True

    • 功能: 启用 4 位量化(4-bit quantization)以减少模型的内存占用。
    • 作用: 将模型参数从通常的高精度(如 FP32 或 FP16)压缩为 4 位表示,显著降低显存使用。
  2. bnb_4bit_compute_dtype = "float16"

    • 功能: 指定训练过程中用于计算的精度类型,这里选择 float16(16 位浮点数)。
    • 作用: 即使模型参数被量化为 4 位,计算时仍使用更高的精度(FP16),以确保训练过程中的数值稳定性和性能。
  3. bnb_4bit_quant_type = "nf4"

    • 功能: 设置 4 位量化的类型,nf4(Normalized Float 4)是一种常见的选择。
    • 作用: nf4 是一种专门设计的量化格式,相比传统的量化类型,能够更好地保留模型权重的数值分布特性,提升量化模型的性能。
  4. use_nested_quant = False

    • 功能: 是否启用嵌套量化(Nested Quantization),即“双重量化”。
    • 作用: 嵌套量化是一种更进一步的量化技术,可以进一步减少内存占用,但可能会对模型性能有一定影响。这里选择禁用该功能。

这组配置是为了使用 BitsAndBytes 库实现 4 位量化,目的是在显存资源有限的情况下训练大型模型,同时尽量保持模型性能。具体设置包括:

  • 启用 4 位量化 来压缩模型权重。
  • 使用 FP16 进行计算,平衡计算速度与精度。
  • 采用 nf4 量化类型 来优化量化模型的效果。
  • 禁用 嵌套量化 以避免额外的复杂性或性能损失。

此配置非常适合需要在低资源环境下进行高效训练的场景。

加载模型

#load dataset
dataset = load_dataset(dataset_name,split = "train")#load tokenizer and model with QLoRA config
compute_dtype = getattr(torch, bnb_4bit_compute_dtype)bnb_config = BitsAndBytesConfig(load_in_4bit = use_4bit,bnb_4bit_quant_type = bnb_4bit_quant_type,bnb_4bit_compute_dtype = compute_dtype,bnb_4bit_use_double_quant = use_nested_quant,)#cheking GPU compatibility with bfloat16
if compute_dtype == torch.float16 and use_4bit:major, _ = torch.cuda.get_device_capability()if major >= 8:print("="*80)print("Your GPU supports bfloat16, you are getting accelerate training with bf16= True")print("="*80)#load base model
model = AutoModelForCausalLM.from_pretrained(model_name,quantization_config = bnb_config,device_map = device_map,
)model.config.use_cache = False
model.config.pretraining_tp = 1

合并模型

同样使用底座合并模型

# Reload model in FP16 and merge it with LoRA weights
base_model = AutoModelForCausalLM.from_pretrained(model_name,low_cpu_mem_usage=True,return_dict=True,torch_dtype=torch.float16,device_map=device_map,
)
model = PeftModel.from_pretrained(base_model, new_model)
model = model.merge_and_unload()# Reload tokenizer to save it
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

总结

模型为量化模型

  • 训练:需要prepare_model_for_kbit_training(model)
  • 合并:加载基础模型进行合并qlora
  • 推理:加载base模型然后加载qlora权重也可以加载合并之后的

模型为基础模型

  • 训练:加载需要使用bnb对基础模型量化
  • 合并:加载基础模型进行合并qlora
  • 推理:加载base模型然后加载qlora权重也可以加载合并之后的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/20310.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Jmeter数据库压测之达梦数据库的配置方法

目录 1、概述 2、测试环境 3、数据库压测配置 3.1 安装jmeter 3.2 选择语言 3.3 新建测试计划 3.4 配置JDBC连接池 3.5 配置线程组 3.6 配置测试报告 3.7 执行测试 1、概述 Jmeter是Apache组织开发的基于Java的压力测试工具,用于对软件做压力测试。 它最…

[ 应急响应进阶篇-1 ] Windows 创建后门并进行应急处置-5:启动项后门

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

编译报错:protoc did not exit cleanly. Review output for more information.

目录标题 解决“protoc did not exit cleanly”的报错问题检查.proto文件的语法 解决“protoc did not exit cleanly”的报错问题 今天做的项目需要用到grpc,然后需要编写proto然后编译后实现grpc的具体方法! 结果编译的时候报了protoc did not exit cl…

Java码农人生开启手册——重载和重写

一、重载 有时在调用现有方法时会出现参数类型不匹配的问题,在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。 注意: 方法名必须相同参数列表必须不同与返回值是否相同无关编译器在编译代码时&…

ComfyUI-unclip模型部署指南

一、介绍 unCLIP 模型是 SD 模型的版本,经过专门调整,除了文本提示之外,还可以接收图像概念作为输入。使用这些模型附带的 CLIPVision 对图像进行编码,然后在采样时将其提取的概念传递给主模型。 它并不是按照传统意义将图像混合…

优雅关闭:避免服务停机带来的业务损失

服务关闭有什么问题? 在“单体应用”复杂到一定程度后,一般会进行系统拆分,也就是微服务架构。服务拆分之后,就需要协同,于是RPC框架就出来了,用来解决各个子系统之间的通信问题。 拆分系统的目的&#x…

硬件知识 cadence16.6 原理图输出为pdf 网络名下划线偏移 (ORCAD)

1. cadence原理图输出为PDF网络名下划线偏移 生这种情况的原因 1. 设计的原理图图纸大小比正常的 A4图纸大。 2. 打印为PDF 的时候,打印机的设置有问题。 2.cadence原理图输出为 PDF网络名下划线偏移的情况 可以看到上图,网络名往上漂移。 3. 解决办法 …

Linux插件zsh(oh-my-zsh)

一、oh-my-zsh基本介绍 oh-my-zsh: https://github.com/ohmyzsh/ohmyzshhttps://github.com/ohmyzsh/ohmyzsh 注意:需要先安装zsh命令,才能安装oh-my-zsh,先测试是否安装了zsh rootserver:/opt # zsh --version zsh 5.8 (x86_6…

异或和之和

//暴力做法 枚举每个子区间 O(n^3) //优化1 利用前缀异或和快速求出区间异或和 O(n^2) //优化2 处理位运算的常用方法:拆位法 常用的思想:贡献法思想 下面详见优化2: 1.拆位贡献法 2.实战真题1 题目链接:1.异或和之和 - 蓝桥…

A039-基于SpringBoot的农产品销售系统的设计与实现

🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…

【大数据学习 | Spark】RDD的概念与Spark任务的执行流程

1. RDD的设计背景 在实际应用中,存在许多迭代式计算,这些应用场景的共同之处是,不同计算阶段之间会重用中间结果,即一个阶段的输出结果会作为下一个阶段的输入。但是,目前的MapReduce框架都是把中间结果写入到HDFS中&…

jmeter操作数据库

简介 Apache JMeter 是一个强大的开源工具,用于负载测试和性能测量。除了Web应用外,JMeter还可以用于测试各种数据库系统,包括MySQL。本文将详细介绍如何使用JMeter来测试MySQL数据库的性能。 环境准备 安装Java:确保你已经安装…

最小生成树——Kruskal、Prim算法

图的存储: 高阶数据结构——图 文章目录 目录 文章目录 一、kruskal算法 二、Prim算法 前言 连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树 就不在连通;反之&#xf…

STL-stack栈:P1981 [NOIP2013 普及组] 表达式求值

这个题用的STL-栈来做 题目来源:洛谷 相关知识 [NOIP2013 普及组] 表达式求值 题目背景 NOIP2013 普及组 T2 题目描述 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。 输入格式 一行,为需要你计算的表达式&#xff…

数字孪生赋能智慧校园:构建全方位校园安全保障新体系

在11月19日最高人民检察院的党组会上,校园安全问题再次被置于重要议程,会议明确指出,校园安全不仅关乎学生的健康成长,更与社会和谐稳定紧密相连。面对侵害学生权益、危害校园安全的犯罪行为,必须采取“零容忍”态度&a…

Openstack15--块存储服务(Cinder)安装

控制节点 安装Cinder软件包 yum -y install openstack-cinder 安装的“openstack-cinder”软件包里包括“cinder-api”和“cinder-scheduler”模块。安装“openstack-cinder”软件包时,和安装其他OpenStack核心组件时一样,会自动创建名为“cinder”的L…

如何用js方法把页面中的表格导出为excel表格(sheetJS)

目录 一,SheetJS库的基本介绍 这里用到的库是SheetJS,官方文档: sheetJS CE 官方文档 官网对库的解释是: SheetJS社区版提供了经过战斗测试的开源解决方案,用于从几乎任何复杂的电子表格中提取有用的数据&#xf…

自动驾驶系列—告别眩光烦恼:智能大灯如何守护夜间行车安全

🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…

爬虫策略——反爬机制

现代网站通常会使用多种反爬手段来限制爬虫访问数据。了解这些机制并针对性地制定绕过策略,是构建高效爬虫的关键。 1. 常见反爬手段 1.1 User-Agent 检查 网站通常会通过检查请求中的 User-Agent 字段,判断访问是否来自真实用户。爬虫默认的请求库&am…

DataWhale—PumpkinBook(TASK03对数几率回归)

一、课程组成及结构 课程开源地址及相关视频链接:(当然这里也希望大家支持一下正版西瓜书和南瓜书图书,支持文睿、秦州等等致力于开源生态建设的大佬✿✿ヽ(▽)ノ✿) Datawhale-学用 AI,从此开始 【吃瓜教程】《机器学习公式详解…