原文:towardsdatascience.com/genai-with-python-rag-with-llm-complete-tutorial-c276dda6707b

简介

AI 无处不在。

每天至少与一个大型语言模型(LLM)互动一次是很困难的。聊天机器人就在这里,它们存在于你的应用中,帮助你写出更好的内容,撰写电子邮件,阅读电子邮件……好吧,它们做很多事情。

我认为这并不坏。事实上,我的观点正好相反——至少到目前为止。我捍卫并倡导在日常生活中使用 AI,因为让我们同意,它让一切变得更容易。

我不必花时间反复阅读文档来查找标点符号问题或打字错误。AI 会帮我完成这些。我不再需要每个星期一都浪费时间写跟进邮件。AI 会帮我完成这些。当我有一个 AI 来总结主要收获和行动要点给我时,我无需阅读那些庞大且无聊的合同!

这些只是 AI 伟大用途中的一部分。如果你想知道更多关于 LLM 如何让我们的生活变得更轻松的用例,我写了一整本书来介绍它们。

现在,作为一个数据科学家,从技术角度来看,并不是所有事情都是那么光鲜亮丽。

LLM 非常适合一些通用的用例,这些用例适用于任何人或任何公司。例如,编码、总结或回答关于截至训练截止日期之前创建的一般内容的疑问。然而,当涉及到特定的商业应用,针对单一目的,或者一些新事物没有达到截止日期时,如果直接使用这些模型,它们将不会那么有用——这意味着它们将不知道答案。因此,需要调整。

训练一个 LLM 模型可能需要数月和数百万美元。更糟糕的是,如果我们不调整和调整模型以适应我们的目的,结果将不会令人满意,或者会出现幻觉(当模型给出的回答不符合我们的查询时)。

那么,解决方案是什么呢?花很多钱重新训练模型以包含我们的数据?

实际上并不是这样。这就是检索增强生成(RAG)变得有用的时刻。

RAG 是一个框架,它结合了从外部知识库中获取信息与大型语言模型(LLM)。它帮助 AI 模型产生更准确和相关的响应。

让我们接下来更多地了解 RAG。

什么是 RAG?

让我给你讲一个故事来阐述这个概念。

我喜欢电影。在过去的一段时间里,我知道哪些电影在奥斯卡最佳电影类别中竞争,或者哪些演员和女演员获得了最佳演员/女演员奖。我当然知道哪些电影获得了那一年的奖项。但现在我对这个主题已经生疏了。如果你问我谁在竞争,我就不知道。即使我尝试回答你,我也会给出一个薄弱的回答。

因此,为了提供高质量的回答,我将做其他人都会做的事情:在网上搜索信息,获取信息,然后将其提供给你。我刚才所做的是与 RAG(Retrieval Augmented Generation)相同的概念:我从外部数据库获取数据来给你一个答案。

当我们增强 LLM,使其能够访问一个内容存储(content store)以检索数据来增强其知识库时,这就是 RAG 框架在起作用。

RAG 就像创建一个内容存储,模型可以在其中增强其知识并更准确地回答问题。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/65bb1c1338730d95fe0e7cc6cf3edd15.png

用户关于内容 C 的提示。LLM 检索外部内容以聚合到答案中。图由作者提供。

总结:

  1. 使用搜索算法查询外部数据源,例如数据库、知识库和网页。

  2. 预处理检索到的信息。

  3. 将预处理的信息整合到 LLM 中。

为什么使用 RAG?

既然我们已经了解了 RAG 框架,让我们来了解一下为什么我们应该使用它。

这里有一些好处:

  • 通过引用真实数据增强事实准确性。

  • RAG 可以帮助 LLM 处理和整合知识,以创建更相关的答案。

  • RAG 可以帮助 LLM 访问额外的知识库,例如内部组织数据。

  • RAG 可以帮助 LLM 创建更准确的特定领域内容。

  • RAG 可以帮助减少知识差距和 AI 幻觉。

