两个模型做的是不同工作

Embedding把文本转换成可比较向量,适合从大量段落召回候选;Reranker把问题与每个候选共同输入,重新评估相关性。重排不能找回第一阶段根本没召回的证据,所以先检查召回率,再谈排序。本文使用Qwen3-Embedding-0.6B和Qwen3-Reranker-0.6B,参考官方模型卡建立本地试验。

建议将两个服务隔离,或者在资源较小的试验机中顺序运行,避免与生成模型一起把显存占满。模型较小不代表处理任意数量、任意长度的候选都没有成本。本文先用三段文本演示,不宣称生产容量。

下载与环境锁定

python3 -m venv .search-env
source .search-env/bin/activate
# 按GPU安装兼容PyTorch,再安装应用依赖
pip install 'transformers>=4.51.0' sentence-transformers accelerate
pip install 'huggingface_hub[cli]'
hf download Qwen/Qwen3-Embedding-0.6B --local-dir /srv/models/qwen3-embed
hf download Qwen/Qwen3-Reranker-0.6B --local-dir /srv/models/qwen3-rerank
pip freeze > search-requirements.lock.txt

上述最低版本不是对未来所有依赖组合的承诺。首次验证成功后固定具体版本和两个模型的revision,记录PyTorch与CUDA。若新版本改变encode接口,核对官方说明,不在生产任务中边运行边升级。

第一阶段用指令化查询召回

import numpy as np
from sentence_transformers import SentenceTransformer
embed = SentenceTransformer('/srv/models/qwen3-embed')
query='32B模型部署需要多少显存?'
docs=['权重显存按参数量和每参数字节数估算,KV缓存另外预留。',
      '服务器液冷需要核对进出水温度。',
      '量化减少权重空间,但运行时和并发也会占用显存。']
instruction='Given a Chinese compute question, retrieve passages that directly help answer it.'
q=embed.encode([f'Instruct: {instruction}\nQuery: {query}'],
               normalize_embeddings=True)
d=embed.encode(docs, normalize_embeddings=True)
scores=(q @ d.T)[0]
order=np.argsort(-scores)
candidates=[docs[int(i)] for i in order]
print(list(zip(candidates,scores[order])))

文档不简单重复查询指令。是否使用具体prompt名称应依官方模型卡,保持查询和文档预处理一致。实际索引保存维度与模型版本;更换Embedding后应重建索引,不混用向量。

第二阶段遵守Reranker聊天格式

Qwen3-Reranker不是普通Embedding模型。官方方法使用规定的指令、Query、Document格式,并比较yes/no token输出。近期Sentence Transformers已在其适配中封装模板,可使用官方模型卡给出的CrossEncoder接口;旧版本若不支持,需按同一模型卡升级并锁定兼容组合,不把普通分类头代替该方法。

接着在上一段编码脚本下加入以下内容,调用完整的交叉评分链路。若GPU资源不足,可先使用CPU或释放Embedding模型后再加载重排模型;首次加载与稳态重排应分别计时。

from sentence_transformers import CrossEncoder
reranker=CrossEncoder('/srv/models/qwen3-rerank',max_length=2048)
scores=reranker.predict([(query, passage) for passage in candidates],batch_size=2)
if len(scores)!=len(candidates):
  raise ValueError('重排输出数量不匹配')
pairs=sorted(zip(candidates,scores),key=lambda x:float(x[1]),reverse=True)
for passage,score in pairs[:3]:
 print(float(score),passage)

默认结果是原始logit差,可为负数。它是排序信号,不是业务正确率;即使加Sigmoid变成0到1,也不等于经过业务校准的可靠概率。候选长度超过2048限制会被截断,关键条件可能因此消失;长文应合理分块,并在结果中恢复来源与页码。

用业务问题决定候选数量

准备100个带相关段落标注的问题,比较仅向量、召回20条后重排、召回50条后重排。记录Recall@20、前3条证据准确率与总耗时。扩大候选数可能提高召回,却增加显存和延迟;若第一阶段质量差,不应通过无限重排掩盖问题。

加入编号、专有名词和否定条件的样本,必要时增加关键词召回。最终生成模型只能引用返回证据,缺少材料时明确拒答,不让重排后的“第一名”自动成为事实。

故障与回滚

分数全相同检查模板与yes/no token;召回正确但重排异常检查截断和padding;OOM降低候选批量与长度;延迟高分开量编码、数据库和重排。回滚保留旧模型、模板和测试集,切回上一索引及评分服务。官方依据:Embedding模型卡、Reranker完整实现。

给中文检索准备可比较的数据

问题集应覆盖口语提问、产品型号、错误码、同义表达和无答案条件。每个问题标注一个或多个相关段落,同时记录哪些看起来接近却不满足条件。比如购买单卡与整机的差别,模型支持与已经测试的差别,都可能在语义相近时影响最终结果。不能仅用训练资料中的标题做查询来证明效果。

分块保持标题、单位与限定条件。技术表格如果只留下数值,没有列名和型号,重排无法恢复丢失信息。图像内文字先解析并抽查,失败页单独标记。每个候选附原文来源、页码与索引版本,重排改变顺序但不改写证据,最终答案也不能把重排评分当成证据内容。

资源共享与延迟预算

同一机器上同时运行编码、重排和生成时,要分别设置并发。文档批量编码放到低优先级队列,避免夜间导入任务占满白天检索资源。查询编码通常较短,重排候选数量和长度则可能成为主要开销;分阶段计时比一个总响应数字更有诊断价值。

候选批量增大后,先观察显存峰值和排队,不无限提高batch。模型加载与第一批计算单独记录,预热结果不能用于冷启动承诺。CPU和GPU版本的结果在数值上可能略有差异,比较实际排序与业务质量,不要求所有分数逐位相同。

何时应该拒绝给出答案

如果召回内容没有回答问题所需的字段,系统应说明资料不足,必要时请用户补充型号、场景或版本。没有经过校准的重排阈值不适合直接决定“事实可信”。将无答案问题加入验收,观察模型会不会从不相关的第一条候选中编出结果。

上线后收集用户标记的错检与漏检,先确认是分块、召回、重排还是生成问题。更新查询指令后重跑固定问题集,留存前后候选和分数,避免把复杂性全部归咎于Embedding模型。质量改善必须同时检查成本与延迟,不能用无限候选和超长等待换一个漂亮的准确率。

来源、翻译与版权说明

来源:网昱算力学院 · 技术编辑。第三方内容版权归原作者或发布机构所有;本站仅在许可证或明确授权允许时提供本地原文。

原文语言
ZH
原文更新时间
未提供
许可证
原创工程内容