用LoRA微调查询改写器:从参数配置到RAG落地的完整实战指南
2026/9/12 8:38:38 网站建设 项目流程

写这篇东西之前,我在朋友圈先放了一句话:“用 LoRA 微调一个查询改写器,是我今年以来投入产出比最划算的模型实验。”没想到一晚上有七八个人来问细节,从“这个跟 RAG 里 rerank 有啥区别”到“7B 模型跑 LoRA 到底需要多大显存”,问题基本覆盖了从选型到落地的全链路。干脆把整个过程整理成一篇完整记录,把踩过的坑、调过的参数、实验对比全都写出来,给后面想动手做类似任务的朋友省点时间。

查询改写,英文叫 Query Rewriting,是搜索、问答、RAG 系统里一个不起眼但非常关键的环节。用户输入一句口语化的、有歧义的、带错别字的话,系统得先把它改写成更适合检索的形态,后面的召回和重排才有的放矢。传统做法是写规则、做同义词替换、训练一个序列到序列的小模型,但效果天花板很低。现在大模型能力这么强,直接用 LoRA 在开源基座模型上微调出一个专用改写器,是性价比最高的方案。

这篇文章不聊玄学,只讲实操。我会从整体设计思路、基座模型选型、训练数据构造、LoRA 参数解析、训练过程、推理与评估、常见问题排查这几个维度展开,全程是真实项目记录,所有配置和步骤都可以直接参考复现。适合手里有 1-2 张消费级显卡、想快速落地一个大模型应用、或者正准备做 RAG 优化的朋友。

1. 项目概述与整体设计思路

1.1 查询改写到底在解决什么问题

先讲清楚业务场景,不然很多参数决策你没法理解。当时我们接到的需求是做一套企业内部知识库的问答系统,用户通过对话框提问,系统需要从几百篇技术文档里检索答案。上线跑了一段时间后发现一个典型问题:用户问“那个什么协议怎么配来着”,系统完全蒙了,因为索引里根本没有“那个什么协议”这种说法。

这就是典型的查询与文档之间的 lexicon gap,用户口语表达和文档书面表达之间存在巨大鸿沟。比如:

  • 用户说“退款没到账”,文档里写的是“退款异常处理流程”;
  • 用户说“卡顿咋整”,文档里写的是“性能调优指南”;
  • 用户说“怎么发版”,文档里写的是“发布部署操作手册”。

如果查询不改写,召回阶段就很难命中正确文档,后面再做 rerank 也是无米之炊。查询改写器的核心任务,就是把用户输入转换成更接近文档表达习惯的检索语句,同时保留原始查询的语义不变。

1.2 为什么用 LoRA 而不是全参微调

确定了要做查询改写之后,摆在我面前的有三条路:

  • 一是全参数微调一个 7B 模型,每轮更新所有参数,效果理论上最好,但需要多卡训练,显存压力大,训练时间长,而且领域迁移能力差,换个业务场景就要重新全量训练;
  • 二是写规则加词典做改写,成本低但只对固定pattern有效,稍微来一句“那个...啥来着”就白搭;
  • 三是用 LoRA 做参数高效微调,只训练低秩适配矩阵,可训练参数量只有全参数的 1% 左右,单卡就能跑,训练速度快,效果逼近全参微调。

我选了 LoRA,最直接的原因是性价比。LoRA 的数学原理不复杂,简单说就是冻结预训练模型权重,在旁边引入两个低秩矩阵 A 和 B,用低秩矩阵的乘积来模拟微调过程中的参数更新量。训练时只更新这两个小矩阵,推理时再把增量合并回原始权重。这相当于给一个已经读了万卷书的模型装了一个“专属领域知识外挂”,不需要重新读书,只需要针对性训练一小部分参数。

用 transformer 库的代码看更直观:

