ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

放弃纯RAG转向微调:四个实战坑与LoRA参数调优指南

放弃纯RAG转向微调:四个实战坑与LoRA参数调优指南 1. 为什么我最终放弃了纯RAG方案1.1 一个让我彻底转向微调的真实场景去年下半年我接手了一个垂直领域的问答项目业务方给的需求很明确让模型能准确回答某类产品的参数、使用限制和故障处理流程。第一反应当然是上RAG毕竟那会儿“RAG解决一切幻觉”的说法满天飞。我花了大概两周时间搭了一套标准流程文档切块、向量化、检索、拼prompt、丢给大模型生成。测试集上跑下来检索命中率看着还行但最终答案的准确率死活卡在60%出头。问题出在哪我逐条分析badcase发现三类情况反复出现。第一类是知识需要跨多个文档片段组合比如“A型号在B条件下能不能用C配件”答案分散在三份文档里检索只捞回来两份模型就开始自己编。第二类是格式和口径要求严格业务方要求回答必须按固定模板输出RAG的prompt里写死了模板模型还是经常漏字段或者改措辞。第三类是领域术语理解偏差模型对行业黑话的理解和实际含义对不上检索到的内容它“读不懂”。这三类问题本质上不是检索的问题是模型本身对这个领域的理解不够深。RAG像是给模型开卷考试但模型连题目都读不利索翻书也翻不明白。我当时的判断是如果领域足够垂直、数据足够干净、对输出格式要求足够高那微调是绕不过去的。于是我开始折腾微调然后踩了四个坑。1.2 RAG和微调到底该怎么选这里先把我自己的决策逻辑摆出来不一定对但至少是我真金白银试出来的。RAG的核心优势是知识可更新、可溯源、成本低适合知识频繁变动、需要引用来源的场景。微调的核心优势是改变模型的行为模式和输出风格适合领域固定、格式要求严、需要模型“内化”知识的场景。我后来总结了一个简单的判断表维度RAG更合适微调更合适知识更新频率高每天/每周变低几个月才变输出格式要求宽松自由文本严格固定模板领域术语密度低通用词汇为主高大量黑话数据量文档多但零散有高质量问答对算力预算低只需推理高需要训练可解释性要求高要溯源低只要结果对实际项目中这两者往往要结合。我最后的方案是微调打底RAG补充微调让模型学会领域语言和输出格式RAG负责注入最新变动的知识。但这是后话先说我踩的坑。2. 四个让我半夜爬起来改代码的坑2.1 坑一数据质量比数据数量重要一百倍我一开始的想法很朴素多搞点数据喂饱模型。于是从各种渠道凑了大概两万条问答对格式五花八门有的答案长达三段有的只有一句话有的问题本身就有歧义。清洗工作做得马马虎虎去重、过滤敏感词、统一格式然后就开训了。结果训练loss降得很漂亮从2.3降到0.4但推理效果一塌糊涂。模型学会了输出冗长的废话因为训练数据里长答案占多数它以为“回答越长越好”。更严重的是有些数据里包含了错误的领域知识模型照单全收推理时理直气壮地胡说八道。我后来复盘问题出在没有做数据质量分层。正确的做法应该是先定标准再收数据明确什么样的问答对是合格的比如答案必须包含哪些要素、长度控制在多少字、必须使用哪些术语。人工抽检至少10%我后来抽了500条人工看发现约15%有各种问题这个比例在训练集里会被放大。用模型辅助清洗让一个强模型对数据打分过滤掉低质量样本。我用的方法是让模型判断“这个答案是否准确、完整、符合格式要求”只保留高分样本。数据量不是越多越好我最后只用了3000条高质量数据效果远超之前的两万条。业界经验是垂直领域微调1000到5000条精标数据通常就够了。注意数据清洗的时间应该占总项目时间的60%以上。我一开始只花了20%后面返工花了三倍时间补课。2.2 坑二LoRA参数乱设训练效果天差地别LoRA是微调里最友好的技术之一但“友好”不等于“随便设”。我第一版配置是拍脑袋定的rank8alpha16dropout0.1学习率2e-4。训练倒是能跑但效果就是差口气。后来我系统性地做了一轮参数对比实验才搞明白每个参数在干什么。rank秩决定LoRA矩阵的维度直接影响模型能学多少新东西。rank太小模型学不动rank太大容易过拟合而且显存占用上去了。我的实验结论是rank效果显存占用适用场景4欠拟合学不到领域知识低不推荐8勉强能用格式学会了但知识不够中简单风格迁移16平衡点大部分场景够用中高推荐起点32知识学得扎实但有过拟合风险高数据量大时用64容易过拟合需要强正则很高谨慎使用alpha是缩放因子一般设为rank的1到2倍。我试过alpharank和alpha2*rank后者收敛更快但稳定性稍差。最终我用的配置是rank16alpha32。学习率是最敏感的。2e-4对LoRA来说偏大训练后期loss震荡明显。降到1e-4后曲线平滑很多。我的建议是从1e-4开始试如果loss不降就加到2e-4如果震荡就降到5e-5。target_modules决定LoRA加在哪些层上。默认通常是q_proj和v_proj但我发现加上k_proj、o_proj和gate_proj后效果更好代价是显存多占一点。这个要看你用的基座模型结构不同模型层名不一样。# 我最终使用的LoRA配置 lora_config { r: 16, lora_alpha: 32, lora_dropout: 0.05, target_modules: [q_proj, k_proj, v_proj, o_proj, gate_proj], bias: none, task_type: CAUSAL_LM }实操心得每次只改一个参数记录loss曲线和推理效果。我做了大概20组对比实验才找到最优配置这个过程省不得。2.3 坑三基座模型选错后面全白干我一开始用的是某个7B的通用模型觉得参数小、训练快。训完发现模型的中文能力本身就一般微调后领域知识是学进去了但语言表达还是磕磕绊绊。后来又换了一个中文能力更强的基座同样的数据和参数效果直接上了一个台阶。选基座模型要考虑几个维度中文能力如果业务是中文场景基座的中文水平决定了微调的上限。我试过几个模型中文能力差距很明显。参数量7B适合快速实验13B效果更好但训练成本翻倍70B基本需要多卡。我的经验是先用7B跑通流程效果不够再上更大的。上下文长度如果训练数据里长文本多要选支持长上下文的基座。社区生态有些模型有成熟的微调工具链支持能省很多事。我最后选的是一个中文能力较强的7B模型配合LoRA单卡24G显存就能跑起来。这里要提一下基座模型的选择没有绝对优劣关键看你的场景。通用能力强的模型微调后泛化更好但领域适配可能需要更多数据领域预训练过的模型起点高但可能在其他方面有短板。2.4 坑四评估体系缺失根本不知道模型好不好这是最隐蔽的坑。训练loss降了我以為模型变好了但实际推理时好时坏。问题在于没有建立可靠的评估体系。我后来搭了一套三层评估第一层自动指标。用BLEU、ROUGE这些算生成答案和标准答案的相似度。但这些指标只能看表面不能判断事实正确性。第二层模型评估。用一个强模型当裁判给生成答案打分。我设计的prompt是让裁判模型从准确性、完整性、格式合规性三个维度打分每个维度1到5分。这个方法成本低但裁判模型本身有偏好需要校准。第三层人工评估。抽100到200条人工逐条看。这是最准的但最贵。我的做法是前两层筛出明显差的人工只评估边界case。评估集要独立于训练集而且要好坏样本都有。我一开始评估集全是简单case模型得分很高上线后遇到难case就崩了。后来我刻意在评估集里加入了30%的困难样本才真实反映模型水平。评估层级成本准确度适用阶段自动指标低低训练中快速看趋势模型评估中中训练后批量筛选人工评估高高上线前最终把关3. 从零到一我的完整微调实操流程3.1 环境搭建与工具选型工具选型上我对比了几个主流框架。LlamaFactory的优势是配置化程度高改改YAML就能跑适合快速实验但灵活性稍差有些自定义需求要改源码。另一个方案是直接基于TransformersPeft写训练脚本灵活但工作量大。我最终用的是LlamaFactory做主力自定义脚本做补充。LlamaFactory覆盖了90%的常规需求剩下10%的特殊处理用脚本搞定。环境搭建的步骤# 创建虚拟环境 conda create -n finetune python3.10 conda activate finetune # 安装核心依赖 pip install torch2.1.0 transformers4.36.0 peft0.7.0 pip install llamafactory0.4.0 pip install datasets accelerate deepspeed # 验证GPU可用 python -c import torch; print(torch.cuda.is_available())显存方面7B模型LoRA微调batch_size4、max_length1024的情况下大约需要18到20G显存。如果显存不够可以开梯度检查点、用8bit优化器、或者减小batch_size配合梯度累积。注意不同版本的Transformers和Peft有兼容性问题建议锁定版本号。我遇到过升级后训练脚本报错的情况回滚版本才解决。3.2 数据准备与格式转换数据格式我统一成Alpaca格式这是最通用的{ instruction: 用户的问题或指令, input: 可选的补充输入, output: 期望的模型回答 }如果数据是对话形式转成ShareGPT格式{ conversations: [ {from: human, value: 用户说的话}, {from: gpt, value: 模型的回答} ] }数据清洗我写了一个脚本主要做几件事去重用SimHash做近似去重阈值设0.85。长度过滤问题少于5个字或答案少于10个字的丢掉。格式校验检查JSON字段是否完整。敏感内容过滤用关键词列表过一遍。质量打分用一个强模型给每条数据打分只保留4分以上的。# 数据质量打分示例 def score_data(instruction, output): prompt f请评估以下问答对的质量从准确性、完整性、格式规范性三个维度打分1-5分返回平均分。 问题{instruction} 回答{output} 只返回分数数字。 score call_llm(prompt) return float(score)3.3 训练配置与启动我的训练配置文件核心参数model_name_or_path: /path/to/base/model stage: sft do_train: true finetuning_type: lora lora_rank: 16 lora_alpha: 32 lora_dropout: 0.05 lora_target: q_proj,k_proj,v_proj,o_proj,gate_proj dataset: my_dataset template: default cutoff_len: 1024 max_samples: 5000 overwrite_cache: true preprocessing_num_workers: 8 per_device_train_batch_size: 4 gradient_accumulation_steps: 4 learning_rate: 1.0e-4 num_train_epochs: 3 lr_scheduler_type: cosine warmup_ratio: 0.1 bf16: true gradient_checkpointing: true logging_steps: 10 save_steps: 500 eval_steps: 500 output_dir: ./output启动训练llamafactory-cli train config.yaml训练过程中要盯几个指标loss是否平稳下降、学习率是否按预期变化、显存是否稳定。我遇到过loss突然飙升的情况通常是学习率太大或者数据里有异常样本需要回滚checkpoint排查。3.4 模型合并与推理测试LoRA训练完是适配器权重推理时可以直接加载适配器也可以合并到基座模型里。合并的好处是推理时不需要额外加载适配器速度快一点。from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer base_model AutoModelForCausalLM.from_pretrained(/path/to/base/model) model PeftModel.from_pretrained(base_model, ./output/lora_weights) model model.merge_and_unload() model.save_pretrained(./merged_model)推理测试要覆盖几类case简单问题、复杂组合问题、边界case、对抗性输入。我建了一个测试集每次训练完都跑一遍对比不同版本的效果。4. 踩坑之后总结的排查手册4.1 训练不收敛怎么办训练loss不降或者震荡按这个顺序排查现象可能原因解决方法loss完全不降学习率太小提高到2e-4或5e-4loss震荡剧烈学习率太大降到5e-5加warmuploss降了又升过拟合减少epoch加dropout减rankloss为NaN数据有异常值检查数据用fp32训练排查loss降但效果差数据质量问题重新清洗数据检查标注我遇到过一次loss正常但推理全是乱码的情况排查了半天发现是tokenizer和模型不匹配。基座模型和tokenizer必须来自同一个路径混用会出各种奇怪问题。4.2 显存不够的几种省法显存不够是微调最常见的拦路虎。我按效果排序梯度检查点省显存最明显代价是训练慢20%左右。8bit优化器省优化器状态显存效果损失很小。减小batch_size梯度累积batch_size降到1累积步数加上去效果基本不变。减小max_length如果数据不长把cutoff_len从2048降到1024显存省一半。LoRA rank调小从32降到8显存省一些但效果可能下降。换更小的基座7B不行换3B但效果上限会低。我最终的配置是7B模型LoRA rank16梯度检查点bf16batch_size4单卡24G刚好够用。4.3 推理效果不稳定的排查思路推理时好时坏通常不是模型的问题是推理配置的问题。检查这几个点temperature设太高输出随机性大设太低输出死板。我一般用0.7做生成0.1做确定性任务。top_p和temperature配合用一般0.9左右。repetition_penalty模型复读时加上1.1到1.2之间。max_new_tokens设太小答案被截断设太大浪费算力。根据任务定一般512够用。prompt格式训练时用的什么格式推理时必须一致。我遇到过训练用Alpaca格式、推理用ChatML格式效果直接崩了。实操心得把推理参数也当成超参数来调。我建了一个小测试集固定其他变量只调temperature找到最佳值后再调下一个。4.4 微调后的模型怎么和RAG配合回到最初的问题RAG不够用才微调微调完了RAG还要不要我的答案是要但角色变了。微调后的模型负责理解领域术语、按格式输出、处理常见问题。RAG负责注入最新知识、处理长尾问题、提供溯源依据。配合方式我试过两种方案一微调模型RAG检索结果拼prompt。检索到的内容作为上下文传给微调模型模型基于上下文生成答案。这个方案适合知识更新频繁的场景。方案二微调模型做路由。模型先判断问题类型常见问题直接答需要外部知识的走RAG流程。这个方案适合问题类型分明的场景。我最终用的是方案一因为实现简单效果也稳定。关键是微调时要让模型见过“带上下文的问答”格式否则推理时给它上下文它反而不会用了。5. 一些让我少走弯路的经验微调这件事工具和参数都是次要的核心是对数据的理解和把控。我踩的四个坑三个都和数据有关。数据质量、数据格式、数据分布每一个都会直接影响最终效果。另一个体会是不要追求一步到位。我一开始就想训一个完美的模型结果在参数调优上耗了太多时间。后来改成迭代式先跑通流程用少量数据训一版评估找问题补数据再训。每一轮都有明确的目标效率高很多。还有一点评估集要提前准备好而且要和训练集严格分开。我见过有人拿训练集当评估集loss降了就以为模型好了上线才发现过拟合严重。最后说一个具体的技巧用强模型生成训练数据。如果人工标注成本太高可以用一个能力强的模型来生成问答对然后人工筛选。我试过这个方法生成1000条数据人工筛出600条高质量的比纯人工标注快很多质量也够用。但要注意生成的数据要有多样性不能让模型只学一种表达方式。微调不是银弹RAG也不是。搞清楚你的问题到底是什么再选工具比盲目跟风重要得多。我现在的做法是能用prompt解决的不上RAG能用RAG解决的不上微调非要微调就先从LoRA小rank开始试。这个顺序帮我省了很多时间和算力。
返回列表