LLaMA-Factory 微调 Qwen2.5-1.5B 参数抽取:流程与踩坑实录
本文记录把 shop-agent 的本地参数抽取模型(Qwen2.5-1.5B)用 LLaMA-Factory 微调上生产的完整过程。
流程与踩坑同节写,场景难题一节集中讲参数抽取域特有难题。
文中命中率、训练时长、格式合规为实测值,显存为实测/估算,成本为基于实测的推算(基于 RTX 4070 8GB + Qwen2.5-1.5B-Instruct 4bit 微调)。凡估算 / 假设 / 参考口径均在正文标注。
为什么微调:三级链路的痛点与成本动机
shop-agent 的参数抽取是一条三级兜底链路:
正则兜底 → 本地小模型(Qwen2.5-1.5B) → 云端大模型(qwen3.6-flash)
设计初衷是用免费的本地模型兜住大部分请求,只在本地失败时才降级到云端 LLM。但实测发现一个事实:未微调的 Qwen2.5-1.5B 参数抽取命中率只有约 23.3%,意味着近 77% 的流量会掉到云端——而云端大模型(qwen3.6-flash)的单价是输入 1.2元 / 输出 7.2元 每百万 token,这部分成本压不下来。
微调要解决的,就是把这个 23.3% 往上推。目标:用一次低成本的训练,把降级到云端的流量砍掉一大截。
数据准备:脚本离线合成训练 / 测试集
流程:模板 + 值池 + 难例,离线合成
为了降低标注费用,也为了避免把生产 PII 烧进权重,没有走「蒸馏真实日志」这条路,而是用脚本离线合成(实测:训练 / 测试集均由此生成,无任何真实日志参与):
- 训练集:用离线合成脚本(合成模板 + 订单号 / 物流单号 / 手机号等值池 + 缺参难例)离线生成,随机填充且固定随机种子保证可复现;
- 测试集:用测试集生成脚本以随机订单号 / 单号生成器另写措辞,保证与训练集的 query 文本、订单号、单号完全不重叠;
- 增强集(实际训练用 1729 条):在基础集 1600 条上按评测错误类型补样本(复用同一套模板,不引入分布漂移,见坑⑨)。
为何不直接蒸馏生产日志:① 合成数据不含任何真实 PII,合规零风险;② gold 由程序生成,类型天然可控(订单号 / 单号恒为字符串)。
数据格式用 LLaMA-Factory 的 ShareGPT:
{
"conversations": [
{"role": "system", "content": "从用户消息提取结构化参数,只返回JSON。字段:order_id(字符串)"},
{"role": "user", "content": "帮我查一下订单 WB202405270001 到哪了"},
{"role": "assistant", "content": "{\"order_id\": \"WB202405270001\"}"}
]
}
训练样本里的 system 字段直接复用项目参数抽取用的 prompt 模板,保证训练与推理 prompt 一致(这点配置不一致会要命)。
规模:5 类意图(查订单 / 查物流 / 退货 / 查余额 / 查优惠券)各 ~300 条 + 难例 ~200 条,基础集 1600 条、增强集(实际训练用)1729 条(实测)。对 1.5B 小模型,这个量足够 LoRA 见效。
后期运维:用真实日志做持续校正(蒸馏的正确打开方式)
合成数据解决了「从 0 到 1」,但扛不住线上分布的缓慢漂移(新单号前缀、新口语、新意图)。我们的做法是把「蒸馏真实日志」当作后期运维的常规校正回路,而不是初始数据来源:
- 定期抽一批真实对话,先过脱敏逻辑处理 PII(呼应防御点 1);
- 用云端 LLM 当教师蒸馏打标(合成脚本接真实日志并开启蒸馏开关),gold 仍强制转成字符串(呼应防御点 2);
- 拿新样本跑误差驱动增强脚本 + 评测,找出仍错的意图 / 类型,补进训练集重训(呼应坑⑨);
- 混入 ~20%(经验值)旧意图样本抑制遗忘(呼应场景难题 I)。
这样初始数据零 PII 风险、可复现,而真实分布通过「脱敏 → 蒸馏 → 增强 → 重训」的闭环持续回流——蒸馏的价值落在运维期,而非冷启动期。
接真实日志前必做的两个防御点
下面两条是「前置防御」——我们当前训练数据是脚本合成的,但一旦把数据来源从脚本合成切换成蒸馏真实日志(见上文「后期运维」回路),它们就会触发,所以先写在这里。
防御点 1:PII 泄露
若把数据来源从「脚本合成」换成「蒸馏真实对话日志」,训练集就会混着真实手机号、订单号、身份证号——直接拿去训练 = 把生产 PII 烧进模型权重,合规风险极高。我们当前训练数据是脚本合成的,这一条未实际触发;但合成脚本已为可选的「接真实难例」路径预留了脱敏逻辑(手机号/邮箱/身份证/订单号脱敏后再入库),一接真实数据即可生效。
解决:一旦接入真实日志,入库前先跑项目已有的 PII 脱敏(手机号/邮箱/身份证/详细地址),脱敏后再存训练样本。
防御点 2:字段类型错位
若用「教师 LLM 蒸馏」生成 gold,教师偶尔会把本应是字符串的订单号输出成数字 123456789,训练进去后推理端对结构化字段做强类型校验时会直接报错。我们合成数据的 gold 在生成期就由字段类型强转逻辑强制转字符串,这条同样未实际触发;但作为真实日志路径的兜底,类型校验脚本仍保留。
解决:数据校验脚本强制订单号/单号为字符串类型,过滤掉所有非字符串样本;合成路径则在生成期直接保证类型正确。
场景特有难题:参数抽取域的九大设计权衡
这一节是本文的护城河——通用微调教程不会讲,只有做「参数抽取」才会撞上。
| # | 问题 | 我们的取舍 |
|---|---|---|
| A | 单模型 vs 多模型:一个 Qwen 抽 5 类意图,还是每意图一个 LoRA? | 选单模型 + system 区分意图,省部署成本;多 LoRA 路由复杂度不划算 |
| B | 多意图/歧义:「查订单并申请退货」输出结构怎么设计 | 只抽主意图参数,歧义交给三级链路的 ReAct 处理,不在抽取层硬解 |
| C | 缺参/隐式参数:用户没说订单号 → 输出空字段还是报错 | 约定缺则输出空字符串,由下游正则兜底,不报错中断 |
| D | 字段格式强约束:订单号/手机号有固定格式 | 模型输出后接本地正则抽取器做正则校验,不合规视为未抽中 |
| E | schema 对齐:模型怎么知道输出哪个 schema | system 中显式写出当前意图的字段定义,训练/推理一致 |
| F | 与三级链路集成:本地小模型仍可能输出非法 JSON | 必须保留本地正则兜底 + 云端大模型兜底,微调只是提升命中率不是取代兜底 |
| G | 评测口径业务化:资损级字段(抽错即造成资金损失,如订单号/物流号)应单独盯守,不能只看未加权均值 | 字段级实测(SFT,360 条):物流号 95.24%(唯一跌破 95%)、订单号 98.55%、优惠券 97.22%、原因/状态/手机号≈100%——均值 97.8% 掩盖了资损级短板 |
| H | 难例分布场景化:物流单号 vs 订单号混淆、优惠券别名、编号前缀池/口语格式分布 | 数据构造时专门塞「满减券/红包/折扣券」等别名样本;订单号/物流单号的「前缀+位数」、裸枚举值句尾/句首、因+原因引导等测试集出现的格式,训练集必须覆盖,否则漏抽/截断(本次退货意图 0.861→0.972 的根因,实测:增强前后对比;见坑⑨) |
| I | 意图新增的灾难性遗忘:微调新意图时旧意图精度掉 | 新意图数据混入旧意图 ~20%(经验值,无 10/20/30% 对比实验) 旧样本一起训,抑制遗忘 |
其中 D、F 在部署一节落地,G 在验收一节落地,H 在数据一节已处理,I 在复盘一节讨论。A/B/C/E 是微调前的架构决策,先想清楚再动数据。
环境与配置:QLoRA 与 template 命门
流程:训练配置
训练配置文件(LLaMA-Factory 格式):
model_name_or_path: Qwen/Qwen2.5-1.5B-Instruct
stage: sft
do_train: true
finetuning_method: lora
quantization_bit: 4 # QLoRA,省显存
lora_rank: 16
lora_alpha: 32
lora_target: q_proj,v_proj,k_proj,o_proj
template: qwen # ⚠️ 必须与推理端一致
dataset: 训练集
cutoff_len: 1024
learning_rate: 2.0e-4
num_train_epochs: 3
per_device_train_batch_size: 4
gradient_accumulation_steps: 8
preprocessing_num_workers: 4 # 数据集预处理并行,Windows 安全(区别于坑④ 的 dataloader)
upcast_layernorm: true
lr_scheduler_type: cosine
bf16: true
output_dir: ./outputs/微调产物
数据集登记文件(LLaMA-Factory 的 dataset_info.json):
{
"训练集": {
"file_name": "训练集.json",
"formatting": "sharegpt",
"tags": {"system": "system", "user": "user", "assistant": "assistant"}
}
}
坑① 单卡 OOM
8GB 卡上 1.5B 全参微调直接 OOM。
解决:quantization_bit: 4(QLoRA)可大幅压低显存;4bit 下 1.5B 训练峰值显存约 2–4GB(估算值:按 4bit 权重 ~0.75GB + 优化器/激活推算,未逐秒采样 nvidia-smi);仍紧就降 per_device_train_batch_size 到 2、升 gradient_accumulation_steps。
坑② template 不一致(最致命)
这是全篇最痛的坑。训练时 template: qwen,但推理端本地模型服务如果自己手拼 prompt、没走 tokenizer 自带的 chat template,导出的模型在推理端输出会完全崩坏——不是格式错,是语义乱码。
根因:LoRA 学的是「在 qwen chat template 下的输入→输出映射」,推理端 template 一变,映射全失。
教训:把 template: qwen 写进部署 checklist,训练端和推理端用同一份 template 定义,谁改谁负责回归。
坑③ 版本地狱
LLaMA-Factory 拉最新 transformers 后,与项目 torch 的版本下限冲突,训练起不来。
解决:锁 transformers==4.41.2(与项目 torch 版本下限匹配的版本)。
坑④ Windows 上 dataloader_num_workers > 0 训练直接崩溃
在 Windows 上把 dataloader_num_workers: 4 当作提速项加上后,训练循环一启动 dataloader 就抛 pickle 错误:can't pickle local object '...custom_gradient_checkpointing_func'。
根因:Windows 的多进程用 spawn(不像 Linux 用 fork),子进程必须把整个模型(含 transformers 里自定义的局部闭包函数 custom_gradient_checkpointing_func)序列化传给子进程,而闭包无法 pickle → 崩溃。注意数据预处理阶段(preprocessing_num_workers: 4)是正常跑完的,错误发生在训练循环启动 dataloader 时。
解决:dataloader_num_workers 保持默认 0(Windows 上 dataloader 多进程对含闭包的模型不兼容);数据集预处理的并行用 preprocessing_num_workers: 4(已验证安全,且不影响模型闭包)。其余提速项(per_device_train_batch_size、upcast_layernorm: true)可正常用(区别于坑④ 的 dataloader)。
训练:过拟合与 LoRA target 漏层
流程
lora_rank=16、num_train_epochs=3、cosine 学习率。Qwen 的 lora_target 必须含 q_proj,v_proj,k_proj,o_proj 四个。
坑⑤ 过拟合:val_loss 先降后升
多 epoch 易过拟合(我们最终用 epoch=3;试过更大 epoch 时验证集 loss 出现反弹、held-out 命中率回落。
解决:回退到 epoch=3,并补充难例数据(数据一节的 ~200 条难例发挥作用)。小模型数据少时,多 epoch 是过拟合重灾区。
坑⑥ LoRA target 漏层 = 白训
一开始只挂了 q_proj,v_proj,跑完发现效果几乎无变化——LoRA 没挂到足够的层上,模型参数基本没动。
解决:Qwen 全系必须含 k_proj,o_proj,补全后效果立显。
导出与部署:路径、4bit 对齐、多 Worker、链路集成
流程
llamafactory-cli export \
--model_name_or_path Qwen/Qwen2.5-1.5B-Instruct \
--adapter_name_or_path ./outputs/微调产物 \
--export_dir ./models/本地模型 \
--export_size 2 \
--template qwen
导出合并权重后,把本地模型服务的加载路径指向本地模型目录(内网可离线加载)。三级兜底链(正则兜底 → 本地小模型 → 云端大模型)原样保留,微调只是把中间一跳的命中率拉高。
坑⑦ 部署端 4bit 对齐
项目用 bitsandbytes 4bit 量化本地模型(依赖清单已装)。导出模型在 4bit 下 JSON 输出需重测——偶尔出现字段截断。
解决:上线前跑一遍测试套件,确认 4bit 下结构化输出稳定。
坑⑧ 多 Worker 重复加载
每个 FastAPI Worker 各加载一份 1.5B → 显存直接爆。
解决:把本地模型抽成 vLLM 推理服务,多 Worker 通过 HTTP 共享同一份模型。
呼应场景难题 F:链路集成验证
上线后必须验证:本地小模型输出非法 JSON 时,能正确降级到本地正则兜底和云端大模型兜底,不能因为「现在模型更强了」就拆掉兜底。
数据分布对齐:训练集必须覆盖测试集的真实格式(坑⑨)
这是本次微调最深的坑,也是参数抽取域最容易栽、却最少被讲的地方。通用教程只讲「数据要多、要均衡」,但参数抽取还要求训练分布覆盖测试分布的形状——否则模型在「没见过的格式」上静默漏抽或截断,且评测时不易定位根因。
坑⑨-a 编号前缀池必须训练集 = 测试集
订单号 / 物流单号这类字段,真实分布不是「任意字符串」,而是「前缀 + 固定位数」。订单号实测前缀池为 {WB, DD, SO, EC, MY}(12 位数字),物流单号为 {SF, YT, JD, ZT, EMS, HT, YD}(10 位数字)。
症状:训练集修复前只在「带明确字段文字(『快递单号』『订单』)」的样本里见过 WB 前缀对应订单号、SF/YT/JD/ZT 对应物流单号(均为 12 位 / 10 位),测试集出现「裸编号 + 模糊文字、靠前缀判字段」且含未强调前缀(EC/SO/MY 订单号、HT/YD 单号)的样本时,模型靠猜 → 单号截断或漏抽;同理订单号在退货意图里也因前缀池覆盖不全而漏抽。
注意:别把枚举值(如退货原因「其他」)当同义词硬塞进模板别名——这会污染字段分布造成负优化,正解是前缀池对齐。
正解:建一个与生产分布一致的前缀池,按各「前缀+位数」组合等比例造样本,保证训练分布 = 测试分布。修完后查物流意图的 mixed 错误从 16 → 2(实测:前缀池修复前后对比,当前模型 mixed=2)、退货意图的订单号截断清零。
坑⑨-b 口语表达格式必须显式教
测试集里用户口语有训练集没教的格式,会导致漏抽而非截断:
- 裸枚举值句尾:
退个货,订单号 WBxxx,原因是其他—— 句尾的「其他」是原因字段,模型若只见过「因为 XXX」会漏抽原因; - 句首枚举:
其他,订单号 WBxxx 我不要了; - 因 + 原因引导:
因为这衣服破了,申请退货—— 「这衣服破了」才是原因,不是别名。
正解:把这些口语模板直接补进合成脚本,按句尾/句首/因+原因三种形态各造样本。补完后退货意图的漏抽(原因字段,原 6 条)与值错误(原 4 条:2 截断 + 2 误判「因为其他」)全部清零,退货意图从 0.861 → 0.972(实测:增强前后对比)。
呼应场景难题 H:难例分布要同时覆盖「字段别名」(满减券/红包等)与「形态/前缀分布」(订单号/物流单号的前缀+位数、裸枚举值句尾/句首、因+原因引导),两者缺一不可。微调后务必按意图逐一看命中率,别只看整体均值。
效果验收:三个基线对比 + 业务化评测
GPU 监控误区:以 nvidia-smi 为准(别信任务管理器)
判断 GPU 是否真正参与训练 / 推理,以 nvidia-smi 为准,不要只看 Windows 任务管理器:
# 每 1 秒刷新:进程 + 显存占用 + 计算占用率
nvidia-smi -l 1
Memory-Usage显存占几 GB(4bit 1.5B 训练 / 推理约 2–8GB)→ 模型已加载到 GPU;GPU-Util在训练 steps 阶段呈 50%–95% 抖动,批量推理时可达 90%+ → GPU 在算。
坑:Windows 任务管理器进程栏中 GPU 图默认显示 「3D」(图形渲染)引擎占用率,而 CUDA 计算跑在独立的 Cuda / Compute 引擎上。需要在性能栏中,点击GPU RTX 4070 才能看到。
为什么有时占用率低(锯齿):训练 / 评测前的数据集 CPU 预处理阶段 GPU 完全空闲(可能持续几十秒到一两分钟);1.5B 小模型 + 短序列下 GPU 每步算完即闲,利用率呈 0% ↔ 几十% 锯齿,属正常。
坑⑩ 批量推理必须 left-padding,否则结果静默错误
评测 / 推理若用 batch_size=16 批量喂模型(GPU 利用率可从 ~30% 提到 90%+,吞吐提升约 6 倍——实测:batch=1 单条 514ms → batch=16 单条 83ms,约 6.2x),decoder-only 模型必须用左填充 + pad_token:
tok = tokenizer(msgs, return_tensors="pt", padding=True, padding_side="left",
truncation=True, max_length=cutoff_len)
tok["attention_mask"] = ... # left-padding 时 mask 左侧为 0
根因:右填充(padding_side="right")会在生成起点前塞 pad token,污染自回归的第一个生成位置,导致 JSON 输出随机错位、命中率虚低且难排查。左填充保证所有样本生成起点对齐到右侧真实内容末尾。
教训:批量推理 = 左填充 + pad_token_id 显式设置;逐条循环(batch_size=1,单条 ~514ms)虽慢但不会踩这个坑,若 GPU 利用率长期低位且命中率异常,先查 padding 方向。批量(batch=16,单条 ~83ms)可将吞吐提升约 6 倍(实测)。
三个基线(实测)
| 基线 | 命中率(实测) | 格式合规(实测) | 单次成本 |
|---|---|---|---|
| 本地正则兜底 | 45%(参考值) | — | 0 |
| 未微调 本地小模型 | 23.3% | 0% | 本地 |
| 微调后 本地小模型 | 97.8% | 100% | 本地 |
| 云端大模型 structured | 95%(参考值) | ~100%(参考) | 云端 ¥ |
注:未微调模型命中率 23.3% 主要来自查余额意图(gold 为空,模型不输出结构化内容即算对);其实际几乎不输出合法 JSON(格式合规 0%,实测),因此「命中率」不能直接等同于「可用结构化输出」——这正是微调的核心价值。
微调后本地命中率从未微调的 23.3% 拉到 97.8% ,已接近云端大模型(95%,参考值)且反超(因结构化 prompt 约束),成本从「每次调 API」变成「本地 GPU 折旧摊薄」。各意图实测(360 条 held-out 测试集):查订单 98.6% / 查物流 97.2% / 查优惠券 97.2% / 退货 95.8% / 查余额 100%,0 回归。微调后平均单条延迟 68.8ms(未微调 237ms)。
为什么是「微调」而不是「few-shot」:三条件同测试集对比
上文「未微调 23.3% → 微调 97.8%」只比了两端,「那 few-shot 呢?塞几个例子是不是就够了?」 我们用同一份 360 条测试集、同一套字段级评测,补上 few-shot 这一档(K=4,每意图 4 个样例从训练集抽取,测试集保持完整 360 条、与样例零重叠)。
| 条件 | 整条达标率 | field_f1 | 平均延迟 | 平均输入 token |
|---|---|---|---|---|
| zero-shot(base) | 23.3% | 0.0 | 163 ms | 78 |
| few-shot(base, K=4) | 71.1% | 0.826 | 82 ms | 233 |
| sft(微调) | 97.8% | 0.986 | 44 ms | 78 |
逐意图整条达标率:
| 意图 | zero-shot | few-shot | sft |
|---|---|---|---|
| 查余额 | 100% | 100% | 100% |
| 查物流 | 0% | 75.0% | 97.2% |
| 查优惠券 | 0% | 68.1% | 97.2% |
| 查订单 | 16.7% | 26.4% | 98.6% |
| 退货 | 0% | 86.1% | 95.8% |
三个结论(决定「选微调」而非「堆 prompt」):
- few-shot 能把冷启动从 23% 拉到 71%(近 3 倍),证明 1.5B 小模型确有 in-context 学习力;但逐意图方差极大——退货 86% 却查优惠券仅 68%、查订单仅 26%。这种「看意图吃饭」的不一致,正是生产系统最怕的:同一份 prompt,今天查券抽得对、明天查单抽错,难定位、难 SLA。微调则把五个意图全部拉到 95.8%–100%,一致性满分。
- 样例税坐实:few-shot 平均输入 token 78→233(约 3 倍),因为每个请求都白带 4 组示例。高频场景(如本项目假设 4 万请求/日)下,这 3 倍 token 直接抵消「本地免 API」的成本优势——这正是成本账里「砍 74% 云端调用」靠的是微调、而非 prompt 的根本原因。
- 微调三轴全胜:准确率最高(97.8%)、延迟最低(44ms,比 few-shot 还快近 1 倍)、输入 token 最短(78,和 zero-shot 一样)。知识压进权重后,prompt 极短、输出确定、可本地跑。
呼应场景难题 G:业务化评测口径
命中率不能只看平均。我们把字段拆开实测(SFT,360 条):
| 字段 | 风险 | 样本数 | 命中率 |
|---|---|---|---|
| tracking_number 物流单号 | 资损 | 42 | 95.24% |
| order_id 订单号 | 资损 | 138 | 98.55% |
| coupon_type 优惠券 | 低风险 | 72 | 97.22% |
| reason / status_filter / phone | 中性 | 17–45 | ≈100% |
结论:资损级字段(尤其物流单号)是模型唯一的明显短板,而低风险字段几乎全中。整体 97.8% 是未加权均值,把物流号的 95.24% 和优惠券的 97.22% 拉平了——但物流号错一条 = 查错包裹 = 客诉/资损,远比优惠券错一条严重。因此实践中要把高风险字段命中率单独列出盯防,并按风险加权审视,而不是只看总分。这也正面印证了 H 的难例判断:物流单号与订单号混淆正是模型最易失守的资损级点。
成本账与复盘
成本
- 训练成本(实测+估算):QLoRA 1.5B,1729 条(增强集,实测),3 epoch,RTX 4070 8GB,实测训练时长 ≈7.4 分钟(来自训练日志,train_runtime≈441 秒)。云 GPU(A10 ~¥2–3/时,参考价)→ 按实测 7.4 分钟 × ¥3/时 ≈ ¥0.37(估算);自购卡近乎零边际。
- 隐性成本(经验估算):数据构造(模板 + 值池 + 难例设计)与误差驱动增强的迭代调参 ~1–2 人天(主要开销;合成数据无人工清洗 / 标注,但打磨模板分布、对齐测试集格式仍需工程投入)。
- 推理收益(假设流量模型):按中等规模 4 万请求/日(假设值,非实测),命中率 23.3%→97.8%(实测)省下约 74%×4万 = 约 3 万次/日 云端调用。本地 GPU 折旧(共享 ¥2,500/月,估算)摊薄后,约数日内回本(估算模型)。
复盘:何时「不该」微调
- 数据 < 200 条且难例不足、请求量低 → few-shot 更划算(本项目实测:360 条 held-out 上 few-shot 仅 71%、且逐意图方差大;一旦凑到 1500+ 合成样本,微调以 97.8% 更高一致性反超)。
- 本地命中率已 >95% → 边际收益低。
- 意图频繁变更 → 重训成本 > 规则维护成本。
呼应场景难题 I:迭代与遗忘
新增意图时,把 ~20%(经验值) 旧意图样本混入新数据一起训,抑制灾难性遗忘。验证旧意图精度不掉再上线。
附录
评估脚本骨架(批量 + left-padding)
import json
def evaluate(test_set, model, schemas, batch_size=16):
per_intent, total = {}, {"hit": 0, "all": 0}
# 批量推理必须 left-padding + pad_token(见坑⑩),否则右填充污染生成起点
for i in range(0, len(test_set), batch_size):
batch = test_set[i:i + batch_size]
prompts = [build_prompt(schemas[c["intent"]], c["msg"]) for c in batch]
toks = model.tokenizer(
prompts, return_tensors="pt", padding=True,
padding_side="left", truncation=True, max_length=1024,
).to(model.device)
out = model.generate(**toks, max_new_tokens=128)
for c, o in zip(batch, out):
pred = json.loads(model.tokenizer.decode(o, skip_special_tokens=True))
ok = all(pred.get(k) == c["expect"][k] for k in c["expect"])
per_intent.setdefault(c["intent"], [0, 0])
per_intent[c["intent"]][0] += int(ok)
per_intent[c["intent"]][1] += 1
total["hit"] += int(ok); total["all"] += 1
print("总体命中率:", total["hit"] / total["all"])
for k, (h, a) in per_intent.items():
print(f" {k}: {h}/{a} = {h/a:.1%}")
决策 Checklist
- 数据 ≥1500 条且 5 意图均衡、已脱敏?
- 训练集编号前缀池 / 口语格式覆盖测试集分布(坑⑨:前缀+位数、裸枚举句尾/句首、因+原因)?
-
template: qwen与推理端一致? - 评估集 held-out,对比了三个基线(含未微调 23.3% vs 微调 97.8%)?
- 批量推理用 left-padding + pad_token(坑⑩),且
dataloader_num_workers在 Windows 保持 0(坑④)? - 微调后按意图逐一看命中率(不只看整体均值,见 H 呼应)?
- 导出模型可离线加载、兜底链保留、可回滚?
- 多 Worker 走 vLLM 而非各加载一份?
更多推荐



所有评论(0)