from peft import LoraConfig, get_peft_model lora_config = LoraConfig( r=8, lora_alpha=16, lora_dropout=0.05, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], task_type="CAUSAL_LM", ) model = get_peft_model(base_model, lora_config) model.print_trainable_parameters() # 输出类似:trainable params: 4,194,304 || all params: 7,523,905,536 || trainable%: 0.0557

看到 trainable% 只有 0.05% 级别,就能理解为什么一张消费级显卡也能跑得动了。

1.3 整体技术路线

整个项目拆成五个环节:

  1. 选基座模型,确定一个中文能力强的开源模型作为底座;
  2. 构造训练数据,把常见业务查询整理成 query-doc_query 配对;
  3. 用 LoRA 配置微调模型;
  4. 推理测试并调参;
  5. 评估效果并决定是否上线。

数据环节是整个项目的重心,后面专门用一整节细讲。这里先把技术路线图立起来,后面每个环节展开。

2. 工具选型与基座模型选择

2.1 选基座模型的三个硬指标

选模型的时候,我的判断标准就三条:中文能力要强,指令跟随能力要好,模型权重能顺利转成 PEFT 支持的格式。行业里现在可选的模型其实不少,Qwen 系列、Yi 系列、Baichuan 系列、InternLM 系列都是国内团队做的,中文语料覆盖都不差。但综合来看,Qwen 系列在指令跟随、中文语义理解、生态工具支持这三项上目前最均衡。

当时对比过几个候选方案,我整理成一张表:

模型中文能力指令跟随PEFT 生态支持显存门槛(7B/8B级)
Qwen2.5-7B-Instruct好,Llama-Factory 直接支持16G 可跑 LoRA
Yi-6B-Chat较强一般16G 可跑 LoRA
Baichuan2-7B-Chat较强一般16G 可跑 LoRA
InternLM2-7B-Chat一般16G 可跑 LoRA
LLaMA-3.1-8B中(中文需额外优化)16G 可跑 LoRA

不要小看“PEFT 生态支持”这一项。Llama-Factory、PEFT、transformers 这些框架对 Qwen 系列的支持是最原生的,LoRA target_modules 不需要自己猜,数据格式也完全兼容。选一个生态成熟的模型,能在训练和推理阶段省掉大量排查时间。

2.2 为什么把“指令跟随能力”排在语义能力前面

很多人选模型只盯着排行榜上的指标,忽略了任务自身的特性。查询改写这个任务,表面看是语义转换,实际考验的是模型“是否严格遵守指令约束”的能力。

举个例子,输入“帮我看看打印机为什么不能用了”,如果改写器老老实实输出“打印机 故障 排查”,这是合格。但如果模型自作聪明加了“您好,关于您的问题,我们建议您先检查以下步骤,首先...”这种回复式内容,就会污染检索语句。指令跟随能力弱的模型,很容易在大段对话训练后产生“多说几句”的惯性。

Qwen 系列在这一块做得不错,不是因为它的基座有多特殊,而是因为它在预训练阶段混入了大量高质量对话和指令数据,对“只输出改写结果,不要多余解释”这类约束理解得好。实际测试时,这个差异会直接体现在输出尾部是否干净上。

2.3 工具链选型:Llama-Factory 还是手写训练脚本

训练工具我最终选了 Llama-Factory,原因很实际:它把 Qwen 系列的数据格式、对话模板、LoRA 参数组合都预设好了,一个 YAML 配置文件就能跑起来,不需要手工处理 tokenizer 和 chat template 的拼接逻辑。对于查询改写这个场景,我们不需要对训练过程做特殊改造,Llama-Factory 是效率最优解。

如果你更喜欢直接写脚本训练,用 PEFT + transformers 也是完全可行的,控制粒度更细,但代码量大概多出两三百行。下面的实操流程我会优先讲 Llama-Factory 的方式,因为对大多数想快速出结果的朋友来说,这条路径最平滑。

3. 训练数据构造:查询改写任务的地基

3.1 数据字段设计与 Schema

训练数据是整个项目的核心资产,数据质量直接决定微调效果的上限。很多人忽略这一点,拿几百条通用数据就开跑,结果模型学会的是“改写”而不是“查询改写”。