如前所述,我倾向于说,使用 RAG 框架,我们为想要添加到知识库的内容提供了一个内部搜索引擎。

好吧。所有这些都很有趣。但让我们看看 RAG 的一个应用。我们将学习如何创建一个 AI 驱动的 PDF 阅读助手。

项目。

这是一个允许用户上传 PDF 文档并使用 AI 驱动的自然语言处理(NLP)工具对其内容提出问题的应用程序。

  • 该应用程序使用Streamlit作为前端。

  • Langchain、OpenAI 的 GPT-4 模型和FAISS(Facebook AI Similarity Search)用于后端文档检索和问答。

让我们分解这些步骤以更好地理解:

  1. 加载 PDF 文件并将其分割成文本块。

    1. 这使得数据优化以供检索。
  2. 将文本块呈现给嵌入工具。

    1. 嵌入(Embeddings)是数据的数值向量表示,用于以机器可以理解的方式捕捉关系、相似性和意义。它们在自然语言处理(NLP)、推荐系统和搜索引擎中得到广泛应用。
  3. 接下来,我们将这些文本块和嵌入放入同一个数据库中进行检索。

  4. 最后,我们将它提供给 LLM(大型语言模型)。

数据准备。

为 LLM 准备内容存储需要一些步骤,正如我们刚才看到的。所以,让我们首先创建一个可以加载文件并将其分割成文本块以进行高效检索的功能。

# Imports
from  langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

def load_document(pdf):
    # Load a PDF
    """
    Load a PDF and split it into chunks for efficient retrieval.

    :param pdf: PDF file to load
    :return: List of chunks of text
    """

    loader = PyPDFLoader(pdf)
    docs = loader.load()

    # Instantiate Text Splitter with Chunk Size of 500 words and Overlap of 100 words so that context is not lost
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
    # Split into chunks for efficient retrieval
    chunks = text_splitter.split_documents(docs)

    # Return
    return chunks

接下来,我们将开始构建我们的 Streamlit 应用程序,并在下一个脚本中使用该功能。

网络应用程序。

我们将开始导入 Python 中必要的模块。其中大部分将来自 langchain 包。

FAISS用于文档检索;OpenAIEmbeddings将文本块转换为数值分数,以便 LLM 进行更好的相似性计算;ChatOpenAI使我们能够与 OpenAI API 交互;create_retrieval_chain实际上是 RAG 所做的工作,检索并增强 LLM 的数据;create_stuff_documents_chain将模型和 ChatPromptTemplate 粘合在一起。

注意:您需要 生成一个 OpenAI 密钥 才能运行此脚本。如果您是第一次创建账户,您将获得一些免费积分。但如果您已经有一段时间了,您可能需要添加 5 美元的积分才能访问 OpenAI 的 API。一个选项是使用 Hugging Face 的 Embedding

# Imports
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.chains import create_retrieval_chain
from langchain_openai import ChatOpenAI
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from scripts.secret import OPENAI_KEY
from scripts.document_loader import load_document
import streamlit as st

这第一个代码片段将创建应用程序标题,创建一个文件上传框,并准备要添加到load_document()函数中的文件。

# Create a Streamlit app
st.title("AI-Powered Document Q&A")

# Load document to streamlit
uploaded_file = st.file_uploader("Upload a PDF file", type="pdf")

# If a file is uploaded, create the TextSplitter and vector database
if uploaded_file :

    # Code to work around document loader from Streamlit and make it readable by langchain
    temp_file = "./temp.pdf"
    with open(temp_file, "wb") as file:
        file.write(uploaded_file.getvalue())
        file_name = uploaded_file.name

    # Load document and split it into chunks for efficient retrieval.
    chunks = load_document(temp_file)

    # Message user that document is being processed with time emoji
    st.write("Processing document... :watch:")

机器比文本更擅长理解数字,所以最终,我们不得不向模型提供一个数字数据库,以便它在执行查询时进行比较和检查相似性。这就是embeddings在创建vector_db的下一部分代码中将变得有用的地方。

