RAG 入门指南:从零开始构建一个 RAG 系统

2024-08-05


图片

通过亲手构建一个RAG(红-黄-绿)系统,我们能够更直观地理解其各个组件及运作机制,这种方法对于深入掌握RAG概念而言尤为有效。鉴于之前我已对RAG的概念、运作原理及实际应用进行了阐述,现在,我将引领大家通过一步步的教程,从零开始搭建一个RAG应用,以此加深理解。

RAG 简介

在开始之前,我还是打算再次简要的介绍一下 RAG。

在 Meta 的官方 Blog 上有这样一段话:

Building a model that researches and contextualizes is more challenging, but it's essential for future advancements. We recently made substantial progress in this realm with our Retrieval Augmented Generation (RAG) architecture, an end-to-end differentiable model that combines an information retrieval component (Facebook AI’s dense-passage retrieval system) with a seq2seq generator (our Bidirectional and Auto-Regressive Transformers BART model). RAG can be fine-tuned on knowledge-intensive downstream tasks to achieve state-of-the-art results compared with even the largest pretrained seq2seq language models. And unlike these pretrained models, RAG’s internal knowledge can be easily altered or even supplemented on the fly, enabling researchers and engineers to control what RAG knows and doesn’t know without wasting time or compute power retraining the entire model.

这段话精炼地概述了RAG(检索增强生成)模型的核心价值和优势,以下是简化后的要点:

挑战与前景:构建能进行深度研究和上下文分析的模型虽具挑战性,但对推动技术革新至关重要。

卓越性能:RAG通过针对知识密集型任务微调,展现出超越现有最大预训练序列到序列模型的卓越表现。

灵活性与可控性:与传统模型不同,RAG的内部知识库可动态调整,无需全面重训,让研究人员和工程师能精准控制模型的知识边界。

对于初学者而言,RAG的精髓可简化为:在大型语言模型(LLM)处理任务时,通过检索工具向其提供额外的、针对性的数据,以增强其生成内容的准确性和相关性。

接下来,我们将着手准备构建自己的RAG应用。

RAG 系统的高层组件

  • • 一组文档,正式说法为语料库

  • • 用户输入

  • • 语料库和用户输入之间的相似性度量

这是简化版的 RAG 组件系统,我们不需要考虑向量存储,甚至目前还不需要 LLM。

以下是一篇 RAG 论文中的系统概述:

图片

它假设了很多背景信息,比我们预设的简化版要复杂的多。

对于想要深入研究的人来说,这篇论文很有价值,但是对于我们想要入门的人来说,通过一步一步构建自己的 RAG 系统来学习才更适合。

RAG 系统的查询步骤

  1. 1. 查询用户输入

  2. 2. 进行相似性度量

  3. 3. 对用户输入和检索到的文档进行后处理

这里的后处理即将检索到的文档和用户输入发送给 LLM 进行处理,最终生成回答。

相似性度量是指用来评估两个对象之间相似程度的方法。在文本处理和信息检索中,相似性度量可以帮助我们确定两个文本之间的相似度。在 RAG 系统中,我们可以使用这些相似性度量方法之一来比较用户输入和文档集合中的每个文档,从而找到最相关的文档。

从零开始构建 RAG 系统

现在,我们将以一个具体的案例从零开始来构建一个 RAG 系统。

以下是简化版的流程图。

图片

以下是具体步骤。

获取文档集合

我们首先定义一个简单的文档语料库。

corpus_of_documents = [
    "Take a leisurely walk in the park and enjoy the fresh air.",
    "Visit a local museum and discover something new.",
    "Attend a live music concert and feel the rhythm.",
    "Go for a hike and admire the natural scenery.",
    "Have a picnic with friends and share some laughs.",
    "Explore a new cuisine by dining at an ethnic restaurant.",
    "Take a yoga class and stretch your body and mind.",
    "Join a local sports league and enjoy some friendly competition.",
    "Attend a workshop or lecture on a topic you're interested in.",
    "Visit an amusement park and ride the roller coasters."
]

定义和执行相似性度量

现在我们需要一种方法来衡量我们将要接收的用户输入与我们组织的文档集合之间的相似性。

可以说,最简单的相似性度量是杰卡德相似性。

杰卡德相似性(Jaccard Similarity)是一种衡量两个集合相似程度的简单方法。它计算两个集合的交集和并集的比例,用于比较两个文本之间的相似性。简而言之就是,杰卡德相似性看两个集合中共同元素的数量占所有元素的总数量的比例。

对语料库进行预处理

由于我们需要进行相似性度量,所以需要将字符串处理成集合。

我们可以使用最简单的方式来进行预处理,也就是将字符串转换为小写并按照空格分割。