我的数据格式采用 Alpaca 风格的 instruction-input-output 三元组,这是 LoRA 微调里最常见的格式:

{ "instruction": "你是一个查询改写专家。请将用户输入改写为适合检索的查询语句,要求:1) 保留原意;2) 使用文档中的规范术语;3) 去除口语化表达;4) 只输出改写结果,不要任何解释。", "input": "那个什么协议怎么配来着", "output": "RPC协议 配置方法" }

Instruction 是任务描述,input 是原始查询,output 是理想的改写结果。这里有几个设计细节值得展开:

第一,instruction 不要一成不变。一万条数据都用同一句指令,模型会把这个任务当成“从 input 到 output 的机械映射”,遇到分布外的查询容易慌。我在构造数据时准备了五六种指令模板随机轮换,比如“请将查询改写为适合搜索的语句”“请将用户问题规范化为检索表达式”等,效果差异实测挺明显。

第二,input 和 output 的对照关系要体现“改写”,而不是“翻译”。用户输入是口语,output 是检索语言,两者之间的转换关系要清晰。比如 input 写成“打印机坏了”,output 应该写成“打印机 故障 排查”,而不是把 input 换个说法变成“打印机出故障了”。

第三,数据量控制在 3000 到 5000 对是一个甜点区间。太少了模型学不到充分的转换模式,太多会引入数据冗余,训练时间变长但收益递减。对查询改写这个特定任务来说,它本质上是一个指令跟随任务,并不需要注入海量新知识,所以 3000 对精心构造的数据已经能带来非常明显的效果提升。

3.2 数据来源与“改写”的两个层次

查询改写数据从哪来?我建议按两个层次收集:

第一层是从真实业务日志里挖。把搜索、问答系统里的失败 query 捞出来,筛选出高频的、口语化的、带噪声的查询,人工标注成规范化改写。这个过程比较繁琐,但完全是值得的。真实场景里用户问法千奇百怪,只有真实 query 才能覆盖。

第二层是人工构造或利用大模型辅助生成。如果你刚开始做,手里没有足够的真实日志,可以先用 DeepSeek、Kimi 这类大模型帮你批量生成 query 和改写对。具体做法是给大模型一段模拟业务场景的描述,让它扮演不同用户角色,生成口语化提问,然后人工/自动改写为规范查询。这种方式适合冷启动,但生成完一定要人工校验,避免生成结果同质化严重。

这里必须强调,数据要保证“改写”的差异性。如果模型训练集里 80% 的数据都是把“怎么”改成“如何”这种同义替换,它对真正的查询改写的理解就很浅。一个好的改写对应该体现出以下一或多个变换:

  • 口语词转术语:把“卡了”“很慢”转成“性能 异常”“响应延迟”;
  • 补全信息:把“上次说的那个功能”补全成“支付回调 接入流程”;
  • 去除噪声词:去掉“那个”“啥”“怎么弄”等口语填充词;
  • 句式结构化:把疑问句改写成关键词组合,方便 BM25 或向量检索。

3.3 Qwen 的对话模板与 role 设计

在把数据送进模型之前,还有一个容易被忽略的细节:对话模板如何构造。Qwen 系列的 chat template 是 chatml 格式,基本结构是三段:

<|im_start|>system 你是查询改写专家,你的任务是把用户输入改写成适合检索的查询语句。<|im_end|> <|im_start|>user 那个什么协议怎么配来着<|im_end|> <|im_start|>assistant RPC协议 配置方法<|im_end|>

Llama-Factory 在数据预处理阶段会自动完成这个拼接,前提是你把数据格式配对。如果是手写训练脚本,一定要检查 tokenizer 的 chat template 是否正确使用了,很多诡异问题都出在这一步。

这里有个经验之谈:不要因为查询改写这个任务看起来简单,就图省事把 instruction 直接塞到 user 字段里绕开 system 角色。system 角色的作用是全局任务约束,user 字段是具体输入,二者分离能让模型更稳定地理解“这是一直要做的事”和“这一次要处理的内容”。

