最近在做一个企业知识库问答,流程是先用BGE-M3做向量检索,再丢给LLM生成答案。因为领域术语比较多,检索召回一直不太准,我就想着用业务数据微调一下Embedding模型。数据集大概构造了八千多条query-positive-negative三元组,用sentence-transformers的MultipleNegativesRankingLoss训了三个epoch。结果微调完之后,在自建的评测集上Recall@5涨了大概六七个点,但端到端的问答准确率反而掉了。排查发现是模型把一些语义相近但实际不相关的文档也召回了,噪声变多,LLM被带偏。想问下这种情况是微调数据构造有问题,还是损失函数选得不对?有没有人踩过类似的坑,一般怎么定位和解决?
RAG场景下微调Embedding模型,效果反而变差了是怎么回事?
全部回复
共 8 条这个坑我去年踩过一模一样的,当时也是BGE微调完召回指标好看得不行,端到端直接崩。后来发现核心问题出在负样本构造上,你用MultipleNegativesRankingLoss的话,in-batch negatives里很多其实是语义相近的难负样本,但你的业务数据里"语义相近"和"该被召回"之间的界限可能特别模糊,比如同一产品的不同型号文档,向量空间里被强行推开之后,模型学到的边界跟实际问答需求是错位的。评测集Recall涨说明模型确实更贴合你构造的三元组分布了,但那个分布本身可能就偏离了真实检索需求,等于优化了一个代理指标。我建议先别急着换loss,拿几十条badcase看看召回的噪声文档到底长什么样,大概率会发现它们跟query在字面上高度重叠但答案并不在里面。另外可以试试在微调时混入一部分原始BGE的训练数据做正则,防止模型过度拟合业务术语的浅层模式。还有个思路是检索阶段加个rerank,微调后的embedding负责高召回,cross-encoder负责精排,这样噪声就不会直接灌给LLM了。你们现在评测集是怎么构建的,是人工标注还是LLM生成的?这个对判断问题出在哪挺关键的。
这个现象其实挺典型的,微调完检索指标涨了但端到端效果掉,多半是掉进了“语义相似但不相关”的坑里。你用的MNRL本质上是在拉近query和positive、推远negative,如果negative采样太随意,模型学到的可能就是表面词面或主题相近,而不是真正能回答问题的证据。八千条三元组里如果negative很多是随机负样本,模型很容易把同领域但无关的文档也拉近,噪声自然就上来了。另外Recall@5涨不代表排序质量好,可能只是把更多相关文档塞进前五,同时也塞了更多干扰项,LLM上下文一乱就容易被带偏。建议先看看评测集里那些被误召的文档,是不是和query共享大量术语但实际不回答该问题。如果是,得在训练数据里加“难负样本”,尤其是同领域同主题但不含答案的片段。损失函数也可以考虑加个margin或者换用带hard negative的对比学习,别让模型只学粗粒度的主题聚类。端到端评测最好也拆开看,检索阶段单独算一下precision和噪声比例,别只盯Recall。
我之前也踩过类似的坑,八成是负样本太“软”了。你那八千多条三元组里,negative如果只是随机采或者跟positive沾点边,MNRL会把这些边界模糊的样本硬推开,学出来的空间更“激进”,Recall涨了但精度掉得厉害。可以试试加BM25或cross-encoder挖的hard negative,再混一点原始通用数据做正则,别让模型完全漂到业务分布上。另外端到端评测集和训练集的query分布对不对齐也值得看一眼,有时候是你评测集本身偏简单了。
我之前也踩过这个坑,Recall涨了不代表端到端就好,检索和生成的目标其实不完全一致。你这情况大概率是负样本太“软”了,模型学到的边界把那些语义相近但业务上没用的都放进来了。可以试试在负样本里掺一些真实的困难负例,或者干脆加个重排序模型卡一道。另外三个epoch可能有点多,小数据集上微调Embedding很容易过拟合,先降到一两个epoch看看。
负样本采样太随意了吧,同领域里语义近的硬负例才是关键,不然模型学不到细粒度区分。
八成是负样本太弱了,模型只学会了拉开明显不相关的,细粒度的区分没学到。试试加点难负样本,或者换个对比损失。
这个挺典型的,微调后召回率涨了但端到端反而掉,说明指标和业务目标没对齐。你训的时候用的负样本可能太“干净”了,实际检索里那些语义相近但主题不同的文档才是噪声来源,模型没学会区分这种hard negative。建议把线上被误召回的文档捞出来做难负样本,再控制下学习率别训太猛。另外可以试试在LLM那层加个相关性过滤,别啥都往prompt里塞。
这是典型的评测指标和最终目标脱节了。MNRL本质上是在拉近正例、推远负例,你训完Recall涨说明模型确实学到了领域语义,但它同时也把决策边界推得更激进了,那些"长得像但不相关"的文档就容易被误召。问题可能出在负例构造上,如果负例采样太随意,模型学到的"不相关"概念就跟业务实际需求对不上。建议试试加个rerank环节做二次过滤,或者把评测标准从Recall换成端到端指标来指导训练。