上篇对于知识召回的两种方式进行了初步讨论,分析了两者的优劣势和互补性。然后分享了一些基于倒排索引召回中的实践经验细节。本篇会覆盖两路中的另一路 – 向量召回的最佳实践,包括对称召回和非对称召回的分析比较、知识增强、模型演进和选型、向量模型 Finetune、召回 Rerank 以及一些工程经验等。

什么是对称召回和非对称召回?在向量召回中该如何选择?

对称召回,一般是指通过两个句子的相似性来召回,比如输入的 query 匹配相似的另一个 question(query->question),也可以是输入的陈述句匹配相似的另一个陈述句。对应这种数据也叫做 NLI 数据,一般是三元组的形式,比较知名的数据集为 SimCLUE,如:

非对称召回一般是检索数据, 一般是一个相对短的问句和一个相对长的答案(一般为一个文本段落), 比较知名的数据集为 MSMARCO,一些 Question Answering 类的任务都是基于这种数据集,如:

一般来说,对称召回任务比非对称召回任务要简单,特别在很多垂直领域,向量模型对领域知识的理解有限,对称召回很多时候理解了字面意思就能满足要求。为了方便描述,下文把对称召回称为 QQ(Query-Question)召回,把非对称召回称为 QD(Query-Document)召回。

在真实的生产场景中,知识的存在形式有两种 —— FAQ 和文本文档, FAQ 由于是一问一答的形式,天然是比较适合进行 QQ 召回,而文本文档比较适应 QD 召回。但在实际的 case 中,单独 QQ 召回或者 QD 召回都存在一定的问题。相对于 QQ 召回,QD 召回需要 embedding 模型对语义有更强的理解。Question 一般相对比较简短,信息量没有 Document 那么多,因此对于只是使用 QQ 召回,可能会有问题,如下面的例子:

用户输入的问题不一定会和 FAQ 的 Question 的提问角度一致,比如用户可能会问“AWS Clean Rooms 的数据源不在同一个 region,行吗?”,这种问题仅仅考虑 QQ 则会出现问题。

所以生产实践中,推荐同时考虑 QQ 和 QD 召回,对于 FAQ 的知识形态,对 Question 生成向量作为索引,同时把 Answer 作为 Doc 生成向量作为索引,在召回时同时走 QQ 和 QD 召回。对于文本文档的知识形态,除了对 Doc 生成向量以外,还可以通过知识增强的方式生成多组 Question,详细请参见下一小节。

知识增强优化召回

知识增强一般是指,根据上下文重新 Refine 原始的文本使得原始的文本信息更完整,自解释性更强。这个过程由于是离线的一次性处理,所以这里可以借助能力更强的商用 LLM 模型,生成的内容可能会更好。另外由于现在的 LLM 能支撑的上下文 context 长度很长,建议在生成的过程中,在 prompt 中去融入一些该场景的丰富背景信息。

具体的操作方式,可以参考下图:

通过知识增强,可以把文本文档也转化成 FAQ 的形式,使得对于一般的文档也能够同时进行 QQ 和 QD 召回。但这里需要注意的是,对于特别垂直的领域,能力很强的商业 LLM 也可能无法提取出比较合适的问题,一般观察到的现象是提取出的问题过于直接,或者提取出的问题无实际意义,这部分建议走一遍人工审核。

对于质量不高的 FAQ,特别是提问过于简略的,知识增强也能够优化其质量。另外经过优化后的知识,在 LLM 生成阶段,对于提升 LLM 对知识的理解也是有一定帮助的。总的来说,知识增强是简单有效的优化手段。

向量模型的演进与选型

首先需要强调的是向量模型与 LLM 模型是完全不同的,两者的训练目标不同,LLM 模型中 token 的 embedding 的最终目标是生成。向量模型的最终目标是判别,判断 QQ 之间语义是否相似,判断 QD 之间是否具有相关性。两者的训练目标和训练数据是有不小的区别的。一般都不直接利用 LLM 来生成本文向量。

向量模型的大概演进路线,可以参考下图:

从上图可以观察到几个基本趋势,目前的向量模型从单纯的基于 NLI 数据集(对称数据集)发展到基于混合数据(对称+非对称)进行训练,即可以做 QQ 召回任务也能够做 QD 召回任务,通过添加 Instruction 的方式来区分这两类任务,只有在进行 QD 召回的时候,需要对用户 query 添加上 Instruction 前缀。在模型的第一 pretrain 阶段,从对比学习发展到 RetroMAE 自动编码的方法。后续的监督训练阶段差异不大,主要是监督数据的区别。

在之前的实践中,主要采用的模型包括 sentense-bert 系列,text2vec,m3e 这些模型,这些模型在公开数据集上的 benchmark 在垂直领域不一定成立,或者说体现不出明显的优势。所以我们以游戏场景中的 2w+知识增强过的 FAQ,拆分出训练数据和测试数据进行评测。

