尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LLMFit实战:用QLoRA微调大模型的完整指南

LLMFit实战:用QLoRA微调大模型的完整指南 前段时间因为业务需要我搞了一个内部项目代号 LLMFit。名字起得很直白让 LLM 去 fit 你的业务而不是让业务去迁就模型。这个项目前后折腾了大概三周从数据准备、LoRA/QLoRA 微调、效果评估到部署上线踩了不少坑也沉淀出一整套可以复制的工作流。如果你也在纠结“怎么把一个开源底座模型用到自己的垂直场景”这篇文应该能给你省下不少试错时间。LLMFit 解决的其实是个很具体的问题底座大模型虽然通用能力不错但在专业术语、业务规则和回复风格上往往跟真实需求有差距。直接全参微调又太贵数据需求量大普通团队根本顶不住。所以我的思路很简单用 QLoRA 只训练一小部分参数配合高质量的垂直领域数据把模型“扳”到我们需要的轨道上。整套方案在单张 24G 显存的卡上就能跑起来成本可控效果却非常明显。这篇内容适合谁看准备做垂直领域大模型应用的技术人员或者想快速验证“微调这条路是否可行”的产品和技术负责人。你可以没有任何大模型训练经验但最好懂一些 Python至少用过 transformers 或类似的深度学习库。下文会把每个环节的原理、参数设置和实际踩过的坑都拆开讲保证你能照着落地。1. LLMFit 的核心思路模型适配而不是从头训练1.1 为什么是微调而不是全参训练或者纯 RAG大模型落地有两条看似“正统”的路一是用领域数据全参微调二是用 RAG 把知识外挂到检索库里。我一开始也在两条路之间纠结后来被现实教育了。全参微调 7B 模型最低也要几张 A100数据没到几十万条基本看不到稳定收益而且训练周期长迭代一次要等好几天。RAG 虽然部署成本低但在工单回复、格式化输出这类任务上模型需要的是“学会行为”而不只是“查得到资料”。如果单纯靠外挂检索模型还是会用通用话术糊弄业务场景格式不统一术语也不够准。LLMFit 选择的路线是参数高效微调具体来说是 QLoRA。这个方案只训练附加在原有权重旁边的小规模低秩矩阵原模型参数全部冻结。换句话说我们不是让模型重新学一遍语言而是教会它在特定场景下怎么开口说话。这个策略最大的优点是显存占用低7B 模型用 4bit 量化后24G 显存就能训练同时数据量要求也没那么夸张几千到几万条高质量样本就能看到明显改变。跟 RAG 相比它改变的是模型本身的生成偏好和格式习惯而这恰好是很多业务场景最核心的痛点。我个人的判断标准很简单如果任务要求模型“按特定格式输出”“按业务规则思考”微调优先如果任务主要是“从一堆资料里找答案”RAG 优先。很多场景其实两者可以混合使用LLMFit 也留了外挂检索的接口但核心能力一定来自微调之后的模型行为改变。你如果一开始就把所有希望寄托在 RAG 上后面大概率会被模型的“顽固通用风格”气到。1.2 模块划分先搭框架再调模型LLMFit 整个项目我按功能拆成了四个模块数据管道、训练任务、评估系统、部署单元。数据管道负责把原始工单、客服对话等杂七杂八的文本清洗成统一指令格式训练任务管理 QLoRA 的启动、断点续训和日志记录评估系统在每次训练后自动跑一组 case输出对比结果部署单元负责把训练产物合并、导出并提供 HTTP 接口。每个模块之间用配置文件串起来一次训练跑完自动生成一个新版本的模型产物和评测报告。这个框架看着简单但真落地的时候帮我省了很多事。比如有一次队友调了数据清洗规则想看看效果变化传统操作要手动去翻数据脚本、改训练入口、重新跑验证LLMFit 这边只需要改一个 YAML 配置训练脚本会自动读取新数据并触发评估。整个过程数据流是单向的每个环节的输出都有版本记录出问题能快速往前追踪。对于小团队来说我建议你不要一上来就搞复杂的训练平台先做一个能跑通、能记录、能对比的最小闭环比什么都重要。模块之间最容易被低估的是“评估系统”。很多团队训练脚本写得飞起但评估只靠几个人肉眼看几条样本这等于没有评估。LLMFit 把评估设计成独立模块每次训练结束自动跑一份报告里面包含不同任务的指标变化、典型样本对比以及模型输出的错误分类。这样团队成员不用等训练结束再临时商量“怎么看效果”直接打开报告就能对齐信息。1.3 底座模型到底选哪个系LLMFit 对底座模型没有硬性绑定但我实际测试下来7B 参数量级的模型是性价比最好的区间。10B 以下的模型在普通显卡上推理速度快、部署成本低同时经过微调后在垂直任务上完全有能力和十几 B 的大模型掰手腕。再小的 3B 模型虽然跑得更快但指令跟随能力和复杂推理上限比较吃亏当任务需要多步思考时会明显露怯。在具体选型上我优先考虑了中文能力稳定的开源模型比如 Qwen 系列因为它们在中英文混合业务数据上更自然tokenizer 对中文更友好。另外一个很重要的点是你选定的底座模型生态里必须已经有 PEFT 相关的适配不然做完微调后合并导出容易遇到兼容性问题。我建议大家在动手前先看一眼 Hugging Face 模型卡片里对 LoRA 的支持说明尽量挑别人已经踩过坑的模型不要追求新发布但社区反馈很少的版本。除此之外还要考虑模型许可证和商用限制。业务落地不是自己玩很多模型虽然开源但商用条款各不相同尤其是要部署到外部客户环境时更要提前确认。LLMFit 选型的时候专门做了一次许可证排查把所有候选模型的商用条件列成表格才最终确定底座。这一步看起来跟技术没关系但真到了发布节点卡在许可证上会非常被动。2. 数据准备决定微调效果上限的关键环节2.1 原始数据清洗先把垃圾清理干净很多第一次做微调的人把精力都放在模型参数上结果数据一塌糊涂训练出来效果自然拉胯。LLMFit 在数据清洗环节花的时间是整个项目里最多的。我们的原始数据主要来自历史工单、客服聊天记录和帮助文档。第一件事是去重同一用户反复提交的相似问题会把某些样本在训练集中放大很多倍导致模型对这类问题过度拟合。第二件事是清洗无用信息。工单里有大量的时间戳、内部编号、客服备注甚至还有员工之间的闲聊。这些内容如果原样进训练集模型会学到一些莫须有的规律比如看到日期就想到催办。我的做法是先用正则过滤器把明显的结构化噪音删掉再用一个文本分类模型粗筛一遍只保留质量较高的技术问答对。这个过程不需要做得很完美但目标一定要明确每一行样本都应该是“一个指令/上下文 一个标准回答”而不是一段流水账。清洗环节还有一个小技巧把敏感信息做脱敏。真实工单里经常出现手机号、地址、客户姓名这些内容直接进入训练集既不合规也会让模型误以为这些信息是必须输出的。LLMFit 用正则加实体识别把手机号、身份证号、邮箱替换成占位符比如“[手机号]”“[姓名]”。这样模型学到的是“遇到敏感信息时用占位符代替”的行为而不是把客户隐私原样背下来后续部署时能省掉很多合规风险。2.2 指令数据格式用 ChatML 还是自由格式数据格式直接决定训练时 loss 能不能正常收敛。我自己比较推荐 ChatML 格式也就是用一个明确的模板把 system、user、assistant 三段消息包起来让模型在训练时能分清“这条消息是谁说的”。如果你只是简单地把问题和答案拼成一个字符串丢进去模型很容易把 system prompt 和用户输入混在一起微调后出现角色错乱的诡异现象。一个典型的 ChatML 样本长这样{ messages: [ {role: system, content: 你是智能客服助手请根据用户描述判断工单类型并生成不超过50字的回复。}, {role: user, content: 我的宽带在昨天晚上突然掉线重新拨号也连不上麻烦帮我看看。}, {role: assistant, content: 工单类型网络故障。回复已记录您的宽带掉线问题建议先重启光猫和路由器若仍无法联网我们会安排检修人员上门处理。} ] }很多人会忽略一点system 内容在每条样本里要不要一样我的建议是保持稳定但可以有少量变化。完全一样会让模型过度依赖 system 的固定表述稍微换成同义句模型就有点反应不过来。所以我在构造数据时会写几套语义相同的 system 模板按比例随机分配这样模型对任务指令的泛化能力更好。另外数据里最好不要混入“错误答案”。如果你从历史工单里收集数据很多客服回复其实并不标准有的甚至答非所问。这些样本如果直接拿来训练模型会学到错误映射。LLMFit 的做法是让一名业务骨干统一审核所有候选答案保证每条 assistant 回复都是当前业务标准下的最优解。宁可把样本量缩到五千条也不要为了凑数把错误答案塞进来。2.3 数据量、任务比例和多样性怎么把握LLMFit 最终用来微调的样本量大概在一万五千条左右其中工单分类样本八千条回复生成样本五千条剩余是边缘案例和人工构造的困难样本。体感上质量高的五千条数据效果可能会比粗制滥造的两万条更好。所以我的原则是宁可少凑数也不要硬塞重复样本。在任务比例上也要控制不能因为分类任务好标注就塞一大堆生成任务样本太少会导致模型只学会分类不会写回复。另外要特别关注类别均衡。如果工单类型里“网络故障”占了 60%模型很可能把异常类问题都往“网络故障”上归类。我用了一个简单的统计脚本对各个任务的类目数量做分布检查对少数类样本做针对性扩充比如手工改写同义问题、替换设备型号和场景描述。这样跑出来的模型在高频和低频类别上都不会丢分太严重。还有一个容易踩的坑训练集和验证集不能有“数据泄漏”。如果你把同一用户的不同工单分散到两个集合里模型可能只是记住了这个用户的问题模式而不是真正学习了任务逻辑。LLMFit 在切分数据时会按用户 ID 分组让同一个用户的样本只出现在训练集或者只出现在验证集。这样评估出来的指标更真实不会虚高。3. LLMFit 训练实操QLoRA 参数配置与调优记录3.1 环境准备与依赖安装训练环境这块我给 LLMFit 用的是单张 RTX 4090 24G 显存系统是 Ubuntu 22.04Python 3.10。CUDA 版本 12.1PyTorch 2.1 以上。这个组合是目前坑最少的一套很多新版本库对 CUDA 12.4 的兼容性反而不如 12.1 稳定。我的建议是不要追求最新版本能用稳定组合就尽量锁死。核心依赖如下torch2.1.2 transformers4.40.0 peft0.10.0 accelerate0.29.0 bitsandbytes0.43.1 datasets2.18.0 trl0.8.1 einops0.7.0安装完成后先跑一个很小的随机数据集测试确认 bitsandbytes 能正常加载 4bit 模型。我第一次装的时候就因为显卡驱动和 CUDA 版本不匹配直接卡在量化加载那一步报错信息还不直观。后来老老实实按官方要求重装驱动才解决。Windows 环境下 bitsandbytes 的兼容性更麻烦建议直接用 WSL2或者干脆租一台 Linux 云主机。环境问题看起来简单却最容易开头劝退。我碰到的另一个情况是 transformers 和 peft 版本不匹配导致prepare_model_for_kbit_training报参数错误。后来我用 requirements.txt 把整个环境固化下来每台机器都从零安装同一份依赖再也没有这种诡异问题。团队协作时环境一致性比性能优化更重要。3.2 QLoRA 的关键参数每一项都别省先看 LoraConfig这是微调效果的核心。我用的是from peft import LoraConfig, TaskType lora_config LoraConfig( task_typeTaskType.CAUSAL_LM, r16, lora_alpha32, lora_dropout0.05, target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj], biasnone, )这里的 r 是低秩矩阵的秩可以理解为可训练参数的“容量”。r 越大模型适配能力越强但显存占用和过拟合风险也随之增加。我实测 r16 在 7B 模型上效果和 r32 差距不大但训练速度快了将近一半。lora_alpha 控制权重更新幅度一般设为 r 的 2 倍附近比较安全。target_modules 要尽量覆盖所有注意力层和全连接层只改注意力层的话模型在格式学习上会比较迟钝。接下来是 TrainingArguments我的核心设置如下training_args TrainingArguments( output_dir./output/llmfit_v1, num_train_epochs3, per_device_train_batch_size4, gradient_accumulation_steps8, learning_rate2e-4, warmup_ratio0.03, logging_steps20, save_steps500, eval_strategysteps, eval_steps500, bf16True, gradient_checkpointingTrue, optimpaged_adamw_8bit, max_grad_norm0.3, seed42, )几个容易被忽略的点梯度累积步数乘上 batch size 得到的是真正意义上的全局 batch size我的设置是 4×832在这个数量级上训练稳定且收敛速度快。学习率不要照搬全参微调的 5e-5LoRA 通常可以给到 2e-4 甚至 3e-4但太高就容易灾难性遗忘。warmup 比例我固定用 3%太小会让模型在开头几步就把底座分布破坏掉。还有一个细节是optimpaged_adamw_8bit这个优化器比普通 adamw 省显存而且对 QLoRA 训练友好。如果硬件吃紧优先换优化器不要直接砍 batch size。我自己对比过paged 优化器加上 8bit 版本能比默认优化器省下 2-3G 显存训练速度下降也微乎其微。3.3 训练脚本五六十行代码跑完一整个微调流程LLMFit 的训练脚本不复杂核心就是把数据集加载、量化模型、配置 LoRA、启动训练四件事串起来。为了让代码更复用我把数据读取和预处理单独做成函数训练入口只保留配置读取和 Trainer 启动。下面是一段核心代码import torch from transformers import ( AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments, Trainer ) from datasets import load_from_disk from peft import prepare_model_for_kbit_training, get_peft_model model_name Qwen/Qwen2.5-7B-Instruct bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_quant_typenf4, bnb_4bit_compute_dtypetorch.bfloat16, bnb_4bit_use_double_quantTrue, ) model AutoModelForCausalLM.from_pretrained( model_name, quantization_configbnb_config, device_mapauto, torch_dtypetorch.bfloat16, ) tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token model prepare_model_for_kbit_training(model) model get_peft_model(model, lora_config) model.config.use_cache False dataset load_from_disk(./data/llmfit_dataset) trainer Trainer( modelmodel, argstraining_args, train_datasetdataset[train], eval_datasetdataset[eval], data_collatorlambda batch: collator(batch, tokenizer), ) trainer.train() trainer.save_model(./output/llmfit_v1)这段代码里最容易被忽略的是model.config.use_cache False。训练时关掉 KV cache 可以腾出不少显存但推理时一定要改回 True否则生成速度会明显变慢。还有 tokenizer 的 pad_token很多模型的 pad token 是空的如果不设数据拼接时会对齐报错。我在项目里还封装了一个 collator专门处理变长样本。QLoRA 微调时不建议把所有样本都 padding 到同一个 max_length那样太浪费显存。我用动态 padding只让同一个 batch 里的样本对齐到当前批次的最长长度能有效降低训练显存。这个细节看起来不大但对耗时和显存都有影响值得认真写一下。3.4 训练过程中的监控与断点续训训练不是点一下 start 就完事。我习惯开着终端日志每 20 步看一眼 loss 变化趋势。如果 loss 在前几百步不降反升多半是学习率太大或者数据格式有问题这时候最好先停掉不要硬等。如果 loss 下降得很快但验证集 loss 开始上升那么模型可能已经过拟合了这时候可以提前停止或者减少训练轮数。断点续训是长训练任务里救命的。Trainer 的 save_steps 设为 500 后每 500 步会自动保存 checkpoint下次启动时只要trainer.train(resume_from_checkpointTrue)就能从最近的 checkpoint 继续。我有一次训到半途磁盘满了训练中断靠这个功能很快续上没有浪费之前十几个小时的计算量。如果你用的是自己的训练循环也别忘了定期保存模型权重养成好习惯比任何技巧都重要。监控训练曲线时我还会额外看两个指标模型回答的平均长度和特殊 token 的占比。有时候 loss 看着正常但模型生成的内容越来越空很可能是因为数据里短回复比例太高模型学会了“偷懒”。这时候需要回数据管道检查样本分布而不是继续调参数。训练脚本只负责把模型训出来但模型学得对不对还是要靠人对数据的敏感度。3.5 显存踩满怎么办几个立竿见影的优化手段如果 QLoRA 训练时出现 OOM优先做三件事把序列最大长度从 2048 降到 1024看任务是否还能接受开gradient_checkpointing把per_device_train_batch_size降到 2 甚至 1然后提高gradient_accumulation_steps。这三个手段组合起来通常能把 7B 模型的训练显存压到 16G 以内。另外还可以把bf16True换成fp16True但要注意部分老显卡对 bf16 支持不好会报 numeric 错误。如果你用 16G 显存还想训练 13B 模型那就得考虑把序列长度砍到 512并且使用更激进的 4bit 量化设置。但说实话效果会打折扣序列长度太短容易丢失上下文信息训练出来的模型在处理长文本时会出现明显断裂。我的建议是不要为了把一个更大的模型塞进显卡而牺牲序列长度优先用 7B 模型、正常序列长度产出的结果往往更可靠。还有一个偏门但有效的方法检查数据里是不是有异常长的样本。如果你的数据集中混着几万 token 的超长样本哪怕只出现几条也会被填充到 max_length导致显存瞬间飙升。LLMFit 在清洗时会把超过阈值的样本单独处理要么截断要么剔除这样训练过程中的显存波动会小很多。4. 评估闭环让微调效果“看得见、能对比”4.1 离线评估不要只看 loss要看业务指标很多模型训练完大家习惯看一眼 loss 就宣布成功这其实是最大的误区。loss 降下来了只能说明模型在训练集上的拟合程度它到底有没有学会你想要的行为必须用业务指标来验证。LLMFit 的做法是维护一个包含 200 条样本的固定验证集每条样本都有人工标注的标签和理想答案。训练完自动跑一遍记录分类准确率、生成回复的关键词命中率和人工评分。分类任务相对简单直接算准确率和召回率。生成任务就麻烦一点不能只看生成出来的字符串跟参考答案是不是完全一样因为自然语言同一个意思可以有很多种表达。我会先让模型生成回复然后算几项指标BLEU/Rouge 只能做参考真正更有价值的是让人打分。比如“是否识别出了正确的工单类型”“回复是否包含必要的处理建议”“语气是否专业”。这两项评估指标如果比微调前明显变好训练才算真正成功。为了让人工评估更高效我把验证集做成了网页表格每条样本都附上模型的输出评估者只需点“通过/不通过/存疑”三个按钮。这样三个人半小时就能评完全部样本还能自动生成一致率。如果两个人对某条样本判断不一致那条样本就会被标记为困难样本后续可以针对性加训。这套流程简单但比临时发聊天记录靠谱得多。4.2 用 LLM 当裁判的得与失团队人少人工看不过来很多人会想到用一个大模型去给模型生成的回复打分也就是 LLM-as-Judge。这个思路没问题但坑也不少。我第一次做这个评估时直接用 GPT-4 给 Qwen 生成的内容打分分数普遍偏高几乎区分不出好坏。后来加了明确的评分标准要求裁判按“格式正确度、术语准确度、行动建议完整性”三个维度打分还要求给出扣分理由分数才慢慢变得有区分度。有一个细节必须提醒裁判模型和被测模型如果系出同门比如用 Qwen 的大号模型去评 Qwen 的小号模型风格偏好会影响公平性容易出现“自己人夸自己人”的偏向。最好的做法是至少用一个不同系列的模型当裁判或者无论用什么模型都要做人校准。找几十条样本用人工分数和裁判分数对比一下看一致性怎么样再决定要不要信任它。LLM-as-Judge 还有一个隐藏风险裁判模型对长回复天然有好感会认为写得多就是答得好。这跟我们业务想要的“简洁准确”相违背。所以我在评估 prompt 里会明确写“如果回复简洁且包含关键信息应该打高分不要因为字数少而扣分”。另外我会把模型输出和参考回复都放在裁判 prompt 里让裁判先做对比再单独评分这样能降低打分漂移。4.3 合并 LoRA 权重并导出部署训练结束后LoRA 得到的是一个体积很小的 adapter 权重大概几十到几百 MB。部署时有两种选择一种是不合并推理时动态加载 base 模型加 adapter另一种是合并成一个完整的模型再导出。LLMFit 在部署阶段选择合并因为合并后部署流程简单不需要在前端推理服务里额外耦合 PEFT 逻辑。合并代码很简洁from peft import PeftModel base_model AutoModelForCausalLM.from_pretrained(model_name, torch_dtypetorch.bfloat16, device_mapauto) model PeftModel.from_pretrained(base_model, ./output/llmfit_v1) merged_model model.merge_and_unload() merged_model.save_pretrained(./export/llmfit_v1_merged) tokenizer.save_pretrained(./export/llmfit_v1_merged)合并后要用原始加载方式重新加载一次跑几条测试样本确认输出正常。这一步很必要因为有些模型在合并过程中会遇到 embedding 维度对不上或 LoRA scale 未清零的问题不重新验证就可能带着残留在线上出事故。合并后的模型体积通常会比 base 模型稍大一点但差别不大。导出目录里应该有model.safetensors、config.json、tokenizer.json等文件。如果你用的是 vLLM 或 TensorRT-LLM 这类推理框架还要额外转换成对应格式。我一开始图省事直接让推理框架加载合并前的目录结果启动报错后来老老实实按框架文档转换才顺利部署。4.4 线上灰度与 Badcase 回流模型上线之后不能就再也不管。我会先在灰度环境切 5% 流量跑几天每天抽查模型回复把明显的错误案例存下来。这些 badcase 是数据迭代的黄金原料我会把它分类哪些是理解错意图哪些是格式不符合要求哪些是业务规则没学到。然后针对性地补充训练样本让数据管道能自动把 badcase 转成新的训练样本每两周重新训练一个小版本。这样 LLMFit 整体形成了一个持续改进的闭环数据进模型模型出结果结果被评估反馈回流到数据。别小看这一步很多微调项目训练完就宣称效果很好可一旦放到真实流量里立马原形毕露。因为离线验证集总是有限的线上场景千奇百怪。灰度评估的目的不是证明模型完美而是通过小范围试错快速发现大批量样本里才暴露出来的分布偏移。我在灰度期间还会记录一个指标叫“人工接管率”也就是模型回复被客服二次修改的比例。这个数字比任何离线指标都真实它直接反映了模型在真实业务中的可用程度。第一次灰度时接管率高达 40%我们通过两个版本的 badcase 回流训练后降到了 15% 左右。如果你的微调项目没有类似的过程指标很难判断模型是不是真的在进步。5. 避坑速查最容易拖慢进度的四个问题5.1 数据格式错乱loss 像过山车第一次训练 LLMFit 时我用的数据是从多个 CSV 拼凑出来的有的行有多条 assistant 消息有的行缺少 system 消息模型在训练时一会儿学规则一会儿学对话。最明显的症状是训练 loss 波动很大完全不收敛。排查下来问题根源是数据脚本在拼接时写漏了分隔符。后来我给每条数据都加了一个格式校验函数严格检查 messages 序列是否以 system 开头、是否只有一个 assistant 结尾。校验不过的直接丢弃并报警从根上杜绝了脏数据进训练集。这个校验函数看起来很笨但非常必要。它会在数据进入训练流程之前把模板缺失、角色顺序错误、空内容等常见问题全部拦下来。LLMFit 现在甚至会把校验结果输出成一份日志哪条样本因为什么原因被丢掉一目了然。数据管道越自动化越要保证每一步都有痕迹否则出了问题根本定位不到。5.2 灾难性遗忘比过拟合更隐蔽训练到第二第三个 epoch 时模型在业务样本上表现越来越好但稍微问一些常识问题就开始胡说八道。这是很典型的灾难性遗忘问题。我试过把学习率从 2e-4 降到 1e-4效果有好转但收敛速度慢了不少。后来我在训练数据里混入了 5% 到 10% 的通用指令数据让模型一边学业务一边保持通用能力。这个做法见效很快业务指标只掉了不到 2%通用能力却恢复到了正常水平。如果你也发现微调后模型“变呆了”可以考虑往数据池里加一些通用语料。通用指令数据不用太复杂直接从公开的指令数据集中抽一部分即可。关键是混合比例要控制好太多会稀释业务能力太少又起不到抗遗忘的作用。我试过 5% 和 15% 两档5% 时候通用能力恢复有限15% 时候业务指标下降明显最后定在 8% 左右。不同任务和数据分布对最优比例影响很大这个数值需要自己跑实验。5.3 模型学会了“偷懒”只输出格式不输出内容还有一个很有意思的问题模型对 system prompt 做了过拟合学会了问“工单类型是什么”但回答里根本没有具体的类型判断只给了模板话术。这在训练数据里如果存在大量简单样本模型很容易找到捷径。我的对策是增加困难样本比例每一条样本都必须逼着模型先分析业务内容再输出答案同时把回复长度控制在合理范围避免模型为了凑字数而说废话。效果不错但需要你反复去调数据分布这是一个不断试错的过程。我后来还在训练数据里加入了少量“陷阱样本”也就是用户描述非常模糊、表面上像是一个类别、实际上需要进一步确认的情况。这类样本逼着模型学会追问而不是草率下定论。加了这些样本之后模型线上应对模糊问题的能力明显提升不会一上来就给出错误分类。真实业务数据里模糊 case 的比例一般不会太低如果训练数据里全是清晰 case模型上线后就会显得很“愣”。5.4 版本管理混乱改过什么全忘了最后一条算管理上的坑。微调十几次之后如果不对产物和数据进行版本管理很快就分不清哪个模型版本是用哪份数据训出来的。LLMFit 后来接入了最简单的 git 加文件夹命名规则每次数据变更、训练参数变更都要打一个版本号模型产物目录里也带上同样版本号。虽然不复杂但救了我无数次。小团队不要迷信什么重型平台先把版本规则定好就能避免一个人改数据、另一个人还在用旧数据训模型。版本号里我会包含日期和业务标签比如20250401-工单分类-v3这样看到目录名就知道是哪个业务、第几个版本。同时会把每次训练用的数据样本哈希值记录到一个 manifest 文件里方便追溯“当前模型究竟看过哪些数据”。这个习惯在排查线上问题时帮了大忙有一次模型回复异常对照 manifest 发现是某个数据源被误更新了五分钟就定位到了根因。LLMFit 这个项目跑到后面我最大的感受是微调大模型的难度不在“跑通代码”而在把数据、训练、评估、迭代这四个环节串成一个稳定的闭环。模型训练本身已经非常成熟真正决定业务效果的是你愿不愿意花时间把每个细节打磨到位。如果你正准备开始这样的项目建议先把数据管道和评估体系搭好再考虑增加模型规模这条路走得最稳。
返回列表