ARTICLE DETAIL

资讯详情

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

System 1决策微调实战:用LoRA打造低延迟Laya决策模型

System 1决策微调实战:用LoRA打造低延迟Laya决策模型 最近在做智能客服的请求路由改造核心诉求很简单把线上问题在几百毫秒内分到对应处理链路不需要模型给出长篇分析只需要“快、准、稳”的结果。顺着这个方向翻了一圈最终选定了 GitHub 上 17K Star 的开源项目 Laya 做 System 1 决策微调。网上关于 Laya 和 Jev 的讨论不少标题虽然写的是“爆打 Jev”但我的态度很明确不要盲目站队按场景选模型。这篇就把我从安装、数据构造、LoRA 微调、部署到实测的完整过程记录下来包括踩过的坑和可以直接抄走的配置。1. 为什么在决策场景我会先看 Laya这一节先说清楚前提我不打算说服所有人放弃 Jev而是解释我在 System 1 决策场景下选择 Laya 的理由。理解这个取舍比直接抄命令重要得多。1.1 System 1 决策到底指什么System 1 这个概念来自卡尼曼《思考快与慢》指人类大脑里快速、自动、不怎么费力的那套判断机制。放到大模型工程里System 1 决策任务指的就是不需要深度推理链、不要求模型写出完整分析过程但需要快速产出唯一结论的场景。典型例子包括意图识别用户说“我要退款”模型直接判定为refund而不是输出一段“根据您的描述我认为您可能需要进行退款申请具体流程如下...”。请求路由把工单分到售前、售后、技术、投诉等不同队列。工具选择给智能体判断“这个问题应该查订单库还是查库存表”。风险初筛把高风险请求拦截下来交给人工复核。标签分类给文本打上多标签分类结果。这类任务的共同点是延迟敏感、并发量高、准确率要求不低但也没有严格到医疗/金融核心决策那种程度。用大参数模型做这些事等于开着卡车去买菜——能到但成本离谱。而专门为 System 1 场景微调过的小模型反而能跑出更好的性价比。1.2 Laya与Jev的取舍我在选型前测过 Jev也测过 Laya。Jev 的优势在于推理链路完整、可解释性强适合需要“给出理由”的场景。但在我这个决策场景里绝大多数请求根本不需要理由只需要一个标签。把 Jev 的完整推理链跑完再提取结论延迟翻了好几倍对线上 SLA 很不友好。Laya 的定位正好和 System 1 决策贴合小参数量、短输入、单轮决策输出。我在本地 4090 上做了初步对比微调后的 Laya 在意图分类测试集上的准确率和 Jev 相当但 P95 延迟大约只有 Jev 的三分之一。这个结果决定了我最终选 Laya。当然如果你做的是复杂代码生成、长链条逻辑推理、深度问答这类 System 2 场景那 Jev 可能更合适。选型不是比参数大小而是看任务形态。对比项Laya决策微调后Jev通用基座体积小适合单卡部署较大通常需要多卡推理链单轮直接出结论长推理链可解释性强延迟低高适合任务System 1 决策、路由、分类System 2 深度推理、复杂生成微调成本LoRA 单卡可跑需要更多资源许可证/生态开源社区活跃需要关注商用条款2. 环境准备与安装把基础环境一次弄干净很多人微调失败不是模型问题而是环境一团乱。这里分享一下我的安装过程目标是用一个干净的 conda 环境跑通 Laya 微调和部署而不是污染系统全局 Python。2.1 硬件与系统要求我的环境如下可以作为参考操作系统Ubuntu 22.04GPUNVIDIA RTX 4090 24GB单卡驱动版本535CUDA12.1Python3.10内存64GB如果你手中的显卡是 24GB 显存微调 7B 量级的 Laya 用 LoRA 完全够用。如果显存只有 16GB可以把per_device_train_batch_size调小或者用 QLoRA 加 4bit 量化。如果你连 GPU 都没有建议先用 Colab 或云 GPU 实例跑通再上生产。2.2 安装步骤与注意事项先用 conda 创建独立环境避免和服务器上其他项目打架。我在这一步吃过亏之前用全局 Python 环境装 torch 和 transformers结果跟 TensorFlow 版本冲突排查了大半天才发现是两个库的 ABI 不兼容。conda create -n laya python3.10 conda activate laya pip install --upgrade pip接下来安装 PyTorch。这里注意不要直接裸装pip install torch默认源装的可能是 CPU 版或者和你 CUDA 版本不匹配。用官方推荐的搭配方式pip install torch2.1.2 torchvision0.16.2 torchaudio2.1.2 --index-url https://download.pytorch.org/whl/cu121然后安装微调套件 LLaMA-Factory。这个工具的特点是把数据预处理、LoRA/QLoRA 微调、模型导出、推理部署都封在了一套 CLI 和 WebUI 里。对于做决策微调这种常规任务能少写很多胶水代码。git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e .Laya 基座模型的权重下载我建议走 Hugging Face 的镜像或者直接用 LLaMA-Factory 内置下载逻辑。模型体积不大7B 版本大概 15GB 左右。下载完以后把路径记好后续配置文件和命令行都要引用。这里有个容易踩的坑LLaMA-Factory 会根据模型名称自动推断模板template。不同基座模型的 prompt 模板结构差别很大Laya 用的是和 Qwen 系兼容的 ChatML 格式。如果你在自定义数据集时擅自改了模板结构后面训练出来会得到一堆乱输出。后面第三节会展开说。3. 微调前的数据准备System 1 决策数据集怎么构造数据是微调的真正重心。很多人一上来就训练结果模型输出一股“机翻味”核心原因就是数据格式和任务定义不对。这节重点讲决策任务数据集的构造逻辑。3.1 决策类数据的 ChatML 格式微调用的是监督微调SFT数据要组织成“指令-回答”的结构。System 1 决策场景要求输出简洁、无废话所以回答部分必须是一个直接可用的结果而不是解释。这里以“工单自动分类”为例一条训练样本可以组织成{ conversations: [ { from: system, value: 你是工单分类助手只需要输出一个分类标签订单问题、物流问题、售后问题、其他。不要输出任何解释。 }, { from: human, value: 我上周买的东西到现在还没发货物流信息一直停在待揽收。 }, { from: gpt, value: 物流问题 } ] }注意几个细节。system 提示词里明确写了“只要输出标签”这个约束必须写清楚。人类输入是真实场景的用户话术越口语化越好。模型输出是一个不多不少的分类结果。我构造数据集的时候会用脚本把历史工单标题和对应人工分类标签批量转成上述格式。重点是保证标签一致性和边界清晰比如“退款”和“退货”容易被混为一谈要在 system 提示词里做区分或在数据里补充边界样本。3.2 数据常见问题刷掉这些坑数据构造阶段我重复踩了几个坑列出来给你们避雷回答太长某个样本里模型回答写了三行解释这会教坏模型让它输出变得啰嗦。决策类回答必须强制单行短文本。指令污染把原始工单里的敏感信息原样写入既没必要又可能造成隐私问题。文本要脱敏。标签不均衡订单问题占 80%其他类型加起来才 20%模型会学成“不管什么都输出订单问题”。建议先做标签分布分析然后决定要不要过采样/欠采样或加权。system 提示词不统一每条样本的 system 提示词建议全局统一最多只做微调不要每一条都不一样否则模型会混乱。输入输出没做长度控制虽然 Laya 支持最长 8K 上下文但决策任务不需要长文本。我给cutoff_len设置 1024把超长文本截断效率更高。另外一个建议先只构造 500~1000 条高质量样本跑一轮小实验看模型行为是否符合预期再决定要不要扩大到几千几万条。很多决策任务两三千条就够用了数据量不是越多越好噪声样本多了反而拉低效果。4. 用 LLaMA-Factory 做 LoRA 微调环境装好、数据就绪之后才进入正题微调。这里我选择 LoRA而不是全量微调原因有几个下面先讲原理再给配置。4.1 为什么选 LoRA 而非全参全量微调Full Fine-tuning会更新基座模型的全部参数效果上限高但显存开销和训练时间都非常可观。7B 模型全参微调在 4090 上几乎跑不起来除非用多卡或者大量梯度累积而且非常容易灾难性遗忘——模型在决策任务上变好却把基础的指令理解能力搞坏。LoRA 的做法是冻结基座模型全部参数只在 attention 层的权重矩阵旁路插入低秩矩阵训练时只更新这些低秩矩阵。实际效果上LoRA 用很小的参数量就能贴近全参微调的效果训练显存低、速度快、还能随时切换多个任务专用适配器。对于 System 1 决策这类任务LoRA 完全够用因为任务本身不需要学习全新知识只需要改变模型的输出风格和判断偏好。如果你显存十分紧张还能用 QLoRA把基座模型量化为 4bit 再训练 LoRA16GB 显存也能跑 7B 量级。4.2 配置文件与训练命令LLaMA-Factory 的训练入口推荐用 YAML 配置文件比命令行参数写一堆更清晰、好复用。我用的配置文件如下model_name_or_path: /data/models/Laya-7B template: qwen stage: sft finetuning_type: lora dataset: decision_sft cutoff_len: 1024 learning_rate: 2.0e-4 num_train_epochs: 3.0 max_samples: 100000 per_device_train_batch_size: 8 gradient_accumulation_steps: 4 lr_scheduler_type: cosine warmup_ratio: 0.1 logging_steps: 20 save_steps: 500 eval_strategy: steps eval_steps: 500 output_dir: outputs/lora-laya-decision几个关键参数解释一下template: qwen我踩过最大的坑之一。Laya 基座用的是 Qwen 系模板如果你填错模板比如填成llama或chatglmprompt 结构直接错位训练不报错但推理结果会非常离谱。这个参数务必和模型实际模板保持一致。cutoff_len: 1024决策任务不需要长上下文设 1024 足够。设太长会浪费显存训练速度也慢。learning_rate: 2.0e-4LoRA 微调常用 1e-4 到 5e-4 之间我用 2e-4 起步。如果你的数据量少建议降到 1e-4 并加长训练轮数避免过拟合。per_device_train_batch_size: 8在 4090 24GB 上跑 7B LoRA 是安全的。如果你报 OOM先降到 4。gradient_accumulation_steps: 4和 batch size 结合后实际每次更新梯度的 batch 是 32这个规模对决策数据集来说是合理的。数据集要在 LLaMA-Factory 的data/dataset_info.json里注册{ decision_sft: { file_name: decision_sft.json, formatting: sharegpt, columns: { messages: conversations }, tags: { role_tag: from, content_tag: value, user_tag: human, assistant_tag: gpt } } }注册好之后执行训练llamafactory-cli train configs/lora_sft.yaml也可以在浏览器里操作CUDA_VISIBLE_DEVICES0 llamafactory-cli webuiWebUI 对刚上手的人来说比较直观但生产环境落脚本还是推荐 YAML 方式。4.3 训练过程观察与调整训练开始后loss会逐步下降。我观察到的正常情况是第 100 步左右 loss 从 1.2 左右降到 0.6 附近第 500 步后开始缓慢下降并趋于平稳。如果你的 loss 在训练刚开始就非常低比如 0.01那大概率是数据有问题——模型只是背答案没有学会决策逻辑。训练轮数也别贪多。决策任务一般 2~3 个 epoch 足够。超过 5 个 epoch 以后loss 虽然在降但评测集准确率往往开始波动这就是过拟合信号。LoRA 微调时要多盯 eval 指标不要只盯训练 loss。我当时跑了 3 个 epoch配置了eval_strategy: steps在每个 checkpoint 完成后跑一次验证集。最终选择验证集准确率最高的 checkpoint而不是最后一个。5. 部署与 System 1 决策实战训练完成后需要把 LoRA 适配器合并到基座模型里或者保留适配器单独加载。下面讲两种方式并给一段可以直接使用的调用代码。5.1 导出与合并LoRA 适配器训练完成后我建议先合并导出再部署。合并后的模型就是一个正常模型文件部署环节不依赖 LLaMA-Factory也不用在每次加载时额外拼接适配器省心很多。导出的配置文件configs/lora_export.yaml如下model_name_or_path: /data/models/Laya-7B adapter_name_or_path: outputs/lora-laya-decision template: qwen finetuning_type: lora export_dir: outputs/merged-laya-decision export_size: 5 export_legacy_format: false执行导出llamafactory-cli export configs/lora_export.yaml导出后模型存放在outputs/merged-laya-decision可以直接被 vLLM、Ollama 或 Transformers 加载。5.2 用 vLLM 部署并调用我推荐用 vLLM 做在线部署吞吐量比原生 Transformers 高很多对 System 1 决策这种高频调用场景尤其重要。pip install vllm vllm serve outputs/merged-laya-decision --max-model-len 4096 --gpu-memory-utilization 0.85 --port 8000启动完成后用 Python 调用from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keyEMPTY ) def decide(user_input: str) - str: response client.chat.completions.create( modeloutputs/merged-laya-decision, messages[ {role: system, content: 你是工单分类助手只需要输出一个分类标签订单问题、物流问题、售后问题、其他。不要输出任何解释。}, {role: user, content: user_input} ], temperature0.2, max_tokens50 ) return response.choices[0].message.content.strip() if __name__ __main__: test_cases [ 发货三天没物流信息, 收到的商品有破损, 我要退款不要了, 请问你们客服电话是多少 ] for case in test_cases: print(f输入: {case}) print(f决策: {decide(case)})这里有几个细节。第一temperature要调低我设置 0.2。决策任务需要确定性输出温度太高模型会随意跳跃。第二max_tokens不用给大决策回答一般 10 个 token 以内给 50 足够别让模型有空间生成长篇。第三system 提示词必须和训练时保持一致测试时如果换了提示词效果一定会打折扣。如果你不想上 vLLM也可以导出为 GGUF 后用 Ollama 跑适合边缘节点部署。这次我为了压延迟选了 vLLM实测单请求差不多在几十毫秒到一两百毫秒之间效果符合预期。5.3 一套简单的效果评估方法线上效果评估不要只看准确率还要看延迟和输出稳定性。我在验证集上做了三类评测准确率预测标签和真实标签的匹配比例。P95 延迟压测时 95% 请求的响应时间。这个比均值更稳均值容易被长尾拉高。无效输出率模型输出不在这几个合法标签里的比例。这个指标很关键因为决策系统最忌讳模型说出“我觉得您应该联系物流公司”。最终结果大致如下仅代表我这组实验指标LayaLoRA 微调后Jev原版直出准确率94.2%94.8%P95 延迟130ms420ms无效输出率0.4%2.1%部署卡数1 张2 张单看准确率Jev 略高一点点但延迟和无效输出率不占优。决策系统需要的是短平快如果有人拿“准确率高 0.6% 但延迟翻三倍”给你画饼你可以把这篇丢给他看。6. 常见问题与排坑速查最后把这次实操中遇到的高频问题整理成速查表。这些问题看起来小但每一个都能让你卡上半天。症状可能原因解决方式训练直接 OOMbatch size 过大或cutoff_len太长降低per_device_train_batch_size到 4 或 2或开启 QLoRA 4bit 量化训练正常但输出无意义template填错prompt 结构错位确认 Laya 的模板是qwen不要凭感觉填模型输出一堆解释而不是标签训练数据里存在长回答样本temperature 太高清理数据设置temperature0.2max_tokens50准确率上不去loss 很快到 0数据标签不均衡或存在重复样本检查标签分布做去重和过采样模型在真实数据上效果好在测试集上差测试集分布和真实场景偏差大重新划分训练/验证集按时间切分更合理合并导出后模型加载失败导出路径或格式配置不对清空export_dir后重跑注意export_legacy_format: falsevLLM 部署后延迟很高GPU 利用率不足或并发配置太低增大--max-num-seqs并考虑开启--enable-prefix-caching这里单独补充两个经验。首先是训练数据质量优先级高于数据量。我试过把数据从 2000 条扩到 10000 条准确率反而掉了近两个点原因是盲目从历史工单池里捞数据捞进了大量低质量、重复、标签错乱的样本。后来我花了半天清洗数据把数据集压缩回 2600 条效果立刻回升。其次是决策类任务一定要加一层非法输出兜底。无论模型训练得多好总有概率输出不在预设范围内的内容。部署侧我建议在代码里加白名单校验不是合法标签就统一走默认策略。这个兜底逻辑简单但极重要能避免线上出现一堆奇怪的脏数据反馈到下游系统。另外一个容易被忽略的点微调后模型在 LLaMA-Factory 测试界面表现良好不代表生产环境表现一致。测试界面往往不会并发压测。我建议部署后先用脚本跑几百条真实流量回放对比标签分布是否合理确认没问题再切线上。我在实际项目中连续跑了两轮实验最深的体会是System 1 决策微调的成败一半取决于数据构造另一半取决于有没有把部署侧约束想清楚。模型本身反而不是瓶颈。选 Laya 还是 Jev本质上是在延迟、成本和效果之间做权衡没有绝对的黑白对错。如果你刚开始接触这类任务建议先用小数据集把这条链路完整跑通感受一下数据清洗、模板匹配、LoRA 调参、并发部署分别会有什么坑。跑通一次之后后面想换基座模型、换任务场景都会顺手很多。
返回列表