模型层面,BGE 向量模型作为最新的 SOTA 模型,在各类数据集上都表现良好。所以我们选择 sentence-bert 的多语言模型 paraphrase-multilingual-mpnet-base-v2 与 bge-large-zh-v1.5 进行对比。

召回形式层面,分别对 QQ 和 QD 两种召回形式进行评测,得到如下的结论:

1. 模型层面

  • a. 在没有微调的情况下,bge-large-zh 与 sbert 相比,无论从 Recall@N 上还是相似度的值域区分性来看,bge 都有明显优势。

2. 召回形式

  • a. 相似度值域范围在 QQ 和 QD 之间是有区别的,两者分布的 kde 曲线(直方图上的曲线)交点位置有差异,召回时可分别考虑 QQ 和 QD 的截断阈值。另外 QD 召回的负例相似度值域上限要低于 QQ 召回的,所以高分的 QD 召回内容是置信度更高的知识。
  • b. QQ 召回场景中,原始 Q 的召回效果是明显低于增强 Q 的,其 Recall@2 准确率低,且负例的相似度值域分布在 0-1 的整个空间。 其背后的原因主要是由于数据集中原始的 Q 十分简短,且很多都是相似的概念名词。在这种情况下,过于简短的原始 Q(1-2 词)不该生成向量被索引,可考虑去掉这类知识仅保留增强后的 Q。
  • c. QD 场景中,增强 Q 和增强 D 之间的 Recall@2 和相似分区分度都是最理想的,但是由于它是 LLM 生成的,本身存在幻觉的情况,另外如果增强 D 和原始 D 重复性过高,可能导致召回了过多重复的内容挤占了其他内容的槽位。建议结合人审或者其他方式保证这部分知识的质量。

详细实验信息可以参考下列组图:

  • QQ 召回

    • 正样本(原始 Q – 对应增强 Q),负样本(原始 Q – 随机采样增强 Q)

      [sbert] 正例排名和正负例 sim 分布

      [bge] 正例排名和正负例 sim 分布

    • 正样本(原始 Q – 对应增强 Q),负样本(原始 Q – 随机采样原始 Q)

      [sbert] 正例排名和正负例 sim 分布

      [bge] 正例排名和正负例 sim 分布

  • QD 召回

    • 正样本(增强 Q – 增强 D),负样本(增强 Q  – 随机采样增强 D)

      [sbert] 正例排名和正负例 sim 分布

      [bge] 正例排名和正负例 sim 分布

    • 正样本(增强 Q – 原始 D),负样本(增强 Q – 随机采样原始 D)

      [sbert] 正例排名和正负例 sim 分布

      [bge] 正例排名和正负例 sim 分布

Finetune 向量模型是否有收益?

在一些专业领域,通用的向量模型可能无法很好的理解一些专有词汇,导致检索性能不足,  且正例和负例相似度的值域有比较多的重叠,无法通过阈值判断该召回内容是否属于相关信息。过多的无关信息可能会在下游误导大语言模型,如果能在上游做截断或者通过 Prompt 暗示 LLM 可能没有相关信息,则会大大减少 LLM 幻觉的问题,实现更好的拒答。

所以向量模型 Finetune 的主要目标一方面是为了提高 Recall@N 的准确率,另外一方面想要拉开正例和负例的 similarity 值域分布,从而得到更好的分隔阈值,使得当召回的 similarity 值低于阈值时,直接放弃这些无效的召回,提示下游进行拒答。

本文在实验中对 bge-large-zh-v1.5 模型进行 Finetune 以及测试评估,得到如下的实验结论:

  1. 微调对于训练集有明显提升效果,无论 QQ 还是 QD 召回,Recall@2 或者正负样例的值域分布上都会更好。在不考虑泛化性的情况下,假设能持续收集真实的用户反馈不断纳入训练集,最终也能覆盖绝大多数用户的提问场景。
  2. 微调在测试集上表现出一定的泛化性,Recall@2 在 QD 召回上提升 9pp 至 87.2%,QQ 召回上提升 1.2pp 至 85.8%,相似度值域从分布上看区分度更好。

详细实验信息可以参考下列组图:

[bge-v1.5] 微调前的 QD 召回效果[bge-v1.5] 微调前的 QQ 召回效果
[bge-v1.5] 微调后训练集的 QD 召回效果[bge-v1.5] 微调后训练集的 QQ 召回效果
[bge-v1.5] 微调后测试集的 QD 召回效果

[bge-v1.5] 微调后测试集的 QQ 召回效果

【注意】图上的 KDE 交点并不能直接作为召回是否有效的阈值,因为以上的微调数据是基于知识的一问一答对训练出来的,和真实的用户输入在分布上还是有很大区别,在我们的实践中,对应的阈值需要多次尝试确定,一般要比 KDE****交点要高。本质上需要不断收集越来越多的用户真实的正负反馈用于调优向量召回。

向量模型召回结果的 Rerank

在传统的搜索场景中,如何结合这两路召回的结果是一个问题,因为两者不在一个评分体系下。在基于大语言模型的知识问答机器人场景, 可以简单采用倒排召回和向量召回各 TopK 去重取并集的方式。主要是因为大语言模型对召回内容的相关性排序不敏感,且能够容忍一些不相关的知识召回内容。

