从 Embedding 召回到 Reranker 精排:Qwen3 检索微调实战
当候选文档从百万级缩小到 Top 100 后,为什么还需要第二个模型?
因为第一阶段解决的是“快速找出可能相关的文档”,第二阶段解决的是“在有限候选里进行更充分的语义交互”。Embedding 适合对海量文档做离线编码和近似最近邻检索;Reranker 则把 Query 与每篇候选文档放入同一个上下文,花更多计算预算判断相关性。
这篇文章从完整检索链路出发,依次拆解 Qwen3-Embedding 与 Qwen3-Reranker 的模型结构、训练数据格式、Pointwise/Listwise 损失、LoRA/全参训练入口,以及容易让离线指标失真的评测细节。文中的 Reranker 工程使用 ms-swift,提供五种可复用的训练配置;完整效果数据来自 Qwen3-Reranker-0.6B 的 Pointwise LoRA 实验。Embedding 部分用于解释召回模型的结构与通用训练原理,不作为本文的实训结果。
- 两阶段检索系统概览
一个典型的语义检索系统可以拆成“召回”和“精排”两级:

文档侧向量可以提前计算,在线请求只需要编码一次 Query,再通过向量数据库或 Faiss、Milvus、Elasticsearch 等检索候选。这一阶段面对的可能是百万、千万甚至更大规模的语料,因此吞吐、索引容量和召回率是首要目标。
Reranker 接收第一阶段返回的 Top K 文档,对每个 Query-Document 组合计算相关性分数。它不能预计算完整的 Query-Document 交互,也不适合直接扫描整个文档库,但能利用更细粒度的 token 交互修正召回顺序。
这里有一个必须先建立的边界:Reranker 只能重排已经召回的候选,无法找回第一阶段遗漏的文档。如果相关文档没有进入 Top 100,那么无论精排模型多强,最终结果都不可能包含它。因此,两阶段系统必须同时观察召回上限和精排质量,不能只盯着 Reranker 的 MRR 或 nDCG。
- Qwen3-Embedding 模型结构
Qwen3-Embedding 不是传统的双向 BERT Encoder,而是建立在 Qwen3 Decoder Transformer 主干之上。Query 和 Document 分别通过同一个模型编码,再从最后一个有效 token 的 hidden state 得到整段文本的向量表示。
Query + Instruction ─┐
├─> Qwen3 Decoder Transformer ─> last token hidden state ─> L2 Normalize ─> q
Document ────────────┘
score(query, document) = q · d
它的推理过程可以概括为四步:
- 将 Query 与检索任务 instruction 组合成输入;Document 通常直接输入正文。
- 经过多层 Decoder Transformer,得到每个 token 的 hidden state。
- 取最后一个有效 token 的 hidden state,也就是 last-token pooling。
- 对向量进行 L2 归一化,用点积计算 Query 与 Document 的相似度。
归一化以后,点积与余弦相似度等价:
s(q,d)=\frac{h_q}{\lVert h_q\rVert_2}\cdot\frac{h_d}{\lVert h_d\rVert_2}
Qwen3-Embedding 官方提供 0.6B、4B 和 8B 三种规模。主要结构参数如下:
| 模型 | Transformer 层数 | 输出向量维度 | 上下文长度 | MRL | Instruction-aware |
|---|---|---|---|---|---|
| Qwen3-Embedding-0.6B | 28 | 1024 | 32K | 支持 | 支持 |
| Qwen3-Embedding-4B | 36 | 2560 | 32K | 支持 | 支持 |
| Qwen3-Embedding-8B | 36 | 4096 | 32K | 支持 | 支持 |
MRL 是 Matryoshka Representation Learning。直观地说,模型训练时让向量的前若干维也保留可用语义,因此部署时可以根据存储、带宽和检索效果折中,选择小于完整维度的截断向量。是否截断、截断到多少维,仍应通过目标评测集验证,不能只按索引大小决定。
Instruction 也不是装饰字段。对于“网页检索”“问答检索”“代码检索”等不同任务,同一句 Query 的相关性标准并不完全相同。Qwen3-Embedding 官方用法建议给 Query 加任务描述,而 Document 通常不加 instruction。线上和离线评测必须固定相同的 instruction,否则向量分布和指标不再可比。
Embedding 的关键工程优势是独立编码:
- 文档向量可以离线批量计算,并在内容变更时增量更新。
- 一个 Query 只编码一次,就可以与大规模向量索引比较。
- 代价是 Query 与 Document 在编码阶段看不到对方,跨文本的细粒度 token 交互有限。
- Qwen3-Reranker 模型结构
Qwen3-Reranker 使用 Qwen3ForCausalLM 作为主体,但它的任务不是生成一段自然语言答案。模型将 Instruction、Query 和 Document 拼成一个序列,读取最后位置上 yes 与 no 两个 token 的 logits,将二者差值作为相关性分数。
System: 只能回答 yes / no
User: ... ... ...
│
▼
Qwen3 Decoder Transformer
[RMSNorm → GQA Attention → Residual → Gated MLP → Residual] × N
│
▼
LM Head
│
logits_yes, logits_no
│
▼
score = logits_yes - logits_no
与 Embedding 的独立编码不同,Reranker 的自注意力能在同一序列中建立 Query token 与 Document token 的直接联系。例如,Query 中的“向量数据库过滤条件”可以和文档中的“标量过滤”“metadata filter”发生上下文交互,而不是先分别压缩成两个固定向量再比较。
Qwen3 主干内部包含 RMSNorm、旋转位置编码 RoPE、Grouped Query Attention(GQA)和带门控的 MLP。GQA 让多个 Query heads 共享较少的 Key/Value heads,降低 KV 相关开销;Gated MLP 则通过 gate/up/down projection 完成非线性变换。0.6B 与 4B 官方配置的核心参数如下:
| 配置 | Qwen3-Reranker-0.6B | Qwen3-Reranker-4B |
|---|---|---|
| 模型类型 | Qwen3ForCausalLM | Qwen3ForCausalLM |
| Transformer 层数 | 28 | 36 |
| Hidden size | 1024 | 2560 |
| MLP intermediate size | 3072 | 9728 |
| Attention heads | 16 | 32 |
| KV heads | 8 | 8 |
| Head dimension | 128 | 128 |
| Vocabulary size | 151669 | 151669 |
虽然两个模型都使用 Qwen3 主干,但“共享架构家族”不等于“训练目标相同”。Embedding 输出一个可索引的稠密向量,优化的是向量空间中的相对距离;Reranker 输出某个 Query-Document pair 的相关性 logit,优化的是分类或组内排序。服务位置、输入组织、缓存能力和损失函数都不同。
推理时不必真的让模型生成 yes 或 no。直接读取最终位置的两个 logits 更快,也更稳定:
logits = model(**inputs).logits[:, -1, :].float()
yes_logits = logits[:, yes_token_id]
no_logits = logits[:, no_token_id]
scores = yes_logits - no_logits
yes_logit - no_logit 是二分类的 log-odds。它保留了比 BF16 softmax 概率更充分的排序信息,后文会解释为什么这一点会影响评测。
- Embedding 与 Reranker 对比
| 维度 | Embedding 召回 | Reranker 精排 |
|---|---|---|
| 输入方式 | Query、Document 独立编码 | Instruction、Query、Document 联合编码 |
| 输出 | 固定维度向量 | yes/no logits 或相关性标量 |
| 文档预计算 | 可以 | 不可以预计算完整交互 |
| 交互强度 | 向量级相似度 | token 级联合注意力 |
| 典型候选规模 | 全库到 Top K | Top K 到 Top N |
| 在线计算 | 编码一次 Query + ANN | 每个 Query-Document 候选都要打分 |
| 常见训练目标 | InfoNCE/对比学习 | Pointwise/Listwise 排序损失 |
| 主要关注指标 | Recall@K、检索吞吐 | MRR、MAP、nDCG、精排延迟 |
| 系统位置 | 第一阶段召回 | 第二阶段精排 |
两者不是替代关系。一个常见的落地组合是:Embedding 把百万级语料压缩到 Top 100,Reranker 再选出 Top 10,最后交给搜索结果页、推荐模块或 RAG 生成模型。
它们也不必各自维护完全独立的数据生产线。正例标注、BM25/向量召回结果、难负例挖掘和去重规则可以先沉淀成统一候选池,再分别转换为 Embedding 对比学习格式和 Reranker query group 格式。
- 统一候选池数据格式
训练数据首先不要急着展开成 pair。更稳妥的中间层是一行一个 Query,保留正例与不同难度的负例池:
{
”query”: ”向量数据库如何同时进行语义检索和标量过滤?”,
”positives”: [
”向量数据库通常先用向量相似度召回,再通过 metadata filter 对类别、时间或权限字段进行过滤。”
],
”hard_negatives”: [
”向量数据库使用 HNSW 或 IVF 建立近似最近邻索引。”,
”标量数据库可以通过 B+ 树优化范围查询。”
],
”medium_negatives”: [
”混合检索会融合稠密向量分数与 BM25 关键词分数。”,
”向量归一化后可以使用点积近似余弦相似度。”,
”数据库分片能够提高大规模数据的并行查询能力。”
],
”random_negatives”: [
”Python 的生成器可以按需产生数据。”,
”对象存储适合保存图片与归档文件。”
]
}
这层数据的价值在于保留采样空间。Pointwise 可以为每个 Query 选 7 个负例,Listwise 可以选 3 个负例;Embedding 训练还可以把同 batch 的其他正例作为 in-batch negatives,而不需要反复从原始语料重建候选。
一个实用的初始采样比例是:
1 positive + 2 hard negatives + 3 medium negatives + 2 random negatives
三种负例承担不同职责:
- Hard negative 与 Query 表面或主题高度相似,但并不满足相关性标准,负责训练决策边界。
- Medium negative 有一定主题关联,却比较容易区分,用来缓冲过强负例造成的训练噪声。
- Random negative 提供更宽的语料覆盖,防止模型只学会在局部相似候选之间判断。
数据清洗至少要处理三件事。第一,按规范化后的文本去重,避免同一文档同时出现在 positive 和 negative;规范化只用于比较,不要改写真正送给模型的内容。第二,过滤假负例:召回排名靠后不代表一定不相关,教师分数很高或人工复核为相关的候选应删除或重新标注。第三,按 Query 划分训练集和验证集,再转换成具体训练格式。
如果先把一个 Query 展开成八个 Query-Document pair,再随机划分 pair,同一 Query 可能同时出现在训练集和验证集。模型会记住 Query 表达或候选主题,验证指标因此失真。对 Listwise 来说,pair 级切分还会直接破坏候选组边界。
- Embedding 数据与对比学习
从统一候选池转换到 Embedding 训练时,可以把一条样本理解为“带任务描述的 Query、一个正例和若干负例”:
{
”instruction”: ”Given a web search query, retrieve relevant passages that answer the query”,
”query”: ”向量数据库如何同时进行语义检索和标量过滤?”,
”positive”: ”向量数据库通常先用向量相似度召回,再通过 metadata filter 对字段进行过滤。”,
”negatives”: [
”向量数据库使用 HNSW 或 IVF 建立近似最近邻索引。”,
”标量数据库可以通过 B+ 树优化范围查询。”,
”对象存储适合保存图片与归档文件。”
]
}
这段 JSON 表达的是通用的数据语义,具体字段名要以所选训练框架为准。核心目标不变:让 Query 向量靠近正例,远离显式负例和 batch 内其他 Query 的文档。
对一个 Query q _ i q\_i q_i、正例 d _ i + d\_i^+ d_i+ 和候选集合 D _ i D\_i D_i,简化后的 InfoNCE 损失为:
L_i=-\log\frac{\exp(s(q_i,d_i^+)/\tau)}{\sum_{d\in D_i}\exp(s(q_i,d)/\tau)}
其中 s ( q , d ) s(q,d) s(q,d) 是归一化向量点积, τ \tau τ 是温度系数。温度越低,softmax 越关注最难区分的候选;过低可能放大假负例和标注噪声,过高则会让候选分数过于平缓。
在大 batch 或跨设备负例场景下,其他样本的正例也会进入分母,形成 in-batch negatives。这能显著增加负例数量,但有一个副作用:不同 Query 可能共享相关文档,或者语义近似的文档被当成负例。扩大 batch 之前,最好先检查 false-negative 比例,并考虑去重、同主题过滤或教师模型筛选。
Qwen3-Embedding 的技术报告介绍了多阶段训练和改进的 InfoNCE 框架。工程上不必机械复制所有阶段,可以先把数据质量、instruction 一致性和评测协议固定下来,再决定是否加入长文本、跨语言数据或教师蒸馏。
- Reranker 的 ms-swift 数据格式
ms-swift 的 generative_reranker 任务使用 group 数据。一行 JSONL 对应一个 Query group:messages 保存 instruction 与 Query,positive_messages 和 negative_messages 中的 assistant content 保存候选文档。
{
”messages”: [
{
”role”: ”system”,
”content”: ”Given a web search query, retrieve relevant passages that answer the query”
},
{
”role”: ”user”,
”content”: ”向量数据库如何同时进行语义检索和标量过滤?”
}
],
”positive_messages”: [
[
{
”role”: ”assistant”,
”content”: ”向量数据库通常先用向量相似度召回,再通过 metadata filter 对字段进行过滤。”
}
]
],
”negative_messages”: [
[
{
”role”: ”assistant”,
”content”: ”向量数据库使用 HNSW 或 IVF 建立近似最近邻索引。”
}
],
[
{
”role”: ”assistant”,
”content”: ”标量数据库可以通过 B+ 树优化范围查询。”
}
]
]
}
这里看起来像多轮对话,实际上是在借助消息结构表达“任务描述—Query—候选文档”。框架会根据 task_type=generative_reranker 和 loss_type 组装 Reranker 输入与标签。不要把候选文档误放进 user content,也不要预先把 group 拆散后再期望 Listwise loss 恢复分组。
本文实验的混合训练集包含 30,000 个 Query group:mMARCO Chinese 15,000、DuReader Retrieval 13,688、MIRACL Chinese 1,312;验证集使用 800 个 DuReader Query。MIRACL 中文训练 Query 全部保留,剩余数量由 DuReader 补足。数据源虽然不同,但转换后的字段、instruction、负例采样和去重逻辑保持一致。
Pointwise 主训练默认从每组读取 1 个正例和 7 个负例;Listwise 为了控制每组联合计算规模,使用 1 个正例和 3 个负例。候选数改变会同时改变训练难度和计算量,所以比较两种损失时必须在实验记录里明确写出组大小。
- Pointwise 与 Listwise Loss
Pointwise 和 Listwise 的差别,不只是命令行里的一个参数。它们对“什么是一个训练样本”的定义不同。
Pointwise:逐对判断相关或不相关
Pointwise 将每个 Query-Document pair 看成独立二分类样本:正例目标是 yes,负例目标是 no。用 y ∈ 0 , 1 y\in{0,1} y∈0,1 表示标签, p p p 表示预测相关概率,二元交叉熵可以写成:
L _ p o i n t = − [ y log p + ( 1 − y ) log ( 1 − p ) ] L\_{point}=-[y\log p+(1-y)\log(1-p)] L_point=−[ylogp+(1−y)log(1−p)]
它的优点是实现直接、样本组织灵活,也容易通过增加难负例强化分类边界。缺点是每个 pair 独立优化,训练目标没有显式表达“同一个 Query 下,正例应该排在这些负例之前”。正负样本比例变化也会影响分类校准。
Listwise:在同一个 Query 组内优化排序
Listwise 保留一整个候选组。设正例得分为 s + s^+ s+,同组候选得分为 s _ j s\_j s_j,简化的组内 softmax 损失为:
L_{list}=-\log\frac{\exp(s^+/\tau)}{\sum_j\exp(s_j/\tau)}
它直接要求正例在当前候选组内获得更高分,更贴近最终重排任务。不过,Listwise 的效果高度依赖组内负例构成:如果候选太容易,梯度信息有限;如果多个所谓负例其实也相关,模型会被迫压低合理文档的分数。
同一个 Query
├── Positive: 语义检索 + metadata filter 的完整回答
├── Hard negative: 只介绍 HNSW 索引
├── Medium negative: 只介绍混合检索
└── Random negative: 对象存储说明
Pointwise: 形成 4 个独立 yes/no 判断
Listwise: 在同一组内让 Positive 排到最前
因此,公平比较 Pointwise 与 Listwise 至少要固定:基础模型、训练 Query、验证 Query、instruction、最大长度、随机种子、评测候选集和 checkpoint 选择规则。独立 Pointwise、独立 Listwise 都应从同一个预训练 checkpoint 启动;“先 Pointwise 再 Listwise”属于第三种两阶段训练实验,不能拿来替代前两者的对照。
- LoRA 与全参训练
LoRA 不直接更新线性层原始权重 W W W,而是学习一个低秩增量:
W ′ = W + α r B A W'=W+\frac{\alpha}{r}BA W′=W+rαBA
其中 A A A 和 B B B 的秩为 r r r, α \alpha α 控制增量缩放。基础模型保持冻结,只训练新增的小矩阵。
Qwen3 Transformer 中常见的线性投影包括 Attention 的 q_proj、k_proj、v_proj、o_proj,以及 MLP 的 gate_proj、up_proj、down_proj。本文脚本使用 target_modules=all-linear,让框架把 LoRA 注入所有适合的线性层:
Attention: x ─> q/k/v projections ─> GQA ─> o projection ─> residual
▲ ▲
LoRA LoRA
MLP: x ─> gate/up projections ─> activation/product ─> down projection
▲ ▲
LoRA LoRA
LoRA 的 checkpoint 通常保存 adapter_model.safetensors 和 adapter 配置,推理时需要同时加载基础模型与 adapter。全参训练则更新模型权重,checkpoint 中保存 model.safetensors 等完整权重文件,评测时直接将模型路径指向该 checkpoint。
两者的选择不是“哪个永远更好”。LoRA 适合快速验证数据与损失函数、维护多个小型任务适配器;全参训练提供更大的参数调整空间,但学习率通常更低,对数据规模、训练稳定性和 checkpoint 管理要求更高。本文分别保留两种入口,便于在固定实验协议下继续对照,但不能依据训练能否完成直接判断效果优劣。
- 五种 Reranker 训练入口
训练框架使用 ms-swift 4.4.2,统一入口是 swift sft,并通过 task_type=generative_reranker 切换到生成式 Reranker 训练逻辑。实验矩阵如下:
| 模型 | Loss | 训练方式 | 默认学习率 | 每组负例 | Checkpoint 类型 |
|---|---|---|---|---|---|
| Qwen3-Reranker-0.6B | Pointwise | LoRA | 5e-5 | 7 | Adapter |
| Qwen3-Reranker-0.6B | Listwise | LoRA | 3e-5 | 3 | Adapter |
| Qwen3-Reranker-0.6B | Pointwise | 全参 | 5e-6 | 7 | 完整模型 |
| Qwen3-Reranker-0.6B | Listwise | 全参 | 3e-6 | 3 | 完整模型 |
| Qwen3-Reranker-4B | Pointwise | LoRA | 5e-5 | 7 | Adapter |
这里不提供 4B 全参训练入口。训练变量保持在一个明确范围内,先验证 4B 预训练基线与 Pointwise LoRA,再决定是否值得扩大实验矩阵。
下面是 0.6B Pointwise LoRA 的核心命令。项目脚本还统一处理了随机种子、数据加载线程和输出目录等参数:
swift sft \
--model Qwen/Qwen3-Reranker-0.6B \
--task_type generative_reranker \
--loss_type pointwise_reranker \
--tuner_type lora \
--dataset data/swift/train-30k.swift.jsonl \
--val_dataset data/swift/dureader-validation.swift.jsonl \
--torch_dtype bfloat16 \
--use_logits_to_keep true \
--max_length 1024 \
--truncation_strategy right \
--learning_rate 5e-5 \
--lr_scheduler_type cosine \
--warmup_ratio 0.05 \
--weight_decay 0.01 \
--max_grad_norm 1.0 \
--num_train_epochs 1 \
--per_device_train_batch_size 1 \
--per_device_eval_batch_size 1 \
--gradient_accumulation_steps 8 \
--gradient_checkpointing true \
--eval_strategy steps \
--eval_steps 100 \
--save_steps 100 \
--save_total_limit 2 \
--load_best_model_at_end true \
--metric_for_best_model loss \
--greater_is_better false \
--lora_rank 8 \
--lora_alpha 32 \
--lora_dropout 0.05 \
--target_modules all-linear
use_logits_to_keep=true 让训练只保留计算损失所需位置的 logits,避免保存完整序列词表 logits。gradient_checkpointing=true 用额外重算换取激活占用下降;gradient_accumulation_steps=8 将多个 micro-batch 的梯度累积后再更新。余弦学习率、5% warmup、权重衰减和梯度裁剪用于提高训练稳定性。
实际使用时,通过环境变量设置数据路径后调用五个脚本即可:
export TRAIN_DATA=”$PWD/data/swift/train-30k.swift.jsonl”
export EVAL_DATA=”$PWD/data/swift/dureader-validation.swift.jsonl”
bash scripts/train_0p6b_pointwise_lora.sh
bash scripts/train_0p6b_listwise_lora.sh
bash scripts/train_0p6b_pointwise_full.sh
bash scripts/train_0p6b_listwise_full.sh
bash scripts/train_4b_pointwise_lora.sh
Attention 实现可以按环境选择 FlashAttention 或 PyTorch SDPA。它们是算子实现差异,不应改变实验的数据和损失定义。若切换实现,仍需要做数值与指标回归,尤其要确认 padding、截断和 logits 位置没有变化。
- Checkpoint 管理与训练验收
训练结束后,不能只看终端最后打印的 loss。首先检查 trainer_state.json 中的训练步数、日志历史和最佳 checkpoint 路径,再确认权重文件与训练方式匹配:LoRA 应保存 adapter_model.safetensors 和 adapter 配置,全参训练应保存 model.safetensors 等完整权重。
评测 LoRA 时,需要加载原始基础模型,再挂载对应 adapter;评测全参模型时,直接将模型路径指向 checkpoint。把 LoRA 目录误当作完整模型加载,或者给全参 checkpoint 再套一层 adapter,都会导致加载失败或评测对象错误。
脚本启用 load_best_model_at_end=true,按验证 loss 选择较优 checkpoint,并限制保留数量。若目标指标是 MRR 或 nDCG,可以额外在保存点执行统一精排评测,但不能一边查看测试集结果一边选择 checkpoint,否则测试集就参与了调参。
建议把训练配置、数据版本、基础模型版本、随机种子和最佳 checkpoint 路径写入同一份实验记录。这样既能复现最终结果,也能避免后续评测时把不同起点、不同数据或不同截断设置的模型放进同一张对比表。
- 统一评测
评测文件同样是一行一个 Query group,但需要保存真实一阶段候选及相关性标签:
{
”query_id”: ”q-001”,
”query”: ”向量数据库如何同时进行语义检索和标量过滤?”,
”instruction”: ”Given a web search query, retrieve relevant passages that answer the query”,
”candidates”: [
{
”text”: ”向量数据库通常先做向量召回,再使用 metadata filter 过滤字段。”,
”relevance”: 2
},
{
”text”: ”HNSW 是常见的近似最近邻索引结构。”,
”relevance”: 0
}
]
}
模型输入遵循 Qwen3-Reranker 的官方模板:System 要求只能判断 yes/no,User 部分组合 、 和 。模型不生成文本,直接取最后位置 logits:
with torch.inference_mode():
logits = model(**inputs).logits[:, -1, :]
no_logits = logits[:, no_token_id].float()
yes_logits = logits[:, yes_token_id].float()
scores = yes_logits - no_logits
排序时使用 FP32 的 raw log-odds,而不是先将两个 BF16 logits 转成概率。对于置信度很高的样本,低精度 softmax 可能让大量结果都变成精确的 1.0,原本不同的候选因此产生并列分数。logit 差值是单调等价的排序分数,不会被概率饱和过早压平。
本文使用四类指标:
- MRR@10:只关心前 10 名中第一个相关文档的位置,适合问答或首个结果体验。
- MAP:计算每个相关文档出现位置上的 Precision,再对 Query 求平均,适合一个 Query 存在多个相关结果的场景。
- nDCG@10:考虑前 10 名的位置折损和分级相关性,越相关的文档越应排在前面。
- Recall@K:相关文档有多少进入 Top K;在精排评测中也反映给定候选集的可达上限。
所有指标按 Query 做 macro average,避免候选多的 Query 支配结果。分数相同时使用稳定排序,以候选原始位置作为 tie-breaker,保证多次评测可重复。
统一评测最重要的不是多算几个指标,而是固定以下条件:
- 所有模型使用完全相同的候选集合和 relevance 标签。
- 固定 instruction、prompt 模板、最大长度与截断方向。
- 固定
yes/notoken、分数公式和并列排序规则。 - LoRA 正确加载同一基础模型,全参 checkpoint 直接作为模型路径。
- 禁止为了保证每个 Query 有正例而向候选集强行插入 positive。
如果真实 BM25 Top 50 没有相关文档,这个 Query 对 Reranker 来说就是不可恢复的。强行插入正例会把“召回失败”改写成“精排可解问题”,指标因此虚高。本文 mMARCO 评测保留真实 BM25 Top 50 顺序;其他评测集对初始候选顺序做固定种子的确定性打乱,以去除正例总在前面的构造偏差。
- 0.6B Pointwise LoRA 实验结果
实验先评测 Qwen3-Reranker-0.6B 与 4B 预训练模型,再训练 0.6B Pointwise LoRA。完整训练结果只用于这一配置;其他四种训练入口目前只报告流程和 checkpoint 验证,不据此推断 Pointwise、Listwise、LoRA 与全参训练的相对效果。
三个测试集分别覆盖 mMARCO Chinese、DuReader Retrieval 和 MIRACL Chinese。0.6B Pointwise LoRA 的主要结果如下:
| 测试集 | 预训练 MRR@10 | Pointwise LoRA MRR@10 | 变化 | 预训练 MAP | LoRA MAP | 预训练 nDCG@10 | LoRA nDCG@10 |
|---|---|---|---|---|---|---|---|
| mMARCO | 0.4634 | 0.5020 | +0.0385 | 0.4692 | 0.5081 | 0.5543 | 0.5868 |
| DuReader | 0.5715 | 0.6208 | +0.0493 | 0.4301 | 0.4746 | 0.4965 | 0.5483 |
| MIRACL | 0.8263 | 0.8633 | +0.0370 | 0.7688 | 0.8097 | 0.8482 | 0.8768 |
| 三套平均 | 0.6204 | 0.6620 | +0.0416 | 0.5560 | 0.5975 | 0.6330 | 0.6706 |
平均 MRR@10 提升 0.0416,平均 MAP 提升 0.0414,平均 nDCG@10 提升 0.0376。三个测试集的主要排序指标都向同一方向变化,比只看一个平均数更有说服力;但这些结果仍然属于当前公开任务、候选构造和评测协议,不能脱离上下文外推。
预训练 4B 也提供了一个有价值的容量参照。在相同测试协议下,4B 的 MRR@10 分别为 mMARCO 0.4726、DuReader 0.7112、MIRACL 0.8498;0.6B 则为 0.4634、0.5715、0.8263。更大模型在 DuReader 上优势明显,但模型规模、微调数据与线上成本是三个独立变量,不能把“大模型基线更高”直接等同于“小模型微调没有价值”。
训练过程中,最佳 checkpoint 没有出现在最后一步:
| Checkpoint | 验证 Loss | 验证 MRR | 验证 nDCG |
|---|---|---|---|
| Step 500 | 0.0596 | 0.7523 | 0.8141 |
| Step 3750 | 0.1042 | 0.7023 | 0.7762 |
Step 500 之后验证指标回落,说明继续降低训练集目标并不保证泛化排序继续改善。工程上应保存 best checkpoint,限制无效 checkpoint 的数量,并结合验证 loss 与排序指标观察过拟合。最终测试必须使用事先选定的 checkpoint,不能在测试集上回看多个保存点后挑最高值。
- 常见错误与优化技巧
14.1 在 pair 展开后随机切分,造成 Query 级泄漏
同一 Query 的正例进入训练集、负例进入验证集,模型已经见过 Query 本身与主题词。修复方式是在统一候选池阶段按 Query ID 切分,训练、验证、测试之间做 Query 去重,再分别转换格式。
14.2 为评测候选强行插入正例
这种做法改变了第一阶段召回上限,也改变了候选难度。正确方式是保留真实召回结果,同时报告 Recall@K;对于没有相关候选的 Query,可以单独报告端到端指标,或明确区分“召回成功子集上的纯精排指标”。
14.3 难负例越难越好,忽略假负例
Top 排名的未标注文档常常只是“未被标注”,不一定真的不相关。可以综合教师分数、规则、点击反馈或人工抽样筛查;对高风险候选采用降权、删除或软标签,而不是一律作为强负例。
一个更稳的课程式采样是先混合 hard/medium/random,待模型具备基本判断能力后,再逐步提高 hard negative 比例。这样能避免训练早期被少量噪声强负例主导。
14.4 直接用 BF16 softmax 概率排序
当 yes 明显高于 no 时,BF16 概率很容易饱和到 1.0,不同候选丢失相对差异。排序使用 FP32 的 yes_logit - no_logit;如果产品需要展示概率,再单独在验证集做温度缩放或其他校准,不要把“排序分数”和“可解释概率”混为一谈。
14.5 Pointwise 与 Listwise 从不同起点训练
如果 Listwise 从一个已经完成 Pointwise 训练的 checkpoint 开始,而 Pointwise 从预训练模型开始,两者差异同时包含初始化与损失函数影响。应建立三个实验:预训练→Pointwise、预训练→Listwise、预训练→Pointwise→Listwise,分别回答独立损失和两阶段训练的问题。
14.6 Listwise 组内候选没有信息量
一个正例配三个完全随机的负例,模型很快就能区分,排序梯度有限。更合理的组内结构是保留至少一个真正有迷惑性的 hard negative,再加入中等和随机负例维持稳定性。组大小、负例来源和采样种子都应进入实验配置。
14.7 只看平均指标
平均指标可能掩盖某个数据集明显回退。除了三套平均,还要报告每个数据集、最差数据集、同源与跨数据集表现,并检查 Recall 上限。线上决策还需要结合延迟、吞吐、候选数量和文本长度分桶。
14.8 截断策略破坏关键信息
Reranker 输入同时包含 instruction、Query 和 Document。简单右截断可能丢掉长文档尾部的答案,左截断又可能损伤开头。可以先统计 token 长度分布,再尝试文档分块、保留首尾、动态长度或段落级召回;所有策略必须在固定候选集上对照。
14.9 Instruction 在训练与评测中漂移
训练时使用“retrieve relevant passages”,评测时换成另一个任务描述,会引入额外变量。把 instruction 当成模型输入协议的一部分,纳入数据版本和服务配置;多任务训练时也要记录每种 instruction 的占比。
14.10 只增加训练量,不复盘候选池
Reranker 学到的是候选之间的边界。重复的随机负例再多,也不一定比少量高质量、经过假负例过滤的 hard negative 更有效。优先做错误分析:查看正例被哪些候选压过、是实体混淆、时间条件、否定语义、长文本截断,还是标注本身有问题,再定向补充数据。
14.11 忽略蒸馏与分数校准的实验边界
教师模型可以提供软分数、候选排序或假负例过滤信号,但一旦加入蒸馏,就同时改变了监督来源。先得到不含蒸馏的稳定基线,再单独增加 KL、margin 或排序蒸馏,才能判断收益来自哪里。分数校准同理:它服务于阈值和概率解释,不应和基础排序训练混成一次不可拆解的实验。
- 总结
一套可复现的 Qwen3 两阶段检索方案,关键不在于把两个模型串起来,而在于让数据、目标和评测边界保持一致。
第一,Embedding 与 Reranker 各司其职。Embedding 通过独立编码、last-token pooling 和归一化向量承担大规模召回;Reranker 通过 Query-Document 联合注意力和最终位置的 yes/no logits 完成精排。Reranker 无法突破召回上限,因此系统必须同时优化 Recall 与排序质量。
第二,候选池和负例质量往往比机械扩大数据量更值得关注。统一保存 positive、hard、medium、random pools,可以复用到 Embedding 对比学习和 Reranker 训练;去重、假负例过滤和 Query 级切分,则决定了训练信号是否可信。
第三,比较 Pointwise、Listwise、LoRA 和全参训练之前,先固定基础 checkpoint、instruction、候选集、截断、随机种子、排序分数与 best-checkpoint 规则。只有评测协议稳定,指标变化才真正对应训练方案,而不是数据泄漏、正例插入或概率饱和。
本文给出了五种 Reranker 训练入口,0.6B Pointwise LoRA 在三套评测上取得了一致的排序指标提升。下一步若继续扩展,建议按“独立 Listwise 完整训练→0.6B 全参对照→Pointwise 到 Listwise 两阶段训练→蒸馏消融”的顺序推进,每次只改变一个主要变量。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

更多推荐



所有评论(0)