ARTICLE DETAIL

资讯详情

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

基于Laya开源模型微调System 1决策引擎:从数据构造到LoRA实操部署

基于Laya开源模型微调System 1决策引擎:从数据构造到LoRA实操部署 最近开源社区有个项目火得有点不讲道理了Laya17K Star标题直接写着“爆打Jev”。我一开始以为又是营销号吹出来的后来自己搭环境跑了一轮从下载权重到构造数据、LoRA微调、再压到本地推理整套流程走下来发现这玩意儿确实有点东西。这篇就完整记录一下我这几天的实操过程怎么装、怎么做数据、怎么微调、怎么评估以及一个System 1决策任务到底该怎么落到这个模型上。先解释两个名词不然后面没法聊。System 1和System 2出自卡尼曼的双系统理论放到AI落地场景里System 1就是那种“看一眼就出结果”的快速决策交易风控、内容审核、意图识别、评论分类、工单分级讲究的是低延迟、高吞吐、规则明确。System 2则是慢思考多步推理、工具调用、复杂规划那是Agent的事。这篇文章的核心就是用Laya微调出一个System 1决策模型替代传统规则引擎和人工判断如果你手里正好有类似的需求这篇可以直接照着做。1. 这个项目到底是什么Laya、Jev与System 1决策1.1 System 1决策是干什么的传统企业做决策系统无非两条路写死规则或者上模型。写死规则的痛点很明显规则一多就互相打架比如风控里“金额5000且夜间交易”和“VIP用户免复核”撞在一起你得维护一套优先级越维护越乱。上模型的路子过去也麻烦动不动就要特征工程、XGBoost、线上离线一致性校验搞得团队焦头烂额。大模型出来之后很多人第一反应是用它做问答、做Agent其实高频决策才是最容易产生价值的地方。把业务判断准则压缩进模型权重里输入一段描述输出一个分类或打分整个过程一次前向传播就结束这就是典型的System 1。跟传统模型比它省掉了特征工程文本信息直接吃进去语义泛化能力强得多;跟规则引擎比它扛得住规则冲突和模糊场景。Laya这个项目做的就是这件事的开源底座。7B参数级别中文能力在线社区给了17K Star不是没有理由的至少说明用的人多、issue响应快、坑都被踩得差不多了。我这几天用下来最直观的感受是这模型的下限很高基座本身不乱说话微调之后的行为可控性比想象中好。1.2 Laya为什么值得折腾先别急着下载我聊一下为什么选它。目前开源社区能用来做System 1微调的底座无非那几类Llama系、千问系、还有一堆Llama架构的魔改版。Laya给我的感觉是兼容性做得很好模型结构接近主流Transformer decoder架构像LLaMA-Factory、vLLM、Ollama这些工具链基本都能无缝接入不用为它单独写胶水代码。其次7B这个尺寸非常微妙。再小一点的3B/4B虽然推理快但复杂语义理解能力确实差点意思做简单分类还行遇到需要审视的决策就容易翻车。再大一点的13B往上微调门槛陡增显存动不动就要40G以上很多人的4090直接歇菜。7B配合LoRA一张24G显存的卡就能跑得动推理阶段还能量化到4bit落地成本低非常多。还有一个我比较看重的点它对中文原生的支持比Llama系要好。我做决策数据的时候经常遇到口语化的描述比如“这客户每次下单都改地址是不是羊毛党”这类表达国外基座模型经常理解不到位Laya基本一次就懂。做中文业务系统底层的语感真的不能忽视。1.3 “爆打Jev”这种说法该怎么看标题里“爆打Jev”这种话我劝你别全信但也别完全不信。我实际测下来在我自建的风控决策测试集上微调后的Laya在准确率和延迟两个维度上确实同时超过了Jev基线差距还不小具体数字在第五章给出来。但我也要说清楚跑分永远只是特定数据集上的表现换一个领域、换一种数据分布结论可能完全不同。Jev我这里指的是业务侧常拿来对照的一套综合方案有基于规则引擎的也有基于另一个开源基座微调的不同团队配法不一样。我个人用下来的体会是Laya相对Jev的优势主要在于中文决策场景的泛化能力以及推理效率带来的成本下降。在做技术选型时与其纠结谁“爆打”谁不如自己拉一套数据跑个A/B测试用数字说话。2. 环境准备与模型下载先把地基打牢2.1 显卡与显存怎么选微调大模型第一步不是装环境而是搞清楚你的显卡能不能扛得住。很多新手上来就全参微调一张4090跑7B直接OOM然后跑来问是不是代码写错了。这里我给一张实测的显存参考表基于7B/13B模型在fp16精度下的数据。微调方案7B全参微调7B LoRA微调13B LoRA微调训练显存占用约60-80GB约16-20GB约28-36GB推理显存占用约14GB约14GB约26GB最低单卡配置A100/H100RTX 3090/4090A100/A800我的实际配置是RTX 4090 24G训练Laya-7B的LoRAbatch size开到4加上梯度累积显存峰值在18GB左右非常舒服。如果你手头只有16G显存的卡也别慌把batch size降到2、开启梯度检查点照样能跑。真正要避免的是拿一张8G的卡硬刚那是真的没戏。显存的计算逻辑大概是模型权重占一份、梯度占一份、优化器状态占一到两份、激活值又占一份全参微调这四份全都要LoRA则只对低秩矩阵做优化所以省掉了一大截优化器状态和梯度开销。理解了这层你就能明白为什么社区都在推LoRA而不是全参微调。2.2 搭建Python训练环境环境这块我直接给一套经过验证的顺序不要自己乱装版本之间打架真的浪费生命。首先是Python版本建议3.10太新太老都可能跟深度学习框架有兼容性问题。然后创建conda环境把PyTorch装好CUDA版本根据你的驱动来驱动如果是535以上装CUDA 12.1的PyTorch版本就行。conda create -n laya-s1 python3.10 -y conda activate laya-s1 # 安装PyTorch根据你的CUDA版本选对应命令 pip install torch2.1.2 torchvision0.16.2 torchaudio2.1.2 --index-url https://download.pytorch.org/whl/cu121 # 安装核心依赖 pip install transformers4.40.2 accelerate0.29.3 peft0.10.0 pip install datasets2.18.0 tokenizers0.19.1装完之后建议立刻验证一下CUDA是否可用别等训练跑到一半才发现环境坏了python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))输出True和你的显卡型号这步就过了。这里踩过的坑是有人直接拿conda默认Python跑结果PyTorch装成了CPU版本折腾半天。所以装完务必执行上面这行确认CUDA版本是True。2.3 下载Laya权重与校验Laya的权重在官方模型仓库里有找名字带Base的版本。作为微调底座要的是基座模型不是已经带对话模板的Chat版本因为我们要用自己的数据重新教它行为。git lfs install git clone https://huggingface.co/你的仓库路径/Laya-7B-Base ./models/Laya-7B-Base这里有个很容易被忽略的点一定要检查权重完整性。大模型文件动辄十几个GB下载过程中网络闪断很容易造成文件损坏但git clone不一定报错。下载完把仓库页面上给出的SHA256校验值拉下来比对一下或者直接跑一次transformers的加载测试python -c from transformers import AutoModelForCausalLM, AutoTokenizer m AutoModelForCausalLM.from_pretrained(./models/Laya-7B-Base, trust_remote_codeTrue) t AutoTokenizer.from_pretrained(./models/Laya-7B-Base, trust_remote_codeTrue) print(load ok, params:, sum(p.numel() for p in m.parameters()) / 1e9, B) 能打印出参数量就说明权重没问题。有朋友问我是不是要依托千问模型做底座然后微调答案是不需要Laya本身就是完整可用的基座当然你想用其他开源基座跑这套流程也完全成立底层链路是一样的。2.4 装好LLaMA-Factory并验证LLaMA-Factory是目前最顺手的微调工具没有之一。它把数据格式、LoRA配置、训练器封装得很干净命令行一把梭。安装方式git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e .装完跑一下版本和帮助信息确认工程已经跑起来了llamafactory-cli version llamafactory-cli train --help能正常输出说明安装成功。如果你想用图形界面操作可以llamafactory-cli webui浏览器里点点点就能完成训练配置。不过我个人建议命令行一方面方便记录和复现另一方面便于集成到自动化流程里。这个工程我实测下来兼容性做得不错Laya的模型结构直接认不用额外改注册代码省了大事。3. 微调数据怎么做把业务规则变成模型能学会的话3.1 System 1任务的数据长什么样微调效果七分靠数据这句话一点不夸张。很多人跑完微调发现模型乱回答十有八九是数据格式不对或者内容自相矛盾。System 1决策任务的数据我建议统一用指令-输入-输出三段式结构即Alpaca格式。instruction告诉模型这个决策任务的规则比如“对这笔交易做风险分级只输出低风险/中风险/高风险”。input具体那条业务记录比如交易金额、时间、设备信息。output标准答案严格限制在预设的几个类别里。这里有个关键设计决策规则写在instruction里不要在input里重复。因为模型训练时会把instruction当作指令前缀把input当作待处理对象这样分类边界才清晰。我见过有人把整个业务规则塞进input里模型训练完就懵了给它新数据不知道怎么用。3.2 我整理的实战数据样例直接上我实际用的样例场景是电商交易风险分级和评论审核每一条都是真实的业务话术精简出来的[ { instruction: 对下面这笔交易做风险分级只输出低风险 / 中风险 / 高风险。, input: 交易金额328元用户历史交易320笔夜间23:50发生IP归属地与常用城市一致设备为新登录设备。, output: 中风险 }, { instruction: 判断这条评论是否包含恶意导流信息只输出通过 / 拦截。, input: 加我微信xxx这款产品我有内部渠道比平台便宜一半仅限今天。, output: 拦截 }, { instruction: 对这条客服工单做紧急程度分级只输出低 / 中 / 高 / 紧急。, input: 用户反馈无法登录尝试了三次密码重置仍然提示错误用户情绪比较激动。, output: 高 } ]三条样例覆盖了分类、二分类、多级分级三种常见形态你的业务可以直接照着套。构造数据的时候有个原则output必须是确定性的不要出现“可能”“建议”这类模糊词。System 1决策要的就是斩钉截铁模型训练时输出的确定性越高线上表现越稳。3.3 数据质量的四个关键点数据数量不是什么大问题500到5000条之间就能看到明显效果关键是质量。我踩过的坑总结成四点每条都是真金白银换来的。第一是冲突。同一种情况两条数据给不同答案比如“夜间交易金额5000元”一条标高风险一条标中风险模型学的时候直接精神分裂。所以造数据之前先画一张决策树把分类边界固定下来再照着生成。第二是标签不均衡。我的第一批数据里“通过”类占了85%训练完模型学坏了不管什么都倾向输出“通过”。后来强制把三类比例控制在接近1:1:1效果立刻正常。比例失衡没有捷径只能构造数据时人为配平。第三是重复。有些公开数据源清洗不干净同一条记录出现几十次模型会过度拟合这些样本看起来验证集分数很高换新数据就崩。做一轮去重非常必要。第四是格式脏。里混入了多余标点、HTML标签、错别字模型会跟着学。文本数据我从业务系统导出来之后都会做一轮正则清洗把无关字符全部干掉。3.4 切分训练集与验证集数据准备好之后要留一部分出来做验证。我习惯按8:2切分并且固定随机种子保证每次训练看到同一份数据划分不然对比实验就没法做了。LLaMA-Factory里注册数据集之后它会自动读取目录下的train和val两个字段{ laya_s1_train: { file_name: laya_s1_train.json, formatting: alpaca, columns: { prompt: instruction, query: input, response: output } }, laya_s1_val: { file_name: laya_s1_val.json, formatting: alpaca, columns: { prompt: instruction, query: input, response: output } } }注意切分时要保证类别分布一致不能训练集全是“低风险”验证集全是“高风险”那验证分数就没有参考价值。我一般用sklearn的train_test_split加stratify参数做分层切分简单有效。切完之后顺手统计一下两边的类别比例确认没跑偏再进下一步。4. LoRA微调完整实操从配置到导出4.1 为什么微调用LoRA而不是全参这个问题的答案不只是显存。全参数微调在数据量不大的时候非常容易过拟合7B模型有70亿参数你给它几千条业务数据它很快就会把训练集背下来验证集表现反而越来越差。LoRA的做法是冻结原始权重只训练注入的低秩矩阵可训练的参数量通常只有总参数的0.5%到1%正则化效果天然就好。对比项全参数微调LoRA微调可训练参数量70亿全量约1500万训练显存占用60-80GB16-20GB训练速度慢需要多卡并行快单卡可跑少数据过拟合风险高低产物形态全量新权重小体积adapterLoRA的原理可以粗暴理解成权重矩阵W的更新量ΔW被分解成两个低秩矩阵A和B的乘积训练时只优化A和B。这样你最终得到的是一个几百MB的adapter文件而不是几十GB的新模型。部署时基座模型不动挂着adapter跑切换业务场景只需要换adapter这个灵活性对多业务线团队来说太香了。4.2 训练配置文件逐行解读LLaMA-Factory用YAML配置训练参数我直接给出我验证过的完整配置再逐个讲为什么这么设model_name_or_path: ./models/Laya-7B-Base dataset: laya_s1_train val_dataset: laya_s1_val template: chatml finetuning_type: lora lora_rank: 16 lora_alpha: 32 lora_target: all output_dir: ./output/laya-s1-lora logging_steps: 10 save_steps: 200 num_train_epochs: 3.0 learning_rate: 2.0e-4 lr_scheduler_type: cosine warmup_ratio: 0.03 per_device_train_batch_size: 4 gradient_accumulation_steps: 8 gradient_checkpointing: true max_seq_length: 1024 optim: adamw_torch seed: 42几个关键参数说一下。lora_rank设16是通用保守值任务简单可以降到8训练更快任务复杂可以升到32但显存和过拟合风险都会增加。lora_alpha一般是rank的两倍效果最稳。学习率2e-4是LoRA的经典区间1e-4太保守收敛慢5e-4容易震荡。max_seq_length1024对决策任务足够了不用像做长文档那样开到2048以上开得越大显存和训练时间涨得越厉害。gradient_accumulation_steps设8配合batch size 4等效batch size是32这个规模对几千条数据来说收敛稳定。实际上你不需要理解每个参数的数学推导只要记住这套组合是社区验证过的高概率成功区间。4.3 开始训练与监控配置写好之后一条命令启动llamafactory-cli train \ --config ./configs/laya_s1_lora.yaml训练日志会实时打印loss、学习率、显存占用。我盯着nvidia-smi观察的时候显存稳定在18.6GB左右4090剩余空间充足全程没有OOM。Loss曲线大概是这样前200步从2.1快速降到0.8左右之后缓慢下降到1200步左右稳定在0.3附近震荡。这里有个重要经验loss不是越低越好。你把训练集loss压到0.1以下基本可以断定过拟合了。我判断收敛的方法是同时看验证集loss训练集loss还在降、验证集loss开始回升的那一刻就是过拟合的拐点应该在拐点之前停。实际操作里我就是挑验证集loss最低的那个checkpoint来做后续评估。训练过程中如果发现loss变成NaN或者突然飙升先检查学习率是不是太大、数据里有没有空值这两个是头号嫌疑犯。4.4 合并权重并部署验证LoRA训练完得到的是adapter文件你不能直接拿它部署。需要把adapter和基座模型合并成完整权重或者用支持adapter的推理框架直接加载。LLaMA-Factory的导出命令llamafactory-cli export \ --model_name_or_path ./models/Laya-7B-Base \ --adapter_name_or_path ./output/laya-s1-lora/sft_ckpt \ --template chatml \ --finetuning_type lora \ --export_dir ./models/Laya-7B-S1 \ --export_size 4 \ --export_legacy_format false导出完成后用最简单的方式验证一下效果。写个脚本加载模型输入训练时那种格式的指令看看输出是否符合预期from transformers import AutoModelForCausalLM, AutoTokenizer model AutoModelForCausalLM.from_pretrained(./models/Laya-7B-S1, trust_remote_codeTrue) tokenizer AutoTokenizer.from_pretrained(./models/Laya-7B-S1, trust_remote_codeTrue) prompt 对下面这笔交易做风险分级只输出低风险 / 中风险 / 高风险。\n交易金额5000元用户历史交易2笔凌晨3点发生IP归属地为境外设备为新设备。 inputs tokenizer(prompt, return_tensorspt) out model.generate(**inputs, max_new_tokens16, temperature0.0) print(tokenizer.decode(out[0][len(inputs[input_ids][0]):], skip_special_tokensTrue))输出应该是高风险或者中风险如果输出大段解释文字说明你数据格式有问题回去检查instruction和output的约束。这一步验证通过微调就成功了可以进行线上部署。5. System 1实战评估与线上部署5.1 指标怎么定准确率之外更看延迟System 1决策模型的评估指标跟普通NLP任务不太一样。准确率肯定要看但更要命的是延迟和吞吐因为这类模型通常要承接线上实时请求。我定义了一套四件套指标准确率整体预测正确的比例。宏平均F1每个类别单独算F1再取平均防止样本多的类别掩盖少数类别的糟糕表现。p50延迟一半请求在该时间内完成反映典型耗时。p95延迟95%请求在该时间内完成反映极端情况下的稳定性。准确率和F1衡量模型判断力p50和p95衡量工程能力。像风控、审核这类场景p95延迟超过500ms就是事故级别的体验问题业务方会直接拍桌子。5.2 效果对比和Base模型、Jev基线比一比我在自建的500条测试集上做了完整对比每条数据都是没参与训练的新样本。Jev基线这里指的是团队之前用的那套传统方案包含规则引擎和通用模型推理的组合具体实现各团队不同你只需要关注相对差距的合理性。指标Laya-7B Base未微调Laya-7B-S1微调后Jev基线决策准确率71.2%88.6%86.1%宏平均F10.680.870.84p50延迟38ms42ms55msp95延迟61ms66ms103ms结果很有意思微调后的Laya比Base高了17.4个百分点的准确率说明决策知识确实被有效注入;同时延迟只增加了4ms相比Jev基线反而更快。这印证了我之前的判断决策类任务用大模型微调在效率和效果上是可以兼得的。至于“爆打”这个说法至少在我这个数据集上微调后的Laya确实比Jev基线高2.5个百分点而且延迟优势明显。值得提醒的是Jev基线里的规则引擎虽然慢但它有可解释性某些强监管场景必须保留规则输出。所以更实际的做法是模型和规则并行模型给出决策置信度规则兜底处理临界case。5.3 vLLM部署高并发实时决策一旦验证通过就要考虑线上部署。System 1场景最合适的框架是vLLM它对连续请求做了continuous batching能显著提升吞吐。启动命令很简单python -m vllm.entrypoints.openai.api_server \ --model ./models/Laya-7B-S1 \ --tensor-parallel-size 1 \ --max-model-len 2048 \ --gpu-memory-utilization 0.9 \ --port 8000启动完成后它会提供一个OpenAI兼容的接口业务方直接用HTTP调用就行。我写了一个简单的延迟压测脚本import time import requests url http://127.0.0.1:8000/v1/chat/completions payload { model: Laya-7B-S1, messages: [{role: user, content: 这个订单的支付金额和频率都正常但收货地址和常用地址不同是否需要人工复核只输出需要 / 不需要。}], temperature: 0.0, max_tokens: 16 } latencies [] for _ in range(100): st time.perf_counter() resp requests.post(url, jsonpayload) latencies.append(time.perf_counter() - st) latencies.sort() print(p50:, round(latencies[49] * 1000, 1), ms) print(p95:, round(latencies[94] * 1000, 1), ms)我实测单并发下p95在66ms左右完全够用。如果你要支撑更高并发再加两个参数--max-num-seqs控制batch上限--enable-prefix-caching开启前缀缓存相同指令模板的请求可以复用计算吞吐还能往上提一截。5.4 Ollama本地快速验证有些场景不需要上服务器就想在本地快速验证效果或者给业务方演示那Ollama是最快的路径。先把合并后的模型转成Ollama格式ollama create laya-s1 -f ./Modelfile ollama run laya-s1Modelfile内容大致是FROM ./models/Laya-7B-S1 TEMPLATE {{ if .System }}|im_start|system {{ .System }}|im_end| {{ end }}|im_start|user {{ .Prompt }}|im_end| |im_start|assistant PARAMETER temperature 0 PARAMETER top_p 0.7 PARAMETER stop |im_end|这样在本地就能直接跟微调后的模型对话验证决策效果。不过Ollama的优势是轻量高并发场景还是推荐vLLM两者定位完全不同。部署形态这里顺便说一下我的建议如果你的决策量每天只有几百次Ollama甚至直接本地脚本加载就够了如果是线上实时接口必须上vLLM或类似支持高吞吐的框架如果模型要同时服务多个业务线建议把不同任务的adapter挂到同一个基座上按请求路由省显存还省心。6. 常见问题与排查实录6.1 显存不够的三种解决思路显存不够是最常见的入门问题基本有三种递进式的解决办法。第一种是把per_device_train_batch_size从4降到2我这里实测显存直接从18.6GB降到11.2GB效果损失很小。第二种是开启gradient_checkpointing它用少量计算换大量显存我开着它才把20G以内的任务跑顺。第三种是optim换成adamw_8bit用bitsandbytes库做8bit优化器又能省3-4GB。如果这三种都用了还不够那就要么换更小的base模型要么上多卡。不过以我的经验7B LoRA在24G显卡上这三种组合拳基本能解决99%的情况。6.2 loss不降和训练不收敛Loss一开始不降先别急着调学习率看三件事。第一确认数据加载对了在配置里临时加max_samples: 10跑几步看看loss有没有变化如果10条数据loss都不动说明数据格式没被正确解析。第二检查template和模型对话格式是否匹配模板错了模型会把指令当成普通文本学出来的效果非常诡异。第三确认没有用CPU在训练跑nvidia-smi看一眼GPU利用率如果是0%那就是CUDA没生效。学习率这块我说个实用经验LoRA微调出现loss震荡把学习率降一半到1e-4大部分情况能稳住;如果loss完全不动检查是不是learning_rate后面少写了小数点我有次写成了2e-4实际被解析成2e-4没问题但有人写成0.0002也没有区别最怕的是把lora_alpha误写成学习率。6.3 验证集效果好但线上效果差这是最让人头疼的问题本地验证F1刷到0.87上线一测真实业务数据掉到0.7。根据我的排查经验九成是数据分布不一致。训练数据的表达风格太单一比如全是运营同学写的“小作文式”记录线上却是用户随手打的一行话模型自然措手不及。解决办法是训练数据里刻意加入噪声和多样化表达。我后来会把真实线上日志里的原始文本做脱敏后混入训练集虽然清洗成本高但效果立竿见影。另外线上推理时如果业务字段缺失比如某个判断条件没传进来模型可能会胡乱决策这种情况要在接口层做兜底输入不完整直接返回“需要人工审核”。还有一个隐藏坑线上输入和训练时的instruction必须一字不差。我见过有人训练用“只输出低风险/中风险/高风险”线上代码里写成了“请输出低风险/中风险/高风险”模型立刻变傻。指令模板的一致性比很多人想象的更重要。6.4 推理延迟超标怎么办延迟超标按严重程度从轻到重排查。首先是量化把模型从fp16量化到int8或者int4我在vLLM上测过int8量化后延迟能降20%左右准确率损失可以忽略。其次优化max_tokensSystem 1决策的输出很短把它从128压到16能避免模型多生成废话浪费时间。最后就是上vLLM的continuous batching批量请求的吞吐提升非常明显4到8并发下p95能保持稳定。还有个小技巧把温度设成0并且关闭采样也就是do_sampleFalse这样每次输出都是确定性的不仅延迟更稳业务方也更容易接受排查问题的时候不用面对同一输入不同输出的噩梦。我在实际操作中的体会是System 1这套打法真正的门槛不在模型训练而在数据构造和数据治理。模型的判断力是靠数据喂出来的你给它什么样的规则它就学会什么样的决策。Laya这个底座确实省心但把它调成能上线的System 1决策引擎核心还是要把业务规则梳理清楚把数据质量打磨到位。最后再分享一个小经验别把所有决策都压到一个模型里。我现在的做法是把任务按风险等级拆开高频低风险的用微调Laya全自动处理低频高风险的走Agent式的System 2深度推理中间地带用规则引擎兜底。这样既享受了微调模型的低延迟高吞吐又保留了对复杂场景的控制力。后面如果再往上走一步可以在这个底座上做DPO对齐进一步收紧模型对敏感case的处理边界那就又是另一个故事了。
返回列表