另外一个思路是再训练一个排序模型的方式来进行进一步相关性打分。这种方式可以更好的结合倒排和向量召回的结果。比如两路召回各生成了10个候选,但最终我用于构建 Prompt 的只需要 4 个,那么通过 Rerank 就能很好的达到目的并科学的结合两路结果。而且也可以不断的把线上用户的真实反馈信息作为训练数据不断迭代效果。

如下图所示,向量模型一般是双塔结构,两侧只有生成 embedding 后,才会发生交叉。而 Rerank 模型,一般从底层就开始交叉,模型能力的上限更高, 相应的模型复杂度也高,计算时延长,所以一般只对向量模型召回的 Topk 进行打分,类似于搜广推中的精排模型。

向量模型(双塔)

Rerank 模型

由于在 Rerank 阶段,所有的候选都是向量模型召回的 Topk,所在训练的时候正负例的选择范围也应该在召回的 TopK 中。微调阶段使用的随机负采样的训练数据是不合适的,建议以上游打分 Topk 作为训练数据。 以 bge 的 Rerank 模型为例,可以先挖掘难样本然后把这部分作为排序模型的训练数据。一般情况会带来一定的正向提升,特别是结合真实的用户反馈数据。下面两图是采样 500 条测试集数据的验证效果。

无 Rerank 模型的正例排名分布

有 Rerank 模型的正例排名分布

多路融合的工程实践

为了简化 LLM 应用开发和多种 LLM 模型的集成流程,我们采用了 LangChain 作为基本的开发框架。LangChain 是一个基于大语言模型(LLM)用于构建端到端语言模型应用的框架, 它提供了一套工具、组件和接口,可以简化创建基于LLM的应用。

langchain.retriver 提供了一系列获取知识库文档的方法,但是为了更灵活处理我们的多路召回逻辑并且跟 langchain 其他组件兼容,我们复用 retriver 接口,自定义实现从 open search service endpoint 发起向量语义和倒排召回,并返回混合结果。

在实际的场景中,可能会出现用户的问题通过语义向量相似度匹配度不够准确,或者分数较低,但是可以通过倒排索引召回出知识语料进行补充。如下图所示:

用户的原始问题中“城市外观粉色”的信息,在向量召回的结果中并没有(前面 2 条 score < 1 的结果 ),但是在第 3 条倒排召回(score > 1)的知识中,含有相关信息:

最终大语言模型的回答使用到了这条知识,正确的回答了用户的问题。

总结与结论

本文介绍了对称召回和非对称召回的分析比较,向量模型演进和选型比较、利用 LLM 来对原有知识增强和扩充、 向量模型 Finetune 的实际效果、对召回结果进行 Rerank 的技术方法,以及在 Langchain 框架下集成多路召回的一些工程经验。为方便阅读和借鉴,这些经验可快速总结为以下几条要点:

  • 利用 LLM 对知识语料进行增强和扩充,合成新的 FAQ 语料。可以参考 Enhance_FAQ.pyEnhance_Doc.py
  • 推荐同时考虑 QQ(对称: 问题->问题)和 QD(非对称: 问题->文档)召回, FAQ 与原始文档这两种知识形式可以通过知识增强在形式上达成统一。
  • 用 QQ 和 QD 语料对向量模型进行 Finetune,在训练数据集上有很明显的提升效果,在测试数据集上也表现出一定泛化能力。实际生产中,可以用新增加的知识语料和用户产生的正负反馈迭代得到训练数据,定期对向量模型 Finetune,随着训练数据的丰富,能够覆盖的用户问题也将越来越多。
  • 基于 RetroMAE 方法预训练的 bge-large-zh-v1.5 模型在垂直领域优势的确很明显的,值得基于这个模型进行调优。本文提供了向量模型的评估方法与实现,可用于其他模型的评估。
  • 多路召回对提升知识问答的覆盖能力有正向帮助,结合 Rerank 之后,对多路召回的结果排序之后,更能发挥多路召回的优势。
  • 文本中所有的实验过程均可以通过 bge_zh_research.ipynb 复现,其中包含一些分析/可视化方法供借鉴参考。实验环境为 SageMaker Studio Notebook 的 pytorch2.0 GPU python 3.10,机型为 ml.g5.12xlarge。

更多相关的介绍,请参考本系列博客其他文章,包括:

  1. <基于大语言模型知识问答应用落地实践 – 知识库构建(上)>
  2. <基于大语言模型知识问答应用落地实践 – 知识库构建(下)>
  3. <基于大语言模型知识问答应用落地实践 – 知识召回调优(上)>
  4. <基于大语言模型知识问答应用落地实践 – 知识召回调优(下)>(本篇)

另外,本文提到的代码细节可以参考配套资料:

  1. 代码库 aws-samples/private-llm-qa-bot
  2. Workshop <基于 Amazon Open Search +大语言模型的智能问答系统> (中英文版本)

本篇作者