# Generate embeddings
    # Embeddings are numerical vector representations of data, typically used to capture relationships, similarities,
    # and meanings in a way that machines can understand. They are widely used in Natural Language Processing (NLP),
    # recommender systems, and search engines.
    embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_KEY,
                                  model="text-embedding-ada-002")

    # Can also use HuggingFaceEmbeddings
    # from langchain_huggingface.embeddings import HuggingFaceEmbeddings
    # embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

    # Create vector database containing chunks and embeddings
    vector_db = FAISS.from_documents(chunks, embeddings)

接下来,我们创建一个检索对象来在vector_db中导航。

# Create a document retriever
    retriever = vector_db.as_retriever()
    llm = ChatOpenAI(model_name="gpt-4o-mini", openai_api_key=OPENAI_KEY)

然后,我们将创建system_prompt,这是一组指示 LLM 如何回答的指令,并且我们将创建一个提示模板,准备将其添加到模型中,一旦我们从用户那里获得输入。

# Create a system prompt
    # It sets the overall context for the model.
    # It influences tone, style, and focus before user interaction starts.
    # Unlike user inputs, a system prompt is not visible to the end user.

    system_prompt = (
        "You are a helpful assistant. Use the given context to answer the question."
        "If you don't know the answer, say you don't know. "
        "{context}"
    )

    # Create a prompt Template
    prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system_prompt),
            ("human", "{input}"),
        ]
    )

    # Create a chain
    # It creates a StuffDocumentsChain, which takes multiple documents (text data) and "stuffs" them together before passing them to the LLM for processing.

    question_answer_chain = create_stuff_documents_chain(llm, prompt)

接下来,我们创建 RAG 框架的核心,将retriever对象和prompt粘贴在一起。此对象从数据源(例如,向量数据库)添加相关文档,并使其准备好使用 LLM 进行处理以生成响应。

# Creates the RAG
     chain = create_retrieval_chain(retriever, question_answer_chain)

最后,我们为用户输入创建变量question。如果这个问题框中填入了查询,我们将其传递给chain,它调用 LLM 进行处理并返回响应,该响应将在应用程序屏幕上打印。

# Streamlit input for question
    question = st.text_input("Ask a question about the document:")
    if question:
        # Answer
        response = chain.invoke({"input": question})['answer']
        st.write(response)

这里是结果的截图。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/73f083a7e8b5dd484de5042cab9d6029.png

最终应用程序的截图。图片由作者提供。

这还有一个 GIF,您可以从中看到文件读取 AI 助手在行动中的样子!

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1b9ca0fbc0ea7fe4b2516c2b584a6936.png

文件读取 AI 助手正在行动中。图片由作者提供。

在您离开之前

在这个项目中,我们学习了 RAG 框架是什么以及它是如何帮助 LLM 表现更好,并且也能与特定知识表现良好的。

AI 可以通过来自操作手册的知识、公司的数据库、一些财务文件或合同的知识来增强,然后针对特定内容查询进行微调以准确响应。知识库通过内容存储进行增强。

总结一下,这是框架的工作方式:

1️⃣ 用户查询 → 接收输入文本。

2️⃣ 检索相关文档 → 在知识库(例如,数据库、向量存储)中进行搜索。

3️⃣ 增强上下文 → 将检索到的文档添加到输入中。

4️⃣ 生成响应 → 一个大型语言模型(LLM)处理组合输入并生成答案。

GitHub 仓库

github.com/gurezende/Basic-Rag

关于我

如果你喜欢这个内容并想了解更多关于我的工作,请访问我的网站,在那里你还可以找到我的所有联系方式。

gustavorsantos.me

参考文献

cloud.google.com/use-cases/retrieval-augmented-generation

www.ibm.com/think/topics/retrieval-augmented-generation

youtu.be/T-D1OfcDW1M?si=G0UWfH5-wZnMu0nw

python.langchain.com/docs/introduction

www.geeksforgeeks.org/how-to-get-your-own-openai-api-key

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