4. LoRA 参数解析与训练配置

4.1 关键参数:rank、alpha、dropout

LoRA 的参数选择在外行看来就是调几个数字,但这几个数字背后的逻辑关系值得花点时间理清。

最重要的参数是 low-rank 矩阵的秩 r,也就是 LoRA 适配矩阵的维度。r 越大,可训练参数量就越多,模型适配能力越强,但过拟合风险也会增大。查询改写这个任务本身不复杂,它是从一种表达方式到另一种表达方式的转换,不是让模型学习大量新知识,所以 r 取 8 就够了。实测 r=4 效果稍有下降,r=16 时在训练集上表现更好但验证集上的泛化反而变差,差距不大但 r=8 最稳健。

第二个参数是 lora_alpha,它控制 LoRA 增量在最终输出中的缩放比例。通常设置成 r 的两倍,也就是 lora_alpha=16。一个常用的理解方式是:实际生效的缩放系数是 alpha/r,当 alpha 是 r 的两倍时,这个系数是 2,意味着 LoRA 对原始权重的修正力度有一定放大,但又不会大到彻底覆盖原模型行为。如果你的实验结果过于保守,输出跟原模型几乎没差别,可以试着把 alpha 调大,比如 r=8, alpha=32。

第三个参数是 lora_dropout,避免过拟合用的。查询改写任务数据量不大,dropout 取 0.05 是一个比较合适的中间值。如果发现训练集 loss 降得很快但验证集 loss 开始回升,那就是过拟合了,可以适当调高到 0.1。

完整配置如下:

model_config: model_name: Qwen/Qwen2.5-7B-Instruct lora_config: r: 8 lora_alpha: 16 lora_dropout: 0.05 target_modules: - q_proj - k_proj - v_proj - o_proj - gate_proj - up_proj - down_proj

关于 target_modules,我强烈建议把 attention 和 MLP 相关的层都加上。很多人只加 q_proj, v_proj 就觉得够用了,实践下来,只改 attention 层的话模型的语言表达能力提升受限,在查询改写中表现为输出表述生硬、多样性差。把 MLP 层也放到 LoRA 作用范围内,能明显提升改写句子的自然度。

4.2 训练超参设定与估算

训练阶段的超参我给出可直接套用的配置:

training_config: learning_rate: 2e-4 batch_size: 4 gradient_accumulation_steps: 8 num_train_epochs: 5 warmup_ratio: 0.03 max_seq_length: 512 logging_steps: 5 save_steps: 100

有几个关键决策点。

学习率:LoRA 训练的学习率通常比全参微调高一个量级。全参微调通常用 2e-5 左右,LoRA 用 1e-4 到 5e-4。这里选 2e-4,在实际测试中既能让 loss 快速下降,又不至于震荡。如果你跑的时候发现 loss 曲线像锯条一样上下剧烈波动,先检查学习率是不是设太大了。

batch size 与梯度累积:单卡 24G 显存下,batch_size=4 是 Qwen2.5-7B 的 LoRA 训练的安全值。为了等效扩大 batch,我配置了梯度累积步数为 8,所以实际等效 batch size 是 4 × 8 = 32。这里有一个经验参考:batch size 越大,训练越稳定,但也不要盲目加大,等效 32 对 5 万 token 以内的查询改写任务已经足够。

轮次:我设了 5 个 epoch,但对查询改写这种小数据集,第 3 到第 4 个 epoch 的时候验证集 loss 往往已经到底了,后面开始缓慢回升。建议训练时打开验证集评估,选 checkpoints 里面验证 loss 最低的那个权重,而不要傻傻地用最后一轮。

4.3 训练过程的关键细节

