LangChain是一個基于大語言模型(如ChatGPT)用于構建端到端語言模型應用的 Python 框架。它提供了一套工具、組件和接口,可簡化創(chuàng)建由大型語言模型 (LLM) 和聊天模型提供支持的應用程序的過程。LangChain 可以輕松管理與語言模型的交互,將多個組件鏈接在一起,以便在不同的應用程序中使用。
今天我們來學習DeepLearning.AI的在線課程:LangChain for LLM Application Development的第四門課:Q&A over Documents,也就是對文檔進行問答。根據(jù)Langchain官方文檔的說明,針對文檔的問答包括五個步驟:
- 創(chuàng)建文檔加載器Loder
- 創(chuàng)建索引(index)
- 從該索引創(chuàng)建一個檢索器(retriever)
- 創(chuàng)建問答鏈(chain)
- 對文檔進行提問
創(chuàng)建文檔加載器Loder
在對文檔進行問答之前,我們需要做一些基礎性工作,比如設置openai的api key,以及l(fā)angchain所需要的一些關于文檔文檔的基礎庫,下面我們導入本地環(huán)境配置文件.env, 在.env文件中我們存放了opai的api key?
import os
import pandas as pd
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import CSVLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import DocArrayInMemorySearch
from IPython.display import display, Markdown
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
今天我們需要實驗的對象是一個csv文件,我們要對這個csv文檔的內(nèi)容進行問答,下面我們先導入該csv文件:
df=pd.read_csv("OutdoorClothingCatalog_1000.csv")
df
該文檔主要包含2列,name和description,其中name表示商品的名稱,description表示該商品的說明信息,下面我們查看一下其中的某個商品信息:
print(df[10:11].name.values[0])
print()
print(df[10:11].description.values[0])
?下面我們將該商品的信息翻譯成中文,這樣便于大家理解:
?要實現(xiàn)對文檔內(nèi)容的問答,我們首先需要創(chuàng)建一個文檔的加載器Loader,這里因為是csv文件,所以我們需要創(chuàng)建一個CSVLoader:
file = 'OutdoorClothingCatalog_1000.csv'
loader = CSVLoader(file_path=file)
創(chuàng)建索引(index)
創(chuàng)建了文檔加載器loder以后,我們需要創(chuàng)建一個用于檢索文檔內(nèi)容的索引器,這里我們需要指定指定一個向量數(shù)據(jù)庫,我們使用DocArrayInMemorySearch作為向量數(shù)據(jù)庫,DocArrayInMemorySearch是由Docarray提供的文檔索引,它將會整個文檔以向量的形式存儲在內(nèi)存中,對于小型數(shù)據(jù)集來說使用DocArrayInMemorySearch會非常方便,接下來我們還要指定一個數(shù)據(jù)源loder:
index = VectorstoreIndexCreator(
vectorstore_cls=DocArrayInMemorySearch
).from_loaders([loader])
這里我們需要說明的是當加載文檔后將執(zhí)行三個主要步驟:
- 將文檔分割成塊
- 為每個文檔創(chuàng)建embeddings
- 將文檔和embeddings存儲到向量數(shù)據(jù)庫中
接下來我們可以直接使用索引來進行簡單的對文檔進行問答:
#問題: 請以 Markdown 形式在表格中列出您所有具有防曬功能的襯衫,并總結每一件襯衫。
query ="Please list all your shirts with sun protection \
in a table in markdown and summarize each one."
#通過索引進行查詢
response = index.query(query)
#在jupyter notebook中展示查詢結果
display(Markdown(response))
?這里我們提出的問題是:“請以 Markdown 形式在表格中列出您所有具有防曬功能的襯衫,并對每一件襯衫進行總結。”,從上面llm返回的結果中我們可以看到,llm找到了4件具有防嗮功能的襯衫,并且它對每一件襯衫都進行了總結,最后還加了一個最終的總結,如果我們沒有對格式有特殊要求的話,這樣的回答基本符合我們的要求。
?Embeddings
?前面我們使用的是DocArrayInMemorySearch組件在內(nèi)存中向量化存儲數(shù)據(jù),所以它只適合該小型數(shù)據(jù)集向量化存儲,由于大型語言模型一次只能檢索幾千個單詞,所以當遇到較大規(guī)模的文檔時DocArrayInMemorySearch就不再適用了。對于大型文檔,我們需要使用詞嵌入(word Embedding)技術,所謂word Embedding是一種將文本轉換成數(shù)字的技術,因為計算機只認識數(shù)字,對于文本信息計算機是無法理解的,讓要計算機能理解文本信息,我們將需要將文本信息轉換成一組計算機可以理解的數(shù)字,這組數(shù)字稱為向量,兩個含義相近的詞,他們所在的向量空間中的位置可能會比較接近,而兩個含義不同的詞,他們在向量空間中的距離可能就比較遙遠,請看下面的例子:
?在上面的這個例子中的三句話:
- ?My dog Rover likes to chase squirrels.(我的狗Rover喜歡追松鼠。)
- ?Fluffy, my cat, refuses to eat from a can.(我的貓蓬松拒絕吃罐頭食品。)
- The Chevy Bolt accelerates to 60 mph in 6.7 seconds.(雪佛蘭 Bolt 在 6.7 秒內(nèi)加速至 60 英里/小時。)
中第一句和第二句都是描述動物的,第三句是描述汽車的,所以第一句和第二句經(jīng)過Embedding以后生成的兩組向量,這兩組向量在向量空間中的位置會比較接近,我們稱這種情況為兩個向量具有相似性,也就是說第一句話和第二句話有相似性(因為他們都在描述動物),第三句話與前兩句話不相似。
向量數(shù)據(jù)庫
前面我們解釋了小規(guī)模文檔檢索和Embedding基本原理,那么對于大規(guī)模文檔文檔該如果處理呢?對于大規(guī)模文檔,首先需要將文檔進行切片分割操作,把文檔切分成一個個塊(chunks),然后對每個塊做embedding,最后再把由embedding生成的所有向量存儲在向量數(shù)據(jù)庫中,如下圖所示:
向文檔提出問題
當我們完成了大規(guī)模文檔的向量數(shù)據(jù)庫存儲以后,接下來在用戶提問時,系統(tǒng)會將用戶的問題進行Embedding操作并生成一組向量,接著將該組向量與向量數(shù)據(jù)庫中的所有向量進行比較,找出前n個最相似的向量并將其轉換成對應的文本信息,如下圖所示:
?
這些與用戶問題最相似的文本信息最后會喂給大型語言模型(LLM),并由LLM生成最終的回復信息,如下圖所示:
接下來我們就來對之前的數(shù)據(jù)集做Embedding,然后我們再生成一個問答chain來實現(xiàn)對文檔的問答:
#1.加載文檔,并進行文檔切割
file = 'OutdoorClothingCatalog_1000.csv'
loader = CSVLoader(file_path=file)
docs = loader.load()
#2.創(chuàng)建embeddings
embeddings = OpenAIEmbeddings()
#3.創(chuàng)建向量數(shù)據(jù)庫
db = DocArrayInMemorySearch.from_documents(
docs,
embeddings
)
#4.創(chuàng)建檢索器
retriever = db.as_retriever()
#5.創(chuàng)建RetrievalQA
qa_stuff = RetrievalQA.from_chain_type(
llm=ChatOpenAI(temperature = 0.0),
chain_type="stuff",
retriever=retriever,
verbose=True
)
這里需要說明的是由于我們的數(shù)據(jù)集是一個1000行的csv文件,所以我們使用DocArrayInMemorySearch作為向量數(shù)量數(shù)據(jù)庫,因為它比較擅長處理小規(guī)模的數(shù)據(jù)集,然后我們創(chuàng)建了一個檢索器retriever,最后我們創(chuàng)建了一個RetrievalQA的chain,該chain包含三個主要的參數(shù),其中l(wèi)lm參數(shù)被設置為openai的llm,默認為"gpt-3.5-turbo", retriever參數(shù)設置設置為前面我們由DocArrayInMemorySearch創(chuàng)建的retriever,最后一個重要的參數(shù)為chain_type,該參數(shù)包含了四個可選值:stuff,map_reduce,refine,map_rerank 其中:
stuff:這種最簡單粗暴,會把所有的 document 一次全部傳給 llm 模型進行總結。如果docume很? 多的話,勢必會報超出最大 token 限制的錯。
map_reduce
: 這個方式會先將每個 document 通過llm 進行總結,最后將所有 document 總結出的結果再進行一次總結。
refine:這種方式會先總結第一個 document,然后在將第一個 document 總結出的內(nèi)容和第二個 document 一起發(fā)給 llm 模型再進行總結,以此類推。這種方式的好處就是在總結后一個 document 的時候,會帶著前一個的 document 進行總結,給需要總結的 document 添加了上下文,增加了總結內(nèi)容的連貫性。
map_rerank
: 這種方式會通過llm對每個文檔進行一次總結,然后得到一個分數(shù),最后選擇一個分數(shù)最高的總結作為最終回復。
?
下面我們調(diào)用qa_stuff來實現(xiàn)對文檔的問題,我們的問題還是與之前用index來進行文檔問答的問題一樣:"Please list all your shirts with sun protection in a table in markdown and summarize each one."? 即,“請以 Markdown 形式在表格中列出您所有具有防曬功能的襯衫,并每一件襯衫進行總結。”
query = "Please list all your shirts with sun protection in a table \
in markdown and summarize each one."
response = qa_stuff.run(query)
display(Markdown(response))
?這里我們看到,通過qa_stuff返回的結果與之前用index來提問時返回的結果非常接近,llm找到同樣的四件具有防嗮功能的襯衫,并在最后對每一件襯衫進行了總結,這也符合我們的要求。
Index 與??RetrievalQA
之前我們用index.query()這個方法只用一行代碼也實現(xiàn)了上述的文檔問答的功能,也許有讀者會問,既然index.query()可以只用一行代碼就完成了文檔問答功能又何必要舍近求遠搞一個RetrievalQA這樣的對象來實現(xiàn),并且增加很多繁瑣的步驟(有5個步驟)來實現(xiàn)同樣的效果呢?Langchain框架的作者Harrison Chase在課件視頻中是這么解釋的,通過index來進行文檔問答,只需一行代碼,但是這其中其實隱藏了很多的實現(xiàn)細節(jié),如果我們使用的是RetrievalQA對象來實現(xiàn)文檔問答功能,那么我們就可以了解其中的細節(jié)比如Embeddings,向量數(shù)據(jù)庫等內(nèi)容,反正各有各的好處吧。
參考資料
DocArrayInMemorySearch | ????? Langchain文章來源:http://www.zghlxwxcb.cn/news/detail-563766.html
Retrievers | ????? Langchain文章來源地址http://www.zghlxwxcb.cn/news/detail-563766.html
到了這里,關于LangChain大型語言模型(LLM)應用開發(fā)(四):Q&A over Documents的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!