? ? ? ?隨著大型語(yǔ)言模型(LLM)(如ChatGPT和GPT-4)的興起,現(xiàn)在比以往任何時(shí)候都更容易構(gòu)建比普通熊更智能的智能聊天機(jī)器人,并且可以瀏覽堆積如山的文檔,為您的輸入提供準(zhǔn)確的響應(yīng)。
? ? ? ?在本系列中,我們將探索如何使用pre-trained的LLM創(chuàng)建一個(gè)聊天機(jī)器人,該聊天機(jī)器人可以分析、總結(jié)PDF文檔并回答問(wèn)題,使其成為企業(yè)和個(gè)人都非常有用的工具。無(wú)論您是想構(gòu)建個(gè)人助理、定制聊天機(jī)器人還是自動(dòng)文檔分析系統(tǒng),本系列都將為您提供構(gòu)建自己的LLM聊天機(jī)器人所需的知識(shí)。所以,讓我們用LangChain和LlamaIndex深入LLM和聊天機(jī)器人的世界吧!
創(chuàng)建什么?
? ? ? ?使用ChatGPT作為助手來(lái)幫助用戶基于多個(gè)文檔進(jìn)行問(wèn)答系統(tǒng)搭建的想法是非??帷F鸪?,我們的想法是用特定的數(shù)據(jù)對(duì)模型進(jìn)行微調(diào),以實(shí)現(xiàn)這一目標(biāo),但這可能成本高昂,并且需要龐大的數(shù)據(jù)集。此外,對(duì)模型進(jìn)行微調(diào)只能教會(huì)它一項(xiàng)新技能,而不能提供有關(guān)文檔的完整信息。
? ? ? 另一種方法是使用提示工程在(多)文檔QA的提示中提供上下文。然而,GPT模型的注意力范圍有限,將上下文傳遞給API也可能很昂貴,尤其是在處理大量客戶反饋電子郵件和產(chǎn)品文檔時(shí)。
那么如何創(chuàng)建呢?
以下是實(shí)現(xiàn)這些目標(biāo)的具體步驟:
- 首先加載文檔(PDF、HTML、文本、數(shù)據(jù)庫(kù)等);
- 然后將數(shù)據(jù)分割成塊,并對(duì)這些塊建立embedding索引,這樣方便使用向量檢索工具進(jìn)行語(yǔ)義搜索;
- 對(duì)于每個(gè)問(wèn)題,通過(guò)搜索索引和embedding數(shù)據(jù)來(lái)獲取與問(wèn)題相關(guān)的信息;
- 將問(wèn)題和相關(guān)數(shù)據(jù)輸入到LLM模型中。在這個(gè)系列中使用OpenAI的LLM;
? ? ? ?實(shí)現(xiàn)上述過(guò)程主要的兩個(gè)框架,分別是:Langchain(https://python.langchain.com/en/latest/)和LLamaIndex(https://gpt-index.readthedocs.io/en/latest/)
我們?nèi)绾伍_(kāi)始
下面是使用Langchain和ChatGPT實(shí)現(xiàn)PDF問(wèn)答系統(tǒng)的大致框架:
? ? ? 在本文,不會(huì)詳細(xì)介紹Langchain或LLamaIndex具體原理和實(shí)現(xiàn)細(xì)節(jié),后面會(huì)專門(mén)介紹。本文主要介紹如下內(nèi)容:
- 基于Langchain的生成式問(wèn)答
- LLamaIndex生成式問(wèn)答
- 獎(jiǎng)金部分。
準(zhǔn)備工作
? ? ?首先我們需要在OPenAI官網(wǎng)獲取API秘鑰,具體步驟是:轉(zhuǎn)到https://platform.openai.com,登錄或注冊(cè)新帳戶→ 點(diǎn)擊您的個(gè)人資料→ 查看API密鑰并創(chuàng)建新的密鑰,如下圖所示:
Note:實(shí)際上,我們可以使用其他LLM模型。
? ? ? ?下面準(zhǔn)備安裝相關(guān)的python包,需要保證Python>=3.7來(lái)進(jìn)行操作,然后創(chuàng)建一個(gè)虛擬環(huán)境并安裝以下Python庫(kù):
## to create virtual environment
$ python3 -m venv llm_app_env
?
## on MacOS or Linux
$ source llm_app_env/bin/activate
?
## on Window
$ llm_app_env\Scripts\activate.bat
?
## then install the following libraries.
openai[embeddings]==0.27.6
langchain==0.0.155
pypdf==3.8.1
tiktoken==0.3.3
faiss-cpu==1.7.4
unstructured==0.6.2
chromadb==0.3.21
llama-index==0.6.1
jupyterlab
Langchain介紹
? ? ? ?LangChain是一個(gè)強(qiáng)大的開(kāi)源工具,可以輕松地與大型語(yǔ)言模型交互并構(gòu)建應(yīng)用程序。將其視為一個(gè)中間人,將您的應(yīng)用程序連接到廣泛的LLM提供商,如OpenAI、Cohere、Huggingface、Azure OpenAI等。
? ? ? ?然而,LangChain不僅僅是一個(gè)訪問(wèn)預(yù)訓(xùn)練語(yǔ)言模型的工具,它還提供了許多有用的特性和功能,允許您構(gòu)建自定義應(yīng)用程序和工具。例如:
- 使用自己的文檔進(jìn)行問(wèn)答和文本摘要
- 處理內(nèi)存和具有有限令牌問(wèn)題的長(zhǎng)文檔。
- 與OpenAI ChatGPT Retriever插件的出色集成
- 多個(gè)鏈來(lái)處理您定義的問(wèn)題,或者使用Agent將其提升。
- 還有更多。
? ? ? ?Langchain是一個(gè)偉大的框架,它使人工智能應(yīng)用程序的創(chuàng)建能力現(xiàn)在掌握在您手中。更令人驚訝的是,它是開(kāi)源的,所以你知道它掌握在優(yōu)秀社區(qū)的手中。
下面使用Langchain來(lái)搭建一個(gè)問(wèn)答系統(tǒng):
設(shè)置OpenAI API密鑰
import logging
import sys
import os
?
os.environ["OPENAI_API_KEY"] = "<YOUR_OPENAI_API_KEY>"
加載并拆分?jǐn)?shù)據(jù)
## load the PDF using pypdf
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
?
# load the data
loader = PyPDFLoader('../notebooks/documents/Apple-Financial-Report-Q1-2022.pdf')
?
# the 10k financial report are huge, we will need to split the doc into multiple chunk.
# This text splitter is the recommended one for generic text. It is parameterized by a list of characters.
# It tries to split on them in order until the chunks are small enough.
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
data = loader.load()
texts = text_splitter.split_documents(data)
?
# view the first chunk
texts[0]
簡(jiǎn)單問(wèn)答
? ? ? ?我們將使用OpenAI作為L(zhǎng)LM提供者,因此使用OpenAI?Embedding,但請(qǐng)注意,OpenAI?Embedding?API使用的是“text-davinci-003”模型(定價(jià)參考:https://openai.com/pricing)
? ? ? ?接下來(lái),我們將導(dǎo)入Chroma,Chroma是嵌入數(shù)據(jù)庫(kù),不像傳統(tǒng)的SQL數(shù)據(jù)庫(kù),也不像你通常使用的NoSQL數(shù)據(jù)庫(kù)。它嵌入了數(shù)據(jù)庫(kù),使構(gòu)建LLM應(yīng)用程序變得容易。
通過(guò)Chroma官方網(wǎng)站
? ? ? ?我們的文檔以文本的形式表示,因此很難根據(jù)問(wèn)題找到相關(guān)信息。假設(shè)你需要在1000頁(yè)中找到蘋(píng)果上一季度的收入,并將收入與前幾年進(jìn)行比較。這可能需要多大的挑戰(zhàn)性和耗時(shí)?因此,為了讓我們的搜索更容易,我們首先需要以數(shù)字格式轉(zhuǎn)換或表示單詞或短語(yǔ),這些單詞或短語(yǔ)可以用作機(jī)器學(xué)習(xí)模型的輸入。換句話說(shuō),幫助機(jī)器理解文本。embeddings將每個(gè)單詞或短語(yǔ)映射到實(shí)數(shù)向量,通常具有數(shù)百個(gè)維度,使得相似的單詞或短語(yǔ)被映射到嵌入空間中的相似向量。
? ? ? ?使用embeddings的主要優(yōu)點(diǎn)之一是,它們可以捕捉單詞或短語(yǔ)之間的語(yǔ)義和句法關(guān)系。例如,在嵌入空間中,“國(guó)王”和“王后”的向量比“蘋(píng)果”的向量更接近,因?yàn)樗鼈冊(cè)谡Z(yǔ)義上與王室頭銜相關(guān)。
? ? ? ?因此,嵌入數(shù)據(jù)庫(kù)正是這樣做的。它將把所有embeddings數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中,然后給我們提供非常多的索引,使我們能夠執(zhí)行類似數(shù)據(jù)檢索的操作,并以可擴(kuò)展的風(fēng)格進(jìn)行操作。如果你需要得到之前關(guān)于尋找蘋(píng)果上季度收入的問(wèn)題的答案,我們首先需要在嵌入Chroma等數(shù)據(jù)庫(kù)的基礎(chǔ)上進(jìn)行相似性搜索或語(yǔ)義搜索,以提取相關(guān)信息,并將這些信息提供給LLM模型來(lái)獲得答案。
? ? ? ?聽(tīng)起來(lái)太復(fù)雜了!這就是Langchain拯救我們的地方,所有的艱苦工作都將在后臺(tái)完成。Just do it!
# import Chroma and OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.embeddings.openai import OpenAIEmbeddings
?
# initialize OpenAIEmbedding
embeddings = OpenAIEmbeddings(model='text-embedding-ada-002')
?
# use Chroma to create in-memory embedding database from the doc
docsearch = Chroma.from_documents(texts, embeddings, metadatas=[{"source": str(i)} for i in range(len(texts))])
?
## perform search based on the question
query = "What is the operating income?"
docs = docsearch.similarity_search(query)
? ? ? ?您可以看到,我們能夠執(zhí)行相似性搜索,從嵌入數(shù)據(jù)庫(kù)中獲取相關(guān)信息。
? ? ? ?現(xiàn)在,我們將使用Langchain的主要組件之一Chain將LLM提供程序合并到我們的代碼中。請(qǐng)記住,本文的目的是建立問(wèn)答機(jī)器人。因此,只需按照步驟進(jìn)行操作,如果你很好奇,迫不及待地想了解更多細(xì)節(jié),請(qǐng)隨時(shí)訪問(wèn)Langchain的官方網(wǎng)站。瓦爾哈拉在等著你!?。?!
Langchain提供了四種預(yù)先構(gòu)建的問(wèn)答Chain,具體如下:
- 問(wèn)答:load_qa_chain
- 有來(lái)源問(wèn)答:load_qa_with_sources_chain
- 檢索問(wèn)題答案:RetrievalQA
- 資源檢索問(wèn)答:RetrievalQAWithSourcesChain
? ? ? 它們非常相似,RetrievalQA和RetrievalQAWithSourcesChain分別使用load_qa_chain和load_qa_with_sources_chain,唯一的區(qū)別是前兩者將把所有嵌入都饋送到LLM中,而后兩者只向LLM提供相關(guān)信息。我們可以使用前兩個(gè)來(lái)首先提取相關(guān)信息,并僅將該信息提供給LLM。此外,前兩個(gè)比后兩個(gè)給了我們更多的靈活性。
下面的代碼將演示我們是如何做到這一點(diǎn)的。
## importing necessary framework
from langchain.chains.question_answering import load_qa_chain
from langchain.chains.qa_with_sources import load_qa_with_sources_chain
from langchain.chains import RetrievalQA
from langchain.chains import RetrievalQAWithSourcesChain
?
from langchain.chat_models import ChatOpenAI
現(xiàn)在我們將嘗試4種不同的問(wèn)答鏈
1.load_qa_chain
## use LLM to get answering
chain = load_qa_chain(ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'),
chain_type="stuff")
query = "What is the operating income?"
chain.run(input_documents=docs, question=query)
2.load_qa_with_sources_chain
chain = load_qa_with_sources_chain(ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'),
chain_type="stuff")
query = "What is the operating income?"
chain({"input_documents": docs, "question": query}, return_only_outputs=True)
3.RetrievalQA
qa=RetrievalQA.from_chain_type(llm=ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'), chain_type="stuff",
retriever=docsearch.as_retriever())
query = "What is the operating income?"
qa.run(query)
4.RetrievalQAWithSourcesChain
chain=RetrievalQAWithSourcesChain.from_chain_type(ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'), chain_type="stuff",
retriever=docsearch.as_retriever())
chain({"question": "What is the operating income?"}, return_only_outputs=True)
? ? ? ?上面的大部分代碼都是非?;镜?。我們只想在深入研究框架能提供什么之前完成這項(xiàng)工作。在此之前,讓我們轉(zhuǎn)到另一個(gè)可以與Langchain結(jié)合使用的框架,它將為您提供更多的能力來(lái)創(chuàng)建更好的LLM應(yīng)用程序。
LLamaIndex介紹
? ? ? ?我首先介紹了Langchain,如果你花一些時(shí)間瀏覽它的官方文件,你可能會(huì)想“哇,沒(méi)有什么好的東西可以超越這一點(diǎn)”。
? ? ? ?好吧,我的朋友們,有一個(gè)完整的SaaS行業(yè)建立在AWS之上,只是為了讓您更好、更容易地使用AWS服務(wù)。其他LLM框架或LLM模型之間的競(jìng)爭(zhēng)也是如此。我們生活在一個(gè)今天好的東西明天可能會(huì)過(guò)時(shí)的世界里。我個(gè)人認(rèn)為L(zhǎng)angchain將有一個(gè)非常光明的未來(lái),并將成為用于構(gòu)建LLM應(yīng)用程序的核心技術(shù)。LLamIndex甚至讓我們的工作變得更容易,它還通過(guò)處理一些痛苦的眾所周知的問(wèn)題和現(xiàn)有方法的局限性引入了自己的優(yōu)勢(shì),這些問(wèn)題和局限性將花費(fèi)您的時(shí)間和手動(dòng)操作,例如:
- 文本塊缺少全局上下文。通常,這個(gè)問(wèn)題需要特定區(qū)塊中索引之外的上下文。
- 仔細(xì)調(diào)整前k/相似性得分閾值。如果值太小,就會(huì)錯(cuò)過(guò)上下文。讓價(jià)值變得太大,成本/延遲可能會(huì)隨著不相關(guān)的上下文而增加。
- Embeddings并不總是為問(wèn)題選擇最相關(guān)的上下文。Embeddings本質(zhì)上是在文本和上下文之間分別確定的。
? ? ? ?LLamaIndex(GPT索引)有自己的機(jī)制來(lái)處理這些限制。同樣,這個(gè)博客的目的是完成這項(xiàng)工作。我不會(huì)詳細(xì)介紹LLamaIndex是如何工作的(可以在官方文件上找到)。
那么LLM是什么
? ? ? ?一張由Jerry Liu抄寫(xiě)的羊皮紙,他在羊皮紙上公布了LlamaIndex,這是一個(gè)利用GPT的力量,利用詢問(wèn)者提供的知識(shí),形成對(duì)詢問(wèn)的回復(fù)的門(mén)戶網(wǎng)站。
? ? ? ?簡(jiǎn)而言之,LlamaIndex是通過(guò)以下步驟將LLM連接到用戶來(lái)響應(yīng)查詢的另一種方式(類似于Langchain的方式):
-
加載文檔(手動(dòng)或通過(guò)數(shù)據(jù)加載程序)
-
將文檔解析為節(jié)點(diǎn)
-
構(gòu)造索引(從節(jié)點(diǎn)或文檔)
-
[可選,高級(jí)]在其他指數(shù)之上構(gòu)建指數(shù)
-
查詢索引
? ? ? ? 簡(jiǎn)單來(lái)說(shuō),LlamaIndex將數(shù)據(jù)加載到文檔對(duì)象中,并將其轉(zhuǎn)換為索引。當(dāng)您輸入查詢時(shí),索引會(huì)將其發(fā)送到GPT提示符以生成響應(yīng),默認(rèn)情況下使用OpenAI的text-davinci-003模型。盡管這個(gè)過(guò)程看起來(lái)很復(fù)雜,但只要幾行代碼就可以執(zhí)行,您很快就會(huì)了解到這一點(diǎn)。
? ? ? ?您很快就會(huì)看到LLamaIndex是多么容易使用,因?yàn)樗呀?jīng)完成了所有的艱苦工作。你的工作只是閱讀它的官方文件,學(xué)習(xí)不同類型的索引,然后分析你的應(yīng)用程序需求,看看什么最適合你。當(dāng)然,你的應(yīng)用程序中可能需要越來(lái)越多復(fù)雜的東西,LLamaIndex的高級(jí)API可能不足以處理此類情況。這就是LLamaIndex可以與Langchain等其他工具集成以加快開(kāi)發(fā)過(guò)程的便利之處。
讓我們從設(shè)置簡(jiǎn)單索引和加載文檔開(kāi)始。
import logging
import sys
?
## setup your OpenAI Key
import os
os.environ["OPENAI_API_KEY"] = "<YOUR_OPENAI_API_KEY>"
# enable logs to see what happen underneath
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
LlamaIndex的核心是指數(shù),有多種類型的指數(shù)。
- 列表索引
- 矢量存儲(chǔ)索引
- 樹(shù)索引
- 關(guān)鍵字表索引
- 圖形索引
- SQL索引。
? ? ? ?每個(gè)索引都有其獨(dú)特的用途,具有不同的用途。好處是,您可以將索引堆疊在其他索引之上,這樣做將使您的應(yīng)用程序更強(qiáng)大,能夠理解您的文檔上下文和應(yīng)用程序需求。
第一步是加載文檔
from llama_index import GPTVectorStoreIndex
from llama_index import download_loader
?
# we will use this UnstructuredReader to read PDF file
UnstructuredReader = download_loader('UnstructuredReader', refresh_cache=True)
loader = UnstructuredReader()
# load the data
data = loader.load_data(f'../notebooks/documents/_10-Q-Q1-2022-(As-Filed).pdf', split_documents=False)
Document表示數(shù)據(jù)源的輕量級(jí)容器??梢赃x擇下面兩步驟之一:
-
將Document對(duì)象直接輸入索引
-
首先,將文檔轉(zhuǎn)換為Node對(duì)象
? ? ? ?同樣,本系列的目的是幫助您盡快構(gòu)建第一個(gè)應(yīng)用程序,因此我將直接討論索引構(gòu)建。我將在未來(lái)的一篇文章中介紹LLamaIndex的所有方面。
索引構(gòu)建與查詢
? ? ? ?我們現(xiàn)在可以在這些Document對(duì)象上建立一個(gè)索引。最簡(jiǎn)單的高級(jí)抽象是在索引初始化期間加載Document對(duì)象。
index = GPTVectorStoreIndex.from_documents(data)
query_engine = index.as_query_engine()
response = query_engine.query("What is the operating income?")
print(response)
? ? ? 根據(jù)您使用的索引,LlamaIndex可能會(huì)進(jìn)行LLM調(diào)用以構(gòu)建索引。GPTVvectorStoreIndex不會(huì)調(diào)用LLM,但GPTTreeStoreIndex會(huì)調(diào)用。
自定義LLM
? ? ? 默認(rèn)情況下,LlamaIndex使用OpenAI的text-davinci-003模型。在構(gòu)造索引時(shí),您可以選擇使用另一個(gè)LLM。
from llama_index import LLMPredictor, PromptHelper, ServiceContext
from langchain.chat_models import ChatOpenAI
?
# define LLM
llm_predictor = LLMPredictor(llm=ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'))
# define prompt helper
# set maximum input size
max_input_size = 4096
# set number of output tokens
num_output = 256
# set maximum chunk overlap
max_chunk_overlap = 20
prompt_helper = PromptHelper(max_input_size, num_output, max_chunk_overlap)
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor, prompt_helper=prompt_helper)
index = GPTVectorStoreIndex.from_documents(
documents,
service_context=service_context
)
query_engine = index.as_query_engine()
response = query_engine.query("What is the operating income?")
print(response)
? ? ? ?在短短幾行代碼中,我們就能夠構(gòu)建一個(gè)LLM應(yīng)用程序,可以進(jìn)行基本的問(wèn)答。
? ? ? ?對(duì)于具有機(jī)器學(xué)習(xí)工程師或數(shù)據(jù)科學(xué)背景的人來(lái)說(shuō),這是相當(dāng)簡(jiǎn)單明了的,但我相信對(duì)于一些新手來(lái)說(shuō),有時(shí)會(huì)覺(jué)得很困惑。我理解這一點(diǎn),但很難在一篇帖子中解釋所有內(nèi)容。這篇文章的目的只是讓你體驗(yàn)一下現(xiàn)在構(gòu)建這樣一個(gè)令人驚嘆的LLM應(yīng)用程序是多么容易。你現(xiàn)在可能有很多問(wèn)題,甚至可能幾行代碼都不懂,但這沒(méi)關(guān)系。
? ? ? ?您將很快收集組件的所有知識(shí)和方面,以構(gòu)建自己的LLM應(yīng)用程序。你可以等到我的下一篇文章,因?yàn)槲覍⒃谙乱黄薪榻BLlamaIndex,或者如果你足夠好奇,請(qǐng)通過(guò)閱讀官方文件來(lái)做好準(zhǔn)備。
? ? ? ?在此之前,我希望這篇文章能夠幫助您擴(kuò)展編碼知識(shí),并為L(zhǎng)LM提供有價(jià)值的見(jiàn)解。記得保持好奇心,繼續(xù)探索人工智能的廣闊世界。
? ? ? ?祝賀你走到這一步!作為對(duì)您努力的獎(jiǎng)勵(lì),這里有一段代碼,您可以使用它與文檔聊天
# do imports
from langchain.agents import Tool
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent
?
from llama_index.langchain_helpers.agents import LlamaToolkit, create_llama_chat_agent, IndexToolConfig
query_engine = index.as_query_engine()
tool_config = IndexToolConfig(
query_engine=query_engine,
name=f"Financial Report",
description=f"useful for when you want to answer queries about the Apple financial report",
tool_kwargs={"return_direct": True}
)
toolkit = LlamaToolkit(
index_configs=[tool_config]
)
memory = ConversationBufferMemory(memory_key="chat_history")
llm=ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo')
agent_chain = create_llama_chat_agent(
toolkit,
llm,
memory=memory,
verbose=True
)
while True:
text_input = input("User: ")
response = agent_chain.run(input=text_input)
print(f'Agent: {response}')
YouTube視頻結(jié)果展示地址:https://youtu.be/FuKFjNNbSVM
參考文獻(xiàn):
[1] https://langchain.readthedocs.io/en/latest/index.html(LangChain docs)
[2] https://langchain.readthedocs.io/en/latest/modules/memory.html#memory(LangChain Prompt Memory module)
[3] https://github.com/hwchase17/langchain(LangChain Repo)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-677069.html
[4] https://gpt-index.readthedocs.io/en/latest/index.html(LlamaIndex docs)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-677069.html
到了這里,關(guān)于LLM本地知識(shí)庫(kù)問(wèn)答系統(tǒng)(一):使用LangChain和LlamaIndex從零構(gòu)建PDF聊天機(jī)器人指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!