训练时遇到的第一个问题就是“loss 里混进了 input 和 instruction 的部分”。Qwen 这类因果语言模型,默认会对整条序列计算交叉熵损失,也就是说,模型句子的损失会把 instruction 和 input 位置的生成错误也算进去。但对指令微调来说,我们只想计算 assistant 输出部分的 loss。

Llama-Factory 默认已经做了 masks the instruction part 的处理,只计算 assistant 内容部分的 loss,你不需要额外配置。如果是手写训练脚本,需要在数据 collator 里手动设置 labels,把 instruction 和 input 对应的位置设为 -100。这个细节非常关键,忘了做的话模型会花大量算力“学习”去预测推理时的指令文本,训练效率大打折扣。

训练完成后会得到 adapter 权重文件,通常是 adapter_model.safetensors 和 adapter_config.json。推理时有两种方案:

  • 方案一:不合并权重,直接用 PeftModel 加载基座和 adapter;
  • 方案二:先合并再保存完整模型,用常规加载方式。

方案一适合频繁迭代的实验阶段,方案二适合部署上线阶段。合并权重的命令在 Llama-Factory 里是:

llamafactory-cli export \ --model_name_or_path Qwen/Qwen2.5-7B-Instruct \ --adapter_name_or_path ./saves/query_rewrite_lora \ --template qwen \ --finetuning_type lora \ --export_dir ./merged_model \ --export_size 4 \ --export_legacy_format false

合并脚本会等待模型加载完成,然后把 LoRA 增量写回原始权重,保存为一个独立的全量模型。

5. 推理评估与效果实测

5.1 推理配置的取舍

模型合并好之后,下一步是推理测试。我当时的推理环境是一台 24G 显存的机器,加载合并后的 Qwen2.5-7B 模型,用 transformers 直接推理,核心配置如下:

from transformers import AutoModelForCausalLM, AutoTokenizer model_path = "./merged_model" tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype="auto", device_map="auto", ) def rewrite_query(query: str) -> str: messages = [ {"role": "system", "content": "你是一个查询改写专家。请将用户输入改写为适合检索的查询语句,要求:1) 保留原意;2) 使用规范术语;3) 去除口语化表达;4) 只输出改写结果,不要任何解释。"}, {"role": "user", "content": query}, ] text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer(text, return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_new_tokens=128, temperature=0.3, top_p=0.9) response = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True) return response.strip() test_queries = [ "那个什么协议怎么配来着", "好多人的订单都没收到钱", "怎么让页面打开快一点", "帮我看看登录失败是什么问题", ] for q in test_queries: print(f"原始查询:{q}") print(f"改写结果:{rewrite_query(q)}") print("-" * 40)

三个值得“抄走”的配置:

  • temperature=0.3,我记得最开始测试时用默认的 0.8,结果模型疯狂发挥,输出各种“‘好的,我把您的查询改写为如下...’”的前缀废话,改成 0.3 之后干净了很多。
  • max_new_tokens=128,查询改写本身输出长度很短,几十个 token 以内就够,设置太长的反向影响是模型有更大的空间去发散。
  • 推理时用和训练时完全相同的 instruction template,这一步很关键但常被忽略,如果你把训练时的那套系统提示词换成了另一套说法,模型的表现会有非常明显的退步。

5.2 效果评估方案:先看单点,再看批量

单点推理结果只能说明“看起来不错”,真正判断是否达到上线标准,得用更大的测试集做系统评估。我的评估方案分了三个层次:

第一层是人工抽样评测。准备一两百条模拟真实业务的测试查询,让三位同学对改写结果打分,评分维度有三项:

评分维度说明
长度合适是否过滤了口头废话,且没有无端扩展
术语准确是否将口语表达转换为了领域规范术语
信息保留是否保留了原查询中的所有关键信息,没有丢信息

三个维度各 1-3 分,总分 9 分。我当时拿到的人工平均分是 7.4 分左右,比未微调的基座模型(6.2 分)有明显提升。

