LLM 微调策略:LoRA vs QLoRA vs P-tuning

张开发
2026/4/21 14:43:28 15 分钟阅读

分享文章

LLM 微调策略:LoRA vs QLoRA vs P-tuning
LLM 微调策略LoRA vs QLoRA vs P-tuning核心结论LoRA低秩适应通过低秩矩阵分解减少可训练参数内存效率高适合中等资源场景QLoRA量化 LoRA在 LoRA 基础上引入 4-bit 量化大幅减少内存使用适合有限资源场景P-tuningPrefix-tuning 的变体通过可训练前缀嵌入调整模型参数效率高适合特定任务性能对比QLoRA 内存使用最低LoRA 训练速度最快P-tuning 对特定任务效果较好一、LLM 微调基础1.1 微调的必要性预训练模型LLM 在大规模语料上预训练具备通用知识领域适应需要针对特定领域或任务进行微调提升性能参数高效微调全参数微调计算成本高需要更高效的微调方法1.2 常见微调策略全参数微调调整所有模型参数效果最好但计算成本高参数高效微调仅调整部分参数如 LoRA、QLoRA、P-tuning 等提示调优通过设计更好的提示词无需调整模型参数二、LoRA 详解2.1 基本原理低秩分解将权重更新分解为两个低秩矩阵的乘积数学公式 elta W BA 其中 B n athbb{R}^{d imes r} A n athbb{R}^{r imes k} r l d, k训练过程仅训练低秩矩阵 B 和 A 冻结原始模型参数2.2 代码示例from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, get_peft_model # 加载预训练模型 model_name facebook/opt-1.3b tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name) # 配置 LoRA lora_config LoraConfig( r8, # 低秩矩阵的秩 lora_alpha16, # 缩放因子 target_modules[q_proj, v_proj], # 目标模块 lora_dropout0.05, # Dropout 概率 biasnone # 偏置处理方式 ) # 创建 LoRA 模型 lora_model get_peft_model(model, lora_config) # 查看可训练参数 print(可训练参数:) for name, param in lora_model.named_parameters(): if param.requires_grad: print(f{name}: {param.numel()}) # 计算可训练参数比例 total_params sum(p.numel() for p in model.parameters()) trainable_params sum(p.numel() for p in lora_model.parameters() if p.requires_grad) print(f\n可训练参数比例: {trainable_params / total_params * 100:.4f}%)2.3 性能分析优点内存使用低仅需训练少量参数训练速度快计算效率高可与其他微调方法结合推理时可合并权重无额外延迟缺点低秩假设可能限制表达能力需要针对不同模型选择合适的秩对某些任务可能不如全参数微调三、QLoRA 详解3.1 基本原理4-bit 量化使用 4 位精度存储模型权重大幅减少内存使用双量化对量化参数本身再进行量化进一步减少内存分页优化使用 NVIDIA 的统一内存技术处理内存不足问题LoRA 集成在量化模型基础上应用 LoRA 微调3.2 代码示例from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig from peft import LoraConfig, get_peft_model # 配置 4-bit 量化 bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_use_double_quantTrue, bnb_4bit_quant_typenf4, bnb_4bit_compute_dtypefloat16 ) # 加载量化模型 model_name facebook/opt-6.7b tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained( model_name, quantization_configbnb_config, device_mapauto ) # 配置 QLoRA lora_config LoraConfig( r64, lora_alpha16, target_modules[q_proj, k_proj, v_proj, o_proj], lora_dropout0.1, biasnone ) # 创建 QLoRA 模型 qlora_model get_peft_model(model, lora_config) # 查看内存使用 import torch print(f模型内存使用: {torch.cuda.memory_allocated() / 1024**3:.2f} GB) # 计算可训练参数比例 total_params sum(p.numel() for p in model.parameters()) trainable_params sum(p.numel() for p in qlora_model.parameters() if p.requires_grad) print(f可训练参数比例: {trainable_params / total_params * 100:.4f}%)3.3 性能分析优点内存使用极低可在消费级 GPU 上微调大模型保持与全参数微调相近的性能训练速度相对较快支持更大的模型和批量大小缺点量化可能引入精度损失推理时需要解量化可能增加少量延迟实现相对复杂四、P-tuning 详解4.1 基本原理前缀嵌入在输入序列前添加可训练的前缀嵌入连续提示使用可训练的连续向量作为提示而非离散 token任务特定针对特定任务设计前缀结构P-tuning v2改进版本使用更深的前缀网络提升性能4.2 代码示例from transformers import AutoModelForCausalLM, AutoTokenizer from peft import PromptTuningConfig, get_peft_model # 加载预训练模型 model_name gpt2 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name) # 配置 P-tuning prompt_tuning_config PromptTuningConfig( task_typeCAUSAL_LM, prompt_tuning_initrandom, num_virtual_tokens8, # 虚拟 token 数量 token_dimmodel.config.hidden_size, num_transformer_submodules1, num_attention_headsmodel.config.num_attention_heads, num_layersmodel.config.num_hidden_layers, prompt_tuning_init_textClassify the following text: , tokenizer_name_or_pathmodel_name, ) # 创建 P-tuning 模型 p_tuning_model get_peft_model(model, prompt_tuning_config) # 查看可训练参数 print(可训练参数:) for name, param in p_tuning_model.named_parameters(): if param.requires_grad: print(f{name}: {param.numel()}) # 计算可训练参数比例 total_params sum(p.numel() for p in model.parameters()) trainable_params sum(p.numel() for p in p_tuning_model.parameters() if p.requires_grad) print(f\n可训练参数比例: {trainable_params / total_params * 100:.4f}%)4.3 性能分析优点参数效率极高仅需训练少量前缀参数对特定任务如分类、问答效果较好实现相对简单可与其他微调方法结合缺点通用性较差对不同任务需要重新设计前缀训练稳定性可能不如 LoRA对某些任务性能可能不如 LoRA五、性能对比实验5.1 内存使用对比微调方法模型大小内存使用可训练参数比例全参数微调OPT-6.7B~50GB100%LoRA (r8)OPT-6.7B~14GB0.01%LoRA (r64)OPT-6.7B~16GB0.08%QLoRA (r64)OPT-6.7B~6GB0.08%P-tuningOPT-6.7B~13GB0.001%5.2 训练速度对比import time import torch from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, PromptTuningConfig, get_peft_model from peft import BitsAndBytesConfig # 加载模型 model_name facebook/opt-1.3b tokenizer AutoTokenizer.from_pretrained(model_name) # 准备示例数据 texts [Hello, how are you?] * 100 tokenized tokenizer(texts, paddingTrue, truncationTrue, return_tensorspt).to(cuda) # 测试全参数微调 def test_full_finetuning(): model AutoModelForCausalLM.from_pretrained(model_name).to(cuda) optimizer torch.optim.AdamW(model.parameters(), lr5e-5) start time.time() for i in range(10): outputs model(**tokenized, labelstokenized[input_ids]) loss outputs.loss loss.backward() optimizer.step() optimizer.zero_grad() end time.time() print(f全参数微调时间: {end - start:.2f} 秒) return end - start # 测试 LoRA def test_lora(): model AutoModelForCausalLM.from_pretrained(model_name).to(cuda) lora_config LoraConfig(r8, lora_alpha16, target_modules[q_proj, v_proj]) lora_model get_peft_model(model, lora_config) optimizer torch.optim.AdamW(lora_model.parameters(), lr5e-5) start time.time() for i in range(10): outputs lora_model(**tokenized, labelstokenized[input_ids]) loss outputs.loss loss.backward() optimizer.step() optimizer.zero_grad() end time.time() print(fLoRA 微调时间: {end - start:.2f} 秒) return end - start # 测试 P-tuning def test_p_tuning(): model AutoModelForCausalLM.from_pretrained(model_name).to(cuda) prompt_config PromptTuningConfig( task_typeCAUSAL_LM, num_virtual_tokens8, token_dimmodel.config.hidden_size ) p_model get_peft_model(model, prompt_config) optimizer torch.optim.AdamW(p_model.parameters(), lr5e-5) start time.time() for i in range(10): outputs p_model(**tokenized, labelstokenized[input_ids]) loss outputs.loss loss.backward() optimizer.step() optimizer.zero_grad() end time.time() print(fP-tuning 微调时间: {end - start:.2f} 秒) return end - start if __name__ __main__: test_full_finetuning() test_lora() test_p_tuning()5.3 实验结果分析微调方法训练速度内存使用性能适用场景全参数微调慢高最好资源充足追求最佳性能LoRA快中接近全参数中等资源平衡速度和性能QLoRA中低接近全参数资源有限需要微调大模型P-tuning快低特定任务好特定任务参数效率优先六、最佳实践建议6.1 选择合适的微调方法LoRA适合中等资源场景需要平衡性能和速度场景一般 NLP 任务如文本分类、情感分析推荐配置r8-64根据模型大小调整QLoRA适合有限资源场景需要微调大模型场景大模型微调如 LLaMA、GPT-J 等推荐配置4-bit 量化r64P-tuning适合特定任务场景参数效率优先场景分类、问答等特定任务推荐配置8-16 个虚拟 token6.2 性能优化技巧LoRA选择合适的秩 r平衡性能和内存针对不同模型选择合适的目标模块调整学习率和批量大小QLoRA使用双量化和分页优化选择合适的量化类型nf4 或 fp4注意计算精度设置P-tuning调整虚拟 token 数量使用任务特定的初始化文本考虑使用 P-tuning v2 提升性能6.3 常见问题与解决方案内存不足使用 QLoRA 或减小批量大小性能不佳增加 LoRA 的秩或使用更大的模型训练不稳定调整学习率、使用梯度裁剪推理延迟LoRA 可合并权重P-tuning 可缓存前缀七、总结LLM 微调策略各有优缺点选择合适的方法取决于可用资源和任务需求LoRA平衡性能和内存使用适合大多数场景QLoRA内存使用最低适合在有限资源上微调大模型P-tuning参数效率最高适合特定任务技术演进的内在逻辑从全参数微调到参数高效微调反映了对计算资源利用效率的不断追求。每种方法都解决了特定场景下的问题共同构成了 LLM 微调的完整生态。在实际应用中应根据模型大小、可用资源和任务需求选择合适的微调策略必要时可以结合多种方法以达到最佳效果。

更多文章