大模型之深入了解Retrievers解析器 原創
前言
在上一章【??大模型之深入探索RAG流程???】中,我們對RAG流程中 ??文檔讀取(LOAD)?
?? -> ??文檔切分(SPLIT)?
?? -> ??向量化(EMBED)?
?? -> ??存儲(STORE)?
?? 進行了深入了解,本章將接著深入了解 ??解析(Retrieval)?
? 的使用
解析器簡介
簡介:在 RAG(Retrieval-Augmented Generation)流程中,Retrieval(檢索)是關鍵環節,其主要目標是從大量文檔或知識庫中提取與用戶查詢相關的信息。
目的:
- 信息獲取:根據用戶的查詢,從外部知識庫中獲取相關文檔或片段,以增強生成模型的上下文信息。
- 提高準確性:通過提供具體的、相關的信息,幫助生成模型(如語言模型)產生更準確和上下文相關的回答。
流程
- 用戶查詢:用戶輸入一個查詢或問題。
- 檢索器:使用檢索算法(如 BM25、TF-IDF 或基于嵌入的檢索)搜索知識庫,找到與查詢最相關的文檔。
- 文檔評分:對檢索到的文檔進行評分,通常依據相關性得分來排序。
- 返回結果:將最相關的文檔或片段返回給生成模型。
解析器的基礎使用
創建知識庫
第一步:啟動Chroma數據庫
chroma run --path chroma_xiyou --port 8000
第二步:使用RAG基礎流程:Load->Split->EMBED->STORE創建一個知識庫
from langchain_text_splitters importRecursiveCharacterTextSplitter
from langchain.embeddings importHuggingFaceEmbeddings
from langchain.document_loaders importPyMuPDFLoader
from langchain_chroma importChroma
from chromadb importSettings
from chromadb importClient
from utils import get_ernie_models
import chromadb
# 連接大模型
llm_ernie, chat_ernie, embed_ernie = get_ernie_models()
# 初始化 HuggingFaceEmbeddings
embedding_function =HuggingFaceEmbeddings(model_name="bert-base-chinese")
# 加載文檔
pdf_loader =PyMuPDFLoader("testfiles/西游記.pdf")
documents = pdf_loader.load()
# 切分文檔
spliter =RecursiveCharacterTextSplitter(chunk_size=128, chunk_overlap=64)
docs = spliter.split_documents(documents)
# 配置連接信息
client = chromadb.HttpClient(host='localhost', port=8000)
chroma_db =Chroma(
embedding_functinotallow=embedding_function,
client=client
)
batch_size =6# 每次處理的樣本數量
# 分批入庫
for i inrange(0,len(docs), batch_size):
batch = docs[i:i + batch_size]# 獲取當前批次的樣本
print(f'Processing batch {i} to {i + batch_size}, total {len(batch)} samples')
chroma_db.add_documents(documents=batch) # 入庫
說明:
- 向量化說明:由于Qwen和百度千帆的向量接口限制較多,對于向量化西游記這本書來說,經常會遇到超出限制等問題,所以此處我將向量化接口換為HuggingFaceEmbeddings(),該接口可能會存在被Ban的風險,請自行更換向量化接口。
- 測試文檔:西游記下載地址請見夸克網盤:西游記
創建解析器
第三步:創建一個解析器
retriever = chroma_db.as_retriever()
創建chain鏈
創建chain鏈有兩種方法:一種是管道符連接,一種是使用 create_retrieval_chain 。本章我們兩種方法都做嘗試,以便對比代碼的寫法。
方式一:傳統的管道符構建chain
# RAG系統經典的 Prompt
prompt =ChatPromptTemplate.from_messages([
("human","""You are an assistant for question-answering tasks. Use the following pieces
of retrieved context to answer the question.
If you don't know the answer, just say that you don't know.
Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:""")
])
defformat_docs(docs):
return"\n\n".join(doc.page_content for doc in docs)
# RAG 鏈
rag_chain =(
{"context": retriever | format_docs,
"question":RunnablePassthrough()}
| prompt
| chat_ernie
|StrOutputParser()
)
rag_chain.invoke(input="孫悟空三打白骨精時,白骨精分別變成了哪些形態?")
運行結果:
方式二:使用create_retrieval_chain構建
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts importChatPromptTemplate
prompt =ChatPromptTemplate.from_messages([
("system","""Answer any use questions based solely on the context below:
<context>
{context}
</context>"""),
("placeholder","{chat_history}"),
("human","{input}"),
])
combine_docs_chain = create_stuff_documents_chain(
llm=chat_ernie,prompt= prompt
)
chain = create_retrieval_chain(retriever=retriever, combine_docs_chain=combine_docs_chain)
chain.invoke(input={"input":"孫悟空三打白骨精時,白骨精分別變成了哪些形態?"})
說明:
- 上述的prompt可以在smith.langchain.com上查詢?
?langchain-ai/retrieval-qa-chat?
?得到。
運行結果:
通過對比,可以看到使用 ??create_retrieval_chain?
? 創建chain時,可以減少format_docs()步驟以及使用管道符部分的代碼量,簡化調用步驟。
以上調用即為完整的解析器使用流程,其中第三步中 ??retriever = chroma_db.as_retriever()?
? 可以有多種方式構建解析器。本章內容,我們著重對此深入研究。
解析器的不同類型
除了上面基礎的向量存儲檢索器之外,Langchain 還提供了多種高級檢索類型,包括多查詢檢索器(MultiQueryRetriever)、結合檢索器(EnsembleRetriever)等。
多查詢檢索器 MultiQueryRetriever
簡介: ??MultiQueryRetriever?
? 是一種檢索算法,它通過使用大型語言模型(LLM)生成多個查詢,從而自動化提示調優過程。
功能: 它為給定的用戶輸入查詢生成多個不同視角的查詢。對于每個查詢,它檢索一組相關文檔,并通過所有查詢的唯一聯合來獲得更大的一組潛在相關文檔。這種方法提高了檢索的準確性和多樣性。
使用場景: 當用戶的問題復雜并且需要多條不同的信息來回答時,??MultiQueryRetriever?
? 特別有用。它可以在需要關于多個主題的信息時提供更全面的結果
使用方法: 第一步:構建知識庫(此處復用上面的知識庫,詳細內容不再贅述)
第二步:使用MultiQueryRetriever
import logging
from langchain.retrievers.multi_query importMultiQueryRetriever
question ="孫悟空三打白骨精時,白骨精分別變成了哪些形態?"
# 把向量操作封裝為一個基本檢索器
retriever = chroma_db.as_retriever()
retriever_from_llm =MultiQueryRetriever.from_llm(
retriever=chroma_db.as_retriever(), llm=chat_ernie
)
logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)
unique_docs = retriever_from_llm.get_relevant_documents(query=question)
unique_docs
print(f'返回的文檔個數:{len(unique_docs)}')
print(f'返回的文檔內容:')
for doc in unique_docs:
print(doc.page_content)
運行結果:
INFO:langchain.retrievers.multi_query:Generated
queries: ['以下是三個不同版本的生成問題,旨在從不同角度探索原始問題的相關信息:',
'1. 孫悟空在三打白骨精的情節中,白骨精分別化作了哪些生物或物體形態?',
'2. 孫悟空三打白骨精時,白骨精變身的形態有哪些?請詳細列出。',
'3. 在《西游記》中,白骨精在孫悟空三打她的過程中,她變換成了哪些不同的身份或外觀?',
'希望以上問題可以幫助你從不同的角度獲取相關信息,從而更全面地回答原始問題。']
第三步:使用 create_retrieval_chain 進行完整查詢
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts importChatPromptTemplate
# 創建結合文檔的鏈
prompt =ChatPromptTemplate.from_messages([
("system","""Answer any user questions based solely on the context below:
<context>
{context}
</context>"""),
("placeholder","{chat_history}"),
("human","{input}"),
])
combine_docs_chain = create_stuff_documents_chain(
llm=chat_ernie, prompt=prompt
)
# 創建檢索鏈
chain = create_retrieval_chain(retriever=retriever_from_llm, combine_docs_chain=combine_docs_chain)
# 使用鏈進行查詢
result = chain.invoke(input={"input": question})
# 輸出最終結果
print("最終回答:", result)
運行結果:
最終回答: {'input':'孫悟空三打白骨精時,白骨精分別變成了哪些形態?',
'context':[
Document(metadata={'author':'吳承恩','creationDate':"D:20240329064911+00'00'",'creator':'calibre 3.23.0 [https://calibre-ebook.com]','file_path':'testfiles/西游記.pdf','format':'PDF 1.4','keywords':'','modDate':'','page':7,'producer':'calibre 3.23.0 [https://calibre-ebook.com]','source':'testfiles/西游記.pdf','subject':'','title':'西游記(人文社經典彩皮版,長銷70年,以明代世德堂本為底本;三次修訂重校;豆瓣上萬條評論;2020年教育部指導目錄圖書)','total_pages':675,'trapped':''}, page_cnotallow='了許多新的成果,為了能讓廣大讀者更好地理解該作品,這次特意邀請北京大學劉勇強教授重新為該書撰\n寫了前言。\n六、關于取經途中所遇的八十一難順序,第九十九回諸神給觀音菩薩提供的簿子所記,與小說的敘述'),
....(內容過多,此處省略)
Document(metadata={'author':'吳承恩','creationDate':"D:20240329064911+00'00'",'creator':'calibre 3.23.0 [https://calibre-ebook.com]','file_path':'testfiles/西游記.pdf','format':'PDF 1.4','keywords':'','modDate':'','page':6,'producer':'calibre 3.23.0 [https://calibre-ebook.com]','source':'testfiles/西游記.pdf','subject':'','title':'西游記(人文社經典彩皮版,長銷70年,以明代世德堂本為底本;三次修訂重校;豆瓣上萬條評論;2020年教育部指導目錄圖書)','total_pages':675,'trapped':''}, page_cnotallow='五、更換前言。本書的前言,過去一直沿用華東師范大學郭豫適、簡茂森教授一九七二年所寫的文\n字,限于當時的歷史環境,政治化色彩較濃,而《西游記》研究在過去的幾十年中取得了很大發展,出現')],
'answer':'根據原文信息得出,孫悟空三打白骨精時,白骨精分別變成了美麗的村姑、年滿八旬的老婦人、和一位白發蒼蒼的老公公。'}
通過以上實踐,可以看到:
1、MultiQueryRetriever會借助大模型生成新的多個查詢 queries;
2、MultiQueryRetriever會通過這些 queries 檢索出相關的文檔;
3、最后,通過 combine_docs_chain 將檢索出的文檔交給大模型得到最終答案。
結合檢索器 EnsembleRetriever
簡介: EnsembleRetriever是一種結合多個檢索器結果的算法,通過重新排序來提高檢索效果。
功能: 該算法從多個檢索器中獲取文檔,并將它們組合在一起,以提高文本相似性匹配和信息檢索的準確性。通過結合多種補充的向量搜索算法,EnsembleRetriever提供了最先進的文本相似性匹配和信息檢索能力。
使用場景: 當需要結合多種檢索方法以獲得更高的檢索準確性時,EnsembleRetriever是理想的選擇。這種方法適用于需要從大量文檔集合中進行信息檢索的應用場景
使用方法 第一步:安裝依賴包
pip install rank_bm25
pip install faiss-gpu
第二步:準備知識數據
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.vectorstores import FAISS
# 初始化 HuggingFaceEmbeddings
embedding =HuggingFaceEmbeddings(model_name="bert-base-chinese")
# 中文文檔列表
doc_list_1 =[
"我喜歡蘋果",
"我喜歡橙子",
"蘋果和橙子都是水果",
]
doc_list_2 =[
"你喜歡蘋果嗎?",
"你喜歡橙子嗎?",
]
第三步:創建組合檢索器
# 初始化 BM25 檢索器
bm25_retriever = BM25Retriever.from_texts(
doc_list_1, metadatas=[{"source":1}]*len(doc_list_1)
)
bm25_retriever.k =2
# 創建 FAISS 向量存儲
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})
# 初始化組合檢索器
ensemble_retriever =EnsembleRetriever(
retrievers=[bm25_retriever, faiss_retriever], weights=[0.5,0.5]
)
第四步:進行查詢
# 輸入查詢
docs = ensemble_retriever.invoke("我想知道關于蘋果的信息")
print(docs)
運行結果:
[
Document(metadata={'source':1}, page_cnotallow='蘋果和橙子都是水果'),
Document(metadata={'source':2}, page_cnotallow='你喜歡蘋果嗎?'),
Document(metadata={'source':1}, page_cnotallow='我喜歡橙子'),
Document(metadata={'source':2}, page_cnotallow='你喜歡橙子嗎?')
]
說明:
- BM25Retriever 是一種基于經典信息檢索模型 BM25 的檢索器。它的優點:
簡單易用,適合處理短文本和關鍵詞檢索。
對于傳統的文本匹配任務表現良好。
- FAISS(Facebook AI Similarity Search)是一個高效的相似性搜索庫,專門用于處理大規模向量數據。它的優點:
- 適合處理復雜的語義檢索任務,尤其是在向量空間中。
- 能夠處理大規模數據集,檢索速度快。
- 將 BM25Retriever 和 FAISS 組合在一起:
- 互補性:BM25 更適合處理基于關鍵詞的檢索,能夠有效地從文本中找到相關文檔;FAISS 則擅長處理語義相似性,通過向量化表示捕捉文本的深層含義。
- 提高檢索:通過組合兩種方法,可以充分利用 BM25 的詞頻特性和 FAISS 的向量相似性,從而提高整體檢索的準確性和全面性。
長上下文重排序 LongContextReorder
簡介: LongContextReorder是一種用于處理長上下文信息的技術,旨在提高模型在長文本中的信息提取和理解能力。它通過重新排序文檔,以使最相關的信息更易于被模型捕捉。
功能: 信息重排序: LongContextReorder從多個檢索器中獲取文檔,并對它們進行重新排序,使得最相關的文檔位于上下文窗口的開頭和結尾。這有助于模型更好地關注整個上下文中的關鍵內容。 上下文優化: 通過優化上下文的排列,模型能夠更有效地利用長文本中的信息,從而提高回答的準確性和相關性。
使用場景: 這種技術特別適合需要在長文本中提取關鍵信息的應用場景,比如法律文書分析、學術論文閱讀和長篇故事理解等。
使用方法:
第一步:啟動 Chroma 數據庫
chroma run --path chroma_test --port 8000
說明:
- 此處是避免與前面的代碼產生數據污染,所以最好關閉之前的chroma數據庫,重新啟動一個新的。
第二步:準備數據
import os
from langchain.chains importLLMChain,StuffDocumentsChain
from langchain.prompts importPromptTemplate
from langchain_chroma importChroma
from langchain_community.document_transformers import(
LongContextReorder,
)
from langchain_community.embeddings importHuggingFaceEmbeddings
# 測試數據
texts =[
"籃球是一項很棒的運動。",
"《Fly me to the moon》 是我最喜歡的歌曲之一。",
"凱爾特人是我最喜歡的球隊。",
"這是關于波士頓凱爾特人的一篇文章。",
"我最喜歡的游戲是《黑神話:悟空》",
"波士頓凱爾特人以20分的優勢贏得了比賽。",
"在《西游記》中,孫悟空三打白骨精時,白骨精分別變成了村姑、老婦和老翁這三種形態。"
"孫悟空三大白骨精是西游記中一段精彩的篇章。",
"L. Kornet 是凱爾特人隊中最優秀的球員之一",
"Larry Bird 是一位標志性的NBA球員。",
]
# 初始化 HuggingFaceEmbeddings
embeddings =HuggingFaceEmbeddings(model_name="bert-base-chinese")
第三步:創建Retriever并查詢相關信息
# 創建一個 retriever
retriever = Chroma.from_texts(texts, embedding=embeddings).as_retriever(
search_kwargs={"k": 10}
)
query = "請告訴我關于白骨精的事情"
# Get relevant documents ordered by relevance score
docs = retriever.get_relevant_documents(query)
for doc in docs:
print(doc.page_content)
運行結果:
在《西游記》中,孫悟空三打白骨精時,白骨精分別變成了村姑、老婦和老翁這三種形態。孫悟空三大白骨精是西游記中一段精彩的篇章。
這是關于波士頓凱爾特人的一篇文章。
我最喜歡的游戲是《黑神話:悟空》
L. Kornet 是凱爾特人隊中最優秀的球員之一
《Fly me to the moon》 是我最喜歡的歌曲之一。
凱爾特人是我最喜歡的球隊。
Larry Bird 是一位標志性的NBA球員。
籃球是一項很棒的運動。
波士頓凱爾特人以20分的優勢贏得了比賽。
第四步:使用LongContextReorder進行重排序
reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)
for doc in reordered_docs:
print(doc.page_content)
運行結果:
在《西游記》中,孫悟空三打白骨精時,白骨精分別變成了村姑、老婦和老翁這三種形態。孫悟空三大白骨精是西游記中一段精彩的篇章。
我最喜歡的游戲是《黑神話:悟空》
《Fly me to the moon》 是我最喜歡的歌曲之一。
Larry Bird 是一位標志性的NBA球員。
波士頓凱爾特人以20分的優勢贏得了比賽。
籃球是一項很棒的運動。
凱爾特人是我最喜歡的球隊。
L. Kornet 是凱爾特人隊中最優秀的球員之一
這是關于波士頓凱爾特人的一篇文章。
對比上面的結果,可以看到,通過使用 LongContextReorder,可以將文檔重新排序,將與西游相關的信息排在前面,有助于提高模型的理解能力。
ElasticSearchBM25Retriever
簡介: ElasticSearchBM25Retriever使用BM25算法,這是信息檢索中的一種經典方法,廣泛應用于ElasticSearch等搜索引擎中。
功能: BM25是一種基于概率模型的檢索算法,能夠根據文檔和查詢之間的詞頻和逆文檔頻率,計算文檔的相關性得分。
使用場景: ElasticSearchBM25Retriever適用于需要高效文本搜索的場景,尤其是在需要處理大量非結構化文本數據時。它在搜索引擎優化和文本挖掘中非常有用。
使用方法:
第一步:安裝必要的庫
pip install elasticsearch langchain
第二步:安裝Elasticsearch并啟動服務
- 1. 啟動Elasticsearch的Docker容器,確保 Elasticsearch 服務正在運行。
- 2. 在瀏覽器中訪問 http://localhost:9200 可以得到如下內容。
說明:Elasticsearch 的安裝方法較長,由于不是本章的主線內容,所以我將這方面內容放在附錄部分。
第三步:準備數據并創建索引
from elasticsearch importElasticsearch
from langchain.retrievers importElasticSearchBM25Retriever
elasticsearch_url ="http://elastic:mytest@localhost:9200"
# 創建檢索器
retriever =ElasticSearchBM25Retriever.create(elasticsearch_url,"langchain-index-5")
# 準備測試數據
documents =[
"籃球是一項很棒的運動。",
"《Fly me to the moon》 是我最喜歡的歌曲之一。",
"凱爾特人是我最喜歡的球隊。",
"這是關于波士頓凱爾特人的一篇文章。",
"我最喜歡的游戲是《黑神話:悟空》"
]
# 添加文本
retriever.add_texts(documents)
運行結果:
['39c92ff7-4aba-42fb-bc88-2997b5e0c297',
'b1e9c357-b67c-43e0-bf95-2591c9e2a103',
'e4f964cf-92a0-4b4a-b648-9d0c2f8594b0',
'9edcfd38-e954-4bb1-b0d5-68ebf13b3443',
'ea0fc62f-6afd-4e27-9f85-a357ef9221a3']
說明: ??http://elastic:mytest@localhost:9200?
? 中:
- ?
?elastic?
? 是用戶名 - ?
?mytest?
? 是密碼
第四步:使用 ??retriever?
? 進行查詢
# 查詢
query ="請告訴我關于凱爾特人的事情"
try:
docs = retriever.get_relevant_documents(query)
# 輸出結果
for doc in docs:
print(doc.page_content)
print("-"*20)
exceptExceptionas e:
print(f"An error occurred: {e}")
運行結果:
這是關于波士頓凱爾特人的一篇文章。
--------------------
凱爾特人是我最喜歡的球隊。
--------------------
我最喜歡的游戲是《黑神話:悟空》
--------------------
《Fly me to the moon》 是我最喜歡的歌曲之一。
--------------------
籃球是一項很棒的運動。
--------------------
附錄
ElasticSearch的Docker安裝教程
由于在系統環境中安裝ElasticSearch非常繁瑣,需要安裝(JDK等)基礎環境,所以我們使用Docker來安裝ElasticSearch。
安裝Docker
Docker的安裝教程比較多且詳細,所以本文不再贅述,詳情請查看: 10分鐘學會Docker的安裝和使用
創建網絡
docker network create es-net
運行結果:
拉取鏡像
docker pull elasticsearch:8.6.0
運行結果:
創建掛載點目錄
# 選擇合適的目錄創建三個文件夾
mkdir -p /Users/deadwalk/Code/elasticsearch/data /Users/deadwalk/Code/elasticsearch/config /Users/deadwalk/Code/elasticsearch/plugins
運行結果:
啟動容器
命令行中輸入命令啟動Docker容器
docker run -d \
--restart=always \
--name es \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
--privileged \
-v /Users/deadwalk/Code/elasticsearch/data:/usr/share/elasticsearch/data \
-v /Users/deadwalk/Code/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
elasticsearch:8.6.0
運行結果:
使用 ??docker ps?
? 可以看到Docker容器已經啟動。
修改elasticsearch的密碼
第一步:進入es容器
docker exec -it es /bin/bash
第二步:命令行輸入重置密碼命令
bin/elasticsearch-reset-password -i -u elastic
根據提示重置密碼,例如:mytest
第三步:使用瀏覽器訪問http://localhost:9200/ 使用用戶名 ??elastic?
?? 和重置的密碼 ??mytest?
? ,驗證可以登錄。
內容小結
- RAG系統在進行Load->Split->Embedding->Store->Query流程時,需要使用?
?Retriever?
? 進行文檔檢索。 - Retriever進行文檔檢索時,有兩種方法構建Chain鏈:一種是傳統的管道符構建,另一種是使用?
?create_retrieval_chain?
? 。 - 使用Retriever時,除了基礎的向量存儲檢索器之外,還可以使用多查詢檢索器(?
?MultiQueryRetriever?
?)、結合檢索器(??EnsembleRetriever?
?)等。 - ?
?MultiQueryRetriever?
? 是一種檢索算法,它通過使用大型語言模型(LLM)生成多個查詢,從而自動化提示調優過程。 - ?
?MultiQueryRetriever?
? 的工作流程如下:
1.??MultiQueryRetriever?
?? 會借助大模型生成新的多個查詢 ??queries;?
?
2.??MultiQueryRetriever?
?? 會通過這些 ??queries?
? 檢索出相關的文檔;
3.最后,通過 ??combine_docs_chain?
? 將檢索出的文檔交給大模型得到最終答案。
- ?
?EnsembleRetriever?
? 是一種結合多個檢索器結果的算法,通過重新排序來提高檢索效果。 BM25Retriever
是一種基于經典信息檢索模型 BM25 的檢索器。FAISS(Facebook AI Similarity Search)
是一個高效的相似性搜索庫,專門用于處理大規模向量數據。EnsembleRetriever
可以將BM25Retriever
和FAISS
組合使用,從而提高整體檢索的準確性和全面性。LongContextReorder
是一種用于處理長上下文信息的技術,旨在提高模型在長文本中的信息提取和理解能力。ElasticSearchBM25Retriever
可以與開源搜索引擎ElasticSearch
進行交互,以實現向量搜索。
本文轉載自公眾號一起AI技術 作者:熱情的Dongming
