一 RAG回顾
RAG(Retrieval-Augmented Generation)的改进方案的提纲。RAG是一种结合了检索和生成的技术,用于提高问答系统的性能。以下是对提纲内容的简单分析:
-
RAG回顾以及有可能存在的问题:
-
文档的读取的准确性,及数据清洗的逻辑。
-
拆分文档也比较麻烦,拆分为chunk
-
query 的不完整性,提问问题的处理。
-
搜索相似的文档,比较 向量的相似度,搜索结果是否能满足回复问题的背景信息。
-
是否对搜索文档做排序,怎么排序?
-
是否需要 对模型回复的结果做 post-process?
-
-
RAG的改进方案
-
这一部分详细列出了四个改进方案:
-
Query的改造:可能涉及对查询语句的优化,使其更有效地检索相关信息。
-
Retriever的改造:可能指的是对检索器的改进,以提高检索结果的相关性和准确性。
-
Ranking的改造:可能涉及对检索结果进行重新排序,以确保最相关的信息排在前面。
-
RAGAS:RAG评估:可能是指对RAG系统进行评估的方法或工具,以衡量其性能和改进效果。
-
-
-
Advanced RAG + 实战项目讲解
-
这一部分可能会介绍更高级的RAG技术,以及如何在实际项目中应用这些技术。这可能包括具体的案例分析和最佳实践。
-
二 RAG优化
2.1 Self-querying retrieval
是RAG优化中的一种技术,它涉及到自我查询检索的过程。以下是对这一概念的简单解读:
-
Self-querying:自我查询是一种策略,其中 模型会生成自己的查询来检索信息。这意味着模型不仅仅是被动地接收输入查询,而是能够主动地生成查询以获取更相关的信息。
-
Retrieval:检索是指从大量数据中找到与查询最相关的信息的过程。在RAG模型中,检索器负责从文档集合中找到与输入查询最相关的文档片段。
-
Self-querying retrieval in RAG:在RAG模型中,自我查询检索可能涉及到模型在生成答案的过程中,根据当前的上下文和已检索到的信息,动态地生成新的查询来进一步检索更精确的信息。这种方法可以帮助模型更深入地理解问题,并从文档中检索到更相关的内容,从而提高生成答案的质量。
2.1.1 写入数据库
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddingsdocs = [Document(page_content="A bunch of scientists bring back dinosaurs and mayhem breaks loose",metadata={"year": 1993, "rating": 7.7, "genre": "science fiction"},),Document(page_content="Leo DiCaprio gets lost in a dream within a dream within a dream within a ...",metadata={"year": 2010, "director": "Christopher Nolan", "rating": 8.2},),Document(page_content="A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea",metadata={"year": 2006, "director": "Satoshi Kon", "rating": 8.6},),Document(page_content="A bunch of normal-sized women are supremely wholesome and some men pine after them",metadata={"year": 2019, "director": "Greta Gerwig", "rating": 8.3},),Document(page_content="Toys come alive and have a blast doing so",metadata={"year": 1995, "genre": "animated"},),Document(page_content="Three men walk into the Zone, three men walk out of the Zone",metadata={"year": 1979,"director": "Andrei Tarkovsky","genre": "thriller","rating": 9.9,},),
]
vectorstore = Chroma.from_documents(docs, OpenAIEmbeddings())
2.1.2 自查询
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain_openai import ChatOpenAImetadata_field_info = [AttributeInfo(name="genre",description="The genre of the movie. One of ['science fiction', 'comedy', 'drama', 'thriller', 'romance', 'action', 'animated']",type="string",),AttributeInfo(name="year",description="The year the movie was released",type="integer",),AttributeInfo(name="director",description="The name of the movie director",type="string",),AttributeInfo(name="rating", description="A 1-10 rating for the movie", type="float"),
]
document_content_description = "Brief summary of a movie"
#llm = ChatOpenAI(temperature=0)
retriever = SelfQueryRetriever.from_llm(llm,vectorstore,document_content_description,metadata_field_info,
)# This example only specifies a filter
retriever.invoke("I want to watch a movie rated higher than 8.5")
2.2 parent-child splitting and retrieval
Parent-Child Splitting and Retrieval 是一种文档检索技术,它通过将文档分割成多级结构的文本块来提高检索精度和效率。这种方法特别适用于处理大规模和复杂的信息,如电子商务、知识管理、法律和医疗文献检索等领域。
具体来说,该技术包含以下几个步骤:
-
文档分割:首先,将文档分割成较大的文本块,称为Parent Chunks,每个Parent Chunk代表文档中的一个较大部分,通常包含多个段落或章节。然后,每个Parent Chunk进一步细分为更小的块,称为Child Chunks,每个Child Chunk通常包含一个段落或几句话。
-
向量嵌入:使用预训练的模型(例如BERT、GPT等)将每个Child Chunk转换为向量,这些向量代表了文本块的语义信息,可以通过向量检索算法来进行比对和匹配。
-
保持关联关系:Child Chunks与其对应的Parent Chunk保持关联,在后续的检索过程中可以对Parent Chunk进行合并,确保用户获取的信息上下文连贯。
from langchain.retrievers import ParentDocumentRetrieverfrom langchain.storage import InMemoryStore
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddingsloaders = [TextLoader("example_data/FDR_State_of_Union_1944.txt"),TextLoader("example_data/Lincoln_State_of_Union_1862.txt"),
]
docs = []
for loader in loaders:docs.extend(loader.load())# This text splitter is used to create the parent documents
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# This text splitter is used to create the child documents
# It should create documents smaller than the parent
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# The vectorstore to use to index the child chunks
vectorstore = Chroma(collection_name="split_parents", embedding_function=OpenAIEmbeddings()
)
# The storage layer for the parent documents
store = InMemoryStore()retriever = ParentDocumentRetriever(vectorstore=vectorstore,docstore=store,child_splitter=child_splitter,parent_splitter=parent_splitter,
)retriever.add_documents(docs)sub_docs = vectorstore.similarity_search("justice breyer")print(sub_docs[0].page_content)retrieved_docs = retriever.get_relevant_documents("justice breyer")
len(retrieved_docs[0].page_content)
2.3 Multiquery Retriever
MultiQueryRetriever是一种检索技术,它通过使用大型语言模型(LLM)从不同角度为给定的用户输入查询生成多个查询,从而自动化提示调优的过程。对于每个查询,它检索一组相关文档,并在所有查询中取唯一的并集,以获取更大的一组潜在相关文档。通过对同一问题生成多个视角,MultiQueryRetriever可以减轻基于距离的检索的一些局限性,并获得更丰富的结果集。
具体来说,MultiQueryRetriever的工作原理如下:
-
接收用户输入的查询。
-
使用LLM生成多个相关但不同的查询。
-
对每个生成的查询进行向量数据库检索。
-
合并所有检索结果,去重后返回。
# Build a sample vectorDB
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings# Load blog post
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
data = loader.load()# Split
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
splits = text_splitter.split_documents(data)# VectorDB
embedding = OpenAIEmbeddings()
vectordb = Chroma.from_documents(documents=splits, embedding=embedding)from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_openai import ChatOpenAIquestion = "What are the approaches to Task Decomposition?"
llm = ChatOpenAI(temperature=0)
retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vectordb.as_retriever(), llm=llm
)unique_docs = retriever_from_llm.get_relevant_documents(query=question)
len(unique_docs) # 5
2.4 Ensemble Retriever
Ensemble Retriever是一种检索工具,它能够集成多个基础检索器(Base Retriever)的结果,通过结合每个检索器的输出来提升整体的检索效果。这种方法利用了不同检索算法的优势,例如将稀疏检索器(如BM25)与密集检索器(如基于向量嵌入的检索)结合起来,因为它们的优势互补,这也被称为"混合搜索"。稀疏检索器擅长通过关键字找到相关文档,而密集检索器则擅长通过语义相似性找到相关文档。
EnsembleRetriever通过初始化一个包含多个BaseRetriever对象的列表,并使用Reciprocal Rank Fusion算法对这些检索器的结果进行重排序。最常见的模式是结合稀疏检索器和密集检索器,因为它们在不同场景下表现出不同的优势。
# !pip install rank_bm25
from langchain.retrievers import BM25Retriever, EnsembleRetriever
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddingsdoc_list_1 = ["I like apples","I like oranges","Apples and oranges are fruits",
]# initialize the bm25 retriever and faiss retriever
bm25_retriever = BM25Retriever.from_texts(doc_list_1, metadatas=[{"source": 1}] * len(doc_list_1)
)
bm25_retriever.k = 2doc_list_2 = ["You like apples","You like oranges",
]embedding = OpenAIEmbeddings()
faiss_vectorstore = FAISS.from_texts(doc_list_2, embedding, metadatas=[{"source": 2}] * len(doc_list_2)
)
faiss_retriever = faiss_vectorstore.as_retriever(search_kwargs={"k": 2})# initialize the ensemble retriever
ensemble_retriever = EnsembleRetriever(retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5]
)docs = ensemble_retriever.invoke("apples")
docs