第二层是自动化批量评估。用 bge-reranker 或相似度模型计算改写前后语义相似度,再加上一个“改写率”指标,统计有多少比例的输出不是直接复制原文。这个指标很有用,如果一个所谓“改写器”的改写率只有 30%,说明它大概率还在偷懒抄原文。

第三层是端到端测评。把改写器接到检索系统里,用一批真实失败 query 做回归测试,观察 doc 召回率、MRR 和 Hit Rate 的变化。这一步是最终的“体检报告”。我记得当时 Hit Rate 提升了约 18 个百分点,所有部门才认可这个方案值得上线。

5.3 与全参微调的对比

由于项目时间充裕,我把全参微调也跑了一版做对比。全参微调使用相同数据,训练了 3 个 epoch,显存占用约为 LoRA 的 2.5 倍,训练时间是 LoRA 的 4 倍左右。在同样的测试集上,两者得分非常接近,全参微调略高 0.2 分左右,但差距在统计意义上并不显著。

这个结果充分说明,在查询改写这类任务上,用 LoRA 做参数高效微调,能够在几乎不影响效果的情况下,把训练成本降一个数量级。当你需要在多个业务场景各做一个专用改写器时,这种成本优势会被进一步放大。

6. 常见问题与排查技巧实录

实操过程中我踩过不少坑,也帮朋友排查过类似的问题,这里挑出高频的几个整理成速查表,后面展开说每个问题的排查思路。

问题现象可能原因解决方向
训练时 loss 不降或震荡明显学习率过高 / 数据格式错误调低学习率到 1e-4,检查数据模板拼接
输出几乎等于输入原文数据集中改写差异小 / adapter 权重过小提高改写差异性,检查 alpha 是否过小
输出开头有“好的”“您好”等前缀推理温度过高 / 指令跟随训练不足调低 temperature 到 0.3,检查数据质量
显存不足 OOMbatch_size 过大 / 序列过长减小 batch_size,开启 gradient checkpointing
测试效果好但线上变差数据分布偏差收集更多真实失败 query 扩充训练集
加载 adapter 时模型报错基座模型版本与训练时不一致严格使用同一版本的 base model

6.1 Loss 不降或震荡

这个问题大多数时候出在数据格式上,而不是模型能力上。检查两个位置:

先看 tokenizer apply chat template 之后的文本是否有破绽。Qwen 的模板要求每段对话以<|im_end|>结尾,最后还要加<|im_start|>assistant作为生成起点,漏一个标签整个训练就乱了。Llama-Factory 已经处理好了这些,但如果手动写训练脚本,务必打印一条样本看看。

再看 labels 是否正确 mask。如果不 mask instruction 部分,模型会花大量精力去学习预测用户输入,表现为 loss 前期下降慢,训练集 acc 上不去。在训练脚本里加一段检查代码,打印 loss 计算位置的区间,确认只有 assistant 部分是有效标签。

排除了数据问题之后,再看学习率。LoRA 的默认经验值是 2e-4 到 5e-4,如果你用的优化器是 AdamW 加上 warmup 不对,前期 loss 尖峰是正常的,等 warmup 结束再看曲线。如果到第三步还在 1.0 以上完全不下探,把学习率降到 1e-4。

6.2 输出几乎等于输入原文

这个问题我用一句话总结:训练数据教它“换几个词”,它就会“换几个词”;数据教它“重构表达”,它才会真正重构。所以这个问题的根源九成在数据集。

我做过一次实验,把训练集里“强改写”样本的比例从 50% 调整到 80%,生成结果的改写率从 40% 涨到了 65%。所谓“强改写”是指输出不再保留原始句子的主干结构,完全以关键词组合或术语重述的方式出现。比如输入“那个支付平台总是弹出来说系统错误”,强改写成“支付网关 报错 排查”,而弱改写可能是“支付平台 系统错误”。

如果你的训练数据里大量是弱改写,模型就会学到“把句子缩一缩但保留骨架”的偷懒策略。

