RAG 的 Embedding 如何用对比学习微调?难负样本和假负样本有什么区别?
简化版
检索 Embedding 微调通过对比学习提高问题与相关文档的相对相似度,让相关文档排在不相关文档前面。难负样本是“很像但不能回答”的文档,假负样本却是“本来相关却被错标为负”的文档,后者会把训练方向推反。
详细版
- 训练数据可以是问题与正例文档对,也可以额外提供已确认不相关的负例。
- 常用 batch 内负例:对一个问题,把同批其他问题的文档当作候选负例,前提是它们确实不相关。
- InfoNCE 类损失对候选相似度做 softmax,提高正例相对概率;温度控制区分强度。
- 难负例可由 BM25 或已有向量模型召回后审核得到,能训练模型区分相近概念与业务条件。
- 去重、多正例处理和相关性复核可减少假负例,不能把所有未标注结果都当成错误答案。
- 验收用隔离的检索评测集,保持分块、候选库和指标口径一致;模型更新后通常需要重新生成文档向量。
完整版教学
一、为什么通用语义相似不等于业务相关
问题“企业版怎么退费”与“个人版怎么退费”字面非常接近,但适用政策可能不同。 通用向量模型容易学到共同的“退费”主题,却未必足够重视版本、日期或资格条件。 检索微调的目标是让向量空间体现任务相关性,而不只是同义词关系。 因此正例应是能够支持回答的文档,不能简单选择词汇最多的片段。
例如问题明确询问企业版,正例包含企业版退款条件,难负例包含个人版退款条件。 如果两个版本恰好适用同一政策,后者又可能成为相关文档,必须按真实业务判断。 标签表达的是“对于该问题是否相关”,不是文档本身永久属于好或坏。
二、对比损失怎样推动排序
设一个问题 q 对应正例 d+,另外有若干负例。 分别编码问题与文档,计算相似度,再把相似度除以温度送入 softmax。 对正例概率取负对数,正例相对其他候选越突出,损失越小。 这里优化的是候选间相对区分,不要求余弦相似度本身就是经过校准的相关概率。
s(q,d) = cosine(encode_query(q), encode_document(d))
P(正例 | 当前候选集) = exp(s(q,d+)/τ) / Σ_j exp(s(q,d_j)/τ)
L = -log P(正例 | 当前候选集)
τ > 0;分母包含正例与所有候选负例
例如正例和两个负例的相似度为 0.8、0.6、0.2,温度取 0.2。 缩放后 logits 是 4、3、1,正例概率约 0.705,loss 约 0.349。 若正例变成 0.9,其余不变,概率约为 0.798,loss 约为 0.226。 这个变化来自相对间隔扩大,而不是模型突然学会了输出一个可信的置信度。
三、为什么同批文档可以充当负例
一个 batch 有 B 对问题与正例时,可以计算 B×B 的相似度矩阵。 对角线是每个问题自己的正例,非对角线通常作为负例。 这样文档编码一次就能给多个问题提供对比信号,减少单独构造随机负例的成本。 但“别人的正例就是我的负例”只是数据假设,不能自动成立。
d1 d2 d3
q1 正 负 负
q2 负 正 负
q3 负 负 正
B=3:共有 9 个相似度,每个问题有 1 个正例、2 个候选负例
如果 q1 和 q2 都在问退款期限,d2 可能也能回答 q1,矩阵里的“负”就错了。 这会要求模型同时拉近和推远语义相近的有效答案,形成冲突监督。 可通过相关文档分组、去重采样或多正例损失处理,不能只靠增大 batch。 这种训练组织方式可参考 Sentence Transformers 的损失说明。
四、如何区分难负例与假负例
负例有价值,是因为它要求模型识别决定答案是否适用的细微条件。 随机抽一篇天气预报作为退款问题的负例通常太容易,模型很快就能分开。 换成另一个套餐的退款政策,模型才需要学习套餐约束。 但如果误把有效的另一份退款说明当负例,再难也只是错误标签。
| 类型 | 退款问题中的示例 | 训练影响 |
|---|---|---|
| 容易负例 | 无关的天气预报 | 学会粗粒度主题区分 |
| 难负例 | 不适用的另一套餐政策 | 学习细粒度适用条件 |
| 假负例 | 能回答问题的另一份政策说明 | 错误地推远有效证据 |
| 正例 | 对应套餐的有效退款条款 | 提供应被拉近的目标 |
一种流程是先检索前 50 条候选,排除已知正例和重复文档,再审核语义相近的候选。 可让重排模型辅助筛选,但高分不自动说明它是假负例,低分也不证明它一定无关。 应抽检边界案例,保存采样模型版本,定期更新负例以免训练长期只面对同一种错误。 难度与标签可信度要同时考虑,不存在“越难越好”的通用规则。
五、微调后怎样证明检索真的更好
训练集与测试集应按文档来源、业务实体或时间划分,避免近重复问题落入两边。 比较时固定分块策略与文档库,否则检索提升可能来自数据变化而非向量模型。 先在精确向量搜索中观察排序质量,再测试实际近似索引,分开模型误差和索引误差。 除平均分外,还要单看日期、版本、缩写、长文档等易错分组。
假设每个问题只有一份标注相关文档,100 个测试问题里有 72 个在前 5 条找到了它,Recall@5 就是 72%。 如果每题有多个相关文档,则应计算该题“召回相关数/相关总数”再按口径聚合,不能仍套用单正例命中率。 排序靠前与否还可以用 MRR 或 nDCG 检查,并观察下游回答是否有可支持的证据。 检索 loss 降低并不保证这些保留集指标同步提高。
构造相关性标签 -> 划分独立评测集 -> 微调问题/文档编码器
-> 重编码文档库 -> 构建新索引 -> 固定查询回归 -> 切换模型与索引版本
模型改变后,新问题向量与旧文档向量即使维度相同,也未必处于兼容空间。 因此应让模型版本、文档向量版本和索引版本绑定,并保留成套回滚能力。 还要确认问题与文档使用的前缀、池化和归一化方式与训练一致。 否则会把部署格式错误误判成微调无效。
六、常见误区与追问
- 误区:未标注相关的文档就是负例。 相关性标注往往不完整,未标注只代表未知,应排查可回答问题的其他文档。
- 误区:负例越难效果越好。 最相似的候选更可能含假负例,应同时控制标签可靠性、难度分布和评测收益。
- 追问:一个问题有多个正确文档怎么办? 可使用多正例目标或屏蔽已知相关候选,避免把另一个正确答案放在负例位置。
- 追问:温度越低越好吗? 温度降低会放大相似度差异,也可能放大错误负例的影响,应结合数据噪声和验证集调节。
- 误区:训练 loss 下降就代表 RAG 答得更准。 训练目标只反映当前候选上的排序,还需检验保留集检索与下游证据利用。
- 追问:微调后只替换问题编码器可以吗? 常规双塔微调通常要求同步更新文档向量;只有明确设计并验证了兼容训练方案才可例外。
七、加强记忆
把检索微调想成训练“能否用这份材料回答这个问题”的判断:正例给方向,难负例教边界,假负例则把正确道路封住。先保证标签可信,再用对比损失拉开相对距离,最后以独立测试集和成套更新的索引验证效果。记住相似不一定适用,未标注也不等于不相关,才能把公式与真实业务连起来。
记忆钩子:难负例是像答案但答不了,假负例是能回答却被错判;先修标签,再谈拉开距离。