跳到正文
LlamaIndex:产品、工程与评测·· 2023-08-25精选AI 评分74

LlamaIndex 教程:用合成数据微调嵌入模型提升 RAG 检索性能

Fine-Tuning Embeddings for RAG with Synthetic Data

AI 导读

LlamaIndex 发布端到端指南,教你在无标注的非结构化语料上用 LLM 生成合成问题对来微调开源嵌入模型 BAAI/bge-small-en。

推荐理由

原文提供了完整的无标注数据微调嵌入模型方法和评测结果,读者可以直接复用笔记本改进自己的 RAG 检索。

正文 · AI 翻译

更新于 2023/9/10:我们已将嵌入微调抽象纳入 LlamaIndex 仓库,因此本仓库在技术上已过时!请查看核心文档中的嵌入微调指南。

立即探索我们的免费和付费方案。

我们创建了一份全面的端到端指南,向你展示如何微调嵌入模型,以提升检索增强生成(RAG)系统在任何非结构化文本语料上的性能(无需标签!)。

结果是检索评估指标提升 5–10%——我们微调后的 bge 模型在命中率方面几乎达到了 text-embedding-ada-002 的检索性能水平。这实现了更准确的检索,从而整体上带来更好的 RAG 系统。

本教程对任何构建 RAG 系统的人都有帮助:

  • 如果你是微调新手,没问题!我们有分步 notebook 带你走完关键步骤。只需将文件链接替换为你自己的数据,然后运行每个单元格即可。
  • 微调嵌入模型很轻量,不需要 GPU。这些 notebook 已在 M2 Macbook Pro 上测试过。

资源

背景/上下文

当前的 RAG 技术栈

RAG 是一种流行的范式,用于将大型语言模型(LLM)与其训练语料中不存在的外部数据源连接起来。它通过 LLM 的输入提示空间,将知识库上的检索模型与 LLM 配对。RAG 技术栈通常如下所示:

  • 索引:准备一个非结构化文本语料库,对其进行解析/分块。然后嵌入每个块并放入向量数据库。
  • 查询时:使用 top-k 嵌入相似度查找从向量数据库中检索上下文,并将上下文塞入 LLM 输入空间。

(当然,RAG 可以比这复杂得多,LlamaIndex 为简单和高级 RAG 都提供了工具)

遗憾的是,RAG 通过拼凑不同组件很容易做原型,但很难投入生产。这个简单技术栈有许多失败模式,而且问题往往出在糟糕的检索上——如果返回的上下文与查询无关,那么 LLM 的能力也无关紧要;答案永远会很糟糕。

我们如何让检索更好?

我们可以尝试更复杂的检索算法(例如混合搜索、重排序)。

然而,我们最近生产级 RAG 网络研讨会中的一个洞见是,嵌入本身可能并不处于适合你数据的最优潜在空间。预训练模型生成的嵌入可能会根据预训练目标而彼此接近/远离,但可能并不完全符合你自己的检索目标。例如,如果你在构建针对 ML ArXiv 论文的搜索,你可能希望嵌入在语义上与特定的 ML 概念(例如“LLMs”、“NLP”)对齐,而不是与填充词“This paper is…”对齐。

微调是解决这个问题的一种方法。随着技术进步以及易用服务的出现,微调的概念在 LLM 领域变得越来越流行。

在本教程中,我们专注于微调嵌入模型。我们展示了微调嵌入模型如何能带来更好的检索性能。

挑战/考虑因素

当你微调嵌入时,你需要训练示例。对于嵌入而言,这通常意味着你需要有“正”和“负”示例——即应该彼此接近和彼此远离的文本对。

一个问题是,我们事先并没有这些正或负示例。给定一个非结构化文本数据集,是否有可能自动生成这些示例对?

使用 LlamaIndex,你可以做到!我们使用 LlamaIndex 模块从非结构化文本块中自动生成一组问题。这些(问题,文本块)对随后被用作正示例,作为模型的训练信号(负示例则从其他文本块中随机采样)。

下一节将展示我们所有模块的完整演练。

演练

在高层次上,我们执行以下操作:

  1. 生成用于训练和评估的合成数据集(Notebook)
  2. 微调一个开源嵌入模型(Notebook)
  3. 评估嵌入模型(Notebook)

生成用于训练和评估的合成数据集