如果数据已经做了充分强改写,输出还是接近原文,再检查 lora_alpha。有些情况下 alpha 设得太小(比如和 r 相等),LoRA 修正力度不够,模型更倾向于“什么都不做”,这时候把 alpha 从 16 提到 24-32 会有肉眼可见的变化。

6.3 显存不足 OOM

Qwen2.5-7B 在 24G 显存下跑 LoRA 训练完全没问题,但如果你用的是 16G 或者更小显存的卡,需要做几个调整。最直接的是开启 gradient checkpointing,Llama-Factory 配置文件里加上gradient_checkpointing: true。原理是用计算换显存,在反向传播时重新计算中间激活值,显存占用能减少大概 30%-50%。

其次是可以用 4bit 量化作为 base model 的加载方式。LoRA 训练和 QLoRA 的区别就在这,QLoRA 会把基座模型量化到 4bit/8bit,再叠加 LoRA adapter,这样基座模型本身的显存占用大幅下降。16G 卡上量化到 4bit 后,Qwen2.5-7B 是完全跑得动的,效果损失在查询改写这种任务上几乎可以忽略。

还有一招是控制 max_seq_length。查询改写任务里,训练样本一般不超过 512 token,如果你默认用 2048,等于白白占了几倍的显存。把它压到 512,基本不影响效果,但显存压力小很多。

6.4 模型输出“一本正经地胡说”

这个问题最隐蔽。我遇到过模型把“RPC 协议配置”改写成“RCF 协议配置流程”的情况,表面看起来很规范,但实际上引入了一个文档里根本不存在的术语“RCF”。

排查思路是这样:如果模型输出的是完全编造的词,优先怀疑基座模型本身对业务领域不熟。查询改写任务虽然转换的是表达形式,但前提是必须理解术语的真实含义。这种情况下有两种解法:一是换一个更强的基座模型,比如从 Qwen2.5-7B 升级到 Qwen2.5-14B,业务术语理解能力会好不少;二是整理一份领域术语表,放进 system prompt 里做约束,让模型只能从术语表里选择改写用词。实测第二种方法在小模型上效果提升特别明显,因为我们不指望小模型记住所有业务术语,而是给它一个外部词典当作“小抄”。

还有一类“胡说”是模型把改写结果变成了解释,比如输出“我正在查看您的查询,其中提到了支付问题,建议您参考支付异常处理文档的...”。这本质上是把检索语句做成了回复。方向不复杂,但要从数据和推理两个层面一起堵:数据层面,确保 output 字段里没有解释性文本;推理层面,把 max_new_tokens 进一步压缩到 64,减少模型自由发挥的空间。

7. 从实验到落地,我的一点体会

整个项目从开始到上线,前后大约用了 10 天,其中训练和评估只占了不到 3 天,剩下时间都花在数据清洗、人工标注和线上 AB 测试准备上。这符合大模型微调项目的普遍规律:数据才是最大的瓶颈,训练只是把数据里的规律固化成参数而已。

有一个小经验让我印象很深:刚开始受网上各种说法影响,总觉得 r 要设成 16 或 32 才够,但实际测试下来 r=8 在这个任务上足够,而且换场景换基座之后 r=8 依然表现得最稳。LoRA 参数选择本质上是在“适配能力”和“泛化能力”之间找平衡,查询改写任务的复杂度决定了它不需要把模型的权重改动幅度拉得太大。遇到更复杂的任务,不妨从 r=8 开始,用一个小的验证集做 AB 对比,再决定要不要加,而不是一上来就堆配置。

项目上线后,我又做了一次迭代:把线上真实失败 query 捞出来,用规则加人工筛选的方式构造了一批新的训练数据,再在原有 adapter 基础上继续训练了几个 epoch。这次增量训练的收益非常明显,线上 Hit Rate 又提升了 7 个百分点左右。所以如果你问我对这个项目的最大心得,我会说:不要指望一版微调模型一劳永逸,建立“线上失败数据 -> 人工标注 -> 继续微调 -> 回归评估”的闭环,才是查询改写器能持续变好的真正原因。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询