LoRA和QLoRA微调
LoRA和QLoRA是两种高效微调大语言模型的技术,通过低秩矩阵分解和模型量化显著降低计算资源需求。LoRA冻结预训练权重并注入可训练的低秩矩阵,仅需优化少量参数;QLoRA进一步引入4位量化技术,使大模型微调可在消费级GPU上运行。两种方法在保持模型性能的同时,将参数量降至全量微调的千分之一以下,支持快速任务切换。实践环节详细介绍了代码实现步骤、关键参数配置(如秩、学习率等)、训练监控方法和常见
LoRA 和 QLoRA 都是针对 大语言模型(LLM)高效微调 的技术,核心目标是在降低计算资源消耗的同时,保持甚至提升模型微调效果。
一、LoRA
权重的更新矩阵(ΔW)具有“内在的低秩特性”,因此 ΔW 实际上可以用两个更小的矩阵(A 和 B)的乘积来近似表示。
- 原始权重矩阵W(尺寸d * k)
- 低秩矩阵A(d * r)
- 低秩矩阵B(r * k)
(其中 r≪min(d,k),称为 “秩”),将参数更新表示为 ΔW=BA。
(一)工作原理
- 冻结预训练权重:保持原始模型的所有参数 W 不变,避免了在微调过程中损坏模型已有的知识。
- 注入旁路矩阵:在模型的某些层(通常是Transformer的Attention层的Q, K, V, O投影矩阵)旁,插入一对可训练的、低秩的矩阵 A和B。(矩阵A通常用随机高斯分布初始化。矩阵B通常初始化为零,这样一开始旁路为零,不影响原始模型输出。)
- 仅训练新增参数:在微调过程中,只训练优化这些新增的低秩矩阵 A和B 的参数。原始模型参数保持不变。
- 推理合并:训练完成后,可以将低秩矩阵和原始权重矩阵合并:W_new = W + BA 。这样在推理时不会引入任何额外的延迟或计算开销,因为合并后的模型和原始模型结构完全一样。
(二)局限
仍需加载完整的预训练模型权重(通常为 FP16 精度),内存占用较高(例如,1750 亿参数模型需约 35GB 内存)。
(三)优势
参数量仅为全量微调的千分之一到万分之一;只需存储和优化极少的参数;大大降低了GPU显存需求。更快的训练速度和更低的计算成本。可以为一个基础模型训练多个不同的LoRA适配器(用于不同任务),加载不同的LoRA权重,可以快速在不同任务模型间切换。
二、QLoRA
在 LoRA 基础上引入模型量化,进一步降低内存需求,使大模型微调可在消费级 GPU 上实现。
(一)工作原理:
- 将预训练模型以4位精度(NF4) 量化加载并冻结,
- 再在量化后的权重上应用 LoRA
- 通过反向传播与梯度计算的方式保留其性能,在反向传播过程中,权重始终是以将4位权重反量化回BF16/FP16的高精度形式参与计算的,只是存储时是4位。这确保了训练过程的数值稳定性,使得用4位权重训练的模型最终性能几乎与全精度微调相当。
(二)关键技术
- 4位 NormalFloat (NF4) 量化:一种针对神经网络权重分布最优化的4位数据类型。它比标准的INT4量化更能保持模型的性能。
- 双量化:对第一次量化产生的量化常数(constants)进行第二次量化,进一步节省内存。这相当于对量化参数的“元量化”。
- 分页优化器:利用NVIDIA统一内存特性,在GPU显存不足时自动将优化器状态转移到CPU RAM,防止内存溢出(OOM)错误。类似于电脑内存和虚拟内存的切换。
(三)局限
量化过程需额外计算,初始化时间略长。
(四)优势
内存需求比 LoRA 降低 4-5 倍;几乎不损失模型性能(与 LoRA 效果相当,尽管使用了量化,但通过NF4和反量化技巧,其微调效果被证明可以接近全精度微调)。保留 LoRA 的所有优点(低参数量、快速切换任务等)
三、实践
(一)代码实现步骤
- 平台:本地机器 或者租用GPU的云服务平台(AutoDL);
- 安装核心库;
- 数据处理:加载数据集、格式化数据、分词(Tokenization,将文本转换为 token);
- 加载量化模型:使用bitsandbytes加载 4 位量化的预训练模型;
- 配置 LoRA 适配器:通过peft设置 LoRA 参数;
- 设置训练参数;
- 启动训练;
- 监控显存和损失函数;
- 模型评估与测试:使用验证集评估困惑度(Perplexity)或任务指标(如准确率);
- 加载训练后的模型进行推理,测试生成效果;
- 合并模型:将 LoRA 适配器与基础模型合并,生成完整模型。
常用依赖库:
PyTorch 和 CUDA
PyTorch:深度学习框架
transformers:Hugging Face的模型库,加载预训练模型和tokenizer分词器
PEFT:核心库,提供LoRA、QLoRA等高效微调方法,管理 LoRA 适配器,仅训练少量低秩矩阵参数。
bitsandbytes:核心库,实现模型 4 位量化,将预训练模型权重压缩至低精度,降低显存占用。
datasets:方便地加载和处理数据集(如分词、格式转换)
accelerate:训练加速与分布式支持,自动处理 GPU/CPU 资源分配,支持单卡 / 多卡训练。
trl:简化RLHF过程,其中的SFTTrainer对QLoRA很好用
wandb (可选):训练可视化与监控,查看损失曲线
evaluate:评估工具
pip install peft transformers==4.35.2 datasets bitsandbytes tiktoken transformers_stream_generator accelerate trl evaluate
基座模型:从魔搭社区 或者 Hugging Face下载。
注意:确保下载的是原始预训练权重,而不是已经微调过的版本。
数据集(Dataset):
-
魔搭社区找
-
使用开源指令数据集,如Alpaca等。
-
使用LLM生成。
格式:
[
{
"instruction": "将下面的英文翻译成中文。",
"input": "Hello, world!",
"output": "你好,世界!"
}
]
(二)Llama-Factory开源工具微调
操作步骤:
- GPU云平台租机器(AutoDL)
- 本机通过SSH连接远程租用的服务器,进入/root/autodl-tmp目录
- git克隆Llama-Factory仓库、安装python3.10版本环境
- 进入Llama-Factory目录,安装相关依赖
- 下载基座模型
- 准备训练数据集文件,放到LlaMA-Factory/data目录下,并在dataset_info.json文件里配置训练数据集文件名
- 启动Llama-Factory,在可视化界面微调
(三)核心参数
1、秩(r)
控制 LoRA 新增的 “低秩矩阵” 维度,决定模型的拟合能力上限和参数数量。
r 越小:新增参数越少(更轻量),模型能学习的 “细节”更少;
r 越大:新增参数越多(略重),但能捕捉数据中更复杂的规律。
- 过小:拟合不足,学不会核心规律,微调无效。
- 过大:过拟合,换新数据(测试集)表现差;参数冗余,训练速度变慢、占用显存增加。
2、学习率(lr)
学习的速度,控制每次参数更新的 幅度,即模型根据训练误差调整参数的幅度。
- 过小:训练太慢,需要极多轮次才能收敛,耗时耗资源;只学会了训练数据的表面规律,没理解深层逻辑。
- 过大:损失值(loss)来回震荡,无法稳定下降。也可能loss 越来越大,模型输出完全混乱。
3、缩放因子(alpha)
微调力度的放大器,调整对原模型的 影响幅度,通常建议 alpha =
r,此时更新幅度适中,既能保留原模型能力,又能融入新数据特征。
- 过小:微调 “没劲儿”,训练后模型几乎没变化。
- 过大:原模型的通用能力被 破坏;损失值(loss)剧烈波动。
4、训练轮次(epochs)
控制模型在 训练数据集上学习的次数。
- 过小:欠拟合,模型没学够,连训练数据里的基本规律都没掌握。
- 过大:过拟合,模型把训练数据的 “噪音”(比如数据里的错别字、特殊表述)都当成规律记住,测试新数据时错误率飙升。
5、批大小(batch size)
模型更新时每次同时处理的 “样本数量”。
- 过小:单次样本太少,误差波动大,loss 忽高忽低 跳变;需要更多次更新才能跑完所有样本,导致训练变慢。
- 过大:显卡装不下,显存溢出;泛化差,样本太集中,模型容易 “偏科”。
6、目标模块
指定模型微调哪些模块,即选择插入 LoRA 低秩矩阵的模块,主流是 Transformer 注意力层的 Q/K/V 投影层(如q_proj、k_proj)。
- 选少 / 选错:只调
v_proj
忽略q_proj
,模型无法学习新领域的 “语义匹配规则”,效果差; - 选多:连
embedding
层(词嵌入)都调,参数冗余、破坏原模型基础语义(如 “医生” 词向量偏离通用含义)。
7、权重衰减
给 LoRA 参数加 “惩罚项”(让参数值尽量小),避免过度依赖某类特征。
- 过小:惩罚不足,参数膨胀,过拟合。
- 过多:惩罚过强,参数接近 0,等效 “没微调”。
8、序列长度(max_seq_length)
限制单次处理的文本长度(单位:token),超长截断、不足填充。
- 过小问题:长文本关键信息被截断(如法律合同核心条款丢失)
- 过大问题:显存暴增(OOM)、训练变慢(长文本计算量大)
9、梯度累积步数(gradient_accumulation_steps)
显存不足时,用 “多次 小batch size 累积梯度后 更新参数”,模拟大批量 效果(如 批次batch = 8 batch size + 累积 4 步 = 32)。
- 过小:无法利用显存,仍等效小 batch,训练不稳定。
- 过大:训练速度变慢(需 累积多步 才更新),异常时浪费更多计算。
(四)训练的监控与评估
1、微调效果监控
- 离线工具:TensorBoard —— loss损失函数、梯度、学习率等变化情况。
-
Weights & Biases(W&B,又称 “权重与偏差”):一站式实验管理,自动记录超参数(如 r、alpha、学习率)、指标、代码版本,支持多实验对比(快速找到最优 LoRA 配置);可视化 损失曲线、学习率曲线、显存占用。适合需要对比不同 LoRA 参数组合的场景。
-
显存占用:用 nvidia-smi(Linux)实时查看,若接近 24GB,需降低 batch size。
2、微调结果评估
- evaluate:Hugging Face 生态的官方评估库,集成了 100 + 常用评估指标常用评估指标(如准确率、BLEU、ROUGE、F1 等),支持文本分类、生成、翻译、问答等几乎所有 NLP 任务。开箱即用,直接调用预定义指标(如
evaluate.load("bleu")
计算 BLEU 分数)。 - evalscope:字节跳动推出的,高效的 “一站式评估解决方案”,提供全链路、多维度、可扩展的评估能力,支持 NLP 主流任务评估,支持批量输入测试集,自动调用模型推理、计算指标、生成可视化报告(无需手动拼接多个工具结果);支持分布式评估,可高效处理十万级甚至百万级测试样本;可自定义指标;支持本地部署。
EM:衡量生成答案与标准答案是否完全一致;
F1:衡量词级重叠度。
(五)常见问题与调试
1、显存不足(CUDA Out of Memory)
- 降低 batch size。
- 降低 max_seq_length。
- 降低 LoRA 的 r 值。
-
增加 梯度累积步数 来补偿变小的batch size。
2、训练损失不下降:
- 检查数据格式是否正确。
- 尝试提高学习率 (如 2e-4 -> 5e-4)。
-
确保你的数据集和任务相关且质量高。
损失函数波动大:
- 可降低 学习率
- 减小 batch size
- 或增大
gradient_accumulation_steps
生成的结果毫无意义:
- 可能是训练步数不够,增加
num_train_epochs
。 -
检查提示词模板是否和训练时一致。
防止过拟合:补正则化(增大权重衰减、增大丢弃)
(六)显存占用核心组成
- 量化后的模型权重(4-bit NormalFloat 格式):例如,70 亿参数模型约占用 3.5GB 显存(每个参数 0.5 字节)。
- LoRA 适配器参数:假设秩(Rank)设为 16,LoRA 参数约为原模型的 0.1%(700 万参数),存储 + 优化器状态约需 1.5GB。
- 激活值与临时缓存:与批量大小(Batch Size)和序列长度直接相关。例如,Batch Size=8、序列长度 = 2048 时,需约 6-8GB。
- 系统与框架开销:约 5GB(包括 PyTorch、CUDA 等运行时环境)。
总显存占用估算公式:总显存 ≈ 量化模型+LoRA参数+激活值+系统开销
实践中,通常关注
- 激活值内存:序列长度和 Batch Size 的乘积,是显存消耗的 “杀手”。
- LoRA 超参数:增大秩(Rank)或训练更多层会增加显存。例如,秩从 16 增至 32 时,LoRA 参数和优化器状态可能翻倍。
实战技巧:
- 批量大小:从最小可行值(如 Batch Size=2)逐步增加,直至触发显存溢出,再回退一档。
- 适配器:优先在注意力层的 Query/Value 矩阵插入适配器,而非全连接层。
- 秩:根据任务复杂度选择秩(Rank):简单任务(如风格微调)用 8-16,复杂任务(如多轮对话)用 32-64。
- 使用梯度检查点:通过
torch.utils.checkpoint
减少激活值存储,但会增加训练时间(约 10-20%)。
(七)在 RTX 3090/4090(24GB 显存)上,通过QLoRA 技术可训练的模型大小
基于使用bitsandbytes 库进行NF4量化分析,并采用AdamW优化器。
1. 7B 模型 (例如 LLaMA-2-7b, Mistral-7b)
体验:非常轻松。这是24GB显卡的“甜点”尺寸。
典型配置:
-
Batch Size:可以设置相对较大的批处理大小(如 16, 32 甚至更高)。
-
序列长度:可以轻松支持 2048 或 4096 的上下文长度。
-
LoRA Rank (r):可以设置较高的秩(如 256, 512)以获得更好的性能,而不会爆显存。
-
梯度累积:不需要或只需很少的步数。
显存占用:训练时显存占用通常在 12GB ~ 18GB 之间,留有充足余量。
2. 13B 模型 (例如 LLaMA-2-13b)
体验:依然流畅。这是24GB显卡非常舒适和实用的选择。
典型配置:
-
Batch Size:需要适当降低批处理大小(如 4, 8, 16)。
-
序列长度:2048 很轻松,4096 需要小心调整。
-
LoRA Rank (r):常用的秩(64, 128)完全没问题。
-
梯度累积:可能需要使用较小的梯度累积步数来等效增大batch size。
显存占用:训练时显存占用通常在 18GB ~ 22GB 之间,需要关注显存使用情况。
3. 33B/34B 模型 (例如 LLaMA-2-34b)
体验:可行,但有挑战性。需要“精打细算”地配置参数,已经触及24GB显存的极限。
典型配置:
-
Batch Size:必须设置得很小(通常为 1 或 2)。这是最大的限制。
-
序列长度:可能需要降低到 1024 或 2048。尝试使用 4096 会非常困难,极易OOM(Out Of Memory)。
-
LoRA Rank (r):需要使用较低的秩(如 32, 64)。
-
梯度累积:必须使用较高的梯度累积步数(如 8, 16)来补偿极小的batch size,以确保训练稳定性。
-
启用CPU分页优化器:必须开启,以防止在偶尔的显存峰值下出现OOM。
显存占用:训练时显存占用会非常接近 24GB,任何额外的开销(如更长的序列、更大的秩)都会导致OOM。
建议:
使用监控工具:在训练时,使用 nvidia-smi或 nvitop等工具实时监控显存使用情况。
利用现有代码库:使用像 Axolotl 或 PEFT (Parameter-Efficient Fine-Tuning) 库,它们已经内置了良好的QLoRA默认配置,并支持梯度检查点、分页优化器等特性,能帮你自动处理很多显存优化问题。
四、Adam 和 AdamW 优化器
1、Adam
结合动量(Momentum)和自适应学习率(RMSProp)。维护一阶矩(均值,对应动量)和二阶矩(方差),更新时修正偏差,
2、AdamW
在 Adam 基础上增加了权重衰减,更适合抑制过拟合,将 权重衰减 与 梯度更新 解耦(更新时单独施加权重衰减),解决 Adam 权重衰减的偏差问题,更利于泛化。
五、样本
Zero-shot(零样本)、One-shot(单样本)、Few-shot(少样本)
核心差异 在于 “用于学习新任务的标注样本数量”。
Zero-shot | 模型无任何标注样本,仅靠任务描述完成新任务 | 0 个 | 让模型识别从未见过的 “长尾类别”(如 “区分不同品种的蝴蝶”,未给样本) |
One-shot | 模型仅用 1 个标注样本学习新任务 | 1 个 | 给模型 1 张 “梵高风格画作” 样本,让它生成新的梵高风格画 |
Few-shot | 模型用少量(通常 2~100 个)标注样本学习新任务 | 2~100 个 | 给模型 5 个 “故障设备日志 + 故障类型” 的样本,让它分类新的设备日志 |
更多推荐
所有评论(0)