这里的关键思想是,我们可以利用 LLM 生成假设性问题,这些问题最好由给定的上下文片段来回答。这使我们能够以可扩展的方式生成合成的(查询,相关文档)正例对,而无需人工标注者。

更具体地说,我们首先将给定的文档处理成文本块语料库。我们使用 LlamaIndex 中的 SimpleNodeParser 模块来完成此操作:

parser = SimpleNodeParser()
nodes = parser.get_nodes_from_documents(docs, show_progress=verbose)
corpus = {
  node.node_id: node.get_content(metadata_mode=MetadataMode.NONE) 
  for node in nodes
}

然后,对于每个文本块,我们使用 LLM 生成几个可以用该文本块中的信息回答的假设性问题。示例提示如下所示。

prompt_template = prompt_template or """\
  Context information is below.
  
  ---------------------
  {context_str}
  ---------------------
  
  Given the context information and not prior knowledge.
  generate only questions based on the below query.
  
  You are a Teacher/ Professor. Your task is to setup \
  {num_questions_per_chunk} questions for an upcoming \
  quiz/examination. The questions should be diverse in nature \
  across the document. Restrict the questions to the \
  context information provided."
  """

# for a given node, extract questions (do this over all nodes in outer loop)
query = prompt_template.format(context_str=text, num_questions_per_chunk=num_questions_per_chunk)
response = llm.complete(query)

result = str(response).strip().split("\n")
questions = [
    re.sub(r"^\d+[\).\s]", "", question).strip() for question in result
]
questions = [question for question in questions if len(question) > 0]

最后,我们收集所有问题和文本块对作为数据集。示例查询、文本块和映射如下所示。


# example query
f331640a-b407-4028-8db8-4b8db691dd34: "What is the market value of Lyft's common stock held by non-affiliates as of June 30, 2021, based on the closing sales price of the Class A common stock on that date?"

# example corpus
d5554f3e-cdaf-41d7-ac49-8f0ffe3f5759:"UNITED STATESSECURITIES AND..."

# example mapping
f331640a-b407-4028-8db8-4b8db691dd34: d5554f3e-cdaf-41d7-ac49-8f0ffe3f5759

微调一个开源嵌入模型

我们利用 sentencetransformers 中的高级模型拟合 API 来非常轻松地设置训练过程。

我们使用 MultipleNegativesRankingLoss 作为训练对象,并使用 InformationRetrievalEvaluator 作为训练期间的评估器。此外,我们使用 Hugging Face 上的 BAAI/bge-small-en 作为基础模型,并训练少量轮次。


model_id = "BAAI/bge-small-en"
model = SentenceTransformer(model_id)

...


from sentence_transformers import losses
loss = losses.MultipleNegativesRankingLoss(model)


from sentence_transformers.evaluation import InformationRetrievalEvaluator

...
evaluator = InformationRetrievalEvaluator(queries, corpus, relevant_docs)


...
model.fit(
    train_objectives=[(loader, loss)],
    epochs=EPOCHS,
    warmup_steps=warmup_steps,
    output_path='exp_finetune',
    show_progress_bar=True,
    evaluator=evaluator, 
    evaluation_steps=50,
)

评估嵌入模型

我们将微调后的模型与基础模型以及 OpenAI 嵌入模型 text-embedding-ada-002 进行比较。

我们使用两个主要指标进行评估:

  • 命中率指标:对于每个(查询,相关文档)对,我们使用查询检索前 k 个文档。如果结果包含相关文档,则视为命中。
  • 来自 sentence_transformers 的 InformationRetrievalEvaluator。这提供了一套全面的指标,例如不同前 k 值下的余弦相似度准确率、精确率、召回率。

结果

在命中率指标方面,基础模型在验证数据集上获得 78% 的命中率,而微调后的模型获得 84%。text-embedding-ada-002 获得 87%,这意味着我们微调后的模型仅差 3%!

`text-embedding-ada-002`、基础模型、微调模型的命中率

InformationRetrievalEvaluator 在整个指标套件上显示出类似的改进。与基础模型相比,微调后的模型将评估指标提高了 5–10%。

来自 `InformationRetrievalEvaluator` 的评估套件

结论

我们成功地在未标记的非结构化数据上微调了一个嵌入模型,为下游 RAG 系统提供了更好的检索性能。我们展示了所有指标上 5–10% 的提升!

资源

(摘自简介)

来源:LlamaIndex:产品、工程与评测 · llamaindex.ai