# 将语料库字符串按照空格分割,并返回杰卡德相似性的结果
def jaccard_similarity(query, document):
    query = query.lower().split(" ")
    document = document.lower().split(" ")
    intersection = set(query).intersection(set(document))
    union = set(query).union(set(document))
    return len(intersection)/len(union)

然后,我们需要定义一个函数,该函数接受用户的精确查询和我们的语料库,并根据相似性的结果将最匹配的文档返回给用户。

def return_response(query, corpus):
    similarities = []
    for doc in corpus:
        similarity = jaccard_similarity(user_input, doc)
        similarities.append(similarity)
    return corpus_of_documents[similarities.index(max(similarities))]

现在,我们可以试着运行一下。

定义用户查询输入。

user_input = "I like to hike"

将输出的结果打印出来。

print(return_response(user_input, corpus_of_documents))

如果不想在自己电脑上配置 Python 环境,可以直接使用线上的 Python 编辑器,比如:https://www.programiz.com/python-programming/online-compiler/

至此,我们已经构建出了一个最基本的 RAG 系统。

相似性问题

由于我们选择了一个非常简单的相似性度量方法来学习,所以会带来一些问题。

它没有语义概念,只是简单地看两个文档中有哪些词,然后进行对比。

也就是说,只要我们提供的用户输入里包含这些词,那么我们就会得到相同的结果,因为那就是最接近的文档。

比如,我将用户输入换成了 user_input = "I don't like to hike"。

图片

输出结果和上文的输出结果一样。

这是一个在 RAG 中会经常遇到的话题,我们会在后面解决这个问题。

目前,我们还没有对我们检索到的文档进行任何后处理。只是实现了 RAG 的「检索」功能。

下一步是通过结合 LLM 来增强生成。

添加 LLM

要方便快捷的添加 LLM,我们可以直接在本地机器上运行一个开源的 LLM。

这里,我将使用 Ollama 的 Llama 3.1 模型。当然,你也可以使用 OpenAI 的 GPT-4 或 Anthropic 的 Claude 或者其他 LLM。

可以到 ollama 官网下载安装自己想要的 LLM:https://ollama.com/

现在,我们需要对代码做些修改了。

如果是在本地运行 LLM,那么,你需要在自己电脑上配置好 Python 相关的环境,这样在后续步骤中,才能将代码运行起来。

现在,需要引入一些库。

import requests
import json

在优化后的流程中,我们将遵循以下步骤来操作:

捕获用户输入:首先,我们接收用户提供的文本或指令,这是整个交互过程的起点。

检索最相似文档:随后,利用我们预先设定的相似性度量标准,在知识库中搜索与用户输入最为匹配的文档。这一步骤确保了后续处理能够基于与用户意图高度相关的内容。

传递文档作为LLM提示:一旦找到最相似的文档,我们将其作为输入或“提示”传递给大型语言模型(LLM)。LLM将基于这个提示生成相应的输出或回答。

返回结果给用户:最后,我们将LLM生成的回答或输出整理后,呈现给用户。这一过程实现了从用户输入到智能响应的完整闭环。




user_input = "I like to hike"
relevant_document = return_response(user_input, corpus_of_documents)
full_response = []

prompt = """
You are a bot that makes recommendations for activities. You answer in very short sentences and do not include extra information.
This is the recommended activity: {relevant_document}
The user input is: {user_input}
Compile a recommendation to the user based on the recommended activity and the user input.
"""

定义好以上步骤之后,我们现在来调用 Ollama 的 API。

在编辑此代码之前,你需要先运行 LLM 在后台,直接在命令行里输入 ollama serve 即可。

url = 'http://localhost:11434/api/generate'
data = {
    "model": "llama3.1",
    "prompt": prompt.format(user_input=user_input, relevant_document=relevant_document)
}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(data), headers=headers, stream=True)
try:
    count = 0
    for line in response.iter_lines():
        if line:
            decoded_line = json.loads(line.decode('utf-8'))
           
            full_response.append(decoded_line['response'])
finally:
    response.close()

print(''.join(full_response))

运行以上代码,即可看到结果。

经过我们的努力,一个完整的RAG(检索增强生成)系统已经成功构建。这一系统能够赋能LLM(大型语言模型),使其在处理类似上文提及的复杂问题时展现出更高的智能与准确性。假设用户输入变更为“I don't like to hike.”,那么,借助RAG系统的强大支持,LLM将能够迅速理解这一语境,并生成相应的、贴近用户情感的回答。

这样的回答不仅反映了用户的不喜欢徒步的立场,还可能进一步探讨其背后的原因或提出相关的建议,从而实现了更加个性化与深入的交互体验。


分享
写评论...