
最近有个开源项目在 GitHub 上热度涨得特别猛17K Star 只用了很短时间就到了名字叫Laya。圈子里很多人把它当Jev的平替甚至上位替代来聊尤其是做System 1决策场景的人几乎人手一份。我花了两天时间从安装到微调完整跑了一遍整体体验确实和以往玩别的模型不太一样这篇就当是实操记录想动手的可以直接照着走。先说清楚这是什么东西、解决什么问题。Laya 是一个面向轻量级本地部署的决策模型主打低延迟、快响应非常适合用在那些需要在几百毫秒内给出判断的场景比如实时风控预筛、客服意图分类、内容初审打标这类“看一眼就能拍板”的任务。它跟 Jev 这类偏通用对话和复杂推理的模型不一样Laya 更接近“思维速度”这个方向——也就是 System 1。不需要多步思考、不需要链式推理而是直接用训练好的直觉判断给结果这种特性决定了它在工程落地时非常香。适合谁来参考这份教程一类是刚入行想做模型微调、但被 Jev 的高门槛或授权限制劝退的开发者另一类是自己手上有业务数据、想把分类或决策能力集成到内部系统的工程师。下面内容从环境搭建讲起一直走到 LoRA 微调和最终推理验证全程有命令、有代码、有踩坑记录照着操作基本能做到今天看、今天跑通。1. 先搞清楚Laya 是什么为什么 System 1 决策值得单独做个教程1.1 这个项目到底解决了什么问题常见的通用大模型在回答问题时默认走的是 System 2 路线想清楚再回答先给推理步骤再给结论。这种做法在复杂分析时很好用但在业务系统里却是灾难。举个例子一个在线交易平台需要在 300 毫秒内判断一笔订单是否异常如果每次都让模型“慢慢想”先把上下文捋一遍再输出一大段分析延迟直接没法看。Laya 的设计思路就是绕开这一步。它把判断压缩成了一种“反射式”输出输入一段请求直接给出类别或标签没有多余推理痕迹。这种能力恰恰是靠微调训出来的而不是基座模型天生就有的。所以 Laya 的完整玩法不是“拿来即用”而是“拿来之后针对自己的业务场景做垂直微调”把通用能力收敛到特定决策边界上。我实际测试下来的感受是原始 Laya 权重在泛化任务上表现中规中矩但只要你给它喂几百条自己的业务样本做 LoRA 微调它在那个特定场景下的判断准确率能提升到令人意外的水平而且推理速度几乎不受影响。这也是为什么教程标题里要把“安装”和“微调”放在一起缺了后一半Laya 的威力打不出三成。1.2 System 1 与 System 2 的边界在哪里这里稍微展开一下。System 1 和 System 2 是认知科学里的概念一个代表快思考、直觉判断一个代表慢思考、逻辑推演。做工程落地时一定要先分清手里的任务更适合哪条路线。判断不清楚的话可以这样类比你开车遇到路口突然蹿出一个人踩刹车是 System 1但如果是做一份年度预算报告逐项核对数据、推演不同方案的收益那就是 System 2。大模型架构天然更擅长 System 2因为自回归生成本身就是“一步一步想”的过程。想让模型像 System 1 一样快速反应就需要通过大量的短输入-短输出样本把这种行为固化下来。所以在做数据准备时我强烈建议不要使用那些带思维链的长输出数据集。我见过一些朋友用通用对话数据微调 Laya结果模型学会了长篇大论延迟一下子拉上去完全背离了项目初衷。微调 Laya 的数据原则上就是“问句-短标签”结构尽量压缩 answer 的长度最好控制在 10 个 token 以内。1.3 和 Jev 对比为什么社区都在聊“爆打 Jev”我最早看到“爆打 Jev”这个说法是在社区帖子标题里一开始以为只是标题党实际对比之后发现确实有客观原因列个表大家自己感受。维度JevLaya部署方式官方 API 接入需要鉴权权重开源本地部署推理延迟受网络和 API 限速影响本地纯 GPU 推理延迟稳定微调自由不支持自定义权重完全开放支持 LoRA/QLoRA离线可用不可用完全离线定位通用对话/复杂推理垂直决策/快速判断对于内部系统来说数据不出内网是硬需求。Jev 用起来虽然方便但数据要过一遍外部接口很多业务场景根本不敢这么干。Laya 的优势在于它把决策能力变成可以自己掌控的本地资产加上开源许可宽松二次开发和商业化都比较省心。不过说句公道话“爆打”不是全方位的Laya 在复杂长文本理解和多轮对话上明显不如 Jev跨语言能力也只是及格水平。我的判断是如果你做的是强决策、弱对话的业务场景Laya 比 Jev 合适得多反过来则选 Jev 更稳。2. 安装部署从零开始把 Laya 跑起来2.1 环境准备硬件、Python 和 CUDA 版本先说硬件门槛。Laya 原始权重在 7B 这个档位直接用 FP16 推理大概需要 14GB 显存如果你是 16GB 显存的消费级显卡刚够跑。做微调的话显存就不太够了建议走 QLoRA4bit 量化后训练显存能压到 10GB 以内具体的微调参数后面会写。我的测试环境供参考CPU13 代 i5显卡RTX 4070 12GB内存32GB系统Ubuntu 22.04Python3.10CUDA11.8装依赖之前先确认 CUDA 环境没问题。命令行执行nvidia-smi看到驱动版本和 CUDA 版本就 OK。然后创建虚拟环境避免把系统 Python 弄乱。python3 -m venv laya-env source laya-env/bin/activate2.2 下载模型权重与安装依赖Laya 的权重发布在 Hugging Face 和 ModelScope 上国内环境优先用 ModelScope下载速度快很多不需要额外配置。以 ModelScope 为例pip install modelscope modelscope download --model laya-ai/Laya-7B --local_dir ./models/laya-7b下载完成后目录里应该有config.json、tokenizer.model、pytorch_model-*.bin或 safetensors 文件。这里要注意看下载的文件是 safetensors 还是 bin后续加载代码里要对上。我下载的时候默认给的是 safetensors加载时用trust_remote_codeTrue即可。依赖包建议一次性装齐pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install transformers accelerate bitsandbytes peft datasets需要说明的是 torch 版本必须和 CUDA 匹配。我之前用默认 pip 源装过 CPU 版 torch结果显存完全用不上推理慢到无法接受。装完可以用下面这行验证 GPU 是否可用import torch print(torch.cuda.is_available(), torch.cuda.get_device_name(0))输出True NVIDIA GeForce RTX 4070就说明环境没问题。这一部验证过了后面的坑会少很多。2.3 首次推理验证模型是否真正正常部署之后做的第一件事不是直接上业务数据而是用一条最简单的测试跑一遍推理。我习惯用一段很短的控制台代码确认加载和生成链路都没问题from transformers import AutoModelForCausalLM, AutoTokenizer model_dir ./models/laya-7b tokenizer AutoTokenizer.from_pretrained(model_dir, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypeauto, device_mapauto, trust_remote_codeTrue ) prompt 这笔订单金额 8998收货地址与常用地址不一致新设备登录。请给出风险等级 inputs tokenizer(prompt, return_tensorspt).to(cuda) output model.generate(**inputs, max_new_tokens20) print(tokenizer.decode(output[0], skip_special_tokensTrue))正常情况下输出应该是一个简短结论比如“高风险”或“中风险”。这里有几个容易踩的坑trust_remote_codeTrue必须带上Laya 用了自定义 modeling 文件不带就会报AutoModel找不到类。max_new_tokens不要设置太大System 1 场景建议 10-50太大模型容易放飞自我开始讲故事。如果是 12GB 显存跑 7B FP16首次加载会看到显存占用接近 13GB属于正常现象但推理时如果 OOM 了就改用 4bit 量化加载。4bit 加载方式如下from transformers import BitsAndBytesConfig quant_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypefloat16, bnb_4bit_use_double_quantTrue, bnb_4bit_quant_typenf4 ) model AutoModelForCausalLM.from_pretrained( model_dir, quantization_configquant_config, device_mapauto, trust_remote_codeTrue )这个模式之后做微调也会用到所以建议从第一次接触开始就直接用 4bit省得后面重新适应。3. System 1 决策实战让 Laya 在真实业务场景里干活3.1 什么样的业务流程适合交给 Laya不是所有决策都能用 System 1 解决我梳理了几个识别标准满足越多越适合输入短单条文本不超过 500 字不需要跨上下文理解。输出短结果是一个标签、一个类别、一个分数不需要解释说明。判断快业务要求毫秒级响应不能有长链路思考。边界清类别集合是固定的比如“低/中/高”或“正常/异常”。样本足至少能攒出几百条带标签的历史数据。满足这些特征的典型场景包括客服工单自动分类、内容安全预审初筛、供应链异常告警分级、营销活动人群分桶。我在测试时选了客服工单分类来跑因为这种场景数据好攒、标准明确适合作为教学样例。3.2 用 Laya 做快速分类的完整代码部署通过后直接写一个适配业务的小服务。下面这段代码用 FastAPI 包一层 HTTP 接口方便业务系统调用from fastapi import FastAPI from pydantic import BaseModel from transformers import AutoModelForCausalLM, AutoTokenizer import torch model_dir ./models/laya-7b tokenizer AutoTokenizer.from_pretrained(model_dir, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypeauto, device_mapauto, trust_remote_codeTrue ) model.eval() app FastAPI() class Item(BaseModel): text: str CATEGORIES [售后, 退款, 物流, 咨询, 投诉] app.post(/classify) def classify(item: Item): prompt f将以下客服会话归类为{、.join(CATEGORIES)}之一。\\n会话{item.text}\\n类别 inputs tokenizer(prompt, return_tensorspt).to(cuda) with torch.no_grad(): output model.generate(**inputs, max_new_tokens5, do_sampleFalse) result tokenizer.decode(output[0], skip_special_tokensTrue) label result[len(prompt):].strip() return {label: label}这里有一个非常关键的调参习惯System 1 决策不要打开do_sample。采样模式会让模型在几个相似类别之间摇摆产生不稳定输出。直接把do_sampleFalse可以保证同一个输入在任何时刻都得到同一个结果这在业务审计时非常重要——如果你对同一笔订单重复请求返回不同风险等级客户和风控部门都会很头疼。另外 max_new_tokens 设为 5理论上输出一个词足够给到 5 是留余地。之前我遇到过一个情况模型输出了类别退款\n\n这样的换行不加 strip 的话会把\n带进返回结果前端再存库就会出现乱数据。所以我习惯于label result[len(prompt):].strip().split()[0]只取第一个词作为结论。3.3 延迟与准确率的平衡经验跑完一遍之后我记录了一组关键数据FP16 模式下平均单次推理延迟大约 260ms4bit 模式下大约 340ms。对于 System 1 场景来说这两个值都在可接受范围。如果要求压到 100ms 以内就需要上量化加速或者用小档位模型。还想提升速度的话有两条路可以走。第一条是开启torch.compile实测推理快大概 15%但首次运行要额外花时间做编译适合长期服务场景第二条是把max_new_tokens从 20 压到 5这个改动对延迟影响很大因为自回归解码是逐 token 生成的少生成一个 token 就少一次前向计算。但要注意输出压缩到 5 个 token 要求你的 prompt 写得足够干净明确如果 prompt 里信息不足模型只能瞎猜。准确率端我用了一份 2000 条历史客服工单做测试Laya 原样跑出来的准确率大概是 78%。说实话这个水平直接上线不够硬主要原因是通用权重对业务术语理解有限。这个问题的解决办法就是第 4 章和第 5 章要讲的微调。4. 微调前必须要懂的三个核心概念4.1 基座模型怎么选直接用 Laya 还是基于 Qwen 微调这里应该是社区里问得最多的点是不是需要依托千问Qwen模型来进行微调答案是对的但也别绕太远。Laya 官方给出的推荐路线就是基于 Qwen 系列基座做二次微调。因为 Laya 发布的是完整的开源权重你可以直接用它但如果你想在 Laya 的 System 1 能力基础上同时补充一些中文长文本理解能力那基于 Qwen2.5-7B 起步微调会更稳妥。Qwen 的中文韧性很强做垂直数据拟合时 loss 下降更快而且社区生态好LLaMA-Factory 对 Qwen 的支持非常完善。我的建议是分两种情况业务纯中文、输入较短、对中文口语容忍度要求高基于 Qwen2.5-7B 微调效果一般更好。业务希望保留 Laya 的全部决策特性、且需要和 Laya 社区的权重对齐后续更新直接在 Laya 权重上 LoRA 微调。前者适合大多数国内业务后者适合已经有 Laya 部署基础的朋友。我实际对比过两者在同一份客服分类数据上的表现Qwen 基座微调后准确率高了约 4 个百分点但推理延迟也略高一点。你需要根据线上硬件重新权衡。如果你的显存只有 12GB两个选择都只建议 QLoRA 微调。4.2 LoRA 与全参微调资源不够时的正确姿势大模型微调听起来门槛高但 LoRA 的出现把这扇门几乎拆了。它的思路是不动原模型全部参数只训练一小部分新增的低秩矩阵。打个比方原模型是一本已经写得满满的书全参微调相当于把整本书重写一遍LoRA 则只是在书页空白处加批注成本低、修改可控。用 LoRA 微调 Laya-7B 时需要调整的参数范围大概只有总参数量的 0.5% 到 1%。这意味着你可以用一块 12GB 显存显卡跑完原本需要 40GB 的全参微调任务。LoRA 的核心参数有四个r秩决定新增矩阵的维度常用 8/16/32。任务越难秩适当加大但不是越大越好太大会过拟合。alpha缩放系数一般设为r的两倍。target_modules要对哪些模块插入 LoRA。常见是q_proj, v_proj更充分可以加k_proj, o_proj, gate_proj, up_proj, down_proj。dropout防止过拟合建议 0.05。我用的配置是r16, alpha32, target_modules 包含全部线性层。小批量数据下这个配置收敛稳定也不容易把原模型知识冲掉。4.3 数据准备System 1 场景的数据集长什么样微调效果的 80% 由数据决定。System 1 场景的数据集和通用 SFT 数据集有本质区别核心在于“短平快”。一份合格的训练集大概长这样[ { instruction: 将以下客服会话归类为售后、退款、物流、咨询、投诉之一。, input: 我的耳机坏了想寄回去修一下可以吗, output: 售后 }, { instruction: 将以下客服会话归类为售后、退款、物流、咨询、投诉之一。, input: 这个订单多久能发货, output: 物流 } ]注意 output 必须只保留最核心的标签词不要写解释不要写额外上下文。我在实际准备数据时还做了三类增强。第一是边界样本增强。把容易混淆的样本成对放进去比如“我想退货”和“我想退款”表面上很像但业务上一个是商品问题、一个是资金问题模型要学会区分。第二是噪声输入增强。在部分 input 里加入口语语气词、错别字、停下思考的“呃”等让模型在真实环境里更稳。第三是负例覆盖。每条类别至少安排 10% 比例的边缘样本防止模型把某个类别当成默认答案。数据量建议最少 500 条最多 5000 条。少于 500 条基本学不到决策边界多于 5000 条边际收益递减反而增加过拟合风险。5. 从安装到微调动手跑一遍 LLaMA-Factory5.1 LLaMA-Factory 的安装与配置现在主流微调工具框架已经非常成熟LLaMA-Factory工程基本已经跑起来了它把数据加载、训练、推理、导出都包成了现成模块几乎不用写底层代码。项目地址在 GitHub 上直接 clone 之后装依赖就行。git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e .注意安装时确保虚拟环境还处于激活状态。装完之后在项目根目录创建一个工作目录data把前面准备好的 JSON 训练数据放进去命名为train_data.json。接着需要改一下数据集注册文件。LLaMA-Factory 默认用data/dataset_info.json来识别数据集。在文件里的datasets列表中追加一项{ laya_system1: { file_name: train_data.json, columns: { prompt: instruction, query: input, response: output } } }这里有个细节新手很容易忽略query字段可以留空但不是所有数据集都有input。如果你的 JSON 里没有input字段这里就必须改成query: LLaMA-Factory 才能正确解析。5.2 训练参数设置从模板配置到收敛判断LLaMA-Factory 提供网页界面和命令行两种方式。我倾向于命令行启动方便记录日志和批量跑实验。到项目根目录执行CUDA_VISIBLE_DEVICES0 python src/train.py \ --model_name_or_path ./models/laya-7b \ --dataset laya_system1 \ --stage sft \ --finetuning_type lora \ --quantization_bit 4 \ --template qwen \ --output_dir ./output/laya-lora \ --num_train_epochs 3 \ --per_device_train_batch_size 2 \ --gradient_accumulation_steps 8 \ --learning_rate 2e-4 \ --lr_scheduler_type cosine \ --logging_steps 10 \ --save_steps 100 \ --max_length 512 \ --lora_rank 16 \ --lora_alpha 32 \ --lora_dropout 0.05重点解释几个参数--template qwen因为前面推荐基于 Qwen tokenizer 路线或者 Laya 本身也兼容 Qwen 模板这里用 qwen 模板最稳。如果用默认模板可能出现对话格式错乱。--per_device_train_batch_size 27B 模型 4bit 模式下 batch size 不敢给大12GB 显存给 2 是极限给 4 大概率 OOM。--gradient_accumulation_steps 8这里是把 batch 等效放大到 16保证梯度稳定。实际等价 batch size 2 × 8 16。--max_length 512System 1 数据一般很短512 足够了。设太大只会白白消耗显存。--learning_rate 2e-4LoRA 微调的常见学习率区间是 1e-4 到 5e-4。第一次跑建议 2e-4训练快且不容易震荡。训练过程中可以看 loss 曲线。正常情况下第一个 epoch loss 应该从 1.2 左右降到 0.4 以下后面继续走低但逐渐平缓。如果第二个 epoch 之后 loss 还在明显下降说明数据复杂度足够如果 loss 降得很快但同时验证集表现停滞就要警惕过拟合。5.3 训练启动与显存监控执行脚本后建议开另一个终端窗口实时盯显存watch -n 0.5 nvidia-smi显存占用稳定在 9-11GB 是正常的。如果看到CUDA out of memory优先把per_device_train_batch_size改成 1再把gradient_accumulation_steps调成 16两者乘积保持不变。如果还是 OOM考虑关掉--quantization_bit 4之外的其他功能或者换更小的模型底座。训练结束后输出目录里会有一堆 checkpoint 文件夹比如checkpoint-100、checkpoint-200。选择一个 loss 最低且验证效果好的 checkpoint 做后续推理。不能用最后一个 checkpoint 就完事我记得有一次训练到第三个 epoch过拟合迹象已经很明显用最后一个 checkpoint 推理时一堆样本被错分到高频类别反而是 epoch 2 的 checkpoint 表现最好。5.4 微调后的模型导出与推理验证训练出来的 LoRA 适配器只是一个小文件必须和原模型合并或加载后才能形成完整的可用模型。推理验证时可以用 PeftModel 加载from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer base_model AutoModelForCausalLM.from_pretrained( ./models/laya-7b, torch_dtypeauto, device_mapauto, trust_remote_codeTrue ) model PeftModel.from_pretrained(base_model, ./output/laya-lora/checkpoint-200) model.eval()如果要在生产环境部署建议直接导出合并后的完整权重。LLaMA-Factory 也提供了导出脚本python src/export.py \ --model_name_or_path ./models/laya-7b \ --adapter_name_or_path ./output/laya-lora/checkpoint-200 \ --template qwen \ --finetuning_type lora \ --export_dir ./models/laya-7b-system1导出后要用一份独立的测试集做盲测。我测试下来效果最明显的变化是准确率从 78% 提升到了 94%而且误判集中在“售后”和“退款”这两个本来就很模糊的类别上。如果还想继续优化就往这个方向补充更多边界数据。6. 常见问题与排查技巧实录6.1 显存不足消费级显卡的极限操作12GB 显存跑 7B 微调最危险的就是 OOM。除了前面提到的 batch size 调整还有一个技巧是用 CPU offload把一部分参数暂时放到内存。但代价是训练速度急剧下降30 分钟一个 epoch 变成 2 小时一个 epoch所以不到万不得已不用。另一个经验是关掉验证集评估。LLaMA-Factory 支持在训练中定期跑验证这个功能在消费级显卡上开销很大大部分人根本用不上。直接不加--val_size参数把显存全留给训练验证放到最后统一做。我实测省出来的显存足够把 batch size 从 2 提回 4训练速度还更快了。6.2 微调后效果变差怎么办微调后效果变差一般有三个原因。一是学习率太高权重被冲得太狠模型原有的泛化能力被破坏。解决办法是把学习率调到 1e-4 甚至 5e-5不要一味追求收敛速度。二是训练轮数太多导致过拟合这个问题前面提过可以看 checkpoint 在不同阶段的验证表现选最优的那个。三是数据质量差比如标签错误、类别分布严重不均衡这种情况下怎么调参都没用只能回去清洗数据。还有一种容易被忽略的情况你训练的 LoRA 只适配了某一类 prompt 模板上线后业务侧换了 prompt 写法效果立刻退化。解决方案是在训练数据里加入至少 3 种不同的 prompt 表述让模型对 prompt 变化不敏感。6.3 训练完合并权重时丢失 LoRA 效果peft库在加载和合并时特别容易出问题。我前后踩过两次坑一次是合并后模型输出乱码另一次是合并后效果全无。排查后发现前者是因为 base model 的torch_dtype没指定自动变成了 fp32后者是因为 adapter 路径写错了加载到了另一个 checkpoint。合并后一定要做三轮全量预测对比原模型输出 vs LoRA 模型输出 vs 合并后模型输出。三者中后两者应该高度一致如果不一致多半是合并过程中量化精度对不上。4bit 模型合并时建议先把 base model 切成 fp16 再合并可以规避大多数精度问题。6.4 常见问题速查表症状可能原因解决方案首轮推理 OOMFP16 推理显存不够换 4bit 量化加载微调时显存溢出batch size 过大调小 batch调大梯度累积微调后输出变长数据集中长输出样本太多压缩 output 为短标签微调后分类准确率停滞prompt 模板过于单一增加 3 种以上 prompt 表述合并后效果消失adapter 路径/精度设置错误核对路径合并时转 fp16推理延迟超过 1smax_new_tokens 过大压缩生成长度关闭采样4bit 推理精度下降严重量化方式不合适换 nf4 或改 8bit6.5 一点额外心得千万别忽略日志最后分享一个我每次跑项目都会做的事把训练日志完整留存下来。LLaMA-Factory 默认的logging_steps10只打印在控制台一旦窗口关了就没法回溯。建议加上--logging_dir ./logs或者直接把终端输出用tee重定向到文件。等到你后面想对比不同实验的效果、定位某个 checkpoint 是怎么训出来的时候就会发现这些日志比什么都值钱。按照这套流程走完从安装到微调一共也就两三个小时的事。我自己实际跑下来的感受是Laya 这条路线把 System 1 决策任务的技术门槛拉低了很多以前这类活要么花大价钱调商业 API要么自己吭哧吭哧从零训模型现在一份开源权重加一套成熟工具链就能搞定。如果你正准备做类似的决策分类或者希望在业务里加入更多快判断能力可以在 Laya 基础上按这份教程试一遍相信你会有不一样的收获。