開源 AI 食譜文件
使用自定義非結構化資料構建 RAG
並獲得增強的文件體驗
開始使用
使用自定義非結構化資料構建 RAG
如果您是 RAG 新手,請先在這篇 Notebook 中瞭解 RAG 的基礎知識,然後再回到這裡學習如何使用自定義資料構建 RAG。
無論您是構建自己的基於 RAG 的個人助理、一個個人專案,還是一個企業級 RAG 系統,您都會很快發現大量重要知識以各種格式儲存,如 PDF、電子郵件、Markdown 檔案、PowerPoint 簡報、HTML 頁面、Word 文件等等。
如何預處理所有這些資料,以便用於 RAG?在本快速教程中,您將學習如何構建一個可以整合多種資料型別的 RAG 系統。您將使用 Unstructured 進行資料預處理,使用 Hugging Face Hub 的開源模型進行嵌入和文字生成,使用 ChromaDB 作為向量儲存,並使用 LangChain 將所有內容整合在一起。
讓我們開始吧!我們將從安裝所需的依賴項開始。
!pip install -q torch transformers accelerate bitsandbytes sentence-transformers unstructured[all-docs] langchain chromadb langchain_community
接下來,讓我們獲取一些混合型別的文件。假設我想構建一個 RAG 系統來幫助我管理花園裡的害蟲。為此,我將使用涵蓋 IPM(綜合害蟲管理)主題的各種文件。
- PDF:
https://www.gov.nl.ca/ecc/files/env-protection-pesticides-business-manuals-applic-chapter7.pdf
- Powerpoint:
https://ipm.ifas.ufl.edu/pdfs/Citrus_IPM_090913.pptx
- EPUB:
https://www.gutenberg.org/ebooks/45957
- HTML:
https://blog.fifthroom.com/what-to-do-about-harmful-garden-and-plant-insects-and-pests.html
您可以隨意使用自己選擇的主題的文件,Unstructured 支援的文件型別包括:.eml
, .html
, .md
, .msg
, .rst
, .rtf
, .txt
, .xml
, .png
, .jpg
, .jpeg
, .tiff
, .bmp
, .heic
, .csv
, .doc
, .docx
, .epub
, .odt
, .pdf
, .ppt
, .pptx
, .tsv
, .xlsx
。
!mkdir -p "./documents"
!wget https://www.gov.nl.ca/ecc/files/env-protection-pesticides-business-manuals-applic-chapter7.pdf -O "./documents/env-protection-pesticides-business-manuals-applic-chapter7.pdf"
!wget https://ipm.ifas.ufl.edu/pdfs/Citrus_IPM_090913.pptx -O "./documents/Citrus_IPM_090913.pptx"
!wget https://www.gutenberg.org/ebooks/45957.epub3.images -O "./documents/45957.epub"
!wget https://blog.fifthroom.com/what-to-do-about-harmful-garden-and-plant-insects-and-pests.html -O "./documents/what-to-do-about-harmful-garden-and-plant-insects-and-pests.html"
非結構化資料預處理
您可以使用 Unstructured 庫逐個預處理文件,並編寫自己的指令碼來遍歷目錄,但使用本地源聯結器來攝取給定目錄中的所有文件會更容易。Unstructured 可以從本地目錄、S3 儲存桶、Blob 儲存、SFTP 以及您可能儲存文件的許多其他地方攝取文件。從這些來源攝取文件的過程非常相似,主要區別在於身份驗證選項。在這裡,您將使用本地源聯結器,但您可以在 Unstructured 文件中隨意探索其他選項。
可選地,您也可以為處理後的文件選擇一個目的地——這可以是 MongoDB、Pinecone、Weaviate 等。在本 Notebook 中,我們將所有內容都儲存在本地。
# Optional cell to reduce the amount of logs
import logging
logger = logging.getLogger("unstructured.ingest")
logger.root.removeHandler(logger.root.handlers[0])
>>> import os
>>> from unstructured.ingest.connector.local import SimpleLocalConfig
>>> from unstructured.ingest.interfaces import PartitionConfig, ProcessorConfig, ReadConfig
>>> from unstructured.ingest.runner import LocalRunner
>>> output_path = "./local-ingest-output"
>>> runner = LocalRunner(
... processor_config=ProcessorConfig(
... # logs verbosity
... verbose=True,
... # the local directory to store outputs
... output_dir=output_path,
... num_processes=2,
... ),
... read_config=ReadConfig(),
... partition_config=PartitionConfig(
... partition_by_api=True,
... api_key="YOUR_UNSTRUCTURED_API_KEY",
... ),
... connector_config=SimpleLocalConfig(
... input_path="./documents",
... # whether to get the documents recursively from given directory
... recursive=False,
... ),
... )
>>> runner.run()
INFO: NumExpr defaulting to 2 threads.
讓我們仔細看看這裡的配置。
ProcessorConfig
控制處理管道的各個方面,包括輸出位置、工作執行緒數、錯誤處理行為、日誌詳細程度等。這裡唯一強制的引數是 output_dir
——您希望儲存輸出的本地目錄。
ReadConfig
可用於為不同場景自定義資料讀取過程,例如重新下載資料、保留下載的檔案或限制處理的文件數量。在大多數情況下,預設的 ReadConfig
就可以工作。
在 PartitionConfig
中,您可以選擇是在本地還是透過 API 對文件進行分割槽。本示例使用 API,因此需要 Unstructured API 金鑰。您可以在這裡獲取您的金鑰。免費的 Unstructured API 上限為 1000 頁,併為基於影像的文件提供比本地安裝 Unstructured 更好的 OCR 模型。如果您移除這兩個引數,文件將在本地處理,但如果文件需要 OCR 和/或文件理解模型,您可能需要安裝額外的依賴項。也就是說,在這種情況下,您可能需要安裝 poppler 和 tesseract,您可以透過 brew 獲取它們。
!brew install poppler
!brew install tesseract
如果您使用的是 Windows,可以在 Unstructured 文件中找到替代的安裝說明。
最後,在 SimpleLocalConfig
中,您需要指定原始文件所在的位置,以及是否要遞迴地遍歷目錄。
文件處理完畢後,您會在 local-ingest-output
目錄中找到 4 個 json 檔案,每個處理過的文件對應一個。Unstructured 以統一的方式對所有型別的文件進行分割槽,並返回包含文件元素的 json 檔案。
文件元素 有一個型別,例如 NarrativeText
、Title
或 Table
,它們包含提取的文字以及 Unstructured 能夠獲取的元資料。一些元資料對所有元素都是通用的,例如元素所屬文件的檔名。其他元資料取決於檔案型別或元素型別。例如,Table
元素將在元資料中包含表格的 html 表示,而電子郵件的元資料將包含有關發件人和收件人的資訊。
讓我們從這些 json 檔案中匯入元素物件。
from unstructured.staging.base import elements_from_json
elements = []
for filename in os.listdir(output_path):
filepath = os.path.join(output_path, filename)
elements.extend(elements_from_json(filepath))
現在您已經從文件中提取了元素,您可以將它們分塊以適應嵌入模型的上下文視窗。
分塊
如果您熟悉將長文字文件分割成更小塊的分塊方法,您會注意到 Unstructured 的分塊方法略有不同,因為分割槽步驟已經將整個文件劃分為其結構元素:標題、列表項、表格、文字等。透過這種方式對文件進行分割槽,您可以避免不相關的文字片段最終出現在同一元素中,然後是同一塊中。
現在,當您使用 Unstructured 對文件元素進行分塊時,單個元素已經很小,因此只有在超過所需的最大塊大小時才會被分割。否則,它們將保持原樣。您還可以選擇性地合併連續的文字元素,例如列表項,只要它們一起能適應塊大小限制。
from unstructured.chunking.title import chunk_by_title
chunked_elements = chunk_by_title(
elements,
# maximum for chunk size
max_characters=512,
# You can choose to combine consecutive elements that are too small
# e.g. individual list items
combine_text_under_n_chars=200,
)
這些塊已準備好用於 RAG。要將它們與 LangChain 一起使用,您可以輕鬆地將 Unstructured 元素轉換為 LangChain 文件。
from langchain_core.documents import Document
documents = []
for chunked_element in chunked_elements:
metadata = chunked_element.metadata.to_dict()
metadata["source"] = metadata["filename"]
del metadata["languages"]
documents.append(Document(page_content=chunked_element.text, metadata=metadata))
設定檢索器
本示例使用 ChromaDB 作為向量儲存和 BAAI/bge-base-en-v1.5
嵌入模型,您也可以隨意使用任何其他向量儲存。
from langchain_community.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import utils as chromautils
# ChromaDB doesn't support complex metadata, e.g. lists, so we drop it here.
# If you're using a different vector store, you may not need to do this
docs = chromautils.filter_complex_metadata(documents)
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-base-en-v1.5")
vectorstore = Chroma.from_documents(documents, embeddings)
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 3})
如果您計劃使用 Hugging Face Hub 中的受限模型,無論是嵌入模型還是文字生成模型,您都需要使用您的 Hugging Face 令牌進行身份驗證,您可以在 Hugging Face 個人資料的設定中獲取該令牌。
from huggingface_hub import notebook_login
notebook_login()
使用 LangChain 實現 RAG
讓我們將所有內容整合起來,用 LangChain 構建 RAG。在本示例中,我們將使用 Meta 的 Llama-3-8B-Instruct
模型。為了確保它能在 Google Colab 的免費 T4 執行時中順利執行,您需要對其進行量化。
from langchain.prompts import PromptTemplate
from langchain.llms import HuggingFacePipeline
from transformers import pipeline
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from langchain.chains import RetrievalQA
model_name = "meta-llama/Meta-Llama-3-8B-Instruct"
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=bnb_config)
tokenizer = AutoTokenizer.from_pretrained(model_name)
terminators = [tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids("<|eot_id|>")]
text_generation_pipeline = pipeline(
model=model,
tokenizer=tokenizer,
task="text-generation",
temperature=0.2,
do_sample=True,
repetition_penalty=1.1,
return_full_text=False,
max_new_tokens=200,
eos_token_id=terminators,
)
llm = HuggingFacePipeline(pipeline=text_generation_pipeline)
prompt_template = """
<|start_header_id|>user<|end_header_id|>
You are an assistant for answering questions using provided context.
You are given the extracted parts of a long document and a question. Provide a conversational answer.
If you don't know the answer, just say "I do not know." Don't make up an answer.
Question: {question}
Context: {context}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"""
prompt = PromptTemplate(
input_variables=["context", "question"],
template=prompt_template,
)
qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever, chain_type_kwargs={"prompt": prompt})
結果與後續步驟
現在您有了 RAG 鏈,讓我們問問它關於蚜蟲的問題。它們是我花園裡的害蟲嗎?
question = "Are aphids a pest?"
qa_chain.invoke(question)["result"]
輸出
Yes, aphids are considered pests because they feed on the nutrient-rich liquids within plants, causing damage and potentially spreading disease. In fact, they're known to multiply quickly, which is why it's essential to control them promptly. As mentioned in the text, aphids can also attract ants, which are attracted to the sweet, sticky substance they produce called honeydew. So, yes, aphids are indeed a pest that requires attention to prevent further harm to your plants!
這看起來是一個很有希望的開始!既然您已經瞭解了為 RAG 預處理複雜非結構化資料的基礎知識,您可以繼續在此示例的基礎上進行改進。以下是一些想法:
- 您可以連線到不同的源來攝取文件,例如,一個 S3 儲存桶。
- 您可以在
qa_chain
引數中新增return_source_documents=True
,使鏈返回作為上下文傳遞給提示的文件。這有助於瞭解生成答案時使用了哪些來源。 - 如果您想在檢索階段利用元素元資料,可以考慮使用 Hugging Face 智慧體並建立一個自定義檢索器工具,如這篇 Notebook 中所述。
- 您可以做很多事情來改善搜尋結果。例如,您可以使用混合搜尋而不是單一的相似性搜尋檢索器。混合搜尋結合了多種搜尋演算法,以提高搜尋結果的準確性和相關性。通常,它是基於關鍵字的搜尋演算法與向量搜尋方法的組合。
祝您使用非結構化資料構建 RAG 應用愉快!
< > 在 